fluent python 讀書筆記 1

學習python一段時間會發現,一直都是用python作業務邏輯。每次都是爲了解決問題,而解決問題。而python中豐富的庫會讓咱們欣喜,可是也可能讓咱們變懶,真正對python的理解卻沒有增長多少。而fluent python是進階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__ 方法,能夠直接經過下標直接訪問對應的位置的元素,甚至能夠像列表那樣進行切片操做和遍歷操做。ui

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 操做就會進行順序遍歷操做。spa

至於排序操做,須要咱們提供排序的依據,如今假設排序的是按照先看號碼,再看花色的順序,其中花色按照梅花,方塊,紅桃,黑桃的順序。因此能夠按照這個順序,計算出每張牌的位置索引,以下:scala

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]複製代碼

而後就能夠進行排序操做了:code

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 等等。cdn

模擬數學類型

咱們都在高中學過矢量運算。對象

矢量操做

基本的矢量操做包括,矢量相加,矢量求模,矢量和標量相乘等等。然而咱們但願用咱們習慣的內置操做 + abs *來進行這些運算,因此須要咱們自定義的數據模型實現一些特殊方法。blog

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__做爲備用。

相關文章
相關標籤/搜索