this is my implementation:
```
units = {"auto": -1, "s": 1, "ms": 1e3, "μs": 1e6, "ns": 1e9}
def timef(unit="auto", number=1, garcol=True, get_time=False):
    """A decorator to measure the execution time of a function

    Returns the return of the tested function if get_time is False or the
    execution time in unit if not

    unit: one of "ns" (nanoseconds), "μs" (microseconds), "ms" (milliseconds),
    "s" (seconds) and "auto": (automatic) (default: "auto")
    garcol: if True enables garbage collection (default: True)
    get_time: if true return the execution time of the function (default: False)
    """
    fun = False
    if callable(unit):
        fun = True
        func = unit
        unit = "auto"
    elif unit not in units:
        raise ValueError(
            "valid options: "
            "s(seconds), "
            "ms(milliseconds), "
            "μs(microseconds), "
            "ns(nanoseconds) "
            "and auto(automatic: default)")

    def decorating_function(func):
        return _wrapper(func, number, units, unit, units[unit], garcol, get_time)
    return decorating_function(func) if fun else decorating_function


def _wrapper(func, number, units, unit, factor, garcol, get_time):
    def timed(*args, **kwargs):
        nonlocal unit, factor
        gcold = gc.isenabled()
        # disable garbage collection on demand
        if not garcol:
            gc.disable()
        try:
            if number > 0:
                start = default_timer()
                for _ in range(number):
                    func(*args, **kwargs)
                time = default_timer() - start
                result = func(*args, **kwargs)
        finally:
            if gcold:
                gc.enable()
        if unit == "auto":
            for u, f in units.items():
                if 1 <= int(time * f) < 1000:
                    unit, factor = u, f
                    break
            else:
                unit, factor = "s", 1
        time *= factor
        print(f"{func.__qualname__}: {time:.4f} {unit}")
        return time if get_time else result
    return timed
```
it's not perfect but it works; you can control number of repetitions, garbage collection, unit ...
and it produces a formatted output such as:
`fun_to_time: 24.1056 ms`.

De : Python-ideas <python-ideas-bounces+amjadhedhili=outlook.com@python.org> de la part de Steven D'Aprano <steve@pearwood.info>
Envoyé : dimanche 7 octobre 2018 04:15
À : python-ideas@python.org
Objet : Re: [Python-ideas] add a time decorator to timeit.py
 
On Sun, Oct 07, 2018 at 10:16:08AM +0000, Amjad Ben Hedhili wrote:

> I think that a time decorator will be a useful addition to the sandard
> library, as i find the current way of measuring execution time a bit
> tedious:
>
> timeit.timeit("fun_to_time(a, b)", setup="from __main__ import a, b", number=1)

There are lots of ways to call timeit, but generally if you are calling
it with number=1 then your results may not be trustworthy. (That depends
on the function, of course.) There's a reason why timeit defaults to
number=1000000 rather than 1.


> compared to:
>
> @timef
> def fun_to_time(a, b):
>     ...

The problem with that as an API is that once you have decorated the
function to call timeit, how do you use it *without* calling timeit?

If you use functools.wraps, there will be a func_to_time.__wrapped__ but
I don't think that calling that directly is a good interface.


> or
>
> timef(print)("Hello world!").
>
> I already made a basic implementation of it, and it's working well.

Can you explain what the timef decorator does? What arguments does it
take, how do we use it?


--
Steve
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/