魔法方法之 __new__() 与 __init__()

当我们实例化某个类时,如:a = A(), Python 先调用 __new__(cls[, ...]) 来生成实例

然后传递给 __init__(self[, ...]) 的 self,从而在 __init__(self[, ...]) 中完成初始化

因此 __new__() 负责创建实例对象,而 __init__() 负责初始化该实例对象:

class A:
    def __new__(cls, *args, **kwargs): # cls(即A)为第一个默认参数,剩下的就是实例化A(即A([...]))时传进来的参数
        print('call A.__new__()')
        return super().__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        print('call A.__init__()')

a = A()

输出:

call A.__new__()
call A.__init__()

__new__()

下面先通过对官方文档的解读来看看 __new__() 方法背后的一些细节:

object.__new__(cls[, ...])

Called to create a new instance of class cls. __new__() is a static method (special-cased so you need not declare it as such) that takes the class of which an instance was requested as its first argument. The remaining arguments are those passed to the object constructor expression (the call to the class). The return value of __new__()should be the new object instance (usually an instance of cls).

其实 __new__() 是一个类的静态方法,这里我更加认为是一个类方法

因为其第一个参数默认就是cls,而静态方法不需要cls也不需要self

但注意到这里并没有使用装饰器 @classmethod,这是 __new__() 的特殊之处——不用装饰器修饰就可以成为类方法

__new__() 负责创建类的实例,因此它的返回值应该是一个新的实例,且通常是 cls 的实例。

(注意体会加粗字的含义,后面会解释)

那如何创建 cls 的实例呢?

Typical implementations create a new instance of the class by invoking the superclass’s __new__() method using super().__new__(cls[, ...]) with appropriate arguments and then modifying the newly-created instance as necessary before returning it.

可以通过调用父类的 __new__() 方法来创建:super().__new__(cls[, ...])

然后根据需求更改新创建的实例再返回,以此达到自定义创建实例的目的

If __new__() returns an instance of cls, then the new instance’s __init__() method will be invoked like __init__(self[, ...]), where self is the new instance and the remaining arguments are the same as were passed to__new__().

__new__() 有个很重要的特性就是:如果它返回是参数中 cls 的实例

那么 Python 就会自动调用 cls 的 __init__(self[, ...]) 方法,其中的 self 参数就是 __new__() 返回的 cls 的实例

也就是说我们可以从两个方面去假设(分别对应前面提到的两个加粗字体):

  1. 假如我们不返回实例对象,比如直接返回 None,结果如何?(其实 None 也是一个实例对象,这里描述的不是很严肃)
  2. 假如我们返回实例对象,但我们不返回当前 cls 的实例对象,而是返回其他 cls 的实例对象,结果如何?

下面就用代码来实验一下:

# 第一个假设
class A:
    def __new__(cls, *args, **kwargs):
        print('call A.__new__()') 
        # 有时候就是这样,忘记返回一个实例对象,也是默认返回了 None

    def __init__(self, *args, **kwargs):
        print('call A.__init__()')

a = A()
print(type(a))

输出:

call A.__new__()
<class 'NoneType'>

可以看到,A.__init__() 并没有被调用,且发现 a 其实是一个 NoneType 类型的实例,为什么呢?

我们接着往下进行第二个假设的实验:

# 第二个假设
class A:
    def __new__(cls, *args, **kwargs):
        print('call A.__new__()')
        return super().__new__(B, *args, **kwargs) # 注意这里传给父类的cls是B,则返回的是B的实例对象

    def __init__(self, *args, **kwargs):
        print('call A.__init__()')

class B:
    def __new__(cls, *args, **kwargs):
        print('call B.__new__()')
        return super().__new__(cls, *args, **kwargs) 

    def __init__(self, *args, **kwargs):
        print('call B.__init__()')


a = A()

输出:

call A.__new__()

可以看到 A.__init__() 并没有被调用,且没有调用 B.__init__(),那我们就手动调用一下 a.__init__()

a.__init__()
print(type(a))

输出:

call B.__init__()
<class '__main__.B'>

这下都明白了,其实这里的 a 看起来是 A 的实例,毕竟实例化的时候是调用了 A,但实际上是 B 的实例。

于是对于以上的两个假设和实验,可以得出:

  1. __new__() 方法不仅可以生成本类的实例对象,还可以生成其他类的实例对象,这取决于它最终返回的实例对象是本类的还是其他类的。

  2. 若返回的是其他类的实例对象,则不会自动调用该实例对象的 __init__() 方法,需要我们手动调用来初始化:

If __new__() does not return an instance of cls, then the new instance’s __init__() method will not be invoked.

换句话说,如果 __new__() 返回的是本类的实例对象,那该实例的 __init__() 方法总是会被自动调用

这一点在用 __new__() 方法实现单例模式时要特别注意(设计模式之单例模式

__new__() is intended mainly to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation. It is also commonly overridden in custom metaclasses in order to customize class creation.

这里强调了 __new__() 的使用场景:

  1. 实现自定义的元类(metaclass)。先留个坑,这个后序文章会讨论;

  2. 实现单例模式。前面已经讨论过;

  3. 当我们需要继承内建的不可变类型时(如 int, str 或 tuple)。

    这时候我们需提供自定义实例化过程,因为如果在 __init__() 方法中可能会无效甚至出错。例如:

class CustomInt(int):
    def __init__(self, v):
        super().__init__(self, abs(v))

print(CustomInt(-1))

输出:

---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-17-875f61a070ee> in <module>()
      3         super().__init__(self, abs(v))
      4 
----> 5 print(CustomInt(-1))


<ipython-input-17-875f61a070ee> in __init__(self, v)
      1 class CustomInt(int):
      2     def __init__(self, v):
----> 3         super().__init__(self, abs(v))
      4 
      5 print(CustomInt(-1))


TypeError: object.__init__() takes no parameters

在 py3 下是直接报错的,而在 py2 下是输出 -1

也就是说直接通过在子类的 __init__() 调用父类的 __init__() 有时未必能达到我们期望的效果

但可以通过重写 __new__() 方法来实现:

class CustomInt(int):

    def __new__(cls, v):
        return super().__new__(cls, abs(v))

print(CustomInt(-1))
print(CustomInt(-1) + CustomInt(-2))

输出:

1
3

成功地实现了对 int 的继承。

__init__()

接下来看看 __init__() 方法背后的一些细节:

object.__init__(self[, ...])

Called after the instance has been created (by __new__()), but before it is returned to the caller. The arguments are those passed to the class constructor expression. If a base class has an __init__() method, the derived class’s __init__() method, if any, must explicitly call it to ensure proper initialization of the base class part of the instance; for example: super().__init__([args...]).

Because __new__() and __init__() work together in constructing objects (__new__() to create it, and __init__() to customize it), no non-None value may be returned by __init__(); doing so will cause a TypeError to be raised at runtime.

可以看到,__init__()__new__() 后被调用(前提是 __new__() 返回本类的实例对象)

它的参数除了第一个 self 参数由 __new__() 创建,剩下的参数都是来自调用者在实例化时传入的参数

也就是 object.__new__(cls[, ...]) 中 cls 后面的参数,如果这两处的参数不一致就会产生一个 TypeError:

class A:
    def __new__(cls): # 除了cls没有其他参数
        print('call __new__()')
        return super().__new__(cls) # 这里A的父类是object,它的 __new__() 参数只有cls

    def __init__(self, v1): # 有v1参数
        print(v1)

a = A(1) # 传进一个参数

输出:

---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-19-5effa0b239dd> in <module>()
      7         print(v1)
      8 
----> 9 a = A(1) # 传进一个参数


TypeError: __new__() takes 1 positional argument but 2 were given

这里在一开始的 __new__() 就报错了

class A:
    def __new__(cls, v1): # 有v1参数
        print('call __new__()')
        return super().__new__(cls)

    def __init__(self): # 除了self没有其他参数
        print(v1)

a = A(1) # 传进一个参数

输出:

call __new__()
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-20-fcc859b65f23> in <module>()
      7         print(v1)
      8 
----> 9 a = A(1) # 传进一个参数


TypeError: __init__() takes 1 positional argument but 2 were given

这里成功执行 __new__(),但到了 __init__() 就报错了

class A:
    def __new__(cls, v1): # 有v1参数
        print('call __new__()')
        return super().__new__(cls)

    def __init__(self, v1): ## 有v1参数
        print(v1)

a = A(1) # 传进一个参数

输出:

call __new__()
1

__new__()__init__() 除了第一个参数以外其他参数一致才不会出错

另外 __init__() 不允许返回非None 的对象,尝试这么做也会产生一个 TypeError:

class A:
    def __init__(self):
        return 1

a = A()

输出:

TypeError                                 Traceback (most recent call last)
<ipython-input-20-d00fb74e6c6d> in <module>()
      2     def __init__(self):
      3         return 1
----> 4 a = A()

TypeError: __init__() should return None, not 'int'

参考资料

https://docs.python.org/3/reference/datamodel.html#object.__new__

https://docs.python.org/3/reference/datamodel.html#object.__init__

文章目录
  1. 1. __new__()
  2. 2. __init__()
  3. 3. 参考资料
|