【问题标题】:Format a decorator in Python在 Python 中格式化装饰器
【发布时间】:2016-11-09 05:21:15
【问题描述】:

RANT - 为什么人们说“装饰器是一个返回另一个函数的函数”?这个定义是没有意义的,因为如果这就是装饰器的全部,你为什么不只使用第二个函数而不用第一个函数呢? - 结束咆哮

幸运的是,我在这里找到了一个更明智的定义: http://thecodeship.com/patterns/guide-to-python-function-decorators/

很遗憾,我还是看不懂这些例子,比如这个:

def get_text(name):
   return "lorem ipsum, {0} dolor sit amet".format(name)

def p_decorate(func):
   def func_wrapper(name):
       return "<p>{0}</p>".format(func(name))
   return func_wrapper

my_get_text = p_decorate(get_text)

print my_get_text("John")

#Outputs "<p>lorem ipsum, John dolor sit amet</p>"

请注意,该帖子已有两年多的历史,作者尚未回复任何 cmets。

这是我的问题:为什么不输出

"<p>John</p>"

还是错误?整个字符串如何进入 p 标签而不只是名称?

【问题讨论】:

  • 您混淆了函数和函数返回的内容。装饰器可以根据给定的参数(另一个函数)返回不同的函数。它不调用它返回的函数,但通常它返回的函数,当被调用时,会调用最初给装饰器的函数。
  • 显然你没有阅读那篇文章的其余部分......它详细解释了到底发生了什么,目标是什么,以及如何使用 python 提供的工具来实现该目标。 ..
  • @JeffMercado:也许这就是为什么他提到那篇文章有一个“更合理的定义”......他没有告诉我们哪篇文章对他没有意义。
  • 听起来你的困惑更深层次的根源是闭包。你知道什么是闭包以及它们是如何工作的吗?如果没有,请阅读这些内容。
  • 可能是因为你对你不明白的事情大发雷霆。这不是一个好主意。

标签: python string-formatting python-decorators


【解决方案1】:

编辑:我刚刚意识到您链接了一篇文章,该文章从字面上逐步解释了装饰器是什么......但这已经输入了所以......

Python 中有一个叫做闭包/函数闭包的特性。

自从我试图解释/理解闭包/装饰器在较低级别发生的事情以来已经有一段时间了,所以这实际上可能是错误的。

函数 func_wrapper 在每次调用 p_decorate 时都会被重新定义,并且 func_wrapper 会记住定义时 封闭 命名空间的样子。

这是什么意思:

例如,假设您有一些外部变量嵌套在 p_decorate 中,当调用 func_wrapper 并尝试使用它时,您会认为这会引发错误,因为它不再存在(它对 func_wrapper 来说是非本地的)变量,但它不会因为封闭的命名空间被“记住”

在 python 3 中它是__closure__,在 python 2 中它是 func_closure 用于下面。

def get_text(name):
    return "lorem ipsum, {0} dolor sit amet".format(name)

def p_decorate(func):
    def func_wrapper(name):
        return "<p>{0}</p>".format(func(name)) #Cached result
    print(func_wrapper.func_closure)
    return func_wrapper

my_get_text1 = p_decorate(get_text)
my_get_text2 = p_decorate(get_text2)

func_closure 的东西:

my_get_text1 >>> (<cell at 0x*: function object at 0x*>,)
my_get_text2 >>> (<cell at 0x(different address): function object at (diff address)>,)

我们没有将不同的函数传递给func_wrapper 函数,而是构建func_wrapper 函数的自定义版本,以“记住”它应该使用func 调用什么函数。

如果您为my_get_text1my_get_text2 传入相同的函数两次,那么func_wrapper.func_closure 位、内存地址和function object at * 的所有内容将完全相同,因为它是相同的函数。 (减去当您实际使用可能不同的参数调用它时。)

装饰器

正如您所提到的,装饰器只是一个可调用的,它接受一个函数作为参数并返回一个替换函数。在您的代码中,这将分别是 p_decoratorfunc_wrapper

如果您想以 OOP 方式来考虑这个...您可以将 p_decorate 视为函数 func_wrapper 的构造函数,其中 func 充当私有成员变量/属性,因为您或多或少基本上已构建带有硬编码参数func 的函数。

为什么返回它的作用:

希望以上都是正确的 + 有意义...然后,它返回您看到的值,因为您已经传递了“记住”的 get_text 函数,并且由于返回了 func_wrapper... 您正在调用func_wrapper(arg) 带有 John 的参数现在在这个内部...func_wrapper 返回以下内容:

"<p>{0}</p>".format(func(name))"

funcget_text。所以,get_text 被调用,返回...

"lorem ipsum, {0} dolor sit amet".format(name)

然后在开始和结束p标签之间格式化,然后返回。

您可以拥有多层嵌套函数,并且与 python 中的几乎任何东西一样,它一直是海龟。

为什么还要使用装饰器?

在这个例子中,我认为你是对的,我只使用第二个函数,因为这是一个非常无用的例子。

但是,考虑一些更具体的事情。我将使用一个常见的 django 装饰器作为示例。即使您不熟悉 Django,这仍然是有意义的。

在 django 中,您具有呈现网页的功能/视图,有时您可能希望要求用户登录才能查看页面。

现在,如果您必须为每一个函数编写自定义逻辑,该怎么办?。

那太可怕了。取而代之的是一个漂亮的装饰器,名为 @login_required,您只需将其放在您想要此功能的函数的顶部,并且您只需节省大量时间/重复代码,因为此装饰器将此自定义逻辑包装在您应用的函数周围它到。

装饰器主要在您想要扩展/自定义功能而无需重新编码所有内容时很有用。

比函数更进一步......如果您想将其应用于class 的方法怎么办。也许所有这些或一个符合某些标准?对每种方法都必须这样做会有点乏味并且可能很烦人。您可以编写一个装饰器来包装该类并自动为您将其应用于每个函数,而不是使用 @decorator_name 包装每个方法。

【讨论】:

  • 我需要看看我能不能把这个用我自己的英文写一下,看看我是否理解你:返回参数,(名称),包裹在 p 标签中,但以原始函数,get_text,因此完整的结果变为“lorem ipsum,John dolor sit amet” 因此变量 {0} 既没有被替换也没有被覆盖,而是“格式化”以符合原始函数。那有意义吗?这是一个正确的简单英语解释吗?
  • 我想我听懂了你的意思,如果是这样,那么是的。 get_text 中的{0} 不受func_wrapper 中的{0} 影响。当func_wrapper 调用func(name) 时,它会返回字符串"lorem ipsum, John dolor sit amet",该字符串是通过将name 格式化为get_text 中的{0} 而创建的,然后该字符串被格式化为func_wrapper 中的{0}。所以,&lt;p&gt;(string returned by get_text)&lt;/p&gt; 返回了 func_wrapper
猜你喜欢
  • 2012-01-20
  • 1970-01-01
  • 2020-03-16
  • 2012-03-16
  • 1970-01-01
  • 2011-03-08
  • 1970-01-01
  • 2018-03-10
  • 1970-01-01
相关资源
最近更新 更多