当构造一个对象时,Python会调用其__new__
方法来创建该对象,然后__init__
在返回的对象上进行调用。当您__new__
通过调用从内部创建对象时Triangle()
,将导致对__new__
和的进一步调用__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
或Triangle
不触发调用__init__
,然后__init__
仅被调用一次。
编辑以回答@Adrian关于super如何工作的问题:
super(Shape,cls)
搜索cls.__mro__
以查找Shape
,然后向下搜索序列的其余部分以找到属性。
Triangle.__mro__
是(Triangle, Shape, object)
和Rectangle.__mro__
是(Rectangle, Shape, object)
,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类,该类可以修改形状的构造行为,并且可以具有从现有的三角形和矩形继承但构造不同的专用三角形和矩形。