python 魔術方法 : 讓自定義類更像內建型別

發表於2017-01-12

Python的魔術方法是Python中那些預定義的像__XXX__型別的函式。
使用Python的魔術方法的最大優勢在於python提供了簡單的方法讓物件可以表現得像內建型別一樣。

__str__函式

__str__函式用於處理列印例項本身的時候的輸出內容。如果沒有覆寫該函式,則預設輸出一個物件名稱和記憶體地址。
例如:

輸出:

那麼我們如何讓輸出的結果可讀性更高一點呢?我們可以覆寫__str__函式。例如

輸出結果就是:I'm a student, named Charlie.
我們將str()函式作用於該物件的時候,其實是呼叫了該物件的__str__函式。

_repr_ 函式

__repr__也是將物件序列化,但是__repr__更多的是給python編譯器看的。__str__更多的是可讀性(readable)。
我們將repr()函式作用於摸某一個物件的時候,呼叫的其實就是該函式的__repr__函式。

repr()成對的是eval()函式。eval()函式是將序列化後的物件重新轉為物件。前提是該物件實現了__repr__函式。

上面這一段話基於自己的理解,不知道對錯。

__iter__函式

我們經常對list或者tuple使用for…in…來迭代。那是list繼承自Iterable。Iterable實現了__iter__函式。

要想將一個自定義的物件變成一個可迭代的物件,那麼必須要實現兩個方法:__iter__next.

__iter__函式返回一個物件。迭代的時候則會不斷地呼叫next函式拿到下一個值,直到捕獲到StopIteration停止。
廖雪峰老師教程裡寫的是__next__方法,不知道為啥。

__getitem__函式

上面通過實現__iter__函式實現物件的迭代。
那麼如何實現物件按下標取出元素呢。
這是通過實現物件的__getitem__方法。
我們來舉一個?子。我們新建了一個類MyList,我們要辦它實現普通list的一些功能,比如(1)根據下標獲取值;(2)正數順序單步長切片 (3)任意步長切片

當然,上面實現了根據下標獲取值。但是這還不夠。我們還需要實現切片功能。例如my_list[1:3].
我們對物件進行切片操作的時候,呼叫的氣勢也是__getitem__函式。此時,該函式獲取到的並不是int物件,而是slice物件。
例如下面的程式碼

上面的程式碼終於實現了切片功能,但是還沒考慮負數呢。那麼我們加一把勁再來改一下。程式碼如下:

哇塞,寫完了,棒棒棒

_getattar_

在呼叫某一個物件不存在的屬性或者方法的時候,會丟擲一個一個AttributeError錯誤。
但是如果我們實現了類中的魔術方法__getattar__,那麼在呼叫不存在的屬性或者方法的時候,就會呼叫該魔術方法。

__getattar__函式一個重要的適用場景就是實現鏈式呼叫。例如我們在呼叫某一個api的時候:

那麼我們就希望我們的程式碼可以實現`Api.users.articles.index這麼呼叫。
思考一下,要實現鏈式呼叫,最重要的就是每一個呼叫都是返回一個例項~~。

廖雪峰在他的教程中給我們出了一個題目:
例如呼叫github的api:users/:user/repos一樣,中間的user名需要動態替換。
我們希望能api.users("charlie").repos這麼呼叫。那麼程式碼該如何實現呢?這可能需要用到另一個方法__call__

_call_ 函式

一個物件既有屬性,又有方法。我們在呼叫一個例項的方法的時候,我們可以使用instance.method()的形式呼叫。
其實也可以將例項本身看成一個函式用來呼叫,我們需要做的就是實現__call__函式本身。

此時我們再來看一下上面提到的實現api.users("charlie").repos鏈式呼叫的方法。

相關文章