2017-02-21 7 views
0

通常のlazy propertyデコレータのようなものを使用したいと思いますが、TensorFlowの仕組みと使用方法によって、遅延プロパティはすべて__init__最新(TensorFlowの部分は質問の一部ではありませんが、私が意味するものについてはhereを参照してください)。 「初期化する」とは、getattrを呼び出してプロパティメソッドを一度実行し、結果をキャッシュすることだけです。__init__で自動的に初期化される "レイジー"プロパティ

次作品すでに:

import functools 

def graph_property(getter): 
    property_name = getter.__name__ 
    attribute = '_cache_' + property_name 

    @property 
    @functools.wraps(getter) 
    def decorated(self): 
     if not hasattr(self, attribute): 
      setattr(self, attribute, getter(self)) 
      self._graph.append(property_name) # for illustration 
      print('Initializing ' + property_name) 
     return getattr(self, attribute) 

    return decorated 


class Test: 
    def __init__(self): 
     self._graph = [] 
     self.inputs # DON'T LIKE TO DO THIS 
     self.do_stuff # AND THIS 

    @graph_property 
    def inputs(self): 
     return 42.0 

    @graph_property 
    def do_stuff(self): 
     return self.inputs + 1.0 


if __name__ == '__main__': 
    t = Test() 
    print(t._graph) 

しかし、__init__self.inputself.do_stuffへの手動呼び出しを取り除くためにいいだろう - すぐに退屈されることを。

私は、プロパティがgraph_propertyであることを「覚えている」という複数の方法について考えていましたが、デコレータが適用されているのでクラスはまだ分かっていないので、ただ、self)。

戻ってdecoratedオブジェクトにタグ属性を与えて、Testのメタクラスを作成して、すべてのメソッドを調べ、このタグを持つメソッドを収集し、何らかの方法でそれらのイニシャライザを作成します。私はメタクラスに慣れていないし、propertyの記述子では属性を追加できないため、これを実装できませんでした。

説明されたアプローチが実行可能かどうか(もしそうであれば)?または、手動のオーバーヘッドなしで、同様に素敵な構文で、より簡単な方法がありますが、私はそれを見ていませんか?

答えて

1

単純なmixinを追加してpropertyのサブクラスを定義し、mixinの__init__メソッドでこのカスタムプロパティに関連するすべての初期化を行うことができます。こうすることで、どのクラスを初期化するか、初期化したくないクラスを選択できます。

import functools 


class lazy_property(property): 
    """ 
    This class will help us in identifying our lazy properties, so that we 
    don't confuse them with normal properties. 
    """ 
    pass 

def graph_property(getter): 
    property_name = getter.__name__ 
    attribute = '_cache_' + property_name 

    @lazy_property 
    @functools.wraps(getter) 
    def decorated(self): 
     if not hasattr(self, attribute): 
      setattr(self, attribute, getter(self)) 
      self._graph.append(property_name) # for illustration 
      print('Initializing ' + property_name) 
     return getattr(self, attribute) 

    return decorated 

class InitializeLazyPropertiesMixin: 
    """ 
    This mixin does all of the work of initializing lazy properties 
    """ 
    def __init__(self): 
     cls = type(self) 
     fields = (k for k in dir(cls) if isinstance(getattr(cls, k), lazy_property)) 
     for field in fields: 
      getattr(self, field) 


class Test(InitializeLazyPropertiesMixin): 
    def __init__(self): 
     self._graph = [] 
     # Whenever you're inheriting from this mixin make sure to call 
     # super `__init__` method. 
     super().__init__() 

    @graph_property 
    def inputs(self): 
     return 42.0 

    @graph_property 
    def do_stuff(self): 
     return self.inputs + 1.0 

class Test1: 
    """ 
    Just another class that doesn't require initializing any of the lazy properties 
    """ 
    def __init__(self): 
     self._graph = [] 

    @graph_property 
    def inputs(self): 
     return 42.0 

    @graph_property 
    def do_stuff(self): 
     return self.inputs + 1.0 

デモ出力:

>>> t = Test() 
Initializing inputs 
Initializing do_stuff 
>>> print(t._graph) 
['inputs', 'do_stuff'] 
>>> t = Test1() 
>>> print(t._graph) 
[] 
>>> t.inputs 
Initializing inputs 
42.0 
>>> t._graph 
['inputs'] 
+0

ああ、このサブクラスのトリックは美しいです!私は共通基底クラスを持っていますので、これは完全に適合します。 – phg

+1

'vars(type(self))。items()'はスーパークラスで定義された変数を含んでいません。これは先祖クラスではなく宣言されたクラス自体の記述子をチェックするだけです。 – jsbueno

+0

@jsbueno属性を取得するために、[再帰的に行く] 'dir()'を使用するように更新されました(http://stackoverflow.com/a/33089239/846892)。 –

0

プロパティとクラスの階層を完全に制御できるため、初期化したいプロパティをマークするだけで、すべてを呼び出す基本クラス__init__のコードがあります。

最初に、デコレータで、graph_propertyデコレータの変数を設定して、初期化するメソッドをマークします。以下のための基本やミックスインクラスで、その後

class MarcableProperty(property): 
    pass 

def graph_property(getter): 
    property_name = getter.__name__ 
    attribute = '_cache_' + property_name 

    @MarcableProperty 
    @functools.wraps(getter) 
    def decorated(self): 
     ... 

    decorated._graph_initialize = True 
    return decorated 

そして、: propertyオブジェクトので、関数とは異なり、任意の属性を割り当てることができない、そのための修正プログラムは、ユーザー定義クラスでPythonのネイティブプロパティをラップすることです他のすべての授業は、次のようにしてください:

def __init__(self, *args, **kwargs): 
    super().__init__(*args, **kwargs) 
    for cls_member_name in dir(self.__class__): 
     # "dir" is good because it automatically looks 
     # at the superclasses as well 
     cls_member = getattr(self.__class__, cls_member_name) 
     if getattr(cls_member, "_graph_initialize", False): 
       # Fetch property, initializing its value: 
       getattr(self, cls_member_name) 

これはそうです。

+0

まさに私の最初のアイデアだったが、中に 'decorated'結果に変数を設定する "はAttributeError: 'プロパティ' オブジェクトが何の属性 '_graph_initialize' がありません"。 – phg

+0

これは単にPythonの 'property'が' __slots__'を使用していることを意味します。 'class MyProperty(property):pass' – jsbueno

+0

(私はこの例で答えを更新しました) @ Ashwiniの答えと同じように、クラス属性がMarcablePropertyのインスタンスであるかどうかを確認してください) – jsbueno

関連する問題