2016-08-01 8 views
1

最初にアクセスしたときに関数を実行して値を返し、次に(この型の変更など)という属性のattrのクラスを持っています。同様の現象がで得ることができるクラスプロパティを置き換える/バイパスする方法は?

class MyClass(object): 
    @property 
    def attr(self): 
     try: 
      return self._cached_result 
     except AttributeError: 
      result = ... 
      self._cached_result = result 
      return result 

obj = MyClass() 
print obj.attr # First calculation 
print obj.attr # Cached result is used 

これを行うときしかし、.attrは、初期結果になっていません。そうした方が効率的です。

obj.attrがプロパティに設定された後、無限ループが自然に表示されるため、他のものに簡単に設定することはできません。したがって、上のコードでは、obj.attrプロパティはセッターを持たないため、直接変更することはできません。セッターが定義されている場合、このセッターのobj.attrを置き換えると、無限ループが作成されます(セッターはセッター内からアクセスされます)。私はまた、最初のdel self.attrと普通のことができるようにセッターを削除することを考えましたが、これはプロパティの削除者(もしあれば)を呼び出し、無限ループの問題を再現します(self.attrの変更は、プロパティルールを介して)。

したがって、プロパティのメカニズムをバイパスし、バインドされたプロパティobj.attrを何かで置き換える方法がありますか?MyClass.attr.__getter__

答えて

3

これは時期尚早の最適化のように見えます。記述子を変更することでメソッド呼び出しをスキップします。

これは完全に可能ですが、正当化する必要があります。

プロパティからディスクリプタを変更するには、クラスを編集する必要がありますが、これはおそらく必要ではありません。引数が "attrの" であればそうでないAttributeError

を上げ、obj.attr = new_valueobj.attr

  • オーバーライド__getattr__を定義しない

    • 私はこれを実装するためのより良い方法は、になると思いますobj.attrが設定されるとすぐに、__getattr__は、属性が存在しない場合にのみ呼び出されるため、これ以上呼び出されません。

      最初の提案との主な相違点は、メソッドコールのオーバーヘッドが__getattr__であるため、最初の属性のアクセスが遅くなることですが、それは事実になります通常の__dict__ルックアップと同じです。

      例:

      class MyClass(object): 
      
          def __getattr__(self, name): 
           if name == 'attr': 
            self.attr = ... 
            return self.attr 
           raise AttributeError(name) 
      
      obj = MyClass() 
      print obj.attr # First calculation 
      print obj.attr # Cached result is used 
      

      EDIT:あなたは、Python 3.6以上を使用する場合は特に、the other answerを参照してください。

  • +1

    私はちょうど同じことを書いていましたが、私はあなたの代わりにあなたをアップボートします。これは完璧に機能します! – Saksow

    +0

    ありがとうございます。私は、 "あなたのプロパティから記述子を変更するには、あなたのクラスを編集しなければならない"という部分を理解しているのか分かりません:実際には、必要なのはクラス自体の更新ではありません( '新しいインスタンスに対しては「attr」)、_instance_の 'attr'属性の更新です。右? – EOL

    +0

    いいえ、 'attr'が両方**クラス**と**データ記述子**で定義されている限り、代わりに使用されるためです。 Pythonの属性検索の順序はちょっと難しいかもしれませんが、[ここではもう少し詳しく読んでいます](https://utcc.utoronto.ca/~cks/space/blog/python/AttributeLookupOrder)。 – pistache

    1

    記述子プロトコルを使用する新しいスタイルのクラスについては、__get__()メソッドが呼び出される独自のカスタム記述子クラスを作成して、これを実行できます。それが起こると、結果はクラスメソッドと同じ名前のインスタンス属性を作成することによってキャッシュされます。

    ここに私の言いたいことがあります。

    from __future__ import print_function 
    
    class cached_property(object): 
        """Descriptor class for making class methods lazily-evaluated and caches the result.""" 
        def __init__(self, func): 
         self.func = func 
    
        def __get__(self, inst, cls): 
         if inst is None: 
          return self 
         else: 
          value = self.func(inst) 
          setattr(inst, self.func.__name__, value) 
          return value 
    
    class MyClass(object): 
        @cached_property 
        def attr(self): 
         print('doing long calculation...', end='') 
         result = 42 
         return result 
    
    obj = MyClass() 
    print(obj.attr) # -> doing long calculation...42 
    print(obj.attr) # -> 42 
    
    +0

    これはあなたが望むものとほとんど同じですか?プロパティは単に記述子のインスタンスです。 – martineau

    +0

    これはOPが望んだものと全く同じですが、他のデコレータの存在下で予期しないことが起こる可能性のある '__name__'を使う代わりに' __set_name__'を使うことができるPython 3.6ではより良いでしょう。 – pistache

    +0

    これはOPの問題に対するより良い解決策です。彼はsetterを "削除"しようとしましたが、最初は定義されていなかったのですが、 'property'の代わりにディスクリプタを使用しました。私はこれが受け入れられた答えであるべきだと信じています。 – pistache

    関連する問題