-2

我想创建一个属性装饰器,它在对象初始化时计算属性值,而不是每次访问属性。例如:是否可以将代码修改到方法装饰器中的__init__上?

class Foo: 

    def __init__(self, value): 
    self.value = value 

    @cached_property # How to implement this decorator? 
    def foo(self): 
    return self.value * some_heavy_computation() 

我想这是等同于:

class Foo: 

    def __init__(self, value): 
    self.value = value 
    self._foo = self.value * some_heavy_computation() 

    @property 
    def foo(self): 
    return self._foo 

是否有可能以某种方式从装饰方法中添加代码__init__()

+2

在一个方法装饰进行评价时,类本身不是存在,所以你无法查找它的''__init__''方法来修改它,即使它已经被定义了(这是你不能指望的)。但是,您可以编写一个只在第一次调用包装函数的装饰器,将该值保存在实例变量中,然后仅返回缓存的值。 – jasonharper

+1

为什么你认为你需要添加代码到'__init__'?你应该阅读[描述符协议](https://docs.python.org/3/howto/descriptor.html)。 – jonrsharpe

+0

@jasonharper我无法获得同一'cls'的'__init__'并将其替换为修补过的'__init__'吗?假设我假设'__init__'总是在其他方法之前定义的。 – danijar

回答

0

我们需要子类property,使我们可以在以后找对象的所有缓存的性能和__init__后初始化它们:要求第一次当

class CachedProperty(property): 

    pass 

实际的装饰评估方法体,并记住供以后访问结果:

import functools 

def cached_property(method): 
    attribute = '_cached_' + method.__name__ 

    @CachedProperty 
    @functools.wraps(method) 
    def wrapper(self, *args, **kwargs): 
    if not hasattr(self, attribute): 
     setattr(self, attribute, method(self)) 
    return getattr(self, attribute) 

    return wrapper 

现在我们可以使用一个基类从缓存__init__打完访问缓存的属性的值将作为可用:

class InitCachedProperties: 

    def __init_subclass__(cls, **kwargs): 
    super().__init_subclass__(**kwargs) 
    orig_init = cls.__init__ 
    def init(self, *args, **kwargs): 
     orig_init(self, *args, **kwargs) 
     for prop in cls.__dict__.values(): 
     if isinstance(prop, CachedProperty): 
      prop.__get__(self) 
    cls.__init__ = init 

要从问题的工作让这个例子中,我们需要让我们的类继承这个属性初始化基类:

class Foo(InitCachedProperties): 

    def __init__(self, value): 
     self.value = value 

    @cached_property 
    def foo(self): 
     return self.value + 21 
相关问题