私はpythonでクラスを書いていて、計算に比較的長い時間がかかる属性があるので、1回だけ実行したいです。また、クラスのすべてのインスタンスで必要になるわけではないので、デフォルトでは実行したくない in __init__
。
私はPythonは初めてですが、プログラミングは初めてです。私はこれをかなり簡単に行う方法を考え出すことができますが、何かを行う「Pythonic」の方法は、他の言語での経験を使用して思いつく方法よりもはるかに単純であることが何度も何度もありました。
Pythonでこれを行う「正しい」方法はありますか?
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)
または、他の回答で言及されている実装が、ニーズに合った答えを示しています。
または上記のバックポート。
私はこれまで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.
通常の方法は、属性を 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
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})
メモ化を調べてみてください。それが機能する方法は、同じ引数を関数に渡した場合、キャッシュされた結果を返すということです。 python here に実装する)の詳細をご覧ください。
また、コードの設定方法によっては(すべてのインスタンスで必要なわけではないと言う)、何らかのフライウェイトパターンや遅延読み込みを使用することもできます。
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__
_でクラスを除外するという制限があります。 。
dickens
パッケージ(私のものではありません)は、cachedproperty
、classproperty
およびcachedclassproperty
デコレータを提供します。
クラスプロパティをキャッシュするには:
from descriptors import cachedclassproperty
class MyClass:
@cachedclassproperty
def approx_pi(cls):
return 22 / 7
これが私がすることです。これはあなたが得ることができるのと同じくらい効率的です:
class X:
@property
def foo(self):
r = calculate_something()
self.foo = r
return r
説明:基本的に、私は計算された値でプロパティメソッドをオーバーロードしています。したがって、(そのインスタンスの)プロパティに初めてアクセスした後、foo
はプロパティでなくなり、インスタンス属性になります。このアプローチの利点は、キャッシュヒットがself.__dict__
はキャッシュとして使用されており、プロパティが使用されない場合のインスタンスのオーバーヘッドはありません。