Python 里的 super() 函数和 object.__new__ ()、.__del__() 方法

object.__new__()

自定义 __new__() 方法一般并不常用,而且容易与 __init__() 相混淆。实际上这两个方法也确实相似,比如他们都是在创建类的实例时会被默认调用的方法,而且创建实例时传入的参数也都会传到这两个方法里。但他们也有很重要的区别,比如对 __new__() 的调用比 __init__() 要早,__new__() 有返回值,__init__() 木有。

来理一下创建类的实例的过程,即解释器处理 foo = MyClass() 这条语句的步骤的话,大约是这样的(可能并不完全准确):

  1. 创建 foo 这个名字
  2. 调用 MyClass 类的 __new__() 静态方法,返回一个类实例
  3. 将__new__() 返回的类实例赋值给 foo
  4. 这个实例被当做 self 参数传给 __init__(),以完成该实例的初始化工作

即,真正完成构造实例工作的是 __new__() 方法,调用它需要一个默认参数 cls,就是将要返回的这个实例所属的类(MyClass)。一般情况下因为 __new__() 极少被覆盖,最终调用的都是 object.__new__()。这个时候我们的实例已经被创建了,就可以当做 self 参数传给 __init__() 了,__init__() 做的工作其实仅是初始化一些属性值之类的,与严格意义下的“构造”实例无关。

这里拿出 __new__() 来说主要是昨晚看到一道题,说是如何用 Python 实现单例模式,就是某个类同时只允许存在一个实例的意思。比较直接的想法应该是在类属性里添加一个布尔开关(比如就叫 cer 好了),创建实例时检查 cer 的值,True 就允许创建,False 就引发异常。那么这个检查的过程如果安装在 __init__() 里肯定是不行的,因为那时实例已经被创建出来了,所以正确的检查位置应该是 __new__() 里面。第二点,cer 这个开关还应该在第一个实例被删除时自动打回到 True 上,所以还需要覆盖 __del__() 方法:

class Singleton(object):
    def __new__(cls):
        if cls.cer:
            cls.cer = False
            return super().__new__(cls)
        else:raise TypeError('[%s] could be instantiated only once!'%cls.__name__)

    cer = True

    def __del__(self):
        self.__class__.cer = True

运行如下:

>>> a = Singleton()
>>> a
<__main__.Singleton object at 0x0000000009CAC630>
>>> b = Singleton()
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    b = Singleton()
  File "C:\Users\July\Desktop\test.py", line 8, in __new__
    else:raise TypeError('[%s] could be instantiated only once!'%cls.__name__)
TypeError: [Singleton] could be instantiated only once!
>>> b
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    b
NameError: name 'b' is not defined
>>> del a
>>> b = Singleton()
>>> b
<__main__.Singleton object at 0x0000000009CAC358>

看起来非常好,不过上面的运行结果其实被删减过,实际上很有可能:del a 之后也创建不出 b 实例,即那时 Singleton.cer 的值仍为 False。为什么呢,这就要从 Python 的垃圾回收器说起了。

object.__del__()

Python 靠引用计数的机制来运行垃圾回收器,所以 del a 这条语句做的事情其实仅仅是将 a 所指向的实例的引用数 -1(当然 a 这个名字也被删掉了),实例的 __del__() 方法不一定会得到执行(因为不排除还有其他的引用指向该实例)。只有当该实例的引用计数减到 0 的时候,垃圾回收器才会来处理它,并调用 __del__() 方法。不过即使像上面的例子那样,实例只有 a 这一个引用,并且我们也删掉了这个引用,回收器也是不一定会立即回收掉该实例的。你很可能需要“等一会”。。。

如果不想等的话,则可以手动运行一次回收器。回收器有一个名为 gc 的模块接口,运行 gc.collect() 函数就可以手动回收,所以上面例子的完美运行结果其实是这样子的:

>>> a = Singleton()
>>> a
<__main__.Singleton object at 0x0000000009CAC630>
>>> b = Singleton()
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    b = Singleton()
  File "C:\Users\July\Desktop\test.py", line 8, in __new__
    else:raise TypeError('[%s] could be instantiated only once!'%cls.__name__)
TypeError: [Singleton] could be instantiated only once!
>>> b
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    b
NameError: name 'b' is not defined
>>> del a
>>> Singleton.cer
False
>>> import gc
>>> gc.collect()
0
>>> Singleton.cer
True
>>> b = Singleton()
>>> b
<__main__.Singleton object at 0x0000000009CAC358>

可以看到这个 object.__del__() 方法并不怎么靠谱,所以一般还是能不用就不用吧,或者至少知道他们可能不会被立即调用,又或者你非要把 gc.collect() 写进 __new__() 里也行。

super()

在子类中覆盖父类的方法,添加一些代码然后再把父类的同名方法调用起来是一种满典型的用法,所以这里 super() 出现的机会也多。一般我们想要得到一个绑定的父类,都是这样调用 super:

super(type, obj) -> bound super object; requires isinstance(obj, type)       

super(type, type2) -> bound super object; requires issubclass(type2, type)

比如:

class P(object):
    def __init__(self):
        print('calling __init__ of P')

class C(P):
    def __init__(self):
        return super(C,self).__init__()

子类的 C 里面调用父类 P 的 __init__() 方法(绑定的),就给 super() 函数传了 C 和 C 的实例 self 两个参数表示绑定到 self 上的 P 类。这里也可以用 __class__ 、self.__class__ 或者 type(self) 来代替直接给出当前类的名字 C。

不过因为这种使用 super() 的方式太典型了,所以在 Python3 里可以无参数使用 super() 函数,效果和传两个参数一样:

super() -> same as super(__class__, <first argument>)

这里 <first argument>指代上面的 obj 或者 type2(或者说 self 和 cls),都是可绑定的对象。这也就是最上面的代码没有给 super() 传参数也可以运行的原因。至于为什么最开始的例子里 super().__new__(cls)还给传了个 cls 参数,这是因为 __new__ 方法是静态方法,不能像 super().__init__() 一样因为 super 对象已经绑定了 self 就不给 __init__() 传 self 了。

另外如果不需要绑定,可以只给 super() 传一个参数:

super(type) -> unbound super object

最后要说的是调用父类方法时的语句,可以看到上面的两个例子不管父类方法有没有返回值(__new__ 或 __init__),都使用 return 来调用。这样的好处在于不用去关心该方法到底有没有返回值,就算没有,return expression 的 expression 也一样会被执行,然后 return 一个 None,这显然没什么坏处。

    原文作者:python入门
    原文地址: https://my.oschina.net/lionets/blog/193900
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞