-2

プロパティにアクセスするたびにではなく、オブジェクトの初期化時にプロパティ値を1回計算するプロパティデコレータを作成したいとします。たとえば:メソッドデコレータ内から__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

我々は、後にオブジェクトのすべてのキャッシュされたプロパティを見つけて、__init__後にそれらを初期化することができるように我々は、サブクラスpropertyする必要があります:

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 
関連する問題