我想知道初始化类成员的python方法是什么,但只有在访问时才会访问它。 我尝试了下面的代码并且它正在运行,但有什么比这更简单吗?
class MyClass(object):
_MY_DATA = None
@staticmethod
def _retrieve_my_data():
my_data = ... # costly database call
return my_data
@classmethod
def get_my_data(cls):
if cls._MY_DATA is None:
cls._MY_DATA = MyClass._retrieve_my_data()
return cls._MY_DATA
答案 0 :(得分:23)
您可以在@property
上使用the metaclass代替:
class MyMetaClass(type):
@property
def my_data(cls):
if getattr(cls, '_MY_DATA', None) is None:
my_data = ... # costly database call
cls._MY_DATA = my_data
return cls._MY_DATA
class MyClass(metaclass=MyMetaClass):
# ...
这会使my_data
成为该类的一个属性,因此在您尝试访问MyClass.my_data
之前,会推迟昂贵的数据库调用。通过将数据库调用存储在MyClass._MY_DATA
中来缓存数据库调用的结果,只为该类调用一次。
对于Python 2,使用class MyClass(object):
并在类定义主体中添加__metaclass__ = MyMetaClass
attribute以附加元类。
演示:
>>> class MyMetaClass(type):
... @property
... def my_data(cls):
... if getattr(cls, '_MY_DATA', None) is None:
... print("costly database call executing")
... my_data = 'bar'
... cls._MY_DATA = my_data
... return cls._MY_DATA
...
>>> class MyClass(metaclass=MyMetaClass):
... pass
...
>>> MyClass.my_data
costly database call executing
'bar'
>>> MyClass.my_data
'bar'
这是有效的,因为在对象的父类型上查找了像property
这样的数据描述符;对于type
的类,可以使用元类扩展type
。
答案 1 :(得分:5)
使代码更清晰的另一种方法是编写一个执行所需逻辑的包装函数:
def memoize(f):
def wrapped(*args, **kwargs):
if hasattr(wrapped, '_cached_val'):
return wrapped._cached_val
result = f(*args, **kwargs)
wrapped._cached_val = result
return result
return wrapped
您可以按如下方式使用它:
@memoize
def expensive_function():
print "Computing expensive function..."
import time
time.sleep(1)
return 400
print expensive_function()
print expensive_function()
print expensive_function()
哪个输出:
Computing expensive function...
400
400
400
现在你的classmethod看起来如下,例如:
class MyClass(object):
@classmethod
@memoize
def retrieve_data(cls):
print "Computing data"
import time
time.sleep(1) #costly DB call
my_data = 40
return my_data
print MyClass.retrieve_data()
print MyClass.retrieve_data()
print MyClass.retrieve_data()
输出:
Computing data
40
40
40
请注意,这将为函数的任何参数集缓存一个值,因此,如果要根据输入值计算不同的值,则必须使memoize
更复杂一些。 / p>
答案 2 :(得分:4)
此答案仅适用于典型的实例属性/方法,而不适用于类属性/ classmethod
或staticmethod
。
如何同时使用property
和lru_cache
装饰器?后者记住。
from functools import lru_cache
class MyClass:
@property
@lru_cache()
def my_lazy_attr(self):
print('Initializing and caching attribute, once per class instance.')
return 7**7**8
请注意,这需要Python≥3.2。
信用:answer,作者Maxime R。
答案 3 :(得分:0)
请考虑可用于pip安装的Dickens
软件包,该软件包可用于Python 3.5+。它有一个higher
软件包,提供相关的 descriptors
和 cachedproperty
装饰器,其用法如下面的示例所示。它似乎按预期工作。
cachedclassproperty
答案 4 :(得分:0)
Ring
提供类似于lru_cache
的界面,但是可以使用任何类型的描述符支持:https://ring-cache.readthedocs.io/en/latest/quickstart.html#method-classmethod-staticmethod
class Page(object):
(...)
@ring.lru()
@classmethod
def class_content(cls):
return cls.base_content
@ring.lru()
@staticmethod
def example_dot_com():
return requests.get('http://example.com').content
请参阅链接以获取更多详细信息。