In python there are two ways to declare decorators:
Class based
class mydecorator(object):
def __init__(self, f):
self.f = f
def __call__(self, *k, **kw):
# before f actions
self.f(*k, **kw)
# after f actions
Function based
def mydecorator(f):
def decorator(*k, **kw):
# before f actions
f(*k, **kw)
# after f actions
return decorator
Is there any difference between these declarations? In which cases each of them should be used?
So, here in this post, we are going to learn about Decorator Chaining. Chaining decorators means applying more than one decorator inside a function. Python allows us to implement more than one decorator to a function.
A decorator in Python is any callable Python object that is used to modify a function or a class. A reference to a function "func" or a class "C" is passed to a decorator and the decorator returns a modified function or class.
Decorators are a prime-time example of a perfectly implemented feature. It does take a while to wrap your head around, but it's worth it. As you start using them, you'll notice how they don't overcomplicate things and make your code neat and snazzy.
A decorator can be used to simply and cleanly implement retry functionality for asynchronous functions.
If you want to keep state in the decorator you should use a class.
For example, this does not work
def mydecorator(f): x = 0 def decorator(): x += 1 # x is a nonlocal name and cant be modified return f(x) return decorator
There are many workarounds for this but the simplest way is to use a class
class mydecorator(object): def __init__(self, f): self.f = f self.x = 0 def __call__(self, *k, **kw): self.x += 1 return f(self.x)
When you're creating a callable returning another callable, the function approach is easier and cheaper. There are two main differences:
__get__
method.Additionally, the function approach allows you to return the original function, after modifying it or storing it.
However, a decorator can return something other than a callable or something more than a callable. With a class, you can:
classmethod
, property
)If you have any doubt, ask yourself: Do you want your decorator to return a function that acts exactly like a function should? Use a function returning a function. Do you want your decorator to return a custom object that does something more or something different to what a function does? Create a class and use it as a decorator.
In fact there are no 'two ways'. There is only one way (define a callable object) or as many ways as there are in python to make a callable object (it could be a method of other object, a result of lambda expression, a 'partial' object, anything that is callable).
Function definition is the easiest way to make a callable object and, as the simplest one, is probably the best in most cases. Using a class gives you more possibilities to cleanly code more complicated cases (even in the simplest cases it looks quite elegant), but it is not that obvious what it does.
No, there are (more than) two ways to make callable objects. One is to def
a function, which is obviously callable. Another is to define a __call__
method in a class, which will make instances of it callable. And classes themselves are callable objects.
A decorator is nothing more than a callable object, which is intended to accept a function as its sole argument and return something callable. The following syntax:
@decorate
def some_function(...):
...
Is just a slightly nicer way of writing:
def some_function(...):
...
some_function = decorate(some_function)
The class-based example you give isn't a function that takes a function and return a function, which is the bog-standard vanilla decorator, it's a class that is initialised with a function whose instances are callable. To me, this is a little weird if you're not actually using it as a class (Does it have other methods? Does its state change? Do you make several instances of it that have common behaviour encapsulated by the class?). But normal use of your decorated function will not tell the difference (unless it's a particularly invasive decorator), so do whatever feels more natural to you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With