0

I try to implement a decorator class and it works fine if I call it with parentheses, but if I try to use it without parentheses it does not work.

How can I make it work without calling it directly if no arguments are passed?

class DecByClass:

    def __init__(self, retry=None, *args, **kwargs):
        self._retry = retry

    def __call__(self, func, *args, **kwargs):
        def wrapper(*args, **kwargs):
            retval = func(*args, **kwargs)
            return retval
        return wrapper

This works:

@DecByClass()
def try_brackets(t):
    return '2'

This doesn't:

@DecByClass
def try_brackets(t):
    return '2'

What have I missed?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
dmitriy_one
  • 477
  • 1
  • 5
  • 16
  • 2
    The first one becomes `try_brackets = DecByClass()(try_brackets)`, the second `try_brackets = DecByClass(try_brackets)`. If you want to handle both use cases, `__call__` and `__init__` must _both_ be able to receive the wrapped function. – jonrsharpe Oct 15 '21 at 18:30
  • I would recommend just not supporting the use of `@DecByClass` alone. If the decorator *can* take arguments, require `@DecByClass()` even if no arguments are passed. – chepner Oct 15 '21 at 18:33
  • 1
    It's a ways down, but someone [did provide a class-based answer](https://stackoverflow.com/a/64611147/364696) to the duplicate. – ShadowRanger Oct 15 '21 at 18:34
  • @chepner: On the one hand, I agree with you. On the other hand, Python itself has been making a lot of decorators used mostly with defaults allow use without calling them, e.g. [`dataclasses.dataclass`](https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass) (always supported it), and [`functools.lru_cache`](https://docs.python.org/3/whatsnew/3.8.html#functools) (added support for no parens in 3.8, introduced without it in 3.2). The precedents set by the language disagree with both of us. – ShadowRanger Oct 15 '21 at 18:38
  • @jonrsharpe could you please provide code example? – dmitriy_one Oct 15 '21 at 18:43
  • No, adapting the existing answers for your specific situation is down to you. – jonrsharpe Oct 15 '21 at 18:44
  • @ShadowRanger There are quite a few precedents and design choices in Python that I disagree with :) This one is particularly egregious though, IMO, as it flies in the face of "There should be one-- and preferably only one --obvious way to do it." – chepner Oct 15 '21 at 18:46

0 Answers0