《流暢的python》一書是python入門之后進階的一本好書。它不是一本完備的python手冊,而是強調python作為編程語言獨有的特性。這些特性或者是python獨有的,或者是其它編程語言里很少見的。在接下來的一段時間,我會整理這本書的筆記,大家相互學習,共同進步。
一、pythonic風格的代碼是什么樣的?
最簡單的兩個變量a,b交換value:
其它語言大部分需要用到臨時變量,例如:
$a = 100;
$b = 200;
$c = $a;
$b = $a;
$a = $c;
echo $a,$b;#200100
$a 與$b交換值,需要借助臨時$c來實現。如果是python呢?
a,b = 100,200
a,b = b,a
print(a,b)#200 100
對,這就是pythonic的代碼,是不是很簡單易讀?來個更直觀的栗子:
定義一副撲克牌
import collections
Card = collections.namedtuple('Card',['rank','suit'])
class FrenchDeck:
rank = [str(i) for i in range(2,11)]+'JQKA'
suit = 'spades diamonds clubs hearts'.split()
def __init__(self):
self._cards = [Card(rank,suit) for suit in self.suit for card in self.rank]
def __len__(self):
return len(self._cards)
def __getitem__(self,position):
return self._cards[position]
短短十來行代碼就定義 了一副撲克牌,是不是覺得很輕松呢?
注意:collections.namedtuple()方法用以構建只有少數屬性沒有方法的對象,例如數據庫條目。在python2中定義一個類需要顯式的繼承object,定義類名時需要FrenchDeck(object)繼承的父類,python3則默認繼承object不用再寫object了。
python定義列表的特別之處莫過于它的列表生成式了,簡單明了, 敲起來也方便。其中:
rank = [str(i) for i in range(2,11)]+'JQKA'
使用列表生成式定義了撲克牌中的2~A
suit = 'spades diamonds clubs hearts'.split()
定義了撲克牌的四種花色
self._cards = [Card(rank,suit) for suit in self.suit for card in self.rank]
再次使用列表生成式將花色與點數組合起來。其實三行代碼已經將撲克牌定義完了,列表生成式的優點就在于可以用極短的代碼,完成列表的創建。假設使用迭代的方式定義這副撲克牌,很顯然,代碼就不會是三行了。
好了,既然有了一副撲克牌,那我們就要來賭點大的了。
#1.紙牌數量
#因為我們重新定義了特殊方法__len__,當我們需要紙牌數量的時候直接使用len()方法就可以。
#特殊方法的存在是給python解釋器使用的,你不需要調用它們,也就是說沒有my_obj.__len__()這種寫法,
#而應該使用len(my_obj),在執行len(my_obj)的時候,如果my_obj是你自己定義的對象,那么python會去
#調用由你實現的__len__方法
deck = FrenchDeck()
print(len(deck))#52
#抽取第一張或者最后一張
deck[0]或deck[-1]
#隨機抽取一張
from random import choice
choice(deck)
#由于__getitem__方法把[]操作交給了self._cards列表,所以deck支持切片和迭代操作
#取出四張A(切片)
deck[12::13]
#迭代
for card in deck:
print(card)
#反向迭代
for card in reversed(deck):
print(card)
#排序(升序)
suit_value = dict(spades=3,hearts=2,diamonds=1,clubs=0)
def spades_high(card):
rank_value = FrenchDeck.rank.index(card.rank)
return rank_value * len(suit_value) + suit_value[card.suit]
for card in sorted(deck,key=spades_high):
print(card)
二、python中的特殊方法
如__getitem__這些帶雙下劃線的方法,我們稱之為特殊方法,特殊方法的存在是為了被解釋器調用的,你自己并不需要調用它們。很多時候,特殊方法的調用是隱式的,例如循環語句,for i in x:這個語句,背后其實使用的是iter(x),而這個函數的背后是x.__iter__()方法,當然,前提是__iter__這個方法被實現了。
通常來說,代碼無需直接使用特殊方法。除非有大量的元編程存在,直接調用特殊方法的頻率應該遠遠低于你去實現它們的次數。唯一例外的可能是__init__方法,你的代碼里可能經常會用到它,目的是在你自己的子類的__init__方法中調用超類的構造器。
此外,不要自己想當然的隨意添加特殊方法,比如__foo__之類。因為雖然現在這個名字沒有被python內部使用,以后就不一定了。