我试图在下面的代码中理解,传递给装饰函数的参数似乎是如何传递给包装函数中的参数的:
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)
的参数?允许这种情况发生的过程或方法是什么?
答案 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)
有两点需要注意:
函数defenition中定义的变量在此函数local scope中定义,它是任何内部函数的封闭范围,因此这些内部函数中提供了此变量。函数名称(代码中的func
)只是引用函数对象的变量。
*args
语法表示&#34;将所有不匹配的位置参数收集为元组,并将此元组命名为args
&#34;,**kwargs
表示&#34;将所有不匹配的关键字参数收集为字典,并将此字典命名为kwargs
&#34;。 args
和kwargs
只是一个约定(在类中为self
),您可以在此处使用任何名称,但您不应该这样做。在函数调用中,相同的语法*
和**
相反 - 它分别在单个值和键=值对中打破元组和字典。
现在你的代码:
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