功能的魔术属性不一致

时间:2019-02-03 15:22:32

标签: python python-3.x magic-methods

我目前正在一个项目中,我有一个类,其中包含各种我想缓存的昂贵方法。我想自己实现缓存,以用于练习,它的特殊之处在于它专门针对f(f(x)) == xTrue的函数(通过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)

2 个答案:

答案 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