簡體   English   中英

了解閉包在裝飾器和函數包裝器參數中的作用

[英]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 = {}

需要注意的兩件事:

  1. 在函數定義中定義的變量是在此函數的局部作用域中定義的,該作用域是任何內部函數的封閉范圍,因此,這些變量在這些內部函數中可用。 函數名稱(代碼中的func )只是變量引用函數對象。

  2. 函數定義中的*args語法表示“將所有不匹配的位置參數作為元組收集,並給該元組命名為args ”, **kwargs說“將所有不匹配的關鍵字參數作為字典進行收集,並將此字典中的名稱命名為kwargs ”。 argskwargs只是一個約定(就像類中的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM