fluent python 讀書筆記 1

futureshine發表於2017-04-01

學習python一段時間會發現,一直都是用python做業務邏輯。每次都是為了解決問題,而解決問題。而python中豐富的庫會讓我們欣喜,但是也可能讓我們變懶,真正對python的理解卻沒有增加多少。而fluent python是進階python非常好的一本書。

Python的資料模型

Let's be pythonic!

# 例子來自fluent python第4頁
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, item):
        return self._cards[item]複製程式碼

有了這些方法以後,我們就可以像正常操作列表一下操作我們的 FrenchDeck 。

deck = FrenchDeck()
len(deck) #輸出為52複製程式碼

由於提供了 __getitem__ 方法,可以直接通過下標直接訪問對應的位置的元素,甚至可以像列表那樣進行切片操作和遍歷操作。

deck[0] # Card(rank='2', suit='spades')
deck[1] # Card(rank='A', suit='hearts')
deck[0:3] # [Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')]
for card in deck:
    print card  # 輸出省略複製程式碼

需要指出的是遍歷操作並不總是顯式的。如果一個集合沒有實現 __contains__ 方法,則 in 操作就會進行順序遍歷操作。

至於排序操作,需要我們提供排序的依據,現在假設排序的是按照先看號碼,再看花色的順序,其中花色按照梅花,方塊,紅桃,黑桃的順序。所以可以按照這個順序,計算出每張牌的位置索引,如下:

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):
    print card
# 輸出
Card(rank='2', suit='clubs')
Card(rank='2', suit='diamonds')
Card(rank='2', suit='hearts')
... (46 cards ommitted)
Card(rank='A', suit='diamonds')
Card(rank='A', suit='hearts')
Card(rank='A', suit='spades')複製程式碼

通過實現 __len____getitem__ 方法,我們就可以像操作 python 內建的資料型別那樣操作我們的資料模型。不過兩者依然有區別。在 CPython 中,當我們對內建的資料型別進行 len(x) 操作的時候,我們其實並沒有呼叫任何方法,只是取得 C 結構中的一個域。這使得我們可以高效的操作很多內建型別,如 str, list, memoryview 等等。

模擬數學型別

我們都在高中學過向量運算。

fluent python 讀書筆記 1
向量操作

基本的向量操作包括,向量相加,向量求模,向量和標量相乘等等。然而我們希望用我們習慣的內建操作 + abs *來進行這些運算,所以需要我們自定義的資料模型實現一些特殊方法。

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)複製程式碼

實現了這些特殊方法後,就可以直接使用運算子對我們的資料模型進行操作了

v1 = Vector(2,4)
v2 = Vector(2,1)
v1 + v2 # Vector(4,5)
v = Vector(3,4)
abs(v) # 5.0
v * 3 # Vector(9, 12)
abs(v * 3) # 15.0複製程式碼

值得指出的是 __repr__ 方法,如果不實現這個方法的話。直接列印一個 Vector 物件可能會輸出

<Vector object at 0x10e100070>.複製程式碼

而定義了這個方法後,輸出就變得相當易讀

Vector(3,4)複製程式碼

當然,還可以定義 __str__ 來自定義str(x)的行為。如果只想實現一個函式的話,建議實現 __repr__ 函式,因為當沒有 __str__,會呼叫__repr__作為備用。

相關文章