我在YouTube上观看了关于Python元编程的精彩视频。我尝试编写以下代码(从视频中几乎相同):
class Descriptor:
def __init__(self, name):
self.name = name
def __get__(self, instance, cls):
return instance.__dict__[self.name]
def __set__(self, instance, val):
instance.__dict__[self.name] = val
def __delete__(self, instance):
del instance.__dict__[self.name]
class Type(Descriptor):
ty = object
def __set__(self, instance, val):
if not isinstance(val, self.ty):
raise TypeError("%s should be of type %s" % (self.name, self.ty))
super().__set__(instance, val)
class String(Type):
ty = str
class Integer(Type):
ty = int
class Positive(Descriptor):
def __set__(self, instance, val):
if val <= 0:
raise ValueError("Must be > 0")
super().__set__(instance, val)
class PositiveInteger(Integer, Positive):
pass
class Person(metaclass=StructMeta):
_fields = ['name', 'gender', 'age']
name = String('name')
gender = String('gender')
age = PositiveInteger('age')
所以PositiveInteger
继承自Integer
和Positive
,并且这两个类都定义了__get__
方法来进行一些验证。我写了一些测试代码来说服自己两种方法都会运行:
class A:
def test(self):
self.a = 'OK'
class B:
def test(self):
self.b = 'OK'
class C(A, B):
pass
c = C()
c.test()
print(self.a)
print(self.b)
仅发现只有第一个print语句有效。第二个将引发一个AttributeError,表示当名称冲突时,第一个基类获胜。
所以我想知道为什么两种验证都有效?更奇怪的是,当只有整数检查通过时(例如person.age = -3),它的super().__set__(instance, val)
无效,让person.age保持不变。
答案 0 :(得分:2)
Positive
和Integer
的验证逻辑都会运行,因为Type
和Positive
都有__set__
中的这一行:
super().__set__(instance, val)
这不会跳到Descriptor.__set__
。相反,它调用method resolution order中的下一个方法。 Type.__set__
被调用,super().__set__(instance, val)
调用Positive.__set__
。 Positive.__set__
运行其验证并调用Descriptor.__set__
进行设置。这种行为是我们super
。
如果您希望test
方法表现得那样,您需要做两件事。首先,您需要使用A
方法从公共基类继承B
和test
,但不会执行任何操作,因此super
链终止在使用test
方法的地方而不是前往object
:
class Base:
def test():
pass
然后,您需要将super().test()
添加到A.test
和B.test
:
class A(Base):
def test(self):
self.a = 'OK'
super().test()
class B(Base):
def test(self):
self.b = 'OK'
super().test()
如需更多阅读,请参阅Python's super() considered super。
答案 1 :(得分:0)
抱歉,我的不好。
在我暂停并提出这个问题的几分钟后,视频给出了完美的解释。
因此,当多重继承发生时,每个类中定义的MRO事物(方法解析顺序)决定了super()
链中方法的分辨率顺序。
订单由深度优先搜索确定,例如
class A:
pass
class B(A):
pass
class C(B):
pass
class D(A):
pass
class E(C, D):
pass
E.__mro__
将是:
(<class '__main__.E'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>)
有一点需要注意的是,A会多次出现在继承树中,而在MRO列表中,它只会出现在所有A出现的最后一个位置。
这就是诀窍:对super()
的调用不一定会到达它的基础。相反,它会在MRO列表中找到接下来的内容。
所以要解释一下代码中会发生什么:
super()
中的Integer.__get__
电话(继承自Type.__get__
)不会转到Descriptor.__get__
,因为Descriptor
最后出现在MRO列表中。它将落入Positive.__set__
,然后其super()
将落入Descriptor
,最终将设置属性的值。