使用类的__new__方法作为工厂:__init__被调用两次

发布于 2021-01-29 18:16:17

我在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

任何帮助,不胜感激。

关注者
0
被浏览
183
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    当构造一个对象时,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)
    

    它会创建一个RectangleTriangle不触发调用__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类,该类可以修改形状的构造行为,并且可以具有从现有的三角形和矩形继承但构造不同的专用三角形和矩形。



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看