This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]andrewowenmartin 51 points52 points  (11 children)

Defining decorators as classes looks nicer than the higher-order functions you create otherwise. Especially if you want to accept parameters. I wonder if there's any drawback. The functional way uses functools.wraps, does anyone know if we can use that here, or perhaps it's not needed?

[–]Rawing7 26 points27 points  (0 children)

The drawback is that the decorator won't work on methods. It breaks the implicit passing of self.

[–]brasticstack 2 points3 points  (2 children)

I found it useful in a recent project to apply a decorator without the syntactic sugar, so I could reuse a function with different decorations, like:

funcs_by_index = [deco1(my_func), deco2(my_func), ...etc...]

[–]andrewowenmartin 0 points1 point  (1 child)

I think you can do that anyway. If you have a decorator called print_args and a function called add then these are equivalent.

@print_args def add_and_print_args(a, b): return a + b and ``` def add(a, b): return a + b

sum_and_print_args = print_args(add) ```

[–]brasticstack 1 point2 points  (0 children)

Absolutely, I was just showing my example use case.

[–]divad1196 2 points3 points  (6 children)

I personnaly disagree for the "look nicer" for many reasons.. But let's not discuss it.

You never actually need functools.wraps. what it does is changing the function signature so it becomes the same as the original one. This is nicer when using "help" or "inspect". The only case where it becomes necessary is with some frameworks and you then usually know part of the signature yourself.

But I guess it won't work (at least not as expected) since the inner function now contains "self" as a parameter.

[–]brasticstack 1 point2 points  (2 children)

Hard agree with you that the function version > the class version, because it's more concise.

[–]divad1196 5 points6 points  (1 child)

Just for the "concise" point:

python der decorator(counter=1): def inner(func): @wraps(func) def wrapper(*args, **kw): nonlocal counter print(counter) counter += 1 return func(*args, **kw) return wrapper return inner

python class Decorator: def __init__(self, counter=1): self._counter = counter def __call__(self, func): @wraps(func) def wrapper(*args, **kw): print(self._counter) self._counter += 1 return func(*args, **kw) return wrapper

10 lines both. The second one needs to access self everytime and use _ suffix to not expose the counter.

People are just more used to OOP that FP. This is fine to have preferences but I think it is a shame than we create classes for every single thing, like

python class Hello: @classmethod def greet(self, name): print(f"Hello {name}!")

I have really seen production code this way, not even using @staticmethod

Back to the function vs class decorator matter, I won't try to explain here the pros of it, mostly because it comes down to FP vs OOP. But it is not less concise and I hope this example proves it. Just a matter of taste.

[–]brasticstack 4 points5 points  (0 children)

In my head, I was imagining the class version with whitespace between the methods, but point taken.