Python筆記001-類的特殊方法

Python筆記001-類的特殊方法

如下是我學習《流暢的Python》後的我的筆記,如今拿出來和你們共享,但願能幫到各位Python學習者。git

首次發表於: 微信公衆號:科技老丁哥,ID: TechDing,敬請關注。

本篇主要知識點:

  1. 類的特殊方法(通常都在先後帶有兩個下劃線,好比__len__和__getitem__),其存在的目的是被Python解釋器調用,而不是類的對象來調用。github

  2. 對於自定義的類,通常沒法體現出Python語言的核心特性,好比迭代和切片等,可是能夠經過在自定義類中複寫__len__和__getitem__等特殊方法來實現這些核心功能。對象調用len()方法時實際上Python解釋器調用的是自定義類中的__len__,而對某個對象進行切片獲取元素,或者排序時,Python解釋器調用的是複寫的__getitem__。bash

  3. 在自定義類中複寫__getitem__至少能夠得到1.類對象的切片和獲取元素,2.對類對象進行迭代,能夠是順序迭代也能夠是逆序迭代,3.對類對象進行從新排序,4.對類對象進行隨機抽樣等多種功能。微信

  4. 類的特殊方法有多達100種,做爲示例,此處僅僅總結了加減乘除等的複寫方法,並將全部特殊方法都整理成相關表格,便於後續複寫某些特殊方法時做爲參考。函數

1. 特殊方法示例

這些特殊方法長得比較奇怪,那是由於它提醒用戶,不要輕易調用這些方法,由於它基本上是Python解釋器專用。學習

好比下面先創建一整副紙牌,它包含有一個屬性_cards,這個一個list,包含有52個Card對象。在複寫了__len__和__getitem__方法以後,就能夠直接獲取到這個_cards屬性的總長度和對其進行排序,切片來獲取某幾張紙牌。ui

from collections import namedtuple
Card=namedtuple('Card', ['rank', 'suit']) # 單張紙牌類

class FrenchDeck: # 一副紙牌,包含4種花色,每種13種數字
    ranks = [str(n) for n in range(2, 11)] + list('JQKA') # 13種數字
    suits = 'spades diamonds clubs hearts'.split() # 四種不一樣花色:黑桃,方塊,梅花,紅桃

    def __init__(self):
        self._cards = [Card(rank, suit) for suit in self.suits
                                        for rank in self.ranks]
        # _cards屬性是全部紙牌的集合,一個list, 包含4種花色共52張牌

    def __len__(self):
        # 類FrechDeck的特殊方法:會計算全部紙牌的張數,即52
        return len(self._cards)

    def __getitem__(self, position):
        # 類FrechDeck的特殊方法:從52張牌獲取第position張牌
        return self._cards[position]
複製代碼

構建一副紙牌後,咱們想知道里面有多少張牌,或者想知道第1張,第10張,最後一張紙牌分別是什麼:spa

## 經過len()獲取deck中一共有多少張牌
deck=FrenchDeck()
print(len(deck)) # 52
## 經過切片方法獲取某個位置的牌
print(deck[0]) # Card(rank='2', suit='spades')
print(deck[10]) # Card(rank='Q', suit='spades')
print(deck[-1]) # Card(rank='A', suit='hearts')
複製代碼

若是沒有複寫__len__函數,在len(deck)時會報錯:TypeError: object of type 'FrenchDeck' has no len()..net

同理,若是沒有複寫__getitem__方法,在deck[0]時報錯:TypeError: 'FrenchDeck' object does not support indexing3d

能夠對deck進行索引來獲取某一個位置的紙牌,那麼也就能夠經過切片來獲取一個批次,符合必定條件的紙牌,好比下面獲取最後三張牌和牌面全是A的牌。

print(deck[-3:]) # 獲取最後三張牌
print(deck[12::13]) # 獲取全是A的牌,間隔型切片
複製代碼

同理,複寫了__getitem__方法後,就能夠對deck對象進行迭代,不論是順序迭代仍是逆序迭代均可以。

# 順序迭代
for card in deck[:10]:  # 只打印最前面的10張牌
    print(card)

# 逆序迭代
for card in reversed(deck[-10:]):  # 只打印最後面的10張牌
    print(card)
複製代碼

還能夠判斷某張牌是否存在於該副牌內:

## 判斷某張牌是否存在於該副牌內:
print(Card('Q', 'hearts') in deck) # True
print(Card('Z', 'clubs') in deck) # False
複製代碼

有了__getitem__方法,就能夠對全部紙牌進行排序操做,此處咱們排序的規定是:梅花2最小,方塊2次之,紅桃2,黑桃2,梅花3.。。這種形式,因此要自定義一個排序方法,這個方法返回一個整數,表明每張牌的大小,那麼最小的梅花2就是0,最大的黑桃A就是51。

## 使用自定義方法對全部紙牌進行從新排序
suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0) # 將花色轉換爲數值
def spades_high(card):
    rank_value = FrenchDeck.ranks.index(card.rank) # 獲取某張牌的數字
    return rank_value * len(suit_values) + suit_values[card.suit]
    # 將某張牌的數字和花色換成整數返回

for card in sorted(deck, key=spades_high):  
  # 按照自定義方法spades_high獲得的整數進行排序
    print(card)
複製代碼

小結一下:自定義類經過複寫__getitem__方法,能夠得到多種原來不存在的功能:好比切片功能,索引功能,還能夠判斷某個對象是否存在於類對象列表中,還能夠對類對象進行迭代操做和排序操做。

2. 特殊方法的使用

對於自定義的類型,使用這些特殊方法可使得他們的表現具備某些Python核心功能,在你對這些自定義類型進行操做時,好比len(deck)時,Python解釋器會去找deck類的__len__方法。

而Python內置的類型,好比list, str, tuple等,Python解釋器則直接抄近路,會直接返回PyVarObject裏的Ob_size屬性,這是一個長度可變的C語言結構體,因此速度要比調用__len__方法快得多。

你本身對這些特殊方法的使用頻率會很低,由於都是Python解釋器自動調用,只有類定義時的__init__方法例外。

2.1 自定義加減乘除功能

class Person:
    def __init__(self,name,age,score):
        self.name=name
        self.age=age
        self.score=score

    def __add__(self,other):
        # 此處僅僅是將得分相加
        return self.score+other.score

    def __sub__(self,other):
        # 此處將得分相減
        return self.score-other.score

    def __mul__(self,other):
        # 此處將兩個的年齡相乘
        return self.age*other.age

    def __truediv__(self,other):
        # 將兩個的得分相除
        return self.score/other.score
複製代碼

這個自定義類Person複寫了加減乘除方法,根據須要對裏面的屬性進行算術操做,那麼就能夠用符號+,-,*,/等進行操做,好比:

P1=Person('Jack',20,90)
P2=Person('Rose',18,60)
print(P1+P2) # 150
print(P1-P2) # 30
print(P1*P2) # 360
print(P1/P2) # 1.5
複製代碼

2.2 自定義print後的形式

還有一個很是經常使用的特殊函數:__repr__,它決定了print被直接調用後結果表現形式。

class Person:
    def __init__(self,name,age,score):
        self.name=name
        self.age=age
        self.score=score
    def __repr__(self):
        return 'Person(name={},age={},score={})'.format(self.name,self.age,self.score)

P1=Person('Jack',20,90)
print(P1) # Person(name=Jack,age=20,score=90)
複製代碼

若是沒有複寫__repr__,在用print(P1)時,會獲得內存地址信息,人眼沒法判斷出具體內容,複寫以後,就能夠按照咱們想要的形式直接print。

3. 特殊方法彙總

Python內置的特殊方法有將近一百種,其中有不少是實現算數運算,位運算和比較操做的,下面將這些方法的意義總結以下:

下面的整理於:CSDN: Python中特殊方法的分類與總結

TechDing_pic1.PNG

TechDing_pic2.PNG

TechDing_pic3.PNG

TechDing_pic4.PNG

TechDing_pic5.PNG

TechDing_pic6.PNG

TechDing_pic7.PNG

TechDing_pic8.PNG

TechDing_pic9.PNG

TechDing_pic10.PNG

因此,若是自定義類想實現某方面的功能,能夠參考上面的表格來逐一實現便可。

首次發表於: 微信公衆號:科技老丁哥,ID: TechDing,敬請關注。

本文全部代碼都已經上傳到個人github,歡迎下載

參考資料:

  1. 《流暢的Python》,Luciano Ramalho (做者) 安道 , 吳珂 (譯者)。

  2. CSDN:Python中特殊方法的分類與總結

相關文章
相關標籤/搜索