python:super()-类似于代理对象,在指定类上启动MRO搜索
根据文档,python:super()-类似于代理对象,在指定类上启动MRO搜索,python,python-3.x,multiple-inheritance,superclass,super,Python,Python 3.x,Multiple Inheritance,Superclass,Super,根据文档,super(cls,obj)返回 将方法调用委托给父级或同级的代理对象 cls类型的类 我理解为什么super()提供了这个功能,但我需要一些稍微不同的东西:我需要创建一个代理对象,将方法调用(和属性查找)委托给类cls本身;与super一样,如果cls没有实现方法/属性,我的代理应该继续按照MRO顺序(新类而不是原始类)查找。我能写什么函数来实现这一点 例如: class X: def act(): #... class Y: def act(): #...
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)
,但这取决于知道确切的类层次结构和b
在MRO中紧随A
的事实。如果这两个假设中的任何一个在未来发生变化,它都会悄然破裂。(请注意,super
没有做出任何此类假设!)
如果我只需要调用b.act()
,我可以使用b.act(c)
。但是我正在把b
传递给其他人,不知道他们会怎么处理它。我需要确保它不会背叛我,并在某个时候开始表现得像类C
的实例
另一个问题是,super()
(在Python 3.2中)的文档只讨论了它的方法委托,并没有阐明代理的属性查找也是以同样的方式执行的。这是意外遗漏吗
编辑
更新的委托方法也适用于以下示例:
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()
请注意,更新的类委托
比super()
更接近我想要的,原因有两个:
super()
只对第一次调用进行代理;后续调用将正常进行,因为此时将使用对象,而不是其代理super()
不允许属性访问事实证明,在更高的层次上,我试图做一些我不应该做的事情()。本课程应该涵盖最常见的情况:
class Delegate:
def __init__(self, cls, obj):
self._delegate_cls = cls
self._delegate_obj = obj
def __getattr__(self, name):
x = getattr(self._delegate_cls, name)
if hasattr(x, "__get__"):
return x.__get__(self._delegate_obj)
return x
像这样使用它:
b = Delegate(B, c)
(使用示例代码中的名称。)
限制:
\uuuuuuuuuuuuuu class\uuuuuuu
等。(此restistions也适用于super
)class Delegate:
def __init__(self, cls):
self._delegate_cls = cls
def __getattr__(self, name):
x = getattr(self._delegate_cls, name)
if hasattr(x, "__get__"):
return x.__get__(self)
return x
这将代理对象作为self
参数传递给任何调用的方法,并且它根本不需要原始对象,因此我将其从构造函数中删除
如果还希望实例属性可访问,则可以使用以下版本:
class Delegate:
def __init__(self, cls, obj):
self._delegate_cls = cls
self._delegate_obj = obj
def __getattr__(self, name):
if name in vars(self._delegate_obj):
return getattr(self._delegate_obj, name)
x = getattr(self._delegate_cls, name)
if hasattr(x, "__get__"):
return x.__get__(self)
return x
另一个问题是super()的文档(在Python 3.2中)
只谈它的方法授权,并没有澄清这一点
代理的属性查找也以相同的方式执行。它是
意外遗漏
不,这不是偶然的super()
对属性查找不做任何操作。原因是实例上的属性与特定的类没有关联,它们就在那里。考虑以下事项:
class A:
def __init__(self):
self.foo = 'foo set from A'
class B(A):
def __init__(self):
super().__init__()
self.bar = 'bar set from B'
class C(B):
def method(self):
self.baz = 'baz set from C'
class D(C):
def __init__(self):
super().__init__()
self.foo = 'foo set from D'
self.baz = 'baz set from D'
instance = D()
instance.method()
instance.bar = 'not set from a class at all'
哪个类别“拥有”foo
、bar
和baz
如果我想将实例
视为C的实例,那么在调用方法
之前,它是否应该具有baz
属性?之后呢
如果我将instance
视为A的实例,那么foo
应该具有什么值?bar
应该是不可见的,因为它只添加到B中,还是可见的,因为它被设置为类外的值
所有这些问题在Python中都是无稽之谈。用Python的语义设计一个系统是不可能的,它不能给出合理的答案\uuuu init\uuuu
在向类实例添加属性方面甚至没有什么特别之处;它只是一个非常普通的方法,碰巧作为实例创建协议的一部分被调用。任何方法(或者完全来自另一个类的代码,或者根本不来自任何类)都可以在其引用的任何实例上创建属性
实际上,instance
的所有属性都存储在同一个位置:
>>> instance.__dict__
{'baz': 'baz set from C', 'foo': 'foo set from D', 'bar': 'not set from a class at all'}
没有办法知道它们中的哪一个最初是由哪个类设置的,或者是最后由哪个类设置的,或者任何您想要的所有权度量。当然,没有办法让“<代码> A.foo <代码>被<代码> D.foo < /C> >所遮蔽,正如你所期望的C++;它们是相同的属性,一个类(或从其他地方)对其进行的任何写入都将破坏另一个类在其中留下的值
其结果是super()
执行属性查找的方式与执行方法查找的方式不同;它不能,你写的任何代码也不能
事实上,通过运行一些实验,无论是
super
还是Sven的Delegate
实际上都不支持直接属性检索
class A:
def __init__(self):
self.spoon = 1
self.fork = 2
def foo(self):
print('A.foo')
class B(A):
def foo(self):
print('B.foo')
b = B()
d = Delegate(A, b)
s = super(B, b)
然后,这两种方法都按预期工作:
>>> d.foo()
A.foo
>>> s.foo()
A.foo
但是:
然后:
我依靠类的\uuumro\uuuu
属性来正确地确定从何处开始,然后我只使用super
。你可以从这一点开始走MRO链,检查类\uuu dict\uuu
s的方法,如果返回一步使用super
的奇怪之处太多了
我没有试图处理不寻常的属性;那些用描述符(包括属性)实现的,或者Pyth在幕后查找的神奇方法
>>> d.fork
Traceback (most recent call last):
File "<pyshell#43>", line 1, in <module>
d.fork
File "/tmp/foo.py", line 6, in __getattr__
x = getattr(self._delegate_cls, name)
AttributeError: type object 'A' has no attribute 'fork'
>>> s.spoon
Traceback (most recent call last):
File "<pyshell#45>", line 1, in <module>
s.spoon
AttributeError: 'super' object has no attribute 'spoon'
class Delegate:
def __init__(self, cls, obj):
self._delegate_cls = cls
self._delegate_obj = obj
def __getattr__(self, name):
x = getattr(self._delegate_cls, name)
if hasattr(x, "__get__"):
return x.__get__(self._delegate_obj)
return x
class A:
def foo(self):
print('A.foo')
class B:
pass
class C(B, A):
def foo(self):
print('C.foo')
c = C()
d = Delegate(B, c)
s = super(C, c)
>>> d.foo()
Traceback (most recent call last):
File "<pyshell#50>", line 1, in <module>
d.foo()
File "/tmp/foo.py", line 6, in __getattr__
x = getattr(self._delegate_cls, name)
AttributeError: type object 'B' has no attribute 'foo'
>>> s.foo()
A.foo
class MROSkipper:
def __init__(self, cls, obj):
self.__cls = cls
self.__obj = obj
def __getattr__(self, name):
mro = self.__obj.__class__.__mro__
i = mro.index(self.__cls)
if i == 0:
# It's at the front anyway, just behave as getattr
return getattr(self.__obj, name)
else:
# Check __dict__ not getattr, otherwise we'd find methods
# on classes we're trying to skip
try:
return self.__obj.__dict__[name]
except KeyError:
return getattr(super(mro[i - 1], self.__obj), name)