Python 裏的 super() 函數和 object.__new__ ()、.__del__() 方法

object.__new__()

自定義 __new__() 方法通常並不經常使用,並且容易與 __init__() 相混淆。實際上這兩個方法也確實類似,好比他們都是在建立類的實例時會被默認調用的方法,並且建立實例時傳入的參數也都會傳到這兩個方法裏。但他們也有很重要的區別,好比對 __new__() 的調用比 __init__() 要早,__new__() 有返回值,__init__() 木有。python

來理一下建立類的實例的過程,即解釋器處理 foo = MyClass() 這條語句的步驟的話,大約是這樣的(可能並不徹底準確):shell

  1. 建立 foo 這個名字
  2. 調用 MyClass 類的 __new__() 靜態方法,返回一個類實例
  3. 將__new__() 返回的類實例賦值給 foo
  4. 這個實例被當作 self 參數傳給 __init__(),以完成該實例的初始化工做

即,真正完成構造實例工做的是 __new__() 方法,調用它須要一個默認參數 cls,就是將要返回的這個實例所屬的類(MyClass)。通常狀況下由於 __new__() 極少被覆蓋,最終調用的都是 object.__new__()。這個時候咱們的實例已經被建立了,就能夠當作 self 參數傳給 __init__() 了,__init__() 作的工做其實僅是初始化一些屬性值之類的,與嚴格意義下的「構造」實例無關。express

這裏拿出 __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

運行以下:ui

>>> 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 的垃圾回收器提及了。code

object.__del__()

Python 靠引用計數的機制來運行垃圾回收器,因此 del a 這條語句作的事情其實僅僅是將 a 所指向的實例的引用數 -1(固然 a 這個名字也被刪掉了),實例的 __del__() 方法不必定會獲得執行(由於不排除還有其餘的引用指向該實例)。只有當該實例的引用計數減到 0 的時候,垃圾回收器纔會來處理它,並調用 __del__() 方法。不過即便像上面的例子那樣,實例只有 a 這一個引用,而且咱們也刪掉了這個引用,回收器也是不必定會當即回收掉該實例的。你極可能須要「等一會」。。。orm

若是不想等的話,則能夠手動運行一次回收器。回收器有一個名爲 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:ci

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,這顯然沒什麼壞處。

相關文章
相關標籤/搜索