当你构造一个对象时,Python 会调用它的__new__
创建对象的方法然后调用__init__
在返回的对象上。当您从内部创建对象时__new__
通过致电Triangle()
这将导致进一步致电__new__
and __init__
.
你应该做的是:
class Shape(object):
def __new__(cls, desc):
if cls is Shape:
if desc == 'big': return super(Shape, cls).__new__(Rectangle)
if desc == 'small': return super(Shape, cls).__new__(Triangle)
else:
return super(Shape, cls).__new__(cls, desc)
这将创建一个Rectangle
or Triangle
而不触发调用__init__
进而__init__
仅被调用一次。
编辑回答@Adrian 关于 super 如何工作的问题:
super(Shape,cls)
搜索cls.__mro__
找到Shape
然后向下搜索序列的其余部分以查找属性。
Triangle.__mro__
is (Triangle, Shape, object)
and
Rectangle.__mro__
is (Rectangle, Shape, object)
while Shape.__mro__
只是(Shape, object)
。
对于任何这些情况,当您致电时super(Shape, cls)
它忽略 mro 序列中的所有内容,包括Shape
所以剩下的就是单元素元组(object,)
用于查找所需的属性。
如果您拥有钻石继承权,这会变得更加复杂:
class A(object): pass
class B(A): pass
class C(A): pass
class D(B,C): pass
现在 B 中的方法可能会使用super(B, cls)
如果是 B 实例,则会搜索(A, object)
但如果你有一个D
实例相同的调用B
会搜索(C, A, object)
因为D.__mro__
is (B, C, A, object)
.
因此,在这种特殊情况下,您可以定义一个新的 mixin 类来修改形状的构造行为,并且您可以拥有从现有三角形和矩形继承但以不同方式构造的专用三角形和矩形。