學習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__
方法,能夠直接經過下標直接訪問對應的位置的元素,甚至能夠像列表那樣進行切片操做和遍歷操做。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__
做爲備用。