我只是试图简化我的一个类,并引入了一些与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?


当前回答

One should look at __init__ as a simple constructor in traditional OO languages. For example, if you are familiar with Java or C++, the constructor is passed a pointer to its own instance implicitly. In the case of Java, it is the this variable. If one were to inspect the byte code generated for Java, one would notice two calls. The first call is to an "new" method, and then next call is to the init method (which is the actual call to the user defined constructor). This two step process enables creation of the actual instance before calling the constructor method of the class which is just another method of that instance.

现在,在Python中,__new__是一个用户可以访问的附加功能。由于Java的类型化特性,它没有提供这种灵活性。如果一种语言提供了这种功能,那么__new__的实现者可以在返回实例之前在该方法中做很多事情,包括在某些情况下为不相关的对象创建一个全新的实例。而且,这种方法也适用于Python中的不可变类型。

其他回答

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

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

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

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

当实例化一个类时,首先调用__new__()来创建类的实例,然后调用__init__()来初始化实例。

__new__ ():

调用它来创建类cls. ...的新实例 如果在对象构造期间调用__new__(),它返回一个 实例,则新实例的__init__()方法将为 像__init__(self[,…])一样调用,…

__init__ ():

在实例创建后调用(通过__new__()),… 因为__new__()和__init__()在构造对象时一起工作 (__new__()来创建它,__init__()来定制它),…

例如,在实例化Teacher类时,首先调用__new__()来创建Teacher类的实例,然后调用__init__()来初始化实例,如下所示:

class Teacher:
    def __init__(self, name):
        self.name = name
        
class Student:
    def __init__(self, name):
        self.name = name

obj = Teacher("John") # Instantiation

print(obj.name)

输出如下:

<class '__main__.Teacher'>
John

并且,使用Teacher类实例的__new__(),我们可以创建Student类的实例,如下所示:

# ...

obj = Teacher("John")
print(type(obj))
print(obj.name)

obj = obj.__new__(Student) # Creates the instance of "Student" class
print(type(obj))

现在,创建了Student类的实例,如下所示:

<class '__main__.Teacher'>
<__main__.Teacher object at 0x7f4e3950bf10>
<class '__main__.Student'> # Here

接下来,如果我们尝试从Student类的**实例中获取name变量的值,如下所示:

obj = Teacher("John")
print(type(obj))
print(obj.name)

obj = obj.__new__(Student)
print(type(obj))
print(obj.name) # Tries to get the value of "name" variable

发生以下错误是因为Student类的实例还没有被__init__()初始化:

AttributeError: 'Student'对象没有属性'name'

因此,我们初始化Student类的实例,如下所示:

obj = Teacher("John") 
print(type(obj))
print(obj.name)

obj = obj.__new__(Student)
print(type(obj))
obj.__init__("Tom") # Initializes the instance of "Student" class
print(obj.name)

然后,我们可以从Student类的实例中获取name变量的值,如下所示:

<class '__main__.Teacher'>
John
<class '__main__.Student'>
Tom # Here

原因很简单,new用于创建实例,而init用于初始化实例。在初始化之前,应该先创建实例。这就是为什么new应该在init之前调用。

再深入一点!

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

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

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

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

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