python中类的实例化 __new__

__new__()是在新式类中新出现的方法,在Python2.7以前的版本在定义类时都要显示的继承object才能使用。

__new__()方法始终都是类的静态方法,即使没有被加上静态方法装饰器。__new__方法接受的参数虽然也是和__init__一样,但__init__是在类实例创建之后调用。

object类中对__new__()方法的定义:

1
2
3
4
5
class object:
@staticmethod # known case of __new__
def __new__(cls, *more): # known special case of object.__new__
""" T.__new__(S, ...) -> a new object with type S, a subtype of T """
pass

__new__的作用

依照Python官方文档的说法:

  1. __new__方法主要是当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径;
  2. 实现自定义的metaclass。

首先我们来看一下第一个功能,具体我们可以用int来作为一个例子:

假如我们需要一个永远都是正数的整数类型,通过继承int,我们可能会写出这样的代码。

1
2
3
4
5
6
7
8
class PositiveInteger(int):
def __init__(self, value):
super(PositiveInteger, self).__init__(self, abs(value))

i = PositiveInteger(-3)
print i

# -3

但运行后会发现,结果根本不是我们想的那样,我们任然得到了-3。这是因为对于int这种不可变的对象,我们只有重载它的__new__方法才能起到自定义的作用。

1
2
3
4
5
6
7
8
class PositiveInteger(int):
def __new__(cls, value):
return super(PositiveInteger, cls).__new__(cls, abs(value))

i = PositiveInteger(-3)
print i

# 3

关于[实现自定义的metaclass]我会在另一篇文章中单独论述。

类的实例化

接下来我们来了解一下,Python解释器是如何实例化一个类的:

  1. 当我们实例化A类对象时,Python中首先调用的是该A类对象的__new__方法,如果该A类对象没有定义__new__方法,则去父类中依次查找,直到object类;
  2. object类有一个__new__方法,该方法接收一个参数(一般为类对象),将该参数进行实例化并返回一个对象;
  3. Python解释器会将调用__new__方法并将A类对象作为第一个参数传入,最后会返回一个实例对象(这个对象就是A类的实例对象,我们称之为a1);__new__()必须要有返回值,返回实例化出来的实例,可以return父类__new__()出来的实例,也可以直接将object的__new__()出来的实例返回。
  4. Python解释器默认会调用a1对象的__init__方法,并将参数传入。

实例化举例

实例化自身

1
myclass = MyClass(*args, **kwargs)

正如以上所示,一个类可以有多个位置参数和多个命名参数,而在实例化开始之后,在调用 __init__()方法之前,Python首先调用__new__()方法:

1
2
def __new__(cls, *args, **kwargs):
return super(MyClass, cls).__new__(cls, *args, **kwargs)

第一个参数cls是当前正在实例化的类。

如果(新式)类中没有重写__new__()方法,即在定义新式类时没有重新定义__new__()时 ,Python默认是调用该类的直接父类的__new__()方法来构造该类的实例,如果该类的父类也没有重写__new__(),那么将一直按此规矩追溯至object的__new__()方法,因为object是所有新式类的基类。

而如果新式类中重写了__new__()方法,那么你可以自由选择任意一个与该类有继承关系的类__new__()方法来制造实例,包括这个新式类的所有前代类和后代类,但是不能造成递归死循环

实例化祖先类

1
2
3
4
5
6
7
8
9
10
11
12
class Foo(object):
def __init__(self, *args, **kwargs):
pass

def __new__(cls, *args, **kwargs):
return object.__new__(cls, *args, **kwargs)

class Child(Foo):
def __new__(cls, *args, **kwargs):
return object.__new__(cls, *args, **kwargs) # 直接调用了父类的父类的 new方法

child = Child()

实例化后代类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Foo(object):
def __init__(self, *args, **kwargs):
self.name = 'Foo'
print 'Foo init'

def __new__(cls, *args, **kwargs):
print 'Foo new'
return super(Foo, cls).__new__(Stranger, *args, **kwargs) #此处实例化的是Stranger

class Stranger(Foo):
def __init__(self, *args, **kwargs):
self.name = 'Stranger'
print 'Stranger init'

def __str__(self):
return 'stranger'

foo = Foo() # 此事调用了Foo的__new__ 方法和 Stranger的__init__方法
print foo.name

# Foo new
# Stranger init
# Stranger

实例化无继承关系的类

文章中实验说明如果__new__()没有返回cls(即当前类)的实例,那么当前类的__init__()方法是不会被调用的。如果__new__()返回其他类的实例,那么只会调用被返回的那个类的构造方法。

我也尝试了这个实验,实验结论与以上说法不同:

如果__new__()返回其他类的实例,那么只会实例化其他类,但不会调用其他类的__init__方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Foo(object):
def __init__(self, *args, **kwargs):
self.name = 'Foo'
print 'Foo init'

def __new__(cls, *args, **kwargs):
print 'Foo new'
return super(Foo, cls).__new__(Stranger, *args, **kwargs) #此处实例化的是Stranger

class Stranger(object):
def __init__(self, *args, **kwargs):
self.name = 'Stranger'
print 'Stranger init'

def __str__(self):
return 'stranger'

foo = Foo() # 只调用了Foo的__new__方法实例化了Stranger。
print type(foo)
print foo.name # 可以发现name没有被赋值,__init__方法没有被调用

# Foo new
# <class '__main__.Stranger'>
# ---------------------------------------------------------------------------
# AttributeError Traceback (most recent call last)
# <ipython-input-38-15ce8af5d890> in <module>()
# 20 foo = Foo()
# 21 print type(foo)
# ---> 22 print foo.name

# AttributeError: 'Stranger' object has no attribute 'name'

参考资料

Python中的new()方法与实例化

实例解析Python中的new特殊方法