【问题标题】:Understanding the role of closure in decorators and function wrapper arguments了解闭包在装饰器和函数包装器参数中的作用
【发布时间】:2016-04-20 18:05:23
【问题描述】:

我试图在下面的代码中理解,传递给装饰函数的参数是如何传递给包装函数中的参数的:

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

def p_decorate(func):
   def func_wrapper(*args, **kwargs):
       return "<p>{0}</p>".format(func(*args, **kwargs))
   return func_wrapper

my_get_text = p_decorate(get_text)

print my_get_text("John")

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

通过尝试谷歌,我收集到内部函数func_wrapper() 可以通过闭包访问封闭范围内的变量或对象(我想我无论如何都正确理解了这一点)。

我不明白的是,传递给get_text(name) 的参数name 的值是如何由内部函数@ 中的*args**kwargs 访问(或赋予或分配给)的987654327@.

我认为我理解整个 get_text(name) 函数及其参数 name 是正确的987654332@ 允许访问传递给get_text(name) 的参数?允许这种情况发生的过程或方法是什么?

【问题讨论】:

  • 你知道你没有使用装饰器语法,对吧?
  • @thefourtheye 类方法并不是唯一可以修饰的函数。
  • @Lav 这是真的,但无关紧要。关键是缺少@
  • @jonrsharpe 缺少@ 与什么有关?它只是一些 Python 语法糖,与问题没有直接关系。请仔细阅读 Python 文档。
  • @Lav 不要成为一个混蛋或其他任何东西,但请查看我的个人资料(以及 thefourtheye 的个人资料)。我们知道我们在说什么。然后再次仔细阅读第一条评论;他们只是说 OP 没有使用装饰器语法,即@.

标签: python function closures decorator python-decorators


【解决方案1】:

当您执行my_get_text = p_decorate(get_text) 时,您将my_get_text 设置为调用p_decorate(get_text) 的结果。调用p_decorate 的结果是你的func_wrapper。因此,当您调用my_get_text 时,您实际上是在调用func_wrapper

然后看看func_wrapper 做了什么。它接受任何参数(*args*kwargs)并将它们传递给func。由于在您调用 p_decorate 时将 func 设置为 get_text,因此调用 get_text 时使用的参数与调用 my_get_text 时使用的参数相同。

你是对的,有一个闭包,但闭包与调用my_get_text("John") 中的参数如何传递没有任何关系。闭包的作用是确保func(即get_text)的值“保存”在func_wrapper中,这样返回的包装器“知道”它在包装哪个函数。但是一旦创建了包装函数,调用它时发生的实际参数传递只是普通的参数传递。你调用一个带参数的函数,然后那个函数调用另一个带相同参数的函数。与此没有什么不同:

def foo(x, y):
    return x+y

def bar(x, y):
    return foo(x, y)

如果您现在调用bar,它会调用foofoo 使用相同的参数调用,因为 bar 使用相同的参数调用它 bar 被调用。同样,在您的示例中,get_text 获取参数,因为 func_wrapper 调用 get_text 使用相同的参数调用 func_wrapper

【讨论】:

    【解决方案2】:

    当你打电话时......

    my_get_text = p_decorate(get_text)
    

    ...函数p_decorate()func = get_text 一起执行。它定义了一个新函数func_wrapper(),因此可以访问func,因为它是在函数定义时设置的。

    所以,p_decorate() 的返回值是一个新生成的函数,签名为func_wrapper(*args, **kwargs),它也可以方便地访问func 变量。请注意,再次调用p_decorate() 将创建一个不同的 func_wrapper() 函数和不同的func 变量。

    现在,当你调用这个新创建的函数时:

    my_get_text("John")
    

    您实际上是在调用以下内容:

    def func_wrapper(*args, **kwargs):
        func = get_text
        # ...
    

    由于您使用单个位置参数调用它,因此它等效于 args = ("John",), kwargs = {}

    【讨论】:

      【解决方案3】:

      需要注意的两点:

      1. 在函数定义中定义的变量在这个函数局部范围内定义,它是任何内部函数的封闭范围,因此这个变量在这些内部函数中可用。而函数名(代码中的func)只是引用函数对象的变量。

      2. *args 函数定义中的语法表示“将所有不匹配的位置参数作为一个元组收集,并将此元组命名为 args”,**kwargs 表示“将所有不匹配的关键字参数作为字典收集并给出字典名称kwargs"。 argskwargs 只是一个约定(就像类中的 self),你可以在这个地方使用任何名称,尽管你不应该这样做。在函数调用中,*** 的语法正好相反——它分别在单个值和 key=value 对中破坏元组和字典。

      现在你的代码:

      def get_text(name):
         #name is here in local scope and it's just a positional argument
         #if you've used get_text(*args, **kwargs) you could refer to 'name' as 'args[0]'
         return "lorem ipsum, {0} dolor sit amet".format(name)
      
      def p_decorate(func):
         #'func' is saved here in local scope
         #here 'func' will reference 'get_text' from your example
         def func_wrapper(*args, **kwargs):
             #'args' and 'kwargs' are here in local scope and
             #they represent all arguments passed to decorated function
             #regarding your example here will be 'arg[0]="John"'
             return "<p>{0}</p>".format(func(*args, **kwargs))
             #in the line above in function you basicly break 'args' and 'kwargs'
             #into pieces and pass them to func as separate arguments
             #regarding your example you basicly call 'func("John")
             #where "John" is in 'arg[0]' and considering 'func' reference
             #in your example it's basicly 'get_text(name)'
         return func_wrapper
         #line above returns function object which will be assigned
         #to some name as result of decorator call, i.e. 'my_get_text'
      
      my_get_text = p_decorate(get_text)
      #effectively it says "set 'my_get_text' to reference 'func_wrapper'
      #with argument 'func'='get_text'"
      
      print my_get_text("John")
      #effectively is says "call function referenced by 'my_get_text'
      #which is 'func_wrapper' with argument 'John'  is equal
      #to call 'func_wrapper("John")'"
      
      # <p>Outputs lorem ipsum, John dolor sit amet</p>
      

      使用*args**kwargs 使装饰器更加通用。在您的示例中,如果您知道只使用了一个参数,则可以编写如下内容:

      def get_text(name):
         return "lorem ipsum, {0} dolor sit amet".format(name)
      
      def p_decorate(func):
         def func_wrapper(single_argument):
             return "<p>{0}</p>".format(func(single_argument))
         return func_wrapper
      
      my_get_text = p_decorate(get_text)
      
      print my_get_text("John")
      
      # <p>Outputs lorem ipsum, John dolor sit amet</p>
      

      希望这会让你更清楚地理解。

      关于你的问题在 cmets 中提到的装饰器语法,使用 @ 只是语法糖,所以:

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

      等同于:

      def p_devorate(func):
          ...
      
      def get_text(name):
         return "lorem ipsum, {0} dolor sit amet".format(name)
      
      get_text = p_decorate(get_text) 
      #redefining the name of original function to pointed to wrappe function instead 
      

      【讨论】:

        猜你喜欢
        • 2018-10-03
        • 1970-01-01
        • 2011-06-25
        • 2023-04-03
        • 2015-09-03
        • 2016-01-31
        • 1970-01-01
        • 2018-03-07
        • 2014-05-01
        相关资源
        最近更新 更多