01 python資料模型

2021-10-02 04:31:48 字數 4317 閱讀 5474

資料模型:是對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 介面 介面泛指實體把自己提供...