我试图创建一个类,该类封装了一个将在多个其他对象中使用的值。出于计算原因,目的是只计算一次此包装值,并将对该值的引用传递给其用户。我认为这在普通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
属性上的方法
本例中的
+
运算符不起作用,因为DynamicProperty
不从float
继承。见:因此,您需要进行某种动态继承:
输出:
谈论“参考传递”只会让你感到困惑。将这些术语保留在您可以选择的语言中,并保留在不同的语言中。在Python中,您总是将对象四处传递,这种传递相当于“按引用传递”所有对象,从None传递到int,再传递到活动的异步网络连接池实例
这样一来:语言从对象中检索属性所遵循的算法是复杂的,有细节-实现
__getattr__
只是冰山一角。完整阅读名为“Data Model”的文档将使您更好地掌握检索属性所涉及的所有机制这就是说,下面是它对“magic”或“dunder”方法的工作方式-(在名称前有两个下划线,在名称后有两个下划线的特殊函数):当您使用要求存在实现它的方法的运算符时(如
__add__
for+
),该语言会检查对象的类for__add__
方法-而不是实例。类上的__getattr__
可以仅为该类的实例动态创建属性。 但这不是唯一的问题:您可以创建一个元类(从type
继承)并在这个元类上放置__getattr__
方法。对于从Python执行的所有查询,对象的类中似乎有__add__
(或任何其他dunder方法)。但是,对于dunder方法,Python不通过正常的属性查找机制——如果dunder方法“物理地”在类中,它会直接“查看”该类。内存结构中有一些槽保存每个可能的dunder方法的类,它们要么引用相应的方法,要么为“null”(在Python端用C编写代码时,这是“可见的”,默认的dir
将在这些方法存在时显示它们,如果没有,则忽略它们)。如果它们不存在,Python只会“说”对象没有实现该操作和周期使用您想要的代理对象解决这个问题的方法是创建一个代理类,该类要么以您想要包装的类中的dunder方法为特征,要么以所有可能的方法为特征,在被调用时,检查底层对象是否实际实现了被调用的方法
这就是为什么“严肃”代码很少(如果有的话)提供真正的“透明”代理对象的原因。也有例外,但从“Weakrefs”、到“super()”、再到concurrent.futures,在核心语言和stdlib中仅举几例,没有人尝试“完全工作的透明代理”——相反,api更像是在包装器上调用“.value()”或“.result()”方法来访问原始对象本身
然而,正如我上面所描述的,它可以完成。我甚至在pypi上有一个小的(长而未维护的)包,可以实现这一点,为未来包装一个代理。 代码位于https://bitbucket.org/jsbueno/lelo/src/master/lelo/_lelo.py
相关问题 更多 >
编程相关推荐