流暢的python 魔術方法

2021-10-21 17:22:04 字數 4285 閱讀 1175

第一章《流暢的python》裡面的描述:python 的魔術方法(magic method)是特殊方法的暱稱。一般是用「雙下劃線+名稱+雙下劃線」形式來表示,整體念起來也拗口,所以也有人把這種特殊方法名為稱為「雙下方法」(dunder method)。

有關於特殊方法一覽,可以參考data model

## test.py

class frenchdeck:

ranks = [str(n) for n in range(2, 11)] + list('jqka')

suits = 'spades diamonds clubs hearts'.split()

def __init__(self):

self._cards = [card(rank, suit) for suit in self.suits

for rank in self.ranks]

def __setitem__(self, position, card):

self._cards[position] = card

def __len__(self):

return len(self._cards)

def __getitem__(self, position):

return self._cards[position]

建立了乙個字牌類,52張牌,從2到a,花色為黑桃,方塊,梅花,紅心

並且實現了兩個特殊方法「__len__」, "__getitem__",看到這裡可能沒感受到魔法方法魅力

先看一下輸出,用內建函式len檢視牌有幾張

>>>from test import frenchdeck

>>>deck = frenchdeck()

>>>len(deck)

52

如果是從一疊牌中抽取一張牌,或者隨機抽取一張牌,或者從上到下抽4張牌,洗牌後取第一張牌

>>>deck[0]

card(rank='2', suit='spades')

>>>import random

>>>random.choice(deck)

card(rank='5', suit='hearts')

>>>deck[:4]

[card(rank='2', suit='spades'), card(rank='3', suit='spades'), card(rank='4', suit='spades'), card(rank='5', suit='spades')]

>>>from random import shuffle

>>>shuffle(deck)

>>>deck[0]

card(rank='j', suit='spades')

在這個資料模型中,使用這些特殊方法,可以觀察到:

frenchdeck實現__len__方法,在呼叫內建函式len(deck),實際上是呼叫__len__()方法。

frenchdeck實現__getitem__方法,在deck[0],「[0]」中括號取0位置的時候觸發__getitem__(0)方法。

frenchdeck還實現了__setitem__方法,這邊比較隱晦,實際上random.shuffle洗牌過程中,是通過互相調換元素位置來實現的,掉換位置時候涉及到賦值,也就是說必須提供賦值的方法,可以試驗一下當把__setitem__方法都注釋掉,按照一下試一下,注釋掉時候報錯,注釋去掉即可賦值成功。:

>>>from test import card

>>>from test import frenchdeck

>>>deck = frenchdeck()

>>>deck[0]

card(rank='2', suit='spades')

>>>a = card('spades','a')

>>>deck[0] = a

traceback (most recent call last):

file "", line 1, in typeerror: 'frenchdeck' object does not support item assignment

這麼做的好處是什麼?

一致性:python有個很重要的品質就是「一致性」,當我們設計這樣乙個資料模型,我們實現這樣特殊方法,呼叫者只需要呼叫內建函式len,我們不需要額外新建乙個內建函式比如size()這樣的方法,這樣呼叫者不需要記住那麼多方法名,只需要像操作列表那樣操作即可。

相容性:並且實現這樣魔術方法,很大程度上更方便呼叫python標準庫,比如上述的random庫,我們用到了隨機取值和打亂洗牌的動作。

上面我們也可以呼叫in運算子,或者for迴圈:

>>> for card in deck:

... print(card)

...card(rank='2', suit='spades')

card(rank='3', suit='spades')

card(rank='4', suit='spades')

card(rank='5', suit='spades')

card(rank='6', suit='spades')

...>>>card('a','spades') in deck

true

雖然frenchdeck類中沒有實現__iter__方法和__contains__,但是也能使用for迴圈,也能用in運算子,實際上啟動了後備機制,或者說python的for迴圈實際上是相容兩種機制的

一種是迭代器,實現了__iter__和__next__方法,它不斷呼叫__next__方法來獲取迭代器下乙個值

一種是如果沒有實現__iter__方法,但是實現了__getitem__方法的,會從0開始傳入索引,嘗試迭代物件。

in也是如此,也是按順序迭代搜尋。所以python這種機制,讓沒有實現__iter__方法和__contains__,但是有實現__getitem__方法也能用for語句和in運算子。

這裡仿造這種方式定義乙個**的類,**有行列,可以模仿numpy陣列來取值[i,j],可以用len()函式來表示**又多少行和列:

## test.py

class table:

def __init__(self, rows, columns):

self._rows = int(rows)

self._columns = int(columns)

self._table = dict.fromkeys([(x, y) for x in range(rows)

for y in range(columns)])

def __setitem__(self, position, value):

x, y = position

self._table[(position)] = value

def __len__(self):

return self._rows, self._columns

def __getitem__(self, position):

x, y = position

return self._table[(x,y)]

def __repr__(self):

return "table(%s, %s)"%(self._rows,self._columns)

檢視一下輸出結果:

>>>from test import table

>>>table = table(2,2)

>>> table

table(2, 2)

>>>table[0,0] = 1

>>> table[0,0]

1

**輸入是建立幾行幾列的**,並且用乙個字典將他的座標(i,j)作為鍵值,要成為字典的鍵值,須屬於可雜湊資料型別,並且也得實現__hash__方法。原子不可變資料型別(str、bytes 和數值型別)都是可雜湊型別,當然對於元組來說,要每個物件都得是雜湊資料型別也能成為鍵值,我們這邊的,x,y都是數值型別,所以何在一起元組子自然也能成為鍵值。

這邊 __getitem__ 和 __setitem__ 實現方法, 會在table[i,j]呼叫時候以(i,j)元組形式傳進來,如__getitem__((i, j)),這裡的position就是元組(i,j) 

這邊也僅限於取[i,j]的座標的值,需要能實現切片,例如table[0,:],可以在__getitem__裡面另作判斷,加上isinstanc(position, slice)類似的判斷,另作處理。獲得整行或者整列的資料。

python 魔術方法

魔術方法 呼叫方式 解釋 new cls instance myclass arg1,arg2 new 在建立例項的時候被呼叫 init self instance myclass arg1,arg2 init 在建立例項的時候被呼叫 cmp self,other self other,self o...

Python魔術方法

參考文章 python 魔術方法指南 魔術方法,顧名思義是一種可以給物件 類 增加魔法的特殊方法,它們的表示方法一般是用雙下劃線包圍 如 init from os.path import join class fileobject 給檔案物件進行包裝從而確認在刪除時檔案流關閉 def init se...

Python 魔術方法

usr bin env python coding utf 8 author ray time 2018 12 6 魔術方法例項 init 建構函式,在生成物件時呼叫,用來初始化值 del 析構函式,釋放物件時使用 比如編輯檔案,把關閉檔案的操作寫在此方法中,程式結束時就會關閉軟體 str 使用pr...