当前位置: 首页 > 面试题库 >

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

娄利
2023-03-14
问题内容

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

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



 类似资料:
  • 本文向大家介绍浅谈python中的__init__、__new__和__call__方法,包括了浅谈python中的__init__、__new__和__call__方法的使用技巧和注意事项,需要的朋友参考一下 前言 本文主要给大家介绍关于python中__init__、__new__和__call__方法的相关内容,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍: 任何事物都有一个从

  • 问题内容: 如果我有一个python类为: 然后定义一个子类,例如: 如果基类的init函数接受某些参数,而我将它们作为子类的init函数的参数,则如何将这些参数传递给基类? 我写的代码是: 我要去哪里错了? 问题答案: 你可以用 您的缩进不正确,这是修改后的代码: 这是输出:

  • 问题内容: 在讨论元类时,文档指出: 当然,您也可以覆盖其他类方法(或添加新方法)。例如,在元类中定义自定义方法可在调用类时允许自定义行为,例如,并非总是创建新实例。 我的问题是:假设我想在调用类时具有自定义行为,例如缓存而不是创建新对象。我可以通过重写类的方法来做到这一点。什么时候我想用它定义一个元类?这种方法带来了什么是无法实现的? 问题答案: 直接回答你的问题是:当你想要做 更多的 不仅仅是

  • 问题内容: 我了解都和工作。我想知道是否有什么可以做的? 即可以使用以下模式替换: 我想问的是,我想让Python OO的这一方面更适合我。 问题答案: guido帖子中的一个可能答案(感谢@ fraca7): 例如,在pickle模块中,用于反序列化对象时创建实例。在这种情况下,将创建实例,但不会调用该方法。 还有其他类似的答案吗? 我接受这个答案是对我自己的问题的“是”: 我想知道是否有什么可

  • 问题内容: 在最近的问题中,发布者具有以下有趣的代码行: 看到这一点我感到很惊讶。我只看过用于枚举值的 前导点 符号。在这种情况下,is是type,并且是返回的class方法。 为什么这样做?这是调用工厂方法的合法方法吗? 问题答案: 此 功能 称为“ 隐式成员表达式 ” 隐式成员表达式是在类型推断可以确定隐式类型的上下文中访问类型成员(例如枚举用例 或类方法 )的缩写方式。它具有以下形式: 但是

  • 这个__new__确实很少见到,先做了解吧. __new__是一个静态方法,而__init__是一个实例方法. __new__方法会返回一个创建的实例,而__init__什么都不返回. 只有在__new__返回一个cls的实例时后面的__init__才能被调用. 当创建一个新实例时调用__new__,初始化一个实例时用__init__. stackoverflow ps: __metaclass_