|Programmer's Python - Decorators|
|Written by Mike James|
|Monday, 28 May 2018|
Page 2 of 2
You can use more than one decorator and they are applied starting with the decorator closest to the function definition – which might not be what you expect because it goes against the usual top-to-bottom order.
is equivalent to:
This is a better order because it corresponds to the idea that the inner decorator gets to modify the function first.
The final variation is that what follows the @ in a decorator can be a function that is called to provide the function that is the decorator.
That is, we can use a decorator factory.
Other accounts of how this all works tend to refer to this as a "decorator with a parameter", but this isn’t really accurate as the function isn’t the decorator it really is a decorator factory.
The key idea to notice is that is you write @dec then dec is a decorator but if you write @dec() then the dec function is called and it is expected to return
is equivalent to:
that is. decA is called and it returns a function, mydecorator, which is applied to myfunction.
The main reason for using this function factory approach is to provide parameters to customize the decorator.
For example, if you want to add a string to the docstring of a function you might use something like:
This simply returns a decorator which adds name to the docstring.
prints bad programmer.
Notice that this is a simpler form of decorator because it simply modifies an attribute of the function and doesn’t therefore actually wrap the function with another function.
As another example the following decorator will add an attribute and its value to any function:
To use it simply add as many @attrib decorators are you need:
Now myfunc has an attribute called myAttribute set to 1. Notice that this isn’t particularly efficient as it creates a wrapper function for each attribute it adds.
Finally, it is worth pointing out that while decorators are powerful and very attractive features it is easy to create something fragile.
Consider the idea of adding a self reference to a function, see Chapter 4. This would allow the function to be written with a self parameter but then used without it.
After this the function can be called as
and the self parameter would be automatically set. This is fairly easy using the decorator’s wrapper to form a closure and then calling the original function:
you can see the idea; self keeps track of the original function object which is called with self as the first parameter. In fact, you don’t actually need to use self as f is also in the closure, but using self looks better.
If you try this out you will find it works, but if you make a small change to the use of the decorators by reversing their order
you will find it doesn’t work.
The reason is that the order in which the decorators are called is from closest to the def. This means that selfRef keeps track of the original function object, but returns its wrapper function. The attrib decorator then adds the attribute to the wrapper function. When the function is called the function object that self references doesn’t have the attribute.
One solution is to add:
which makes the wrapper’s attribute Dictionary use the original function’s attribute Dictionary. Now it doesn’t matter what order attributes are added because both function object share the same attribute Dictionary.
The problem is that when you do things like adding attributes to functions with decorators you have to ask yourself which function object are the attributes being added to, and with more decorators there are more function objects to be confused about.
Decorators can also be applied to more than just functions and we return to them in Chapter 8.
|Last Updated ( Monday, 28 May 2018 )|