[英]Magic attributes of functions inconsistent
目前,我正在一個項目中,我有一個類,其中包含各種我想緩存的昂貴方法。 我想自己實現緩存,以用於練習,它的特殊之處在於它專門針對f(f(x)) == x
為True
f(f(x)) == x
(通過dict子類,其中d[key] == value and d[value] == key
為True
)。 有時這會深入到python,此刻我有點迷路。
緩存應該附加到定義該方法的類上,因此我需要從將緩存添加到函數的裝飾器中的函數中提取該類。 問題是,當用@dec
裝飾f
時,似乎python確實確實做了f = dec(f)
其他事情。
我的測試代碼和緩存裝飾器的開頭是:
def bidirectional_cache(function):
"""Function decorator for caching
For functions where f(f(x)) == x is True
Requires hashable args and doesn't support kwargs
"""
parent_instance = getattr(function, "__self__", None)
#print(type(function))
#print(dir(function))
if parent_instance is None:
parent_class = globals()[function.__qualname__.rstrip(f".{function.__name__}")]
elif type(parent_instance) is type:
parent_class = parent_instance
else:
parent_class = parent_instance.__class__
print(parent_class)
...
class A():
N = 0
def __init__(self, n):
self.n = n
def __hash__(self):
return hash(self.n)
def __add__(self, other):
return self.__class__(int(self) + int(other))
def __int__(self):
return self.n
@bidirectional_cache
def test(self):
return f"n = {self.n}"
@bidirectional_cache
@staticmethod
def test_static(a, b):
return a + b
@bidirectional_cache
@classmethod
def test_class(cls, b):
return N + b
在不使用緩存裝飾器A
情況下定義A
並執行以下調用(REPL會話)時,將按預期提供輸出:
>>> bidirectional_cache(A.test)
<class '__main__.A'>
>>> bidirectional_cache(A.test_static)
<class '__main__.A'>
>>> bidirectional_cache(A.test_class)
<class '__main__.A'>
>>> a = A(5)
>>> bidirectional_cache(a.test)
<class '__main__.A'>
>>> bidirectional_cache(a.test_static)
<class '__main__.A'>
>>> bidirectional_cache(a.test_class)
<class '__main__.A'>
但是,如果我改為使用裝飾器運行類定義,則裝飾器內部始終會有staticmethod
對象,並且該對象會中斷,因為這些對象沒有__qualname__
。 在Ax
上調用dir
,其中x
是所有測試方法,與在裝飾器中調用dir
時產生完全不同的輸出。
我的問題是,為什么@dec
接收的函數對象與dec(f)
接收的對象不同? 有什么方法可以檢索在裝飾器范圍內定義的函數的類,還是我總是必須手動執行Ax = dec(x)
?
運行裝飾器代碼時,您嘗試訪問的__self__
屬性不存在。
當運行類主體時-即在創建類本身之前,更不用說該類的實例時,以該方法作為參數調用裝飾器主體。
在方法裝飾器中獲取實例( self
)的最簡單方法是,將其作為裝飾器用來替換原始方法的包裝函數中的參數接受:
def bidirectional_cache(function):
"""Function decorator for caching
For functions where f(f(x)) == x is True
Requires hashable args and doesn't support kwargs
"""
def wrapper(self, *args, **kw):
parent_instance = self
parent_class = parent_instance.__class__
print(parent_class)
...
result = function(self, *args, **kw)
...
return result
return wrapper
(要保留方法名稱,您應該使用functools.wraps
裝飾wrapper
內部函數本身)
在此模型中,運行wrapper
的代碼時,您將擁有一個活潑的類實例-並且self
參數是該實例-並且您可以根據所需的內容和存儲的內容來決定是否調用原始函數。從先前的呼叫中緩存。
因此,我只是注意到我實際上不需要將緩存附加到類上,從而使一切都變得更加容易。 詳細信息在我的@jsbuenos答案下的評論中。 最終的解決方案如下所示:
class BidirectionalDict(dict):
def __setitem__(self, key, value):
super().__setitem__(hash(key), value)
super().__setitem__(value, key)
def __delitem__(self, key):
super().__delitem__(self[key])
super().__delitem__(key)
def bidirectional_cache(function):
"""Function decorator for caching
For functions where f(f(x)) == x is True
Requires hashable args and doesn't support kwargs
"""
cache = BidirectionalDict()
@wraps(function)
def wrapped(*args):
if hash(args) not in cache:
cache[hash(args)] = function(*args)
return cache[hash(args)]
return wrapped
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.