通用惰性初始化器 class 和魔术方法
Generic lazy initializer class and magic methods
我想创建一个 class,它在第一次使用对象时在后台延迟初始化对象,但它的行为与初始化对象完全一样。它应该是线程安全的。例如:
In[1]: l = LazyInitializer(list, (1, 2, 3, 4, 5))
In[2]: l.__len__()
5
我目前的实现是这样的:
class LazyInitializer:
def __init__(self, initializer, *args, **kwargs):
self.__initializer = initializer
self.__args = args
self.__kwargs = kwargs
self.__obj = None
self.__lock = Lock()
@property
def _obj(self):
with self.__lock:
if self.__obj is None:
self.__obj = self.__initializer(*self.__args, **self.__kwargs)
return self.__obj
def __getattr__(self, item):
return getattr(self._obj, item)
这适用于常规对象成员(类似的函数和属性),但不适用于魔法方法,例如
In[2]: l.__len__() # works
5
In[3]: len(l) # doesn't work
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-3-e75269d816bd> in <module>
----> 1 len(l)
TypeError: object of type 'LazyInitializer' has no len()
一个临时解决方案可能是在 LazyInitializer
上显式实施所有可能的魔术方法。不过,有没有更好的办法呢?
确实如此。
实际上,我就在本周重新讨论了这个问题。问题是,对于使用魔术方法,通常是执行运算符的结果,Python 不会进行“常规”属性访问:出于性能原因 - 这是语言规范中的一部分,而不仅仅是一个cPython 的详细信息,它将快捷地检查 class 或其中一个超级 class 是否已经存在魔术方法(__getattr__
和 __getattribute__
未勾选),调用它。
因此,确保以透明方式调用任何可能的魔术方法的唯一方法是,如果您的代理惰性对象具有所有魔术方法。
唯一可以以其他方式工作的“代理”是“super
”本身:但它相当特殊,并且是该语言的关键组成部分。通过研究 super
在 C 中的实现,您可能会想出一个类似的透明代理(使用 C 中的一些代码),但无法保证。
我曾经放置了一个类似的代理,可以将任何计算卸载到子进程,这样我就有了一个“透明的未来对象”——它不是漂亮的代码,但我什至在生产中偶尔也使用过它:
我想创建一个 class,它在第一次使用对象时在后台延迟初始化对象,但它的行为与初始化对象完全一样。它应该是线程安全的。例如:
In[1]: l = LazyInitializer(list, (1, 2, 3, 4, 5))
In[2]: l.__len__()
5
我目前的实现是这样的:
class LazyInitializer:
def __init__(self, initializer, *args, **kwargs):
self.__initializer = initializer
self.__args = args
self.__kwargs = kwargs
self.__obj = None
self.__lock = Lock()
@property
def _obj(self):
with self.__lock:
if self.__obj is None:
self.__obj = self.__initializer(*self.__args, **self.__kwargs)
return self.__obj
def __getattr__(self, item):
return getattr(self._obj, item)
这适用于常规对象成员(类似的函数和属性),但不适用于魔法方法,例如
In[2]: l.__len__() # works
5
In[3]: len(l) # doesn't work
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-3-e75269d816bd> in <module>
----> 1 len(l)
TypeError: object of type 'LazyInitializer' has no len()
一个临时解决方案可能是在 LazyInitializer
上显式实施所有可能的魔术方法。不过,有没有更好的办法呢?
确实如此。
实际上,我就在本周重新讨论了这个问题。问题是,对于使用魔术方法,通常是执行运算符的结果,Python 不会进行“常规”属性访问:出于性能原因 - 这是语言规范中的一部分,而不仅仅是一个cPython 的详细信息,它将快捷地检查 class 或其中一个超级 class 是否已经存在魔术方法(__getattr__
和 __getattribute__
未勾选),调用它。
因此,确保以透明方式调用任何可能的魔术方法的唯一方法是,如果您的代理惰性对象具有所有魔术方法。
唯一可以以其他方式工作的“代理”是“super
”本身:但它相当特殊,并且是该语言的关键组成部分。通过研究 super
在 C 中的实现,您可能会想出一个类似的透明代理(使用 C 中的一些代码),但无法保证。
我曾经放置了一个类似的代理,可以将任何计算卸载到子进程,这样我就有了一个“透明的未来对象”——它不是漂亮的代码,但我什至在生产中偶尔也使用过它: