[英]Trying to understand a Python wrapper
對於下面的 function,我試圖理解
一世。 為什么wrapper.count = 0
在包裝器 function 下方初始化? 為什么不在 def counter(func) 下面初始化? 為什么 wrapper.count 不將wrapper.count
重置為 0,因為它運行在包裝器 function 之下?
我試圖了解什么是wrapper.count
? 為什么不初始化一個普通變量count
而不是wrapper.count
?
def counter(func):
def wrapper(*args, **kwargs):
wrapper.count += 1
# Call the function being decorated and return the result
return func
wrapper.count = 0
# Return the new decorated function
return wrapper
# Decorate foo() with the counter() decorator
@counter
def foo():
print('calling foo()')
裝飾器出現錯誤,在包裝器 function 內,您需要:
return func(*args, **kwargs) # instead of `return func`
為什么
wrapper.count = 0
在包裝器 function 下方初始化?
因為如果您在 wrapper function 內部執行此操作,那么它將始終將wrapper.count
的值重置為0
。 除非您檢查它是否尚未定義。 (我在答案的最后給出了一個例子。)
為什么不在
def counter(func)
下面初始化?
因為包裝器 function 沒有在那里定義。 所以口譯員會抱怨它。
為什么
wrapper.count
不將wrapper.count
重置為0
,因為它是在包裝器 function 之下執行的?
因為當你用@counter
裝飾器包裝一個 function 時,這個語句只執行一次,而且它不會在你每次調用foo()
function 時執行。
我試圖了解什么是
wrapper.count
?
這是一個 function 屬性。 或多或少類似於 C++ 等函數內部的static
變量。
為什么不初始化一個普通變量
count
而不是wrapper.count
?
因為那將是一個局部變量,它會在每次調用時將count
重置為 0。
還有另一種方法可以在包裝器 function 中定義wrapper.count = 0
。 所以現在你不需要在wrapper
function 之外定義它。
def counter(func):
def wrapper(*args, **kwargs):
if not hasattr(wrapper, 'count'):
wrapper.count = 0
wrapper.count += 1
return func(*args, **kwargs)
return wrapper
在高層次上,裝飾的 function 維護一個計數器,記錄它被調用的次數。
代碼存在一個主要問題。 包裝器實際上並未按應有的方式調用包裝的 function。 而不是return func
,它只返回 function object,它應該是
return func(*args, **kwargs)
正如@warvariuc 指出的那樣,一個可能的原因是作者沒有或不知道nonlocal
,它可以讓您訪問封閉的命名空間。
我認為一個更合理的原因是您希望能夠訪問櫃台。 函數是具有可變字典的一流對象。 您可以分配和訪問它們的任意屬性。 幾次調用后檢查foo.count
可能會很方便,否則為什么要首先維護它?
wrapper.counter
以這種方式初始化的原因僅僅是在def
語句運行以創建它之前,本地命名空間中不存在wrapper
。 每次運行counter
時,內部def
都會生成一個新的 function object 。 def
通常是每次運行時創建 function object 的分配。
關於您顯示的代碼的另一個小問題是foo.__name__
在裝飾之后將是wrapper
而不是foo
。 為了緩解這種情況,並使其更忠實地模仿原始 function,您可以使用functools.wraps
,它是裝飾器包裝器的裝飾器。 您的代碼如下所示:
from functools import wraps
def counter(func):
@wraps(func)
def wrapper(*args, **kwargs):
wrapper.count += 1
# Call the function being decorated and return the result
return func(*args, **kwargs)
wrapper.count = 0
# Return the new decorated function
return wrapper
# Decorate foo() with the counter() decorator
@counter
def foo():
print('calling foo()')
現在你可以做
>>> foo.__name__
'foo'
>>> foo()
calling foo()
>>> foo()
calling foo()
>>> foo()
calling foo()
>>> foo.count
3
為什么不初始化一個正常的變量計數而不是
variable.count
我的猜測是這種模式首先出現在nonlocal
2 中,其中 nonlocal 語句不可用。 在我看來,該片段的作者只是試圖模擬 static 變量,例如 C 語言( https://stackoverflow.com/296/7 )
因為如果您嘗試使用在 function counter
的頂層聲明的普通變量,您將無法在wrapper
中分配給它。
如果您將count
放在counter
下方,您將使其成為全局變量,因此它將在裝飾器的所有實例之間共享,這可能不是所需的行為:
count = 0
def counter(func):
def wrapper(*args, **kwargs):
global count
count += 1
return func(*args, **kwargs)
return wrapper
@counter
def foo():
print('calling foo()')
這是一個nonlocal
版本(Python 3+):
def counter(func):
def wrapper(*args, **kwargs):
nonlocal count
count += 1
# Call the function being decorated and return the result
return func(*args, **kwargs)
count = 0
# Return the new decorated function
return wrapper
# Decorate foo() with the counter() decorator
@counter
def foo():
print('calling foo()')
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.