web-dev-qa-db-ja.com

Pythonのクラス属性のキャッシュ

私はpythonでクラスを書いていて、計算に比較的長い時間がかかる属性があるので、1回だけ実行したいです。また、クラスのすべてのインスタンスで必要になるわけではないので、デフォルトでは実行したくない in __init__

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

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

54
mwolfe02

Python≥3.2

両方を使用する必要があります @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)

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

48
Maxime R.

私はこれまで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.
44
Jon-Eric

通常の方法は、属性を property にして、初めて計算されたときに値を格納することです

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
37
John La Rooy
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})
2
mouad

メモ化を調べてみてください。それが機能する方法は、同じ引数を関数に渡した場合、キャッシュされた結果を返すということです。 python here に実装する)の詳細をご覧ください。

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

1
NT3RP

Python 3.8には _functools.cached_property_ デコレータが含まれています。

クラスのメソッドをプロパティに変換します。その値は1回計算され、インスタンスの有効期間中は通常の属性としてキャッシュされます。 property()に似ていますが、キャッシングが追加されています。他の方法では効果的に不変であるインスタンスの高価な計算プロパティに役立ちます。

この例はドキュメントから直接です:

_from functools import cached_property

class DataSet:
    def __init__(self, sequence_of_numbers):
        self._data = sequence_of_numbers

    @cached_property
    def stdev(self):
        return statistics.stdev(self._data)

    @cached_property
    def variance(self):
        return statistics.variance(self._data)
_

キャッシュされるプロパティを持つオブジェクトには、可変マッピングである___dict___属性が必要であり、___slots___が___dict___で定義されていない限り、___slots___でクラスを除外するという制限があります。 。

0
SuperShoot

dickens パッケージ(私のものではありません)は、cachedpropertyclasspropertyおよびcachedclasspropertyデコレータを提供します。

クラスプロパティをキャッシュするには

from descriptors import cachedclassproperty

class MyClass:
    @cachedclassproperty
    def approx_pi(cls):
        return 22 / 7
0
Acumenus

これが私がすることです。これはあなたが得ることができるのと同じくらい効率的です:

class X:
    @property
    def foo(self):
        r = calculate_something()
        self.foo = r
        return r

説明:基本的に、私は計算された値でプロパティメソッドをオーバーロードしています。したがって、(そのインスタンスの)プロパティに初めてアクセスした後、fooはプロパティでなくなり、インスタンス属性になります。このアプローチの利点は、キャッシュヒットがself.__dict__はキャッシュとして使用されており、プロパティが使用されない場合のインスタンスのオーバーヘッドはありません。

0
user1054050