我想知道什么是初始化一个类的成员,但只有当访问它,如果访问的蟒蛇方式。 我想下面的代码,它工作正常,但有什么比简单?
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
你可以使用@property
上的元类 ,而不是:
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
属性在类定义体来连接的元类。
演示:
>>> 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
可以使用元类进行扩展。
使代码清洁器的另一种方法是编写一个包装函数,它的期望的逻辑:
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
现在你的类方法将如下所示,例如:
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
有点复杂。
这个答案是一个典型的实例属性/方法而已 ,不是一类属性/ classmethod
,或staticmethod
。
如何同时使用property
和lru_cache
装饰? 后者memoizes。
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。
信用: 答案由马克西姆R.
考虑PIP-安装Dickens
包,它可用于在Python 3.5+。 它有一个descriptors
包,其提供有关cachedproperty
和cachedclassproperty
装饰,其中使用的在下面的示例被示出。 这似乎按预期方式工作。
from descriptors import cachedproperty, classproperty, cachedclassproperty
class MyClass:
FOO = 'A'
def __init__(self):
self.bar = 'B'
@cachedproperty
def my_cached_instance_attr(self):
print('Initializing and caching attribute, once per class instance.')
return self.bar * 2
@cachedclassproperty
def my_cached_class_attr(cls):
print('Initializing and caching attribute, once per class.')
return cls.FOO * 3
@classproperty
def my_class_property(cls):
print('Calculating attribute without caching.')
return cls.FOO + 'C'
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
请参阅链接查看更多细节。