20 python lazy-evaluation lazy-initialization
我想知道初始化类成员的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
Run Code Online (Sandbox Code Playgroud)
Mar*_*ers 25
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):
# ...
Run Code Online (Sandbox Code Playgroud)
这会my_data在类上创建一个属性,因此在您尝试访问之前,会推迟昂贵的数据库调用MyClass.my_data.数据库调用的结果通过存储进行缓存,对类MyClass._MY_DATA只进行一次调用.
对于Python 2,在类定义主体中使用class MyClass(object):并添加__metaclass__ = MyMetaClass属性以附加元类.
演示:
>>> 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'
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为property在对象的父类型上查找数据描述符; 上课那是type,和type可以使用元类进行扩展.
此答案仅适用于典型的实例属性/方法,不适用于类attribute / 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
Run Code Online (Sandbox Code Playgroud)
请注意,这需要Python?3.2。
信用:Maxime R的回答。
使代码更清晰的另一种方法是编写一个执行所需逻辑的包装函数:
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
Run Code Online (Sandbox Code Playgroud)
您可以按如下方式使用它:
@memoize
def expensive_function():
print "Computing expensive function..."
import time
time.sleep(1)
return 400
print expensive_function()
print expensive_function()
print expensive_function()
Run Code Online (Sandbox Code Playgroud)
哪个输出:
Computing expensive function...
400
400
400
Run Code Online (Sandbox Code Playgroud)
现在你的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()
Run Code Online (Sandbox Code Playgroud)
输出:
Computing data
40
40
40
Run Code Online (Sandbox Code Playgroud)
请注意,这将为函数的任何参数集缓存一个值,因此,如果要根据输入值计算不同的值,则必须使其memoize更复杂一些.