1

This is meant to calculate execution time and log / print it:

def timer(logger=None):
    def decorator(func):
        def wrapper(*args, **kwargs):
            start_time = perf_counter()
            result = func(*args, **kwargs)
            total_time = perf_counter() - start_time
            message = f'{func.__name__} Time: ' f'{total_time} seconds'
            if logger is not None:
                logger.info(message)
            else:
                print(message)
            return result

        return wrapper

    return decorator


@timer
def decorated():
    print("Running ...")


if __name__ == '__main__':
    decorated()

I'm expecting this to print time, instead it complains about a missing argument:

TypeError: decorator() missing 1 required positional argument: 'func'

However, when I do:

@timer(None)
def decorated():
    print("Running ...")

or:

@timer(logger=None)
def decorated():
    print("Running ...")

It works! what is this nonsense?

Running ...
decorated Time: 2.3044999999999316e-05 seconds

Notes:

  • To those marking my question as a duplicate, can you explain why other decorators work just fine?
  • In the example below, lru_cache accepts maxsize and typed parameters and works without explicit calls as well.

Example:

from functools import lru_cache


@lru_cache
def decorated():
    print("Running ...")

Out:

Running ...
watch-this
  • 1
  • 4
  • 20
  • 5
    Does this answer your question? [python decorator TypeError missing 1 required positional argument](https://stackoverflow.com/questions/51891951/python-decorator-typeerror-missing-1-required-positional-argument) – Eric Leung Jan 08 '21 at 09:55
  • 1
    Bare ```@timer``` returns ```decorator``` function. On the other hand, ```@timer()``` will execute ```timer``` first then return ```decorator``` which in turn used to decorate ```decorated```. – Henry Tjhia Jan 08 '21 at 09:58
  • @EricLeung It doesn't explain what is happening, and take for example `functools.lru_cache`, it works as well as many other decorators without explicit calls `()` – watch-this Jan 08 '21 at 10:00
  • @bullseye it explains the reason if you read carefully. `lru_cache` is probably not a parametrizable decorator (however, I did not check), but your `timer` decorator is parametrizable, this is the key point. – Tryph Jan 08 '21 at 10:04
  • @Tryph It is parametrizable, it takes `maxsize` and `typed` parameters – watch-this Jan 08 '21 at 10:05
  • from [the source of `lru_cache`](https://github.com/python/cpython/blob/master/Lib/functools.py#L508), we can see that the implementation specifically handles the case when the function is passed directly as the `maxsize` argument (this is the case when you do not provide any parameter to the decorator). But this is not a standard feature, if you want the same behaviour, you will have to implement it by yourself. – Tryph Jan 08 '21 at 10:17
  • `lru_cache` is polymorphic, it can be invoked with or without parameters. The `@foo\ndef bar` syntax is just syntactical sugar for `bar = foo(bar)`. And it takes this `foo` part exactly as is. There's obviously a difference between `bar = foo(bar)` and `bar = foo()(bar)`. – deceze Jan 08 '21 at 10:18
  • 1
    @Tryph https://stackoverflow.com/q/653368/476 – deceze Jan 08 '21 at 10:35

0 Answers0