__new__()
是在新式类中新出现的方法,在Python2.7以前的版本在定义类时都要显示的继承object才能使用。
__new__()
方法始终都是类的静态方法,即使没有被加上静态方法装饰器。__new__
方法接受的参数虽然也是和__init__
一样,但__init__
是在类实例创建之后调用。
object类中对__new__()
方法的定义:
1 | class object: |
__new__
的作用
依照Python官方文档的说法:
__new__
方法主要是当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径;- 实现自定义的metaclass。
首先我们来看一下第一个功能,具体我们可以用int来作为一个例子:
假如我们需要一个永远都是正数的整数类型,通过继承int,我们可能会写出这样的代码。
1 | class PositiveInteger(int): |
但运行后会发现,结果根本不是我们想的那样,我们任然得到了-3。这是因为对于int这种不可变的对象,我们只有重载它的__new__
方法才能起到自定义的作用。
1 | class PositiveInteger(int): |
关于[实现自定义的metaclass]我会在另一篇文章中单独论述。
类的实例化
接下来我们来了解一下,Python解释器是如何实例化一个类的:
- 当我们实例化
A
类对象时,Python中首先调用的是该A
类对象的__new__
方法,如果该A
类对象没有定义__new__
方法,则去父类中依次查找,直到object
类; object
类有一个__new__
方法,该方法接收一个参数(一般为类对象),将该参数进行实例化并返回一个对象;- Python解释器会将调用
__new__
方法并将A
类对象作为第一个参数传入,最后会返回一个实例对象(这个对象就是A
类的实例对象,我们称之为a1
);__new__()
必须要有返回值,返回实例化出来的实例,可以return
父类__new__()
出来的实例,也可以直接将object的__new__()
出来的实例返回。 - Python解释器默认会调用
a1
对象的__init__
方法,并将参数传入。
实例化举例
实例化自身
1 | myclass = MyClass(*args, **kwargs) |
正如以上所示,一个类可以有多个位置参数和多个命名参数,而在实例化开始之后,在调用 __init__()
方法之前,Python首先调用__new__()
方法:
1 | def __new__(cls, *args, **kwargs): |
第一个参数cls是当前正在实例化的类。
如果(新式)类中没有重写__new__()
方法,即在定义新式类时没有重新定义__new__()
时 ,Python默认是调用该类的直接父类的__new__()
方法来构造该类的实例,如果该类的父类也没有重写__new__()
,那么将一直按此规矩追溯至object的__new__()
方法,因为object是所有新式类的基类。
而如果新式类中重写了__new__()
方法,那么你可以自由选择任意一个与该类有继承关系的类的__new__()
方法来制造实例,包括这个新式类的所有前代类和后代类,但是不能造成递归死循环。
实例化祖先类
1 | class Foo(object): |
实例化后代类
1 | class Foo(object): |
实例化无继承关系的类
在文章中实验说明如果
__new__()
没有返回cls
(即当前类)的实例,那么当前类的__init__()
方法是不会被调用的。如果__new__()
返回其他类的实例,那么只会调用被返回的那个类的构造方法。
我也尝试了这个实验,实验结论与以上说法不同:
如果__new__()
返回其他类的实例,那么只会实例化其他类,但不会调用其他类的__init__
方法。
1 | class Foo(object): |