0%

Python_new与init

乍看一下上面两个方法功能相似,但其是其存在区别。

1
2
__new__方法:用来构造一个类实例;
__init__方法:用于初始化一个实例;

那么从上面的差别来看,很明显需要首先调用new方法,构造实例后,才能够调用init初始化该实例。

两个方法的参数差异:

1
2
__new__:方法用于构造实例,调用方法时传入的是类(CLS),而非实例(INSTANCE);
__init__:方法用于初始化实例,调用方法时传入的是实例(INSTANCE)

相关知识:旧类与新类

在Python2中存在旧类与新类的概念,其中旧类指的是继承自type元类的类,而新类是继承自object类的类。

旧类代码:

1
2
class oldStyleClass: # inherits from 'type'
pass

新类代码

1
2
class newStyleClass(object): # explicitly inherits from 'object'
pass

在Python3中所有类默认继承自object,并不需要object作为基类;

我们现在就了解Python2中存在旧类与新类,他们只是继承的父类不同,那么也就继承了父类的方法与属性;相应的对new与init方法存在差异。

Python旧类中两方法的差异

  • 旧类中没有new方法,其init方法起到了构造的作用;
  • 旧类中init方法的返回值无法进行控制;

定义一个旧类

1
2
3
4
5
6
class oldStyleClass:
def __new__(cls):
print("__new__ is called") # this line will never get called during construction

oldStyleClass()
# <__main__.oldStyleClass instance at 0x0000000003C1A5C8>

可以看到即使,我们构造了一个new方法,在旧类的初始化过程中仍然不会调用它;

重载init方法时:

1
2
3
4
5
6
7
8
9
10
11
class oldStyleClass:
def __new__(cls):
print("__new__ is called") # this line will never get called during construction
def __init__(self):
print(self)
print("init is called")

oldStyleClass()
# <__main__.oldStyleClass instance at 0x0000000003C2AE48>
# init is called
# <__main__.oldStyleClass instance at 0x0000000003C2AE48>

我们发现在函数的一开始就已经构造好了实例,并且返回了实例;

带返回值的init:

1
2
3
4
5
6
7
8
9
10
11
12
13
class oldStyleClass:
def __new__(cls):
print("__new__ is called") # this line will never get called during construction
def __init__(self):
print(self)
print("init is called")
return 12

oldStyleClass()
# <__main__.oldStyleClass instance at 0x00000000053BD708>
# init is called
# TypeError: __init__() should return None

在旧类中无法控制,init的返回值。

Python新类中两方法差异

Python的新类允许用户重载__new____init__方法,且这两个方法具有不同的作用。__new__作为构造器,起创建一个类实例的作用。而__init__作为初始化器,起初始化一个已被创建的实例的作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class newStyleClass(object): 
# In Python2, we need to specify the object as the base.
# In Python3 it's default.

def __new__(cls):
print("__new__ is called")
print(cls)
return super(newStyleClass, cls).__new__(cls)

def __init__(self):
print("__init__ is called")
print("self is: ", self)

newStyleClass()
# __new__ is called
# <class '__main__.newStyleClass'>
# __init__ is called
# ('self is: ', <__main__.newStyleClass object at 0x0000000005440DD8>)
# <__main__.newStyleClass at 0x5440dd8>

创建类实例并初始化的过程中__new____init__被调用的顺序也能从上面代码的输出结果中看出:__new__函数首先被调用,构造了一个newStyleClass的实例,接着__init__函数在__new__函数返回一个实例的时候被调用,并且这个实例作为self参数被传入了__init__函数。

这里需要注意的是,如果__new__函数返回一个已经存在的实例(不论是哪个类的),__init__不会被调用。如下面代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
obj = 12 
# obj can be an object from any class, even object.__new__(object)

class returnExistedObj(object):
def __new__(cls):
print("__new__ is called")
return obj

def __init(self):
print("__init__ is called")

returnExistedObj()
# __new__ is called
# 12

同时另一个需要注意的点是:

如果我们在__new__函数中不返回任何对象,则__init__函数也不会被调用。

1
2
3
4
5
6
7
8
9
10
class notReturnObj(object):
def __new__(cls):
print("__new__ is called")

def __init__(self):
print("__init__ is called")

print(notReturnObj())
# __new__ is called
# None

可见如果__new__函数不返回对象的话,不会有任何对象被创建,__init__函数也不会被调用来初始化对象。

总结

  1. __init__不能有返回值
  2. __new__函数直接上可以返回别的类的实例。如上面例子中的returnExistedObj类的__new__函数返回了一个int值。
  3. 只有在__new__返回一个新创建属于该类的实例时当前类的__init__才会被调用。

参考博客