[英]Understanding the role of closure in decorators and function wrapper arguments
我試圖在以下代碼中理解,傳遞給修飾函數的參數似乎傳遞給包裝函數中的參數的方式是:
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>
從嘗試使用Google func_wrapper()
,我已經了解到內部函數func_wrapper()
可以通過閉包訪問封閉范圍內的變量或對象(我想我還是正確地理解了這一點)。
我不明白的是,傳遞給get_text(name)
的參數name
的值究竟是如何被內部函數func_wrapper()
中的*args
或**kwargs
訪問(或賦予或分配給func_wrapper()
。
我認為我的理解是正確的,因為整個get_text(name)
函數及其參數name
都傳遞給p_decorate()
,因此在p_decorate()
范圍內p_decorate()
-但是如何允許將傳遞給func_wrapper()
的參數傳遞給訪問傳遞給get_text(name)
的參數? 所涉及的過程或方法是什么?
當你做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
被調用。
你打電話時...
my_get_text = p_decorate(get_text)
...函數p_decorate()
使用func = get_text
執行。 它定義了一個新函數func_wrapper()
,因此可以訪問在函數定義時設置的func
。
因此, p_decorate()
的返回值是一個帶有func_wrapper(*args, **kwargs)
簽名的新創建的函數,該func
還可以方便地訪問func
變量。 請注意,再次調用p_decorate()
將使用不同的func
變量創建一個不同的 func_wrapper()
函數。
現在,當您調用此新創建的函數時:
my_get_text("John")
您實質上是在調用等效於:
def func_wrapper(*args, **kwargs):
func = get_text
# ...
現在,您僅用一個位置參數調用它,它等效於args = ("John",), kwargs = {}
。
需要注意的兩件事:
在函數定義中定義的變量是在此函數的局部作用域中定義的,該作用域是任何內部函數的封閉范圍,因此,這些變量在這些內部函數中可用。 函數名稱(代碼中的func
)只是變量引用函數對象。
函數定義中的*args
語法表示“將所有不匹配的位置參數作為元組收集,並給該元組命名為args
”, **kwargs
說“將所有不匹配的關鍵字參數作為字典進行收集,並將此字典中的名稱命名為kwargs
”。 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>
希望這將使您更容易理解。
在您的問題的注釋中提到的aboutin裝飾器語法,usin @
只是語法糖,因此:
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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.