翻譯《Writing Idiomatic Python》(一):if語句、for循環

開篇廢話

這是在美國Amazon上評價很不錯的一本書,其實嚴格來講這可能不算書,而是一本小冊子。就像書名同樣,裏面的內容主要是用一些例子講述地道的Python的代碼是怎樣寫的。書中把不少例子用不良風格和地道Python寫法做對比,內容覆蓋談不上很全,可是每一條都頗有表明性。整體而言很是適合新手,同時裏面有些條目老手看了或許也會有豁然開朗的感受。做者Jeff Knupp曾在全球最牛B的高盛和其餘銀行裏作過金融系統開發,在北美Python社區裏也頗有活躍度。html

本身用Python也有些年頭了,作過一年多的商業開發,不過其餘大部分仍是以科研和預研期的算法爲主。最近由於又開始用Python作商業開發,因此想着順便找些書看看,無心中看到了這本小書,以爲很不錯,國內沒有賣的,更別提中文版了。翻譯這本書,算是複習和從新思考下Python,同時也會有少許本身的看法(C++風格註釋綠色粗體),但願能堅持下去吧。我看的版本主要分爲四部分:Control Structures and Functions(控制結構和函數)、Working with Data(數據和類型)、Organizing Your Code(代碼組織)、General Advice(通常性建議)。每一部分裏又分爲不一樣的小章節,一共二十幾個。我會按這個順序不按期放出數目不定的章節。本人英文水平尚可,不過沒有翻譯經驗,雖然不知道會不會有人關注這個系列,仍是但願若是有看官,請輕拍指正:)python

原書參考:http://www.jeffknupp.com/blog/2012/10/04/writing-idiomatic-python/算法

下一篇:翻譯《Writing Idiomatic Python》(二):函數、異常安全


1. 控制結構和函數

1.1 if語句

1.1.1 經過鏈式比較讓語句更加簡明

當使用if語句時,優先使用鏈式比較操做,不只會讓語句更加簡明,也會讓執行效率更好。ide

不良風格:函數

1 if x <= y and y <= z:
2     return True

地道Python:post

1 if x <= y <= z:
2     return True

// Python解釋執行以上兩種不一樣的比較方式時,其實都是先比較x<=y,若是爲真,再比較y<=z。主要的區別在於,鏈式比較時,會先取y的值,而後複製壓棧,整個過程當中y的求值只執行了一次,而用and的方式時,y的求值會執行兩次,也就是說,若是比較的是三個函數或者複雜的對象的話,鏈式比較只會求值三次,而經過and比較的方式則會求值4次。這大概就是爲何做者說執行效率會更好,但實際上若是隻是簡單的變量進行比較,效率未必會有提升。url

1.1.2 避免將條件分支中的代碼和冒號放在同一行

使用縮進來表示代碼塊的結構會讓人更容易判斷條件分支的代碼結構。ifelifelse語句應該都老是獨佔一行,在冒號後沒有代碼。spa

不良風格:翻譯

1 name = 'Jeff'
2 address = 'New York, NY'
3 
4 if name: print(name)
5 print(address)

地道Python:

1 name = 'Jeff'
2 address = 'New York, NY'
3 
4 if name:
5     print(name)
6 print(address)

// 文件中的代碼應該遵循這個規則,在控制檯下放一行也何嘗不可

1.1.3 避免在複合的if語句中重複出現同一個變量名

當想用if語句檢查一個變量是否和許多值中的一個相等時,用==or重複寫許多遍是否相等的檢查會顯得代碼很冗長。簡潔的寫法是判斷該變量是否在一個可遍歷的結構中。

不良風格:

1 is_generic_name = False
2 name = 'Tom'
3 if name == 'Tom' or name == 'Dick' or name == 'Harry':
4     is_generic_name = True

地道Python:

1 name = 'Tom'
2 is_generic_name = name in ('Tom', 'Dick', 'Harry')

1.1.4 避免直接與True, False或者None直接比較

對於任意Python中的對象,不管是內建的仍是用戶定義的,自己都會關聯一個內部的「真值」(truthiness)。因此很天然地,當判斷一個條件是否爲真的時候,儘可能在條件判斷語句中優先依靠這個隱式的「真值」。下面列舉的是「真值」爲False的狀況:

None
False
數值0
空的序列(列表,元組等)
空的字典
當__len__或者__nonzero__被調用後返回的0值或者False

按照上面的最後一條,經過檢查調用__len__或者__nonzero__後返回的值的方式,咱們也能夠定義本身建立的類型的「真值」。除了上面列舉的這些,其餘的狀況都被認爲「真值」爲True

在Python中if語句隱式地使用「真值」,因此你的代碼中也應該這樣作。好比對於下面這種寫法:

if foo == True:

更簡單而直接的寫法是:

if foo:

這樣作的理由有不少。最明顯的一條理由是,若是你的代碼發生了變化,好比當foo變成了一個int型而不是TrueFalseif語句在判斷是否爲0時仍然正確。在更深的層面上,這是基於相等性(equality)和等價性(identity)的差異。使用==檢查的是兩個對象是否有相等或是等效的值(由_eq屬性定義),而is語句則檢查的是兩個對象在底層是否同一個對象。

// Python對相等的實如今C代碼中實現將比較對象用PyInt_AS_LONG轉化成long型,而後再用C中的==進行比較,而is的實現是直接==比較。

因此對FalseNone和和空的序列好比[], {},以及()應該避免直接進行比較。若是一個叫my_list的列表爲空, if my_list 會判斷爲False。固然有些狀況下,雖然不推薦,可是直接和None比較是必須的。當在一個函數中須要判斷一個默認值爲None的參數是否被賦值的時候,好比:

1 def insert_value(value, position=None):
2     """向自定義的容器中插入一個值,插入值
3     的位置做爲可選參數,默認值爲None"""
4     if position is not None:
5         ...

若是使用 if position: 的話,哪裏會出錯呢?設想若是有人想在0位置插入一個值,那麼函數會認爲position這個參數沒有設置,由於 if 0: 會斷定爲False。注意這裏使用的是is not,根據PEP8,和None比較應該老是用is或者is not而不是==

總之,就讓Python的「真值」代替你作比較的工做。

不良風格:

 1 def number_of_evil_robots_attacking():
 2     return 10
 3     
 4 def should_raise_shields():
 5     # 只有當一隻以上的巨型機器人進攻時纔打開防禦罩
 6     # 因此我只須要返回巨型機器人的數量,若是不爲零會自動判斷爲真
 7     return number_of_evil_robots_attacking()
 8 
 9 if should_raise_shields() == True:
10     raise_shields()
11     print('防禦罩已打開')
12 else:
13     print('安全!並無巨型機器人在進攻')

地道Python:

 1 def number_of_evil_robots_attacking():
 2     return 10
 3     
 4 def should_raise_shields():
 5     # 只有當一隻以上的巨型機器人進攻時纔打開防禦罩
 6     # 因此我只須要返回巨型機器人的數量,若是不爲零會自動判斷爲真
 7     return number_of_evil_robots_attacking()
 8 
 9 if should_raise_shields():
10     raise_shields()
11     print('防禦罩已打開')
12 else:
13     print('安全!並無巨型機器人在進攻')

1.1.5 使用if 和 else做爲三元操做符的替代

和許多其餘語言不一樣,Python沒有三元操做符(好比: x ? true : false)。不過Python能夠將賦值推遲到條件判斷以後,因此在Python中三元操做能夠用條件判斷來替代。固然須要注意的是,除非是很簡單的語句,不然三元操做的替代方案會讓語句的可讀性下降。

不良風格:

1 foo = True
2 value = 0
3 
4 if foo:
5     value = 1
6 
7 print(value)

地道Python:

1 foo = True
2 
3 value = 1 if foo else 0
4 
5 print(value)

1.2 For循環

1.2.1 在循環中使用enumerate函數來建立計數或索引

在許多其餘語言中,開發者習慣顯式地聲明一個變量用來做爲循環中的計數或者相關容器的索引。例如在C++中:

1 for ( int i = 0; i < container.size(); ++i )
2 {
3     // Do stuff
4 }

在Python中,內置的enumerate函數就能夠很天然地處理這種須要。

不良風格:

1 my_container = ['Larry', 'Moe', 'Curly']
2 index = 0
3 for element in my_container:
4     print('{} {}'.format(index, element))
5     index += 1

地道Python:

1 my_container = ['Larry', 'Moe', 'Curly']
2 for index, element in enumerate(my_container):
3     print('{} {}'.format(index, element))

1.2.2 使用in關鍵字遍歷可迭代結構

在沒有for_each風格的語言中,開發者習慣於用索引(下標)來遍歷一個容器中的元素。而在Python中,這種操做能夠經過in關鍵字來更爲優雅地實現。

不良風格:

1 my_list = ['Larry', 'Moe', 'Curly']
2 index = 0
3 while index < len(my_list):
4     print(my_list[index])
5     index += 1

地道Python:

1 my_list = ['Larry', 'Moe', 'Curly']
2 for element in my_list:
3     print(element)

1.2.3 使用else去執行一個for循環所有遍歷結束後的代碼

在Python的for循環中能夠包含一個else分句,這是一個很少人知道的技巧。else語句塊會在for循環中的迭代結束後執行,除非在迭代過程當中循環由於break語句結束。利用這種寫法咱們能夠在循環中執行條件檢查。要麼在要檢查的條件語句爲真時用break語句中止循環,要麼在循環結束後進入else語句塊並執行條件未被知足的狀況下要執行的動做。這樣作避免了在循環中單獨使用一個標示變量來檢查條件是否被知足。

不良風格:

 1 for user in get_all_users():
 2     has_malformed_email_address = False
 3     print('檢查 {}'.format(user))
 4     for email_address in user.get_all_email_addresses():
 5         if email_is_malformed(email_address):
 6             has_malformed_email_address = True
 7             print('包含惡意email地址!')
 8             break
 9     if not has_malformed_email_address:
10         print('全部email地址均有效!')

地道Python:

1 for user in get_all_users():
2     print('檢查 {}'.format(user))
3     for email_address in user.get_all_email_addresses():
4         if email_is_malformed(email_address):
5             print('包含惡意email地址!')
6             break
7     else:
8         print('全部email地址均有效!')

轉載請註明出處:達聞西@博客園

下一篇:翻譯《Writing Idiomatic Python》(二):函數、異常

相關文章
相關標籤/搜索