【问题标题】:Is it possibly to patch code onto __init__ from within a method decorator?是否可以从方法装饰器中将代码修补到 __init__ 中?
【发布时间】:2017-07-21 11:26:51
【问题描述】:

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

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__()

【问题讨论】:

  • 在评估方法装饰器时,该类本身还不存在,因此您无法查找其__init__ 方法来修改它,即使它已经定义(这不是你可以指望的)。但是,您可以编写一个装饰器,它只在第一次调用包装函数,将值保存在实例变量中,然后只返回缓存的值。
  • 为什么你认为你需要向__init__添加代码?你应该阅读descriptor protocol
  • @jasonharper 我不能得到相同的cls__init__ 并用修补过的__init__ 替换它吗?假设我假设 __init__ 总是在其他方法之前定义。
  • 什么课?在其定义的整个主体(包括所有修饰的方法)完成执行之前,没有类。
  • 你的例子没有解释为什么这甚至是必要的;你能提供一些更合适的上下文吗?这似乎是xyproblem.info

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


【解决方案1】:

我们需要对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

【讨论】:

    猜你喜欢
    • 2023-03-15
    • 2016-10-09
    • 2018-08-31
    • 2017-02-04
    • 2014-12-13
    • 2011-12-01
    • 1970-01-01
    • 2012-09-03
    • 1970-01-01
    相关资源
    最近更新 更多