-
入門
-
構造和初始化
-
- 構造定製類
-
- 用於比較的魔術方法
- 用於數值處理的魔術方法
-
表現你的類
-
控制屬性訪問
-
創建定製序列
-
反射
-
可以調用的對象
-
會話管理器
-
創建描述器對象
-
持久化對象
-
總結
-
附錄
介紹
此教程爲我的數篇文章中的一個重點。主題是魔術方法。 什麼是魔術方法?他們是面向對象的Python的一切。他們是可以給你的類增加”magic”的特殊方法。他們總是被雙下劃線所包圍(e.g. __init__
或者 __lt__
)。然而他們的文檔卻遠沒有提供應該有的內容。Python中所有的魔術方法均在Python官方文檔中有相應描述,但是對於他們的描述比較混亂而且組織比較鬆散。很難找到有一個例子(也許他們原本打算的很好,在開始語言參考中有描述很詳細,然而隨之而來的確是枯燥的語法描述等等)。
所以,爲了修補我認爲Python文檔應該修補的瑕疵,我決定給Python中的魔術方法提供一些用平淡的語言和實例驅使的文檔。我在開始已經寫了數篇博文,現在在這篇文章中對他們進行總結。
我希望你能夠喜歡這篇文章。你可以將之當做一個教程,一個補習資料,或者一個參考。本文章的目的僅僅是爲Python中的魔術方法提供一個友好的教程。
構造和初始化
每個人都知道一個最基本的魔術方法, __init__
。通過此方法我們可以定義一個對象的初始操作。然而,當我調用 x = SomeClass()
的時候, __init__
並不是第一個被調用的方法。實際上,還有一個叫做 __new__
的方法,來構造這個實例。然後給在開始創建時候的初始化函數來傳遞參數。在對象生命週期的另一端,也有一個 __del__
方法。我們現在來近距離的看一看這三個方法:
__new__(cls, [...)
__new__
是在一個對象實例化的時候所調用的第一個方法。它的第一個參數是這個類,其他的參數是用來直接傳遞給 __init__
方法。__new__
方法相當不常用,但是它有自己的特性,特別是當繼承一個不可變的類型比如一個tuple或者string。我不希望在 __new__
上有太多細節,因爲並不是很有用處,但是在 Python文檔 中有詳細的闡述。
__init__(self, […)
此方法爲類的初始化方法。當構造函數被調用的時候的任何參數都將會傳給它。(比如如果我們調用 x = SomeClass(10, 'foo')
),那麼 __init__
將會得到兩個參數10和foo。 __init__
在Python的類定義中被廣泛用到。
__del__(self)
如果 __new__
和 __init__
是對象的構造器的話,那麼 __del__
就是析構器。它不實現語句 del x
(以上代碼將不會翻譯爲 x.__del__()
)。它定義的是當一個對象進行垃圾回收時候的行爲。當一個對象在刪除的時需要更多的清潔工作的時候此方法會很有用,比如套接字對象或者是文件對象。注意,如果解釋器退出的時候對象還存存在,就不能保證 __del__
能夠被執行,所以 __del__
can’t
serve as a replacement for good coding practices ()~~~~~~~
放在一起的話,這裏是一個 __init__
和 __del__
實際使用的例子。
讓定製的類工作起來
使用Python的魔術方法的最大優勢在於他們提供了一種簡單的方法來讓對象可以表現的像內置類型一樣。那意味着你可以避免醜陋的,違反直覺的,不標準的的操作方法。在一些語言中,有一些操作很常用比如:
在Python中你可以這樣。但是這會讓人迷惑且產生不必要的冗餘。相同的操作因爲不同的庫會使用不同的名字,這樣會產生不必要的工作。然而有了魔術方法的力量,我們可以定義一個方法(本例中爲 __eq__
),就說明了我們的意思:
這只是魔術方法的功能的一小部分。它讓你可以定義符號的含義所以我們可以在我們的類中使用。就像內置類型一樣。
用於比較的魔術方法
Python對實現對象的比較,使用魔術方法進行了大的逆轉,使他們非常直觀而不是笨拙的方法調用。而且還提供了一種方法可以重寫Python對對象比較的默認行爲(通過引用)。以下是這些方法和他們的作用。
__cmp__(self, other)
__cmp__
是最基本的用於比較的魔術方法。它實際上實現了所有的比較符號(<,==,!=,etc.),但是它的表現並不會總是如你所願(比如,當一個實例與另一個實例相等是通過一個規則來判斷,而一個實例大於另外一個實例是通過另外一個規則來判斷)。如果 self < other
的話 __cmp__
應該返回一個負數,當 self == other
的時候會返回0
,而當 self > other
的時候會返回正數。通常最好的一種方式是去分別定義每一個比較符號而不是一次性將他們都定義。但是 __cmp__
方法是你想要實現所有的比較符號而一個保持清楚明白的一個好的方法。
__eq__(self, other)
定義了等號的行爲, ==
。
__ne__(self, other)
定義了不等號的行爲, !=
。
__lt__(self, other)
定義了小於號的行爲, <
。
__gt__(self, other)
定義了大於等於號的行爲, >=
。
舉一個例子,創建一個類來表現一個詞語。我們也許會想要比較單詞的字典序(通過字母表),通過默認的字符串比較的方法就可以實現,但是我們也想要通過一些其他的標準來實現,比如單詞長度或者音節數量。在這個例子中,我們來比較長度實現。以下是實現代碼:
現在,我們創建兩個 Words
對象(通過使用 Word('foo')
和 Word('bar')
然後通過長度來比較它們。注意,我們沒有定義 __eq__
和 __ne__
方法。這是因爲將會產生一些怪異的結果(比如 Word('foo') == Word('bar')
將會返回true)。這對於測試基於長度的比較不是很有意義。所以我們退回去,用 str
內置來進行比較。
現在你知道你不必定義每一個比較的魔術方法從而進行豐富的比較。標準庫中很友好的在 functiontols
中提供給我們一個類的裝飾器定義了所有的豐富的比較函數。如果你只是定義 __eq__
和另外一個(e.g. __gt__
, __lt__
,etc.)這個特性僅僅在Python
2.7中存在,但是你如果有機會碰到的話,那麼將會節省大量的時間和工作量。你可以通過在你定義的類前放置 @total_ordering
來使用。
數值處理的魔術方法
如同你在通過比較符來比較類的實例的時候來創建很多方法,你也可以定義一些數值符號的特性。繫緊你的安全帶,來吧,這裏有很多內容。爲了組織方便,我將會把數值處理的方法來分成五類:一元操作符,普通算數操作符,反射算數操作符(之後會詳細說明),增量賦值,和類型轉換。
一元操作符和函數
僅僅有一個操作位的一元操作符和函數。比如絕對值,負等。
__pos__(self)
實現正號的特性(比如 +some_object
)
__neg__(self)
實現負號的特性(比如 -some_object
)
__abs__(self)
實現內置 abs()
函數的特性。
__invert__(self)
實現 ~
符號的特性。爲了說明這個特性。你可以查看 Wikipedia中的這篇文章
普通算數操作符
現在我們僅僅覆蓋了普通的二進制操作符:+,-,*和類似符號。這些符號大部分來說都淺顯易懂。
__add__(self, other)
實現加法。 __sub__(self, other)
實現減法。 __mul__(self, other)
實現乘法。 __floordiv__(self, other)
實現 //
符號實現的整數除法。 __div__(self, other)
實現 /
符號實現的除法。 __truediv__(self, other)
實現真除法。注意只有只用了 from __future__ import division
的時候纔會起作用。 __mod__(self, other)
實現取模算法 %
__divmod___(self, other)
實現內置 divmod()
算法 __pow__
實現使用 **
的指數運算__lshift__(self, other)
實現使用 <<
的按位左移動 __rshift__(self, other)
實現使用 >>
的按位左移動 __and__(self, other)
實現使用 &
的按位與__or__(self, other)
實現使用 |
的按位或 __xor__(self, other)
實現使用 ^
的按位異或
反運算
下面我將會講解一些反運算的知識。有些概念你可能會認爲恐慌或者是陌生。但是實際上非常簡單。以下是一個例子:
這是一個普通的加法運算,反運算是相同的,只是把操作數調換了位置:
所以,除了當與其他對象操作的時候自己會成爲第二個操作數之外,所有的這些魔術方法都與普通的操作是相同的。大多數情況下,反運算的結果是與普通運算相同的。所以你可以你可以將 __radd__
與 __add__
等價。
__radd__(self, other)
實現反加 __rsub__(self, other)
實現反減 __rmul__(self, other)
實現反乘 __rfloordiv__(self, other)
實現 //
符號的反除__rdiv__(self, other)
實現 /
符號的反除 __rtruediv__(self, other)
實現反真除,只有當 from __future__ import division
的時候會起作用__rmod__(self, other)
實現 %
符號的反取模運算 __rdivmod__(self, other)
當 divmod(other, self)
被調用時,實現內置 divmod()
的反運算 __rpow__
實現 **
符號的反運算 __rlshift__(self, other)
實現 <<
符號的反左位移 __rrshift__(self, other)
實現 >>
符號的反右位移 __rand__(self, other)
實現&
符號的反與運算 __ror__(self, other)
實現 |
符號的反或運算 __xor__(self, other)
實現 ^
符號的反異或運算
增量賦值
Python也有大量的魔術方法可以來定製增量賦值語句。你也許對增量賦值已經很熟悉,它將操作符與賦值來結合起來。如果你仍然不清楚我在說什麼的話,這裏有一個例子:
__iadd__(self, other)
實現賦值加法 __isub__(self, other)
實現賦值減法 __imul__(self, other)
實現賦值乘法 __ifloordiv__(self, other)
實現 //=
的賦值地板除 __idiv__(self, other)
實現符號 /=
的賦值除 __itruediv__(self, other)
實現賦值真除,只有使用 from __future__ import division
的時候才能使用 __imod_(self, other)
實現符號 %=
的賦值取模 __ipow__
實現符號 **=
的賦值冪運算 __ilshift__(self, other)
實現符號 <<=
的賦值位左移__irshift__(self, other)
實現符號 >>=
的賦值位右移 __iand__(self, other)
實現符號 &=
的賦值位與 __ior__(self, other)
實現符號 |=
的賦值位或__ixor__(self, other)
實現符號 |=
的賦值位異或
類型轉換魔術方法
Python也有很多的魔術方法來實現類似 float()
的內置類型轉換特性。 __int__(self)
實現整形的強制轉換 __long__(self)
實現長整形的強制轉換__float__(self)
實現浮點型的強制轉換 __complex__(self)
實現複數的強制轉換 __oct__(self)
實現八進制的強制轉換 __hex__(self)
實現二進制的強制轉換 __index__(self)
當對象是被應用在切片表達式中時,實現整形強制轉換,如果你定義了一個可能在切片時用到的定製的數值型,你應該定義__index__
(詳見PEP357) __trunc__(self)
當使用 math.trunc(self)
的時候被調用。 __trunc__
應該返回數值被截取成整形(通常爲長整形)的值__coerce__(self, other)
實現混合模式算數。如果類型轉換不可能的話,那麼 __coerce__
將會返回 None
,否則他將對 self
和 other
返回一個長度爲2的tuple,兩個爲相同的類型。
表現你的類
如果有一個字符串來表示一個類將會非常有用。在Python中,有很多方法可以實現類定義內置的一些函數的返回值。 __str__(self)
定義當 str()
調用的時候的返回值 __repr__(self)
定義 repr()
被調用的時候的返回值。 str()
和 repr()
的主要區別在於 repr()
返回的是機器可讀的輸出,而 str()
返回的是人類可讀的。 __unicode__(self)
定義當 unicode()
調用的時候的返回值。 unicode()
和 str()
很相似,但是返回的是unicode字符串。注意,如a果對你的類調用 str()
然而你只定義了 __unicode__()
,那麼將不會工作。你應該定義 __str__()
來確保調用時能返回正確的值。
__hash__(self)
定義當 hash()
調用的時候的返回值,它返回一個整形,用來在字典中進行快速比較 __nonzero__(self)
定義當 bool()
調用的時候的返回值。本方法應該返回True或者False,取決於你想讓它返回的值。
控制屬性訪問
許多從其他語言轉到Python的人會抱怨它缺乏類的真正封裝。(沒有辦法定義私有變量,然後定義公共的getter和setter)。Python其實可以通過魔術方法來完成封裝。我們來看一下:
__getattr__(self, name)
你可以定義當用戶試圖獲取一個不存在的屬性時的行爲。這適用於對普通拼寫錯誤的獲取和重定向,對獲取一些不建議的屬性時候給出警告(如果你願意你也可以計算並且給出一個值)或者處理一個 AttributeError
。只有當調用不存在的屬性的時候會被返回。然而,這不是一個封裝的解決方案。 __setattr__(self, name, value)
與 __getattr__
不同, __setattr__
是一個封裝的解決方案。無論屬性是否存在,它都允許你定義對對屬性的賦值行爲,以爲這你可以對屬性的值進行個性定製。但是你必須對使用 __setattr__
特別小心。之後我們會詳細闡述。 __delattr__
與__setattr__
相同,但是功能是刪除一個屬性而不是設置他們。注意與 __setattr__
相同,防止無限遞歸現象發生。(在實現 __delattr__
的時候調用 delself.name
即會發生) __getattribute__(self, name)
__getattribute__
與它的同伴 __setattr__
和 __delattr__
配合非常好。但是我不建議使用它。只有在新類型類定義中才能使用 __getattribute__
(在最新版本Python中所有的類都是新類型,在老版本中你可以通過繼承 object
來製作一個新類。這樣你可以定義一個屬性值的訪問規則。有時也會產生一些帝歸現象。(這時候你可以調用基類的 __getattribute__
方法來防止此現象的發生。)它可以消除對__getattr__
的使用,如果它被明確調用或者一個 AttributeError
被拋出,那麼當實現 __getattribute__
之後才能被調用。此方法是否被使用其實最終取決於你的選擇。)我不建議使用它因爲它的使用機率較小(我們在取得一個值而不是設置一個值的時候有特殊的行爲是非常罕見的。)而且它不能避免會出現bug。
在進行屬性訪問控制定義的時候你可能會很容易的引起一個錯誤。考慮下面的例子。
Python的魔術方法非常強大,然而隨之而來的則是責任。瞭解正確的方法去使用非常重要。
所以我們對於定製屬性訪問權限瞭解了多少呢。它不應該被輕易的使用。實際上,它非常強大。但是它存在的原因是:Python 不會試圖將一些不好的東西變得不可能,而是讓它們難以實現。自由是至高無上的,所以你可以做任何你想做的。以下是一個特別的屬性控制的例子(我們使用 super
因爲不是所有的類都有 __dict__
屬性):
創建定製的序列
有很多方法讓你的Python類行爲可以像內置的序列(dict, tuple,list, string等等)。這是目前爲止我最喜歡的魔術方法,因爲它給你很搞的控制權限而且讓很多函數在你的類實例上工作的很出色。但是在開始之前,需要先講一些必須條件。
必須條件
現在我們開始講如何在Python中創建定製的序列,這個時候該講一講協議。協議(Protocols)與其他語言中的接口很相似。它給你很多你必須定義的方法。然而在Python中的協議是很不正式的,不需要明確聲明實現。事實上,他們更像一種指南。
我們爲什麼現在討論協議?因爲如果要定製容器類型的話需要用到這些協議。首先,實現不變容器的話有一個協議:實現不可變容器,你只能定義__len__
和 __getitem__
(一會會講更多)。可變容器協議則需要所有不可變容器的所有另外還需要 __setitem__
和 __delitem__
。最終,如果你希望你的對象是可迭代的話,你需要定義 __iter__
會返回一個迭代器。迭代器必須遵循迭代器協議,需要有 __iter__
(返回它本身)
和 next
。
容器後的魔法
這些是容器使用的魔術方法。 __len__(self)
然會容器長度。對於可變不可變容器都需要有的協議的一部分。 __getitem__(self, key)
定義當一個條目被訪問時,使用符號 self[key]
。這也是不可變容器和可變容器都要有的協議的一部分。如果鍵的類型錯誤和 KeyError
或者沒有合適的值。那麼應該拋出適當的 TypeError
異常。 __setitem__(self, key, value)
定義當一個條目被賦值時的行爲,使用 self[key] = value
。這也是可變容器和不可變容器協議中都要有的一部分。 __delitem__(self, key)
定義當一個條目被刪除時的行爲(比如 del self[key]
)。這只是可變容器協議中的一部分。當使用一個無效的鍵時應該拋出適當的異常。 __iter__(self)
返回一個容器的迭代器。很多情況下會返回迭代器,尤其是當內置的 iter()
方法被調用的時候,或者當使用 for x in container
方式循環的時候。迭代器是他們本身的對象,他們必須定義返回 self
的 __iter__
方法。 __reversed__(self)
實現當reversed()
被調用時的行爲。應該返回列表的反轉版本。 __contains__(self, item)
當調用 in
和 not in
來測試成員是否存在時候 __contains__
被定義。你問爲什麼這個不是序列協議的一部分?那是因爲當 __contains__
沒有被定義的時候,Python會迭代這個序列並且當找到需要的值時會返回 True
。 __concat__(self, other)
最終,你可以通過 __concat__
來定義當用其他的來連接兩個序列時候的行爲。當 +
操作符被調用時候會返回一個 self
和other.__concat__
被調用後的結果產生的新序列。
一個例子
在我們的例子中,讓我們看一看你可能在其他語言中 用到的函數構造語句的實現(比如 Haskell)。
反射
你可以通過魔術方法控制控制使用 isinstance()
和 issubclass()
內置方法的反射行爲。這些魔術方法是:
__instancecheck__(self, instance)
檢查一個實例是不是你定義的類的實例
__subclasscheck__(self, subclass)
檢查一個類是不是你定義的類的子類
這些方法的用例似乎很少,這也許是真的。我不會花更多的時間在這些魔術方法上因爲他們並不是很重要,但是他們的確反應了Python 中的面向對象編程的一些基本特性:非常容易的去做一些事情,即使並不是很必須。這些魔術方法看起來並不是很有用,但是當你需要的時候你會很高興有這種特性。
可以調用的對象
你也許已經知道,在Python中,方法也是一種高等的對象。這意味着他們也可以被傳遞到方法中就像其他對象一樣。這是一個非常驚人的特性。 在Python中,一個特殊的魔術方法可以讓類的實例的行爲表現的像函數一樣,你可以調用他們,將一個函數當做一個參數傳到另外一個函數中等等。這是一個非常強大的特性讓Python編程更加舒適甜美。 __call__(self, [args...])
允許一個類的實例像函數一樣被調用。實質上說,這意味着 x()
與 x.__call__()
是相同的。注意 __call__
參數可變。這意味着你可以定義 __call__
爲其他你想要的函數,無論有多少個參數。
__call__
在那些類的實例經常改變狀態的時候會非常有效。調用這個實例是一種改變這個對象狀態的直接和優雅的做法。用一個實例來表達最好不過了:
會話管理
在Python 2.5中,爲了代碼利用定義了一個新的關鍵詞 with
語句。會話控制在Python中不罕見(之前是作爲庫的一部分被實現),直到 PEP343 被添加後。它被成爲一級語言結構。你也許之前看到這樣的語句:
回話控制器通過包裝一個 with
語句來設置和清理行爲。回話控制器的行爲通過兩個魔術方法來定義: __enter__(self)
定義當使用 with
語句的時候會話管理器應該初始塊被創建的時候的行爲。注意 __enter__
的返回值被 with
語句的目標或者 as
後的名字綁定。 __exit__(self, exception_type,exception_value, traceback)
定義當一個代碼塊被執行或者終止後會話管理器應該做什麼。它可以被用來處理異常,清除工作或者做一些代碼塊執行完畢之後的日常工作。如果代碼塊執行成功, exception_type
, exception_value
,
和 traceback
將會是 None
。否則的話你可以選擇處理這個異常或者是直接交給用戶處理。如果你想處理這個異常的話,確認 __exit__
在所有結束之後會返回 True
。如果你想讓異常被會話管理器處理的話,那麼就這樣處理。
__enter
和 __exit__
對於明確有定義好的和日常行爲的設置和清潔工作的類很有幫助。你也可以使用這些方法來創建一般的可以包裝其他對象的會話管理器。以下是一個例子。
以下是一個使用 Closer
的例子,使用一個FTP鏈接來證明(一個可關閉的套接字):
你已經看到了我們的包裝器如何靜默的處理適當和不適當的使用行爲。這是會話管理器和魔術方法的強大功能。
創建對象的描述器
描述器是通過得到,設置,刪除的時候被訪問的類。當然也可以修改其他的對象。描述器並不是鼓勵的,他們註定被一個所有者類所持有。當創建面向對象的數據庫或者類,裏面含有相互依賴的屬性時,描述器將會非常有用。一種典型的使用方法是用不同的單位表示同一個數值,或者表示某個數據的附加屬性(比如座標系上某個點包含了這個點到遠點的距離信息)。
爲了構建一個描述器,一個類必須有至少 __get__
或者 __set__
其中一個,並且 __delete__
被實現。讓我們看看這些魔術方法。 __get__(self,instance, owner)
定義當描述器的值被取得的時候的行爲, instance
是擁有者對象的一個實例。 owner
是擁有者類本身。 __set__(self, instance,value)
定義當描述器值被改變時候的行爲。 instance
是擁有者類的一個實例 value
是要設置的值。 __delete__(self, instance)
定義當描述器的值被刪除的行爲。instance
是擁有者對象的實例。
以下是一個描述器的實例:單位轉換。
儲存你的對象
如果你接觸過其他的 Pythoner,你可能已經聽說過 Pickle 了, Pickle 是用來序列化 Python 數據結構的模塊,在你需要暫時存儲一個對象的時候(比如緩存),這個模塊非常的有用,不過這同時也是隱患的誕生地。
序列化數據是一個非常重要的功能,所以他不僅僅擁有相關的模塊( Pickle
, cPickle
),還有自己的協議以及魔術方法,不過首先,我們先討論下關於序列化內建數據結構的方法。
Pickling: 簡單例子
讓我們深入研究 Pickle,比如說你現在需要臨時儲存一個字典,你可以把它寫入到一個文件裏,並且要小心翼翼的確保格式正確,之後再用 exec() 或者處理文件輸入來恢復數據,實際上這是很不安全的,如果你使用文本存儲了一些重要的數據,任何方式的改變都可能會影響到你的程序,輕則程序崩潰,重則被惡意程序利用,所以,讓我們用 Pickle 代替這種方式:
嗯,過了幾個小時之後,我們需要用到它了,只需把它 unpickle 了就行了:
正如你期望的,數據原封不動的回來了!
同時要給你一句忠告: pickle 並不是很完美, Pickle 文件很容易被不小心或者故意損壞, Pickle 文件比純文本文件要稍微安全一點,但是還是可以被利用運行惡意程序。 Pickle 不是跨版本兼容的(譯註:最近剛好在 《Python Cookbook》上看到相關討論,書中描述的 Pickle 是跨版本兼容的,此點待驗證),所以儘量不要去分發 Pickle 過的文本,因爲別人並不一定能夠打開。不過在做緩存或者其他需要序列化數據的時候, Pickle 還是很有用處的。
序列化你自己的對象
Pickle 並不是只支持內建數據結果,任何遵循 Pickle 協議的類都可以,Pickle 協議爲 Python 對象規定了4個可選方法來自定義 Pickle 行爲(對於 C 擴展的 cPickle 模塊會有一些不同,但是這並不在我們的討論範圍內):
__getinitargs__(self)
如果你希望在逆序列化的同時調用 __init__
,你可以定義 __getinitargs__
方法,這個方法應該返回一系列你想被 __init__
調用的參數,注意這個方法只對老樣式的類起作用。
__getnewargs__(self)
對於新式的類,你可以定義任何在重建對象時候傳遞到 __new__
方法中的參數。這個方法也應該返回一系列的被 __new__
調用的參數。
__getstate__(self)
你可以自定義當對象被序列化時返回的狀態,而不是使用 __dict
方法,當逆序列化對象的時候,返回的狀態將會被 __setstate__
方法調用。
__setstate__(self, state)
在對象逆序列化的時候,如果 __setstate__
定義過的話,對象的狀態將被傳給它而不是傳給 __dict__
。這個方法是和 __getstate__
配對的,當這兩個方法都被定義的時候,你就可以完全控制整個序列化與逆序列化的過程了。
例子
我們以 Slate 爲例,這是一段記錄一個值以及這個值是何時被寫入的程序,但是,這個 Slate 有一點特殊的地方,當前值不會被保存。
結論
這份指南的希望爲所有人都能帶來一些知識,即使你是 Python 大牛或者對於精通於面向對象開發。如果你是一個 Python 初學者,閱讀這篇文章之後你已經獲得了編寫豐富,優雅,靈活的類的知識基礎了。如果你是一個有一些經驗的 Python 程序員,你可能會發現一些能讓你寫的代碼更簡潔的方法。如果你是一個 Python 大牛,可能會幫助你想起來一些你已經遺忘的知識,或者一些你還沒聽說過的新功能。不管你現在有多少經驗,我希望這次對於 Python 特殊方法的旅程能夠帶給你一些幫助(用雙關語真的很不錯 XD)(譯註: 這裏的雙關在於標題爲 Magic Methods 這裏是 神奇的旅程 ,不過由於中英語序的問題,直譯略顯頭重腳輕,所以稍微變化了下意思,丟掉了雙關的含義)。
附錄:如何調用魔術方法
一些魔術方法直接和內建函數相對,在這種情況下,調用他們的方法很簡單,但是,如果是另外一種不是特別明顯的調用方法,這個附錄介紹了很多並不是很明顯的魔術方法的調用形式。
魔術方法 | 調用方式 | 解釋 |
---|---|---|
__new__(cls [,...]) | instance = MyClass(arg1, arg2) | __new__ 在創建實例的時候被調用 |
__init__(self [,...]) | instance = MyClass(arg1, arg2) | __init__ 在創建實例的時候被調用 |
__cmp__(self, other) | self == other, self > other, 等。 | 在比較的時候調用 |
__pos__(self) | +self | 一元加運算符 |
__neg__(self) | -self | 一元減運算符 |
__invert__(self) | ~self | 取反運算符 |
__index__(self) | x[self] | 對象被作爲索引使用的時候 |
__nonzero__(self) | bool(self) | 對象的布爾值 |
__getattr__(self, name) | self.name # name 不存在 | 訪問一個不存在的屬性時 |
__setattr__(self, name, val) | self.name = val | 對一個屬性賦值時 |
__delattr__(self, name) | del self.name | 刪除一個屬性時 |
__getattribute(self, name) | self.name | 訪問任何屬性時 |
__getitem__(self, key) | self[key] | 使用索引訪問元素時 |
__setitem__(self, key, val) | self[key] = val | 對某個索引值賦值時 |
__delitem__(self, key) | del self[key] | 刪除某個索引值時 |
__iter__(self) | for x in self | 迭代時 |
__contains__(self, value) | value in self, value not in self | 使用 in 操作測試關係時 |
__concat__(self, value) | self + other | 連接兩個對象時 |
__call__(self [,...]) | self(args) | “調用”對象時 |
__enter__(self) | with self as x: | with 語句環境管理 |
__exit__(self, exc, val, trace) | with self as x: | with 語句環境管理 |
__getstate__(self) | pickle.dump(pkl_file, self) | 序列化 |
__setstate__(self) | data = pickle.load(pkl_file) | 序列化 |
希望這個表格對你對於什麼時候應該使用什麼方法這個問題有所幫助。