根据文档, super(cls, obj)
返回
一个代理对象,它将方法调用委托给类型为cls的父类或兄弟类
我理解为什么 super()
提供了这个功能,但我需要稍微不同的东西:我需要创建一个代理对象,将方法调用(和属性查找)委托给类 cls
本身;和 super
一样,如果 cls
没有实现方法/属性,我的代理应该继续查看MRO顺序(新的而不是原始类) . 有没有我能写的功能可以达到这个目的?
例:
class X:
def act():
#...
class Y:
def act():
#...
class A(X, Y):
def act():
#...
class B(X, Y):
def act():
#...
class C(A, B):
def act():
#...
c = C()
b = some_magic_function(B, c)
# `b` needs to delegate calls to `act` to B, and look up attribute `s` in B
# I will pass `b` somewhere else, and have no control over it
当然,我可以做 b = super(A, c)
,但这依赖于知道确切的类层次结构 and 事实是 B
在MRO中跟随 A
. 如果这两个假设中的任何一个在将来发生变化,它将默默地破裂 . (注意 super
没有做出任何这样的假设!)
如果我只需要调用 b.act()
,我可以使用 B.act(c)
. 但我正在向其他人传递 b
,并且不知道他们背叛了我,并且在某些时候开始像 class C
的实例 .
一个单独的问题, super()
(在Python 3.2中)的文档只讨论了它的方法委托,并没有说明代理的属性查找也以相同的方式执行 . 这是意外遗漏吗?
编辑
更新的Delegate方法也适用于以下示例:
class A:
def f(self):
print('A.f')
def h(self):
print('A.h')
self.f()
class B(A):
def g(self):
self.f()
print('B.g')
def f(self):
print('B.f')
def t(self):
super().h()
a_true = A()
# instance of A ends up executing A.f
a_true.h()
b = B()
a_proxy = Delegate(A, b)
# *unlike* super(), the updated `Delegate` implementation would call A.f, not B.f
a_proxy.h()
请注意,更新的 class Delegate
更接近我想要的 super()
,原因有两个:
-
super()
仅代理第一次通话;后续调用将正常发生,因为到那时使用对象,而不是其代理 . -
super()
不允许属性访问 .
因此,我的问题在Python中有一个(几乎)完美的答案 .
事实证明,在更高的层面上,我试图做一些我不应该做的事情(see my comments here) .
2 回答
本课程应涵盖最常见的情况:
像这样使用它:
(使用示例代码中的名称 . )
限制:
您无法通过此代理从构造函数中传递的类中检索某些特殊属性,如
__class__
等 . (此重述也适用于super
. )如果要检索的属性是某种类型的描述符,这可能会表现得很好 .
Edit :如果您希望问题更新中的代码能够按预期工作,您可以使用以下代码:
这将代理对象作为
self
参数传递给任何被调用的方法,它根本不需要原始对象,因此我将其从构造函数中删除 .如果您还希望可以访问实例属性,则可以使用此版本:
不,这不是偶然的 .
super()
对属性查找没有任何作用 . 原因是实例上的属性与特定类没有关联,它们就在那里 . 考虑以下:哪个类"owns"
foo
,bar
和baz
?如果我想将
instance
视为C的实例,那么在调用method
之前它是否应该具有baz
属性?之后怎么样?如果我将
instance
视为A的实例,foo
应该具有什么值?bar
应该是不可见的,因为它只是在B中添加,或者是可见的,因为它被设置为类外的值?所有这些问题在Python中都是无稽之谈 . 没有办法设计一个具有Python语义的系统,可以为它们提供合理的答案 .
__init__
isn 't even special in terms of adding attributes to instances of the class; it'只是一个非常普通的方法,恰好被称为实例创建协议的一部分 . 任何方法(或者实际上来自另一个类的代码,或根本不来自任何类的代码)都可以在它引用的任何实例上创建属性 .实际上,
instance
的所有属性都存储在同一个地方:肯定没有办法得到“
A.foo
被D.foo
阴影”,正如你对C的预期一样;它们是相同的属性,并且一个类(或来自其他地方)对它的任何写入都会破坏另一个类留在其中的值 .这样做的后果是
super()
不执行属性查找与方法查找相同;它不能,也不能写任何代码 .事实上,通过运行一些实验,
super
和Sven的Delegate
实际上都不支持直接属性检索!然后两者都按预期方法工作:
但:
因此,它们都只能用于调用某些方法,而不是传递给任意第三方代码,假装是您要委派的类的实例 .
不幸的是,它们在存在多重继承时的行为方式不同 . 鉴于:
然后:
因为
Delegate
忽略任何类的完整MRO_delegate_obj
是一个实例,只使用_delegate_cls
的MRO . 虽然super
做了你在问题中提出的问题,但行为似乎很奇怪:它是B的一个实例,因为B的直接实例没有定义foo
.这是我的尝试:
我依靠类的
__mro__
属性来正确地找出从哪里开始,然后我只使用super
. 如果返回一步使用super
的怪异太多,那么你可以自己从那个点开始检查类__dict__
s的MRO链 .我没有试图处理不寻常的属性;那些用描述符(包括属性)实现的,或者那些魔术方法在Python后面查找,这些方法通常从类而不是直接从实例开始 . 但是这种行为就像你问得适度一样(在我的帖子的第一部分中对广告恶心进行了阐述;以这种方式查找属性不会给你任何不同的结果,而不是直接在实例中查找它们) .