mwo*_*e02 47 python memoization
我在python中编写一个类,我有一个属性需要相对长的时间来计算,所以我只想做一次.此外,它会不会被类的每个实例需要的,所以我不想在默认情况下做到这一点在__init__.
我是Python新手,但不是编程.我可以很容易地找到一种方法来做到这一点,但我一次又一次地发现,使用我在其他语言中的经验,"Pythonic"做事的方式通常比我想象的要简单得多.
在Python中有没有"正确"的方法呢?
Max*_* R. 56
Python≥3.2
你应该使用both @property和@functools.lru_cache装饰器:
import functools
class MyClass:
@property
@functools.lru_cache()
def foo(self):
print("long calculation here")
return 21 * 2
Run Code Online (Sandbox Code Playgroud)
这个答案有更详细的例子,并且还提到了以前Python版本的backport.
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)
Run Code Online (Sandbox Code Playgroud)
或者在其他答案中提到的任何实现符合您的需求.
或者上面提到的backport.
Jon*_*ric 45
我曾经这样做过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
Run Code Online (Sandbox Code Playgroud)
这是你如何使用它:
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.
Run Code Online (Sandbox Code Playgroud)
Joh*_*ooy 37
通常的方法是使属性成为属性,并在第一次计算时存储该值
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
Run Code Online (Sandbox Code Playgroud)
Sup*_*oot 20
Python 3.8 包含functools.cached_property装饰器。
将类的方法转换为属性,该属性的值计算一次,然后在实例的生命周期内作为普通属性缓存。与 类似
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)
Run Code Online (Sandbox Code Playgroud)
的限制是,与属性的对象要被缓存必须有一个__dict__属性,该属性是一个可变的映射,以排除类__slots__除非__dict__中定义__slots__。
aja*_*nss 14
如前所述,functools.cached_property适用于缓存的实例属性。对于缓存的类属性:
3.9 <= 蟒蛇 < 3.13
from functools import cache
class MyClass:
@classmethod
@property
@cache # or lru_cache() for python < 3.9
def foo(cls):
print('expensive calculation')
return 42
Run Code Online (Sandbox Code Playgroud)
>>> MyClass.foo
expensive calculation
42
>>> MyClass.foo
42
Run Code Online (Sandbox Code Playgroud)
如果你想要一个可重复使用的装饰器:
def cached_class_attr(f):
return classmethod(property(cache(f)))
class MyClass:
@cached_class_attr
def foo(cls):
...
Run Code Online (Sandbox Code Playgroud)
蟒蛇 >= 3.13
在 3.13 中,链接classmethod和property是不允许的,因此您必须使用元类或自定义装饰器,以下是只读缓存属性的示例:
class MyMeta(type):
@property
@cache
def foo(self):
...
class MyClass(metaclass=MyMeta):
...
MyClass.foo # read-only access
Run Code Online (Sandbox Code Playgroud)
或者自定义装饰器:
class classproperty:
def __init__(self, func) -> None:
functools.update_wrapper(self, func)
def __get__(self, instance, owner):
return self.__wrapped__(owner)
class MyClass:
@classproperty
@cache
def foo(cls):
...
Run Code Online (Sandbox Code Playgroud)
该dickens包(不是我的)提供cachedproperty,classproperty和cachedclassproperty装饰器。
缓存类属性:
from descriptors import cachedclassproperty
class MyClass:
@cachedclassproperty
def approx_pi(cls):
return 22 / 7
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
22864 次 |
| 最近记录: |