我只是试图简化我的一个类,并引入了一些与flyweight设计模式相同风格的功能。

然而,我有点困惑,为什么__init__总是在__new__之后被调用。我没想到会这样。有人能告诉我为什么会发生这种情况,我如何才能实现这个功能吗?(除了将实现放在__new__中,这感觉相当粗糙。)

这里有一个例子:

class A(object):
    _dict = dict()

    def __new__(cls):
        if 'key' in A._dict:
            print "EXISTS"
            return A._dict['key']
        else:
            print "NEW"
            return super(A, cls).__new__(cls)

    def __init__(self):
        print "INIT"
        A._dict['key'] = self
        print ""

a1 = A()
a2 = A()
a3 = A()

输出:

NEW
INIT

EXISTS
INIT

EXISTS
INIT

Why?


当前回答

我认为这个问题的简单答案是,如果__new__返回一个与类类型相同的值,__init__函数将执行,否则它不会执行。在这种情况下,您的代码返回A._dict('key'),它与cls是同一个类,因此将执行__init__。

其他回答

__init__被调用在__new__之后,所以当你在子类中重写它时,你添加的代码仍然会被调用。

如果你试图子类化一个已经有__new__的类,不知道这一点的人可能会通过调整__init__开始,并将调用转发到子类__init__。这种在__new__之后调用__init__的约定有助于按预期工作。

__init__仍然需要允许超类__new__所需的任何参数,但如果不这样做,通常会产生一个明确的运行时错误。__new__可能应该显式地允许使用*args和'**kw',以清楚地表明扩展是OK的。

在同一个类中同时拥有__new__和__init__在同一继承级别通常是不好的形式,因为原来的海报所描述的行为。

__new__是静态类方法,而__init__是实例方法。 __new__必须先创建实例,所以__init__可以初始化它。注意,__init__以self作为参数。在你创造实例之前,没有自我。

现在,我猜想,您正在尝试用Python实现单例模式。有几种方法可以做到这一点。

此外,从Python 2.6开始,您可以使用类装饰器。

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
  ...

当你需要控制时使用__new__ 创建一个新实例。

使用 __init__当你需要控制一个新实例的初始化时。 __new__是创建实例的第一步。它叫做first,并且是 负责退货 类的实例。

相比之下, __init__不返回任何东西;它只负责初始化 实例。 一般来说,你不需要这样做 重写__new__,除非你 继承一个不可变类型的子类 Str, int, unicode或tuple。

从2008年4月的帖子:什么时候使用__new__ vs. __init__?在mail.python.org上。

您应该考虑到您正在尝试做的事情通常是通过Factory完成的,这是最好的方法。使用__new__不是一个好的干净的解决方案,所以请考虑使用工厂。下面是一个很好的例子:ActiveState FᴀᴄᴛᴏʀʏᴘᴀᴛᴛᴇʀɴRecipe。

再深入一点!

CPython中泛型类的类型是type,它的基类是Object(除非你显式地定义了另一个基类,比如元类)。低级调用的序列可以在这里找到。第一个调用的方法是type_call,然后调用tp_new和tp_init。

这里有趣的部分是tp_new将调用对象的(基类)new方法object_new,该方法执行tp_alloc (PyType_GenericAlloc),为对象分配内存:)

此时在内存中创建对象,然后调用__init__方法。如果__init__没有在你的类中实现,那么object_init会被调用,它什么都不做:)

然后type_call只返回绑定到变量的对象。

@AntonyHatchkins回答的更新,您可能需要为元类型的每个类创建一个单独的实例字典,这意味着您应该在元类中使用__init__方法来使用该字典初始化类对象,而不是使它在所有类中都是全局的。

class MetaQuasiSingleton(type):
    def __init__(cls, name, bases, attibutes):
        cls._dict = {}

    def __call__(cls, key):
        if key in cls._dict:
            print('EXISTS')
            instance = cls._dict[key]
        else:
            print('NEW')
            instance = super().__call__(key)
            cls._dict[key] = instance
        return instance

class A(metaclass=MetaQuasiSingleton):
    def __init__(self, key):
        print 'INIT'
        self.key = key
        print()

我已经继续使用__init__方法更新了原始代码,并将语法更改为Python 3符号(在类参数中无参数调用super和元类,而不是作为属性)。

无论哪种方式,这里的重点是,如果找到键,您的类初始化器(__call__方法)将不会执行__new__或__init__。这比使用__new__干净得多,如果你想跳过默认的__init__步骤,则需要标记对象。