2016-05-11 5 views
4

私は大きな配列に対して操作を実行するプロパティを持つpythonクラスを持っています。プロパティにアクセスするたびに操作をやり直すことなく、最初に計算された後のメソッドの結果を保存する最善の方法は何ですか?例えばメソッドの結果を属性として格納するPythonの方法

class MyClass: 
    def __init__(self, x, y): 
     """Input variables are two large data arrays""" 
     self.x = x 
     self.y = y 

    @property 
    def calculate(self): 
     """Some computationally expensive operation""" 
     try: 
      # the result has been calculated already 
      return self._result 
     except AttributeError: 
      # if not, calculate it 
      self._result = self.x * self.y 
      return self._result 

は使用方法:

>>> foo = MyClass(2,3) 
>>> a = foo.calculate 
>>> print(a) 
6 

あなたが見ることができるように、私がこれまでに思い付いたすべての結果が保存されている「隠し」属性を持つことです。これを行うより良い方法はありますか? @propertyの使用はここで正しいですか?

ありがとうございました。

+3

について 'self._result = NONE'でオブジェクトを初期化し、次にあなたが最初self._result' 'かどうかを確認calculate'でどのようにhttps://github.com/pydanny/cached-property –

+2

をチェック'はすでに新しい値を計算する前に値を持っていますか? – TigerhawkT3

+1

私は個人的に '@ property'を使用しません。論理的には、"計算する "というものが動作する関数であると期待しているからです。 – Paul

答えて

1

基本的にはうまく見えます。しかし、xyは変更可能なので、xyが変更される場合は_resultを無効にする必要があります。

私はxyの外にプロパティを作成し、無効化すると_resultを設定します。

一つ、xylistsへの参照であることから、これらのリストはMyClassの範囲の外から変更される可能性があります(私はあなたがarraysを言ったときにことを意味推測します)。その場合、プロパティを使用するように私の提案に従って、はxyがプロパティであってもその変更を検出することはできません。私はその制限を守りながらも、それをうまく文書化します。

+0

あなたは本当にそのような変更を検出することはできません。 – user2357112

+0

私は 'x'と' y'の外にプロパティを作るでしょう。私の更新を確認してください。 – hek2mgl

+0

確かに、プロパティを簡単に(ユーザーがプロパティをバイパスしない限り)検出することができますが、渡された配列を突然変異させるだけで、ユーザーの行に沿ってもっと考えていました。 – user2357112

0

あなたはNoneにオブジェクトを初期化して、それはまだNoneだかどうかを確認できます。

class MyClass: 
    def __init__(self, x, y): 
     """Input variables are two large data arrays""" 
     self.x = x 
     self.y = y 
     self._result = None 

    @property 
    def calculate(self): 
     """Some computationally expensive operation""" 
     if self._result is not None: 
      return self._result 
     # if not, calculate it 
     self._result = self.x * self.y 
     return self._result 
3

あなたがキャッシュされた結果を計算するために使用したxとyの値を覚えておく必要があります。変更された場合は、キャッシュを再計算して更新する必要があります。

class MyClass: 
    def __init__(self, x, y): 
     """Input variables are two large data arrays""" 
     self.x = x 
     self.y = y 
     self._calculate = None 
     self._x = None 
     self._y = None 

    @property 
    def calculate(self): 
     if not self._calculate or self._x != self.x or self._y != self.y: 
      self._calculate = self.x * self.y 
      self._x = self.x 
      self._y = self.x 

     return self._calculate 


>>> a = MyClass(2,3) 
>>> print(a.calculate) 
6 
>>> a.x = 553 
>>> print(a.calculate) 
1659 
+0

これは、結果が再計算される必要がある場合、確実に検出します。しかし、xとyが大きな配列の場合、コピーの追加メモリ消費と比較のための努力は過小評価されるべきではありません。 – hek2mgl

+0

もちろん、すべてはデータサイズと使用方法によって異なります。複数の過去の結果を覚えて、過去の "x、y、result"ディクテーションのストレージリストを作成することも良い考えです。 – C14L

+0

私はxとyを覚えていない。どのようにルックアップを知らずに何かをキャッシュすることができますか? xとyが<100Kの浮動小数点リストの場合、〜1MBのメモリです。それらがそれよりも大きい場合は、Python以外のものを使用してキャッシュを実装する必要があります。つまり、1M +レコードを簡単にキャッシュする方法はありません。 – Charlie

2

あなたのプロパティを実装した方法は問題ありません。それは動作し、オーバーヘッドがほぼゼロになります。

考えられる実験として、非データ記述子の特定のプロパティを利用することができます。非データ記述子は、property(データ記述子)に似ています。非データ記述子は、__get__メソッドのみを定義するオブジェクトです。インスタンスに非データ記述子と同じ名前の属性がある場合、これは非データ記述子をオーバーライドします(猿のパッチ機能と考える)。例えば。

class cache: 
    def __init__(self, func): 
     self.func = func 

    def __get__(self, obj, type_): 
     if obj is None: 
      return self.func 
     value = self.func(obj) 
     setattr(obj, self.func.__name__, value) 
     return value 

class MyClass: 
    def __init__(self, x, y): 
     self.x = x 
     self.y = y 

    @cache 
    def calculate(self): 
     print("calculating") 
     return self.x + self.y 

o = MyClass(1, 2) 
print("first", o.calculate) # prints "calcutating" then "first 3" 
print("second", o.calculate) # just prints "second 3" 
del o.calculate 
print("third", o.calculate) # prints "calcutating" then "third 3" 
関連する問題