When you construct an object Python calls its __new__
method to create the object then calls __init__
on the object that is returned. When you create the object from inside __new__
by calling Triangle()
that will result in further calls to __new__
and __init__
.
What you should do is:
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)
which will create a Rectangle
or Triangle
without triggering a call to __init__
and then __init__
is called only once.
Edit to answer @Adrian's question about how super works:
super(Shape,cls)
searches cls.__mro__
to find Shape
and then searches down the remainder of the sequence to find the attribute.
Triangle.__mro__
is (Triangle, Shape, object)
and
Rectangle.__mro__
is (Rectangle, Shape, object)
while Shape.__mro__
is just (Shape, object)
.
For any of those cases when you call super(Shape, cls)
it ignores everything in the mro squence up to and including Shape
so the only thing left is the single element tuple (object,)
and that is used to find the desired attribute.
This would get more complicated if you had a diamond inheritance:
class A(object): pass
class B(A): pass
class C(A): pass
class D(B,C): pass
now a method in B might use super(B, cls)
and if it were a B instance would search (A, object)
but if you had a D
instance the same call in B
would search (C, A, object)
because the D.__mro__
is (B, C, A, object)
.
So in this particular case you could define a new mixin class that modifies the construction behaviour of the shapes and you could have specialised triangles and rectangles inheriting from the existing ones but constructed differently.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…