通用的懒惰初始器类和魔术方法
我想创建一个类,在第一次使用对象时在后台延迟初始化对象,但其行为与初始化的对象完全相同。它应该是线程安全的。例如:
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
。然而,难道就没有更好的办法了吗?
I want to create a class that lazily initializes objects in the background when they are first used, but that behaves exactly like the initialized objects. It should be thread-safe. For example:
In[1]: l = LazyInitializer(list, (1, 2, 3, 4, 5))
In[2]: l.__len__()
5
My current implementation is this:
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)
This works for regular object members (functions and properties alike), but it does not for magic methods, e.g.,
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()
An ad-hoc solution could be to explicitly implement all possible magic methods on LazyInitializer
. However, isn't there any better way?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
的确。
实际上,本周我恢复了这个问题。问题在于,对于使用魔法方法,通常是由于执行操作员而导致的,Python不会执行“常规”属性访问:出于性能原因 - 它们被烘烤到语言规范中,而不仅仅是Cpython的细节,可以快捷方式检查类或一个超类是否已经存在魔法方法(
__ getAttr __
和__ getAttribute __
未检查),然后调用它。因此,确保以透明方式调用任何可能的魔术方法的唯一方法是,如果您的代理懒惰对象具有所有魔术方法。
否则唯一可以使用的“代理”是“
super
”本身:但是它很特别,也是语言的关键组成部分。通过研究C中的super
的实现,您可能会提出类似的透明代理(C中的某些代码),但不能保证。我曾经放置过类似的代理,可以将任何计算的计算卸载到子过程中,以便我拥有“透明的未来对象” - 它不是很漂亮的代码,但是我甚至在ocational ocational ocational ocational ocation中都使用了它:
https://bitbucket.org/jsbueno/lelo/src/src/master/master/
Indeed.
Actually, I revisted this problem just this week. The problem is that for using the magic methods, often as a consequence of executing an operator, Python won't do "regular" attribute access: for performance reasons - which are baked into the language spec, and not just a detail of cPython, it will shortcut to check if the class or one of the superclasses have the magic method already in place (
__getattr__
and__getattribute__
are not checked), and call it.So, the only way to ensure any possible magic method is called in a transparent way is if your proxy lazy object has all the magic methods.
The only "proxy" that can work otherwise is "
super
" itself: but it is rather special, and a key component of the language. It might be possible that by studying the implementation ofsuper
in C you could come up with a similar transparent proxy (with some code in C), but it is no way guaranteed.I once put a similar proxy that would offload any computation to a subprocess, so that I had a "transparent future object" - it is not pretty code, but I had used it even in production ocasionally:
https://bitbucket.org/jsbueno/lelo/src/master/