繁体   English   中英

功能的魔术属性不一致

[英]Magic attributes of functions inconsistent

目前,我正在一个项目中,我有一个类,其中包含各种我想缓存的昂贵方法。 我想自己实现缓存,以用于练习,它的特殊之处在于它专门针对f(f(x)) == xTrue f(f(x)) == x (通过dict子类,其中d[key] == value and d[value] == keyTrue )。 有时这会深入到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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM