【问题标题】:Python: Do we really need wrappers for decorators? (Revisited)Python:我们真的需要装饰器的包装器吗? (重访)
【发布时间】:2019-11-02 14:37:09
【问题描述】:

已询问similar question,但 OP 将包装器与没有返回的函数进行比较,因此答案集中在该“错误”上。
在阅读this article 之后,我了解了装饰器以及为什么我们使用它们而不是子类。在那里,他们编写了以下示例,其中介绍了包装器的需求:

def uppercase_decorator(function):
    def wrapper():
        funct = function()
        make_uppercase = funct.upper()
        return make_uppercase

    return wrapper

然而,我可以这样写“相同的东西”(我希望你说它不是):

def uppercase_decorator(function): #previously 'function' was called 'message' but 'function' is clearer for comparison. 
    make_uppercase = function().upper
    return make_uppercase

这两个版本都可以应用于这个 fn,在调用 salute() 时产生相同的输出('HI ALL!'):

@uppercase_decorator
def salute():
    return 'Hi all!'

如果 main fn 返回一个随机字符串(感谢@wim 的建议),我们可以注意到每次运行它时,没有包装器的总是在运行sayGarbage() 行时返回相同的内容:

def decorateIt(fn):
    toUpper = fn().upper  
    return toUpper  

def decorateW(fn):  
    def wrapper():  
        funct = fn()  
        toUpper = funct.upper()  
        return toUpper  
    return wrapper  

import random, string  

@decorateIt  
def sayGarbage():  
    return "".join(random.choice(string.ascii_lowercase) for i in range(6))  

sayGarbage()

为什么会这样?

【问题讨论】:

  • 您的uppercase_decorator 版本中的message 在做什么? func() 哪里来的?
  • 完全误解了装饰器的工作原理。装饰器接收待装饰函数作为参数!
  • 不同的是第二个版本不行
  • 抱歉,我错过了将 'funct' 更改为 'message'。现在它应该可以工作了
  • 装饰后,敬礼现在将是方法 'Hi all!'.upper.

标签: python wrapper python-decorators


【解决方案1】:

让我们看看这里发生了什么:

def uppercase_decorator(message): 
    make_uppercase = func().upper
    return make_uppercase

@uppercase_decorator
def salute():
    return 'Hi all!'
  • 首先,uppercase_decorator 被定义。
  • 其次,salute 被定义。
  • 第三,uppercase_decorator调用,传入函数对象 salute 代替第一个位置参数 (message)。

此时,代码将崩溃,因为 func() 被调用,但未在任何地方定义。如果它在您的 Python REPL 会话中工作,那么您一定在之前的全局范围内有名称 func,因此代码似乎只是巧合。

我们真的需要装饰器的包装器吗?

没有。可以像您建议的那样以更好的“无封闭”风格编写装饰器,但不是那样。如果您想编写一个没有嵌套函数的装饰器,请尝试使用流行的库decorator.py。看起来像这样:

from decorator import decorator

@decorator
def uppercase_decorator(func, *args, **kwargs):
    result = func(*args, **kwargs)
    return result.upper()

【讨论】:

  • 不,我想在您上面的评论之后我已经看到了区别,说现在将成为方法 'Hi all!'.upper,但包装器版本会和我看到的一样。为什么将 function() 分配给中间变量 (funct) 会有什么不同?
  • 考虑装修时会发生什么。在您的第一个版本中,该函数被使用,但实际上并未被调用。在第二个版本中,您的函数实际上被调用了。也许,您应该返回一个随机生成的字符串,而不是 return 'Hi all!',它每次都会有所不同,而不仅仅是一个常数值 - 这将更清楚地显示两个版本之间的行为差​​异。
  • 我已经让它返回一个随机字符串并创建了两个装饰器(有和没有包装器)。两个版本每次都返回不同的大写字符串。未能注意到任何差异......(!?):我正在更新问题中的代码,因为我无法在此处正确格式化......
  • 你的例子很好——使用你的decorateIt,每次都会返回 same 随机字符串。使用decorateW 每次都会返回一个不同的随机字符串。我不确定您如何/为什么会看到任何不同的东西,可能是这里的一些 PEBKAC。
  • 我只是在运行整个代码,而不仅仅是 fn 调用,这就是为什么......现在我明白你在说什么了。我还看到这个问题与中间变量没有任何关系,实际上并不需要它,而只是没有包装器。拥有与否是使代码正常工作或返回'str' object is not callable 异常的原因。所以感谢您的提示,我仍然需要看到冰山,但虽然速度很慢,但我越来越接近看到它了......跨度>
猜你喜欢
  • 2018-01-02
  • 2010-09-21
  • 2019-06-14
  • 2019-01-18
  • 1970-01-01
  • 2018-04-17
  • 2011-01-02
  • 2021-05-20
相关资源
最近更新 更多