Когда вы создаете объект, 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__
вызывается только один раз.
Изменить, чтобы ответить на вопрос Адриана о том, как супер работает:
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, который изменяет поведение построения фигур, и вы можете иметь специализированные треугольники и прямоугольники, наследующие от существующих, но построенные по-другому.