预计阅读本页时间:-
Decorator Nesting
Sometimes one decorator isn’t enough. To support multiple steps of augmentation, decorator syntax allows you to add multiple layers of wrapper logic to a decorated function or method. When this feature is used, each decorator must appear on a line of its own. Decorator syntax of this form:
@A
@B
@C
def f(...):
...
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
runs the same as the following:
def f(...):
...
f = A(B(C(f)))
Here, the original function is passed through three different decorators, and the resulting callable object is assigned back to the original name. Each decorator processes the result of the prior, which may be the original function or an inserted wrapper.
If all the decorators insert wrappers, the net effect is that when the original function name is called, three different layers of wrapping object logic will be invoked, to augment the original function in three different ways. The last decorator listed is the first applied, and the most deeply nested (insert joke about “interior decorators” here...).
Just as for functions, multiple class decorators result in multiple nested function calls, and possibly multiple levels of wrapper logic around instance creation calls. For example, the following code:
@spam
@eggs
class C:
...
X = C()
is equivalent to the following:
class C:
...
C = spam(eggs(C))
X = C()
Again, each decorator is free to return either the original class or an inserted wrapper object. With wrappers, when an instance of the original C class is finally requested, the call is redirected to the wrapping layer objects provided by both the spam and eggs decorators, which may have arbitrarily different roles.
For example, the following do-nothing decorators simply return the decorated function:
def d1(F): return F
def d2(F): return F
def d3(F): return F
@d1
@d2
@d3
def func(): # func = d1(d2(d3(func)))
print('spam')
func() # Prints "spam"
The same syntax works on classes, as do these same do-nothing decorators.
When decorators insert wrapper function objects, though, they may augment the original function when called—the following concatenates to its result in the decorator layers, as it runs the layers from inner to outer:
def d1(F): return lambda: 'X' + F()
def d2(F): return lambda: 'Y' + F()
def d3(F): return lambda: 'Z' + F()
@d1
@d2
@d3
def func(): # func = d1(d2(d3(func)))
return 'spam'
print(func()) # Prints "XYZspam"
We use lambda functions to implement wrapper layers here (each retains the wrapped function in an enclosing scope); in practice, wrappers can take the form of functions, callable classes, and more. When designed well, decorator nesting allows us to combine augmentation steps in a wide variety of ways.