使用类的__new__方法作为工厂:__init__被调用两次
我在python中遇到了一个奇怪的错误,其中将__new__
类的方法用作工厂会导致__init__
实例化类的方法被调用两次。
最初的想法是使用__new__
母类的方法根据所传递的参数返回其孩子之一的特定实例,而不必在类外声明工厂函数。
我知道使用工厂功能将是在此处使用的最佳设计模式,但是在项目的这一点上更改设计模式将非常昂贵。因此,我的问题是:在这种模式中,是否有办法避免重复调用__init__
而仅获得单个调用__init__
?
class Shape(object):
def __new__(cls, desc):
if cls is Shape:
if desc == 'big': return Rectangle(desc)
if desc == 'small': return Triangle(desc)
else:
return super(Shape, cls).__new__(cls, desc)
def __init__(self, desc):
print "init called"
self.desc = desc
class Triangle(Shape):
@property
def number_of_edges(self): return 3
class Rectangle(Shape):
@property
def number_of_edges(self): return 4
instance = Shape('small')
print instance.number_of_edges
>>> init called
>>> init called
>>> 3
任何帮助,不胜感激。
-
当构造一个对象时,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类,该类可以修改形状的构造行为,并且可以具有从现有的三角形和矩形继承但构造不同的专用三角形和矩形。