Python筆記001-類的特殊方法
以下是我學習《流暢的Python》后的個人筆記,現在拿出來和大家共享,希望能幫到各位Python學習者。
首次發(fā)表于: 微信公眾號:科技老丁哥,ID: TechDing,敬請關注。
本篇主要知識點:
-
類的特殊方法(一般都在前后帶有兩個下劃線,比如__len__和__getitem__),其存在的目的是被Python解釋器調用,而不是類的對象來調用。
-
對于自定義的類,一般無法體現出Python語言的核心特性,比如迭代和切片等,但是可以通過在自定義類中復寫__len__和__getitem__等特殊方法來實現這些核心功能。對象調用len()方法時實際上Python解釋器調用的是自定義類中的__len__,而對某個對象進行切片獲取元素,或者排序時,Python解釋器調用的是復寫的__getitem__。
-
在自定義類中復寫__getitem__至少可以獲得1.類對象的切片和獲取元素,2.對類對象進行迭代,可以是順序迭代也可以是逆序迭代,3.對類對象進行重新排序,4.對類對象進行隨機抽樣等多種功能。
-
類的特殊方法有多達100種,作為示例,此處僅僅總結了加減乘除等的復寫方法,并將所有特殊方法都整理成相關表格,便于后續(xù)復寫某些特殊方法時作為參考。
1. 特殊方法示例
這些特殊方法長得比較奇怪,那是因為它提醒用戶,不要輕易調用這些方法,因為它基本上是Python解釋器專用。
比如下面先建立一整副紙牌,它包含有一個屬性_cards,這個一個list,包含有52個Card對象。在復寫了__len__和__getitem__方法之后,就可以直接獲取到這個_cards屬性的總長度和對其進行排序,切片來獲取某幾張紙牌。
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張,最后一張紙牌分別是什么:
## 通過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()
.
同理,如果沒有復寫__getitem__方法,在
deck[0]
時報錯:
TypeError: 'FrenchDeck' object does not support indexing
可以對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__方法,就可以對所有紙牌進行排序操作,此處我們排序的規(guī)定是:梅花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中特殊方法的分類與總結
所以,如果自定義類想實現某方面的功能,可以參考上面的表格來逐一實現即可。
首次發(fā)表于: 微信公眾號:科技老丁哥,ID: TechDing,敬請關注。
本文所有代碼都已經上傳到我的github,歡迎下載
參考資料:
-
《流暢的Python》,Luciano Ramalho (作者) 安道 , 吳珂 (譯者)。
-
CSDN:Python中特殊方法的分類與總結
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
