我正在用python编写一个类,我有一个属性需要相对长的时间来计算,所以我只想做一次。此外,课程的每个实例都不需要它,因此我不想在__init__
中默认。
我是Python的新手,但不是编程。我可以很容易地找到一种方法来做到这一点,但我一遍又一遍地发现,使用我在其他语言中的经验,“Pythonic”做事的方式通常比我想象的要简单得多。
在Python中有没有'正确'的方法呢?
答案 0 :(得分:42)
我曾经这样做过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.
答案 1 :(得分:42)
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
This answer有更详细的示例,并且还提到了以前Python版本的backport。
Python&lt; 3.2 强>
Python wiki有一个cached property decorator(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)
或其他适合您需求的答案中提及的任何实施。
或者上面提到的后退。
答案 2 :(得分:33)
通常的方法是将属性设为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
答案 3 :(得分:6)
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)
限制是具有要缓存属性的对象必须具有__dict__
属性,该属性是可变映射,除非__slots__
在{{ 1}}。
答案 4 :(得分: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})
答案 5 :(得分:1)
您可以尝试查看备忘录。它的工作方式是,如果传入一个函数相同的参数,它将返回缓存的结果。您可以在implementing it in python here找到更多信息。
此外,根据您的代码设置方式(您说所有实例都不需要它),您可以尝试使用某种flyweight模式或延迟加载。
答案 6 :(得分:0)
这就是我的工作。这大约是您可以获得的最高效率:
foo
说明:基本上,我只是在用计算值重载属性方法。因此,在您第一次访问该属性(对于该实例)之后,self.__dict__
不再是属性,而是成为实例属性。这种方法的优点是,由于{{1}}被用作缓存,因此命中缓存的成本尽可能低,并且如果不使用该属性,则不会产生实例开销。
答案 7 :(得分:0)
答案 8 :(得分:0)
大多数(如果不是全部)当前答案都是关于缓存实例属性。要缓存 class 属性,您可以简单地使用字典。这可确保每个类计算一次属性,而不是每个实例一次。
mapping = {}
class A:
def __init__(self):
if self.__class__.__name__ not in mapping:
print('Expansive calculation')
mapping[self.__class__.__name__] = self.__class__.__name__
self.cached = mapping[self.__class__.__name__]
为了说明,
foo = A()
bar = A()
print(foo.cached, bar.cached)
给予
Expansive calculation
A A
答案 9 :(得分:-2)
执行此操作的最简单方法可能是编写一个包含该属性的方法(而不是使用属性)(getter方法)。在第一次调用时,此方法计算,保存并返回值;之后它只返回保存的值。