python - 拦截python类中的魔术方法调用

标签 python class proxy

我正在尝试创建一个类来包装将在多个其他对象中使用的值。出于计算原因,目的是让这个包装值只计算一次,并将对该值的引用传递给它的用户。由于其对象容器模型,我认为这在 vanilla python 中是不可能的。相反,我的方法是传递一个包装类,定义如下:

class DynamicProperty():

    def __init__(self, value = None):
        # Value of the property
        self.value: Any = value

    def __repr__(self):
        # Use value's repr instead
        return repr(self.value)

    def __getattr__(self, attr):
        # Doesn't exist in wrapper, get it from the value 
        # instead
        return getattr(self.value, attr)

以下按预期工作:

wrappedString = DynamicProperty("foo")
wrappedString.upper()  # 'FOO'

wrappedFloat = DynamicProperty(1.5)
wrappedFloat.__add__(2)  # 3.5

但是,通过正常语法隐式调用 __add__ 会失败:

wrappedFloat + 2  # TypeError: unsupported operand type(s) for 
                  # +: 'DynamicProperty' and 'float'

有没有一种方法可以拦截这些隐式方法调用,而无需为 DynamicProperty 显式定义魔术方法以调用其 value 属性上的方法?

最佳答案

谈论“通过引用传递”只会让您感到困惑。将该术语保留在您可以选择的语言中,以及它会产生影响的地方。在 Python 中,你总是传递 objects - 这种传递相当于“通过引用传递” - 对于 all 对象 - 从 None 到 int 再到实时异步网络连接池实例。

除此之外:语言遵循的从对象检索属性的算法很复杂,有详细信息 - 实现 __getattr__ 只是冰山一角。完整阅读名为“Data Model”的文档将使您更好地掌握检索属性所涉及的所有机制。

就是说,这里是“魔术”或“双下划线”方法的工作原理 -(名称前后两个下划线的特殊函数):当您使用需要存在实现它的方法的运算符时(如 __add__ 用于 +),该语言检查对象的 class 是否有 __add__ 方法 - 而不是实例。类上的 __getattr__ 只能为该类的实例动态创建属性。 但这不是唯一的问题:您可以创建一个元类(继承自 type)并在该元类上放置一个 __getattr__ 方法。对于您从 Python 执行的所有查询,看起来您的对象在其类中具有 __add__(或任何其他 dunder 方法)。但是,对于 dunder 方法,Python 不会通过正常的属性查找机制 - 如果 dunder 方法“物理上”在那里,它会直接“查找”类。内存结构中有插槽,用于保存每个可能的 dunder 方法的类 - 它们要么引用相应的方法,要么是“空”(在 Python 端用 C 编码时这是“可见的”,默认情况下dir 将在这些方法存在时显示它们,如果不存在则忽略它们)。如果它们不存在,Python 只会“说”该对象没有实现该操作和句点。

使用您想要的代理对象解决此问题的方法是创建一个代理类,该代理类具有您要包装的类中的 dunder 方法,或者具有所有可能的方法,并在被调用时检查是否底层对象实际上实现了调用的方法。

这就是为什么“严肃”的代码很少(如果有的话)提供真正的“透明”代理对象。有异常(exception),但是从“Weakrefs”到“super()”再到 concurrent.futures,仅举几个核心语言和 stdlib 中的例子,没有人尝试“完全工作的透明代理”——相反,api 是更像是您在包装器上调用“.value()”或“.result()”方法来获取原始对象本身。

但是,正如我上面所述,它可以完成。我什至在 pypi 上有一个小的(长期未维护的)包可以做到这一点,为 future 包装一个代理。 代码位于 https://bitbucket.org/jsbueno/lelo/src/master/lelo/_lelo.py

关于python - 拦截python类中的魔术方法调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60620112/

相关文章:

python - pyodbc 不会抛出 SQL Server 错误

python - Django自定义管理命令运行Scrapy : How to include Scrapy's options?

java - 使用泛型创建类时出现 "> expected"错误

c++ - 类数据成员不可访问

java - WSO2 ESB 不会将多部分文件转发到代理服务

c# - C# : HTTP 407 error 中的代理基本身份验证

python - 为什么 plus-equals 对列表和字典有效?

python - 使用艺术家动画每秒拍摄快照

jquery - 在 jQuery 中的两个类之间切换

android - 如何设置具有录制/重播功能的 http(s) 代理?