def reducer(*, name = None, metric = call_default):
    """Decorator to measure a function that consumes many items.

    The wrapped ``func`` should take either a single ``iterable`` argument or
    ``*args`` (plus keyword arguments).

    :arg function metric: f(name, count, total_time)
    :arg str name: name for the metric
    """
    class instrument_reducer_decorator(object):
        def __init__(self, func):
            self.orig_func = func
            self.wrapping = wraps(func)
            self.metric_name = name if name is not None else func.__module__ + '.' +func.__name__
            self.varargs = inspect.getargspec(func).varargs is not None
            if self.varargs:
                self.method = _varargs_to_iterable_method(func)
                self.func = _varargs_to_iterable_func(func)
                self.callme = _iterable_to_varargs_func(self._call)
            else:
                self.method = func
                self.func = func
                self.callme = self._call

        # we need _call/callme b/c CPython short-circurits CALL_FUNCTION to
        # directly access __call__, bypassing our varargs decorator
        def __call__(self, *args, **kwargs):
            return self.callme(*args, **kwargs)

        def _call(self, iterable, **kwargs):
            it = counted_iterable(iterable)
            t = time.time()
            try:
                return self.func(it, **kwargs)
            finally:
                metric(self.metric_name, it.count, time.time() - t)

        def __get__(self, instance, class_):
            metric_name = name if name is not None else\
                ".".join((class_.__module__, class_.__name__, self.orig_func.__name__))

            def wrapped_method(iterable, **kwargs):
                it = counted_iterable(iterable)
                t = time.time()
                try:
                    return self.method(instance, it, **kwargs)
                finally:
                    metric(metric_name, it.count, time.time() - t)

            # wrap in func version b/c self is handled for us by descriptor (ie, `instance`)
            if self.varargs: wrapped_method = _iterable_to_varargs_func(wrapped_method)
            wrapped_method = self.wrapping(wrapped_method)
            return wrapped_method

    return instrument_reducer_decorator
Decorator to measure a function that consumes many items.

    The wrapped ``func`` should take either a single ``iterable`` argument or
    ``*args`` (plus keyword arguments).

    :arg function metric: f(name, count, total_time)
    :arg str name: name for the metric