我目前正在一个项目中,我有一个类,其中包含各种我想缓存的昂贵方法。我想自己实现缓存,以用于练习,它的特殊之处在于它专门针对f(f(x)) == x
是True
的函数(通过dict子类,其中d[key] == value and d[value] == key
是{{ 1}})。
有时这会深入到python,此刻我有点迷路。
缓存应该附加到定义该方法的类上,因此我需要从装饰器中提取该类,该装饰器将缓存添加到函数中。问题是,当用True
装饰f = dec(f)
时,似乎python确实确实做了f
的其他功能。
我的测试代码和缓存装饰器的开头是:
@dec
在不使用缓存装饰器的情况下定义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
并执行以下调用(REPL会话)时,将按预期提供输出:
A
但是,如果我改为用装饰器运行类定义,则装饰器内部始终有>>> 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__
时,在dir
上调用A.x
,其中x
是所有测试方法,将给出完全不同的输出。
我的问题是,为什么dir
接收的功能对象与@dec
接收的对象不同?有什么方法可以检索在装饰器范围内定义的函数的类,还是我总是必须手动执行dec(f)
?
答案 0 :(得分:0)
运行装饰器代码时,您尝试访问的__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
(要保留方法名称,应使用wrapper
装饰functools.wraps
内部函数本身)
在此模型中,运行wrapper
中的代码时,您有一个活泼的类实例-并且self
参数是该实例–您可以决定是否调用该实例。原始功能基于您想要的内容,并已存储在先前调用的缓存中。
答案 1 :(得分:0)
所以我只是注意到我实际上不需要将缓存附加到类上,从而使一切变得更加容易。详细信息在我的@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