資料模型:是對python框架的描述,它規範了這門語言自身構建模組的介面,這些模組包括但不限於序列、迭代器、函式、類和上下文管理器。
python直譯器碰到特殊的句法時,會使用特殊方法去啟用一些基本的物件操作,這些特殊方法的名字以兩個下劃線開頭,以兩個下劃線結尾(例如__getitem__
)。比如obj[key]的背後就是__getitem__方法
1.1 一摞python風格的紙牌
import collections
card = collections.namedtuple(
'card',[
'rank'
,'suit'])
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__len__
(self)
:return
len(self._cards)
def__getitem__
(self, position)
:return self._cards[position]
迭代通常是隱式的,譬如說乙個集合型別沒有實現__contains__方法,那麼in運算子就會按順序做一次迭代搜尋。
我們通過資料模型和一些合成來實現這些功能。通過實現__len__和__getitem__這兩個特殊方法,frenchdeck就跟乙個python自有的序列資料型別一樣
,可以體現出python的核心語言特性(例如迭代和切片)
按照目前的設計,frenchdeck是不能洗牌的,因為這摞牌是不可變的(immutable):卡牌和它們的位置都是固定的,除非我們破壞這個類的封裝性,直接對_cards進行操作。
第11章會講到,其實只需要下面一行**來實現__setitem__
方法,洗牌功能就不是問題了。這是因為可變的序列還必須提供__setitem__方法
。
def
__setitem__
(self, position, card)
: self._cards[position]
= card
1.2 如何使用特殊方法
特殊方法的存在是為了被python直譯器呼叫的,你自己並不需要呼叫它們。也就是說沒有my_object.__len__( )這種寫法,而應該使用len(my_object)。
在執行len(my_object)的時候,如果my_object是乙個自定義類的物件,那麼python會自己去呼叫其中由你實現的__len__方法。
然而如果是python內建的型別,比如列表(list)、字串(str)、位元組序列(bytearray)等,那麼cpython會抄個近路,__len__實際上會直接返回pyvarobject裡的ob_size屬性
。pyvarobject是表示記憶體中長度可變的內建物件的c語言結構體。直接讀取這個值比呼叫乙個方法要快很多。
特殊方法的呼叫是隱式的,比如for i in x:這個語句,背後其實用的是iter(x),而這個函式的背後則是x.__iter__( )方法。當然前提是這個方法在x中被實現了。
通常你的**無需直接使用特殊方法。除非有大量的元程式設計存在,直接呼叫特殊方法的頻率應該遠遠低於你去實現它們的次數。唯一的例外可能是__init__
方法,你的**裡可能經常會用到它,目的是在你自己的子類的__init__
方法中呼叫超類的構造.
通過內建的函式(例如len、iter、str,等等)來使用特殊方法是最好的選擇。
這些內建函式不僅會呼叫特殊方法,通常還提供額外的好處,而且對於內建的類來說,它們的速度更快
python內建的complex類可以用來表示二維向量
,但我們這個自定義的類可以擴充套件到n維向量。
abs是乙個內建函式,如果輸入是整數或者浮點數,它返回的是輸入值的絕對值;如果輸入是複數(complex number),那麼返回這個複數的模。
from math import hypot
class
vector
:def
__init__
(self, x=
0, y=0)
: self.x = x self.y = y
def__repr__
(self)
:return
'vector(%r,%r)'
%(self.x, self.y)
def__abs__
(self)
:return hypot(self.x, self.y)
def__bool__
(self)
:return
bool
(abs
(self)
)def
__add__
(self, other)
: x = self.x+other.x
y = self.y+other.y return vector(x, y)
def__mul__
(self, scalar)
:return vector(self.x * scalar, self.y * scalar)
classa(
):def__init__
(self, x, y)
: self.x = x
self.y = y
def__repr__
(self)
:return
"a(%r, %r)"
%(self.x, self.y)
a = a(
"3",5)
print
(a)# a('3', 5)
如果你只想實現這兩個特殊方法中的乙個,__repr__是更好的選擇,因為如果乙個物件沒有__str__函式,而python又需要呼叫它的時候,直譯器會用__repr__作為替代。
中綴運算子的基本原則
就是不改變操作物件,而是產出乙個新的值。
上例只實現了數字做乘數、向量做被乘數的運算,乘法的交換律則被忽略了。在第13章裡,我們將利用__rmul__
解決這個問題。
為了判定乙個值x為真還是為假,python會呼叫bool(x),這個函式只能返回true或者false。預設情況下,我們自己定義的類的例項總被認為是真的,除非這個類對__bool__或者__len__函式有自己的實現。bool(x)的背後是呼叫x.__bool__( )的結果;如果不存在__bool__方法,那麼bool(x)會嘗試呼叫x.__len__( )。若返回0,則bool會返回false;否則返回true
。
如果想讓vector.__bool__更高效,可以採用這種實現:
def
__bool__
(self)
:return
bool
(self.x or self.y)
因為or運算子可能會返回x或者y本身的值:若x的值等價於真,則or返回x的值;否則返回y的值
。
1.3 為什麼len不是普通方法
如果x是乙個內建型別的例項,那麼len(x)的速度會非常快。背後的原因是cpython會直接從乙個c結構體裡讀取物件的長度,完全不會呼叫任何方法。
獲取乙個集合中元素的數量是乙個很常見的操作,在str、list、memoryview等型別上,這個操作必須高效。換句話說,len之所以不是乙個普通方法,是為了讓python自帶的資料結構可以走後門,abs也是同理。
__repr__和__str__的區別:前者方便我們除錯和記錄日誌,後者則是給終端使用者看的
python資料探勘01 python基礎
1.lambda定義行內函式 f lambda x x 2 定義函式f x x 2 g lambda x,y x y 定義函式g x,y x y 2.資料結構 1 列表和元組 列表方括號 a 1,2,3 元組圓括號b 4,5,6 下標從0開始。注意 列表可修改,元組不可修改。cmp a,b 比較元素...
python 資料模型
usr bin env python coding utf 8 import collections card collections.namedtuple card rank suit class frenchdeck 通過實現特殊方法利用python資料模型的好處 1.作為你類的使用者,他們不必...
Python資料模型
1 了解python資料模型和介面的概念 2 掌握特殊方法的定義,作用和基本用法。1 資料模型 data model 是資料特徵的抽象,這裡是對python框架的描述。資料模型規範了python自身構建模組的介面,模組包括但不限於序列 迭代器 函式 類和上下文管理器。2 介面 介面泛指實體把自己提供...