拦截对象属性上的 __getitem__ 调用 [英] Intercepting __getitem__ calls on an object attribute

查看:51
本文介绍了拦截对象属性上的 __getitem__ 调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题:如何拦截对对象属性的 __getitem__ 调用?

Question: How can I Intercept __getitem__ calls on an object attribute?

说明:

所以,场景如下.我有一个对象将类似 dict 的对象存储为属性.每次调用此属性的 __getitem__ 方法时,我想拦截该调用并根据键对获取的项目进行一些特殊处理.我想要的看起来像这样:

So, the scenario is the following. I have an object that stores a dict-like object as an attribute. Every time the __getitem__ method of this attribute gets called, I want to intercept that call and do some special processing on the fetched item depending on the key. What I want would look something like this:

class Test:

    def __init__(self):
        self._d = {'a': 1, 'b': 2}

    @property
    def d(self, key):
        val = self._d[key]
        if key == 'a':
            val += 2
        return val
t = Test()
assert(t.d['a'] == 3) # Should not throw AssertionError

问题是@property 方法实际上无法访问__getitem__ 调用中的密钥,因此我根本无法检查它来执行我的特殊后处理步骤.

The problem is that the @property method doesn't actually have access to the key in the __getitem__ call, so I can't check for it at all to do my special postprocessing step.

重要提示:我不能只是对 MutableMapping 进行子类化,覆盖我子类的 __getitem__ 方法来进行这种特殊处理,并将子类的实例存储在 self._d.在我的实际代码中,self._d 已经是 MutableMapping 的子类,这个子类的其他客户端需要访问未修改的数据.

Important Note: I can't just subclass a MutableMapping, override the __getitem__ method of my subclass to do this special processing, and store an instance of the subclass in self._d. In my actual code self._d is already a subclass of MutableMapping and other clients of this subclass need access to the unmodified data.

感谢所有帮助!

推荐答案

一种解决方案是代理底层映射的 Mapping.d 属性会将底层的 self._d 映射包装在代理包装器中并返回它,并且使用该代理将展示必要的行为.示例:

One solution would be a Mapping that proxies the underlying mapping. The d property would wrap the underlying self._d mapping in the proxy wrapper and return it, and use of that proxy would exhibit the necessary behaviors. Example:

from collections.abc import Mapping

class DProxy(Mapping):
    __slots__ = ('proxymap',)
    def __init__(self, proxymap):
        self.proxymap = proxymap
    def __getitem__(self, key):
        val = self.proxymap[key]
        if key == 'a':
            val += 2
        return val
    def __iter__(self):
        return iter(self.proxymap)
    def __len__(self):
        return len(self.proxymap)

完成后,您的原始类可以是:

Once you've made that, your original class can be:

class Test:
    def __init__(self):
        self._d = {'a': 1, 'b': 2}

    @property
    def d(self):
        return DProxy(self._d)

然后用户将使用 test.d[somekey] 访问 Test 的实例;test.d 将返回代理,然后根据 somekey 的需要修改 __getitem__ 的结果.他们甚至可以使用 locald = test.d 存储引用,然后使用 locald 同时保留必要的代理行为.如果需要,您可以将其设置为 MutableMapping,但是当目标是读取值时,基于纯 Mapping 的代理可以避免复杂性,从不通过代理修改它们.

Users would then access instances of Test with test.d[somekey]; test.d would return the proxy, which would then modify the result of __getitem__ as needed for somekey. They could even store off references with locald = test.d and then use locald while preserving the necessary proxy behaviors. You can make it a MutableMapping if needed, but a plain Mapping-based proxy avoids complexity when the goal is reading the values, never modifying them through the proxy.

是的,这会在每次访问 d 时创建一个新的 DProxy 实例;如果你愿意,你可以缓存它,但考虑到 DProxy 类的 __init__ 是多么简单,成本只有在通过 d 进行合格访问时才有意义属性在最热的代码路径上频繁执行.

Yes, this makes a new DProxy instance on each access to d; you could cache it if you like, but given how simple the DProxy class's __init__ is, the cost is only meaningful if qualified access via the d attribute is performed frequently on the hottest of code paths.

这篇关于拦截对象属性上的 __getitem__ 调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆