理解装饰器和函数包装器参数中闭包的作用

时间:2016-01-15 07:21:49

标签: python function closures decorator python-decorators

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

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()可以通过闭包访问封闭范围内的变量或对象(我想我无论如何都能正确理解这一点)。

我不理解的是,传递给name的参数get_text(name)的值是如何被*args或{{访问(或赋予或分配)的**kwargs 1}}在内部函数func_wrapper()

我认为我理解整个get_text(name)函数及其参数name传递给p_decorate()是正确的,因此在p_decorate()范围内可用 - 但传递给func_wrapper()的参数如何允许访问传递给get_text(name)的参数?允许这种情况发生的过程或方法是什么?

3 个答案:

答案 0 :(得分:3)

执行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。由于func在您调用get_text时设置为p_decorate,因此调用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

如果您现在致电foo,则会拨打foo。使用相同的参数调用bar,因为bar使用相同的参数get_text调用它。同样,在您的示例func_wrapper中获取参数是因为get_text调用func_wrapper并使用相同的参数调用routes.ex

答案 1 :(得分:1)

当你打电话......

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 = {}

答案 2 :(得分:1)

有两点需要注意:

  1. 函数defenition中定义的变量在此函数local scope中定义,它是任何内部函数的封闭范围,因此这些内部函数中提供了此变量。函数名称(代码中的func)只是引用函数对象的变量。

  2. 函数defenition中的
  3. *args语法表示&#34;将所有不匹配的位置参数收集为元组,并将此元组命名为args&#34;,**kwargs表示&#34;将所有不匹配的关键字参数收集为字典,并将此字典命名为kwargs&#34;。 argskwargs只是一个约定(在类中为self),您可以在此处使用任何名称,但您不应该这样做。在函数调用中,相同的语法***相反 - 它分别在单个值和键=值对中打破元组和字典。

  4. 现在你的代码:

    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>
    

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

    并且考虑到你的问题的评论中提到的装饰器语法,我们@只是语法糖所以:

    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