是否可以在方法装饰器中将代码修补到__init__上?

时间:2017-03-01 22:43:37

标签: python python-3.x decorator python-decorators syntactic-sugar

我想创建一个属性装饰器,它在对象初始化时计算属性值,而不是每次访问属性时。例如:

$.each(data.categories, function(index, value) {
    console.log(index); // current category
    $.each(value, function(indexSub, valueSub) {
        console.log(indexSub);
        console.log(valueSub.id);
        console.log(valueSub.code);
        console.log(valueSub.name);
    });
});

我希望这相当于:

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 添加代码?

1 个答案:

答案 0 :(得分: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