2014-05-25 40 views
1

我在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') 

所以PositiveIntegerIntegerPositive继承的,这两个类都定义做一些验证__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) 

只发现只有第一个打印语句有效。第二个会引发一个AttributeError,这表明当名称冲突时,第一个基类将获胜。

所以我想知道为什么两个验证工作?更奇怪的是,当只有整数检查通过(例如person.age = -3)时,它的super().__set__(instance, val)没有效果,使person.age保持不变。

回答

2

两个PositiveInteger运行的验证逻辑,因为这两个TypePositive这一行中__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和用test方法并不做任何公共基类继承B,所以super链在一个地方结束与test方法,而不是去object

class Base: 
    def test(): 
     pass 

然后,你需要添加super().test()A.testB.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

+0

感谢您的出色解释!我发布了自己的答案,并不知道有人已经回答了这个问题,直到我刷新了页面。 :) –

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,最终将设置该属性的值。