装饰装饰器:尝试让我的头脑理解它
我试图了解如何装饰装饰器,并想尝试以下操作:
假设我有两个装饰器并将它们应用于函数 hello()
:
def wrap(f):
def wrapper():
return " ".join(f())
return wrapper
def upper(f):
def uppercase(*args, **kargs):
a,b = f(*args, **kargs)
return a.upper(), b.upper()
return uppercase
@wrap
@upper
def hello():
return "hello","world"
print(hello())
然后我必须开始添加其他装饰器对于其他函数,但通常 @wrap
装饰器将“包装所有这些”
def lower(f):
def lowercase(*args, **kargs):
a,b = f(*args, **kargs)
return a.lower(), b.lower()
return lowercase
@wrap
@lower
def byebye():
return "bye", "bye"
我如何编写一个装饰器,它装饰我的 @lower
和 @upper装饰器?参见下文:
@wrap
def lower():
...
@wrap
def upper():
...
只需执行以下操作即可获得与上述相同的结果:
@upper
def hello():
...
@lower
def byebye():
...
I'm trying to understand how to decorate decorators, and wanted to try out the following:
Let's say I have two decorators and apply them to the function hello()
:
def wrap(f):
def wrapper():
return " ".join(f())
return wrapper
def upper(f):
def uppercase(*args, **kargs):
a,b = f(*args, **kargs)
return a.upper(), b.upper()
return uppercase
@wrap
@upper
def hello():
return "hello","world"
print(hello())
Then I have to start adding other decorators for other functions, but in general the @wrap
decorator will "wrap all of them"
def lower(f):
def lowercase(*args, **kargs):
a,b = f(*args, **kargs)
return a.lower(), b.lower()
return lowercase
@wrap
@lower
def byebye():
return "bye", "bye"
How do I write a decorator, which decorates my @lower
and @upper
decorators? See below:
@wrap
def lower():
...
@wrap
def upper():
...
To achieve the same result as above by only doing:
@upper
def hello():
...
@lower
def byebye():
...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
Python 中的装饰器
相当于
您想要获得的效果
,
因此应该在
upper
的返回值上调用wrap
:这也相当于在该函数上应用装饰器:
A decorator in Python
is just equivalent to
You want to get the effect of
i.e.
so the
wrap
should be called on the return value ofupper
:which is also equivalent to applying the decorator on that function:
这是一个用装饰器装饰装饰器的通用(有点复杂)的解决方案(耶!)。
Here's a generic (and slightly convoluted) solution for decorating decorators with decorators (Yay!).
Boaz Yaniv 接受的答案可以稍微简化一下,如下所示:
输出:
现在,wrap 装饰器具有如此多嵌套的原因是因为一旦您装饰了另一个装饰器的定义,整个执行过程变得与垂直嵌套装饰有点不同。这就是我要说的:
这就是 Python 对上述代码所做的事情。它以 upper 函数作为参数调用 wrap 装饰器。在 wrap 装饰器的 wrapper 内部,它发现装饰器正在被调用。因此它调用了 upper 函数。由于 upper 本身是函数的装饰器,因此在调用期间 upper 接收 hello 函数作为引用,其参数由包装器 接收>大写。
在 uppercase 内部调用了 hello 函数,因此随后调用 hello 函数,处理其结果,并将其返回给 hello 函数wrap 装饰器中的 >wrapper 函数最终返回到主模块的全局作用域,其中 hello() 在底部被调用。
然而,装饰装饰器的定义是一个不同的故事。采取以下代码:
这里将发生的事情是,一旦您调用 hello(),wrap 装饰器就会像前面的情况一样被调用,带有 upper > 函数作为参数传递给wrap。但是,如果您尝试在 wrapper 中调用 decorator 作为
decorator()
Python 会抛出错误,指出 upper 函数是没有争论就打电话!要修复 wrap 装饰器中的包装器(此处称为 wrapperA)需要接收参数。这个参数是对 upper 已修饰的函数的引用,在我们的例子中是 hello 函数。因此,wrapperA 必须使用 hello 函数 (
fn
) 作为参数来调用 装饰器。但是执行
decorator(fn)
将为我们提供对upper装饰器的uppercase包装器的引用。为了执行它并传递 fn 需要的任何参数,我们需要另一个名为 wrapperB 的包装器,嵌套在 wrapperA 中。在这里,wrapper A 实际上充当 fn 的装饰器,其 wrapperB 将采用 fn 的参数。因此,用于获取对中间装饰器(下部和上部)结果进行任何处理的结果的复合最终调用应该如下所示:
这就是为什么必须嵌套的原因。
垂直装饰或 kennytm 此处 建议的替代方案显然对眼睛要好得多。
The accepted answer from Boaz Yaniv can be simplified a little bit like this:
Output:
Now, the reason the wrap decorator has so much nesting is because once you decorate another decorator's definition, the whole execution process becomes a bit different than the vertical nested decoration. Here's what I am talking about:
Here's what Python does with the above code. It calls the wrap decorator with upper function as argument. Inside wrapper of wrap decorator it finds that the decorator is being invoked. So it invokes it, the upper function. Because upper itself is a decorator of a function, during its invocation upper receives hello function as a reference and its arguments are received by wrapper uppercase.
Inside uppercase a call to hello function is made, so hello function is then invoked, its result is processed, and is returned back to wrapper function in wrap decorator which is finally returned back to main module's global scope where hello() was invoked at the bottom.
However, decorating a decorator's definition is a different story. Take the following code:
What's gonna happen here is once you invoke hello() the wrap decorator would be invoked like in the earlier situation, with upper function passed as an argument to wrap. However, if you try to invoke decorator inside wrapper as
decorator()
Python would throw error that upper function was called without an argument!To fix that your wrapper (called wrapperA here) in wrap decorator needs to receive an argument. This argument is the reference to the function that upper has decorated, which is hello function in our case. So the wrapperA has to call the decorator with hello function (
fn
) as argument.But executing
decorator(fn)
would give us the reference to upper decorator's uppercase wrapper. In order to execute it and to pass any argument that fn would need we need another wrapper called wrapperB nested in wrapperA. Here, wrapper A would literally act as a decorator of fn and its wrapperB would take fn's arguments.So the compounded final call to get the result for any processing on intermediate decorators' (lower and upper) result should look like this:
And that's why so must nesting is needed.
Vertical decorating or the alternative suggested by kennytm here is obviously much better on the eyes.