通用的懒惰初始器类和魔术方法

发布于 2025-01-19 06:33:03 字数 1202 浏览 3 评论 0原文

我想创建一个类,在第一次使用对象时在后台延迟初始化对象,但其行为与初始化的对象完全相同。它应该是线程安全的。例如:

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

放手` 2025-01-26 06:33:03

的确。
实际上,本周我恢复了这个问题。问题在于,对于使用魔法方法,通常是由于执行操作员而导致的,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 of super 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/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文