103

私はPythonでクラスを書いていますが、計算に比較的時間がかかる属性があるので、一度だけやりたいです。また、クラスのすべてのインスタンスで必要になるわけではないため、デフォルトでは__init__.

私はPythonは初めてですが、プログラミングは初めてです。これを非常に簡単に行う方法を思いつくことができますが、何かを行う「Pythonic」の方法は、他の言語での経験を使用して思いつく方法よりもはるかに簡単であることが多いことに何度も気づきました。

Pythonでこれを行う「正しい」方法はありますか?

4

10 に答える 10

144

Python ≥ 3.8 @property@functools.lru_cache統合されてい@cached_propertyます。

import functools
class MyClass:
    @functools.cached_property
    def foo(self):
        print("long calculation here")
        return 21 * 2

Python ≥ 3.2 < 3.8

@property@functools.lru_cacheデコレータの両方を使用する必要があります。

import functools
class MyClass:
    @property
    @functools.lru_cache()
    def foo(self):
        print("long calculation here")
        return 21 * 2

この回答にはより詳細な例があり、以前の Python バージョンのバックポートについても言及しています。

Python < 3.2

Python wiki には、キャッシュされたプロパティ デコレータ(MIT ライセンス) があり、次のように使用できます。

import random
# the class containing the property must be a new-style class
class MyClass(object):
   # create property whose value is cached for ten minutes
   @cached_property(ttl=600)
   def randint(self):
       # will only be evaluated every 10 min. at maximum.
       return random.randint(0, 100)

または、ニーズに合った他の回答で言及されている実装。
または上記のバックポート。

于 2013-11-14T13:48:27.393 に答える
54

以前は gnibbler が提案した方法でこれを行っていましたが、最終的にはちょっとしたハウスキーピングの手順に飽きてしまいました。

そこで、独自の記述子を作成しました。

class cached_property(object):
    """
    Descriptor (non-data) for building an attribute on-demand on first use.
    """
    def __init__(self, factory):
        """
        <factory> is called such: factory(instance) to build the attribute.
        """
        self._attr_name = factory.__name__
        self._factory = factory

    def __get__(self, instance, owner):
        # Build the attribute.
        attr = self._factory(instance)

        # Cache the value; hide ourselves.
        setattr(instance, self._attr_name, attr)

        return attr

使用方法は次のとおりです。

class Spam(object):

    @cached_property
    def eggs(self):
        print 'long calculation here'
        return 6*2

s = Spam()
s.eggs      # Calculates the value.
s.eggs      # Uses cached value.
于 2010-10-27T21:46:28.327 に答える
43

通常の方法は、属性をプロパティにして、最初に計算されたときに値を保存することです

import time

class Foo(object):
    def __init__(self):
        self._bar = None

    @property
    def bar(self):
        if self._bar is None:
            print "starting long calculation"
            time.sleep(5)
            self._bar = 2*2
            print "finished long caclulation"
        return self._bar

foo=Foo()
print "Accessing foo.bar"
print foo.bar
print "Accessing foo.bar"
print foo.bar
于 2010-10-27T20:47:10.587 に答える
2
class MemoizeTest:

      _cache = {}
      def __init__(self, a):
          if a in MemoizeTest._cache:
              self.a = MemoizeTest._cache[a]
          else:
              self.a = a**5000
              MemoizeTest._cache.update({a:self.a})
于 2010-10-27T20:51:12.600 に答える
1

メモ化について調べてみてください。関数に同じ引数を渡すと、キャッシュされた結果が返されます。Python での実装の詳細については、こちらを参照してください。

また、コードの設定方法によっては (すべてのインスタンスで必要というわけではないとおっしゃっています)、何らかのフライウェイト パターンまたは遅延読み込みを使用することもできます。

于 2010-10-27T20:50:16.123 に答える
-3

これを行う最も簡単な方法は、おそらく属性をラップするメソッド (getter メソッド) を (属性を使用する代わりに) 記述することです。最初の呼び出しで、このメソッドは値を計算し、保存して返します。後で、保存された値を返すだけです。

于 2010-10-27T20:38:38.463 に答える