你是不是總是覺得學了python好久,驀然回首,總是感覺寫的程式碼不是那麼有pythonic的味道。看看別人的程式碼(django,webpy),再看看自己的程式碼,覺得就是一java-python的混合體。鑑於這種問題,我準備要多學習別人的一些技術和方式,這不,在網上看到一本不錯的書:[writing idiomatic python ebook] 。告訴怎麼寫出python風格的程式碼。本來想共享到網盤的,但是一想,這樣不好吧,人家的書是掛在上面付費賣的,也不貴,10刀。所以就不共享了。這裡把裡面的一些注意點以筆記的形式分享出來一起學習。
ps:我今天發現在163網易閱讀裡有這本書,提供下連結:http://yuedu.163.com/source/cl_dc23b96c2df84533957ffb1089e90604_4
在if語句中用鏈式比較使之更簡明
x = 5 if x > 1 and x < 6: print 'python'
改成:
if 1 < x < 6: print 'python'
不要在if語句後面直接寫程式碼,而是換行
x = True if x:print 'python' print 'is great!'
改成:
x = True if x: print 'python' print 'is great!'
在條件判斷時,應該避免直接和True,False,None進行比較(==)
比如我們判斷一個集合為空是,會這樣寫:
l = [] if l == []: print 'l is empty list'
改成:
l = [] if not l: print 'l is empty list'
其實在if語條的條件判斷時,我們一般的語境上下文還是很明確,如物件是否為None,是否條件為True,或者集合是否為空,字典是否為空。python支援直接在if後臺跟著這些物件,自動為根據語境轉換為相應的布林值(True或False)。 滿足如下的情況,都會認為是False:
- None
- False
- 為0的數值型例項(number)
- 空的序列,如list, tuple
- 空的字典,如dict
- 在自定義的類中定義了__len__返回0 ,或者定義了__nonzero__返回False
但是有些情況還是不同的,不能適用於這個規則。看下面一個例項:
def getCurrentPositon(position=None): if not position: print 'you must set you current position!' getCurrentPositon(0)
方法的作用是得到根據傳入的位置做相應的工作。如果傳入引數0應該也是合理值,即在座標原點位置。但是方法還是會列印'you must set you current position!'。 因為0值被條件判斷為False。所以應該改成如下 :
def getCurrentPositon(position=None): if position is None: print 'you must set you current position!' getCurrentPositon(0)
顯示的比較是否為None,而不依賴內建的布林轉換。注意這裡是用 is 操作符沒有用 == 比較符。因為None物件在python中是單例項的,而is就是直接比較兩個物件是否一樣(可以理解佔用同一條記憶體)
用if,else替換三元(ternary)運算操作符
flag = 2 if flag == 1: displayValue = 'man' elif flag == 2: displayValue = 'woman'
改成:
displayValue = 'man' if flag == 1 else 'woman'
用enumerate函式代替for迴圈中的index變數訪問
my_container = ['lily', 'lucy', 'tom'] index = 0 for element in my_container: print '{} {}'.format(index, element) index += 1
改成:
for index, element in enumerate(my_container): print '%d %s' % (index, element)
當for迴圈體執行完後,可以用else去執行相關的動作
如這樣的場景:遍歷所有人的年齡資訊,決斷當前所有人是不是都是成年人,假如以大於等於20歲劃分:
all_person_age = [21, 22, 23, 30] is_all_adult = True for age in all_person_age: if age <= 20: is_all_adult = False break if is_all_adult: print 'Tha all are audlt!'
我們這裡加了一個 is_all_adult 的標誌變數,在迴圈體中當有年齡小於20歲的人時,把該變數重新置值, 並跳出迴圈體。我們可以改成 for ... else 的句式,這樣可以省略定義一個標誌變數:
all_person_age = [21, 22, 23, 32] for age in all_person_age: if age <= 20: break else: print 'Tha all are audlt!'
for...else...的用法就是當for中途退出迴圈時(沒有遍歷所有的集合元素)時就不執行else裡的語句,反之則執行。
避免用可變的物件(mutable)作為函式引數中的預設初始化值
def function(l = []): l.append(1) return l print function() print function() print function()
將會列印:
[1] [1, 1] [1, 1, 1] [Finished in 0.0s]
這是因為引數的預設初始值只有在定義的時候執行一次,即是單例項的。以後的每一次呼叫都會用這個物件。改成:
def function(l = None): if l is None: l = [] l.append(1) return l
儘量少定義一些只是用於儲存返回值的變數
def all_equal(a, b, c): result = False if a == b == c: result = True return result
改成:
def all_equal(a, b, c): return a == b == c
試著把函式當成物件例項來用,無論是引數傳入還是返回值
def print_addition_table(): for x in range(1, 3): for y in range(1, 3): print(str(x + y) + '\n') def print_subtraction_table(): for x in range(1, 3): for y in range(1, 3): print(str(x - y) + '\n') def print_multiplication_table(): for x in range(1, 3): for y in range(1, 3): print(str(x * y) + '\n') def print_division_table(): for x in range(1, 3): for y in range(1, 3): print(str(x / y) + '\n') print_addition_table() print_subtraction_table() print_multiplication_table() print_division_table()
像上面的定義的四個函式,基本結構一樣,只是裡面進行的運算不同而也(+, -, *, /),我們可以把運算的函式直接作為引數傳遞進去即可:
import operator as op def print_table(operator): for x in range(1, 3): for y in range(1, 3): print(str(operator(x, y)) + '\n') for operator in (op.add, op.sub, op.mul, op.div): print_table(operator)
在python世界裡,一切都是物件,Function也是
多用EAFP,少用LBYL
EAFP:easier to ask forgiveness than permission
LBYL:look before you leap
EAFP可以理解成一切按正常的邏輯編碼,不用管可能出現的錯誤,等出了錯誤再說;而LBYL就是儘可能每寫一行程式碼,都要提前考慮下當前的前置條件是否成立;
LBYL程式碼風格:
def getPersonInfo(person): if person == None: print 'person must be not null!' print person.info
EAFP程式碼風格:
def getPersonInfo(person):
try: print person.info except NameError: print 'person must be not null!'
其實用EAFP風格的程式碼最大的好處是程式碼邏輯清晰,而LBYL會導致本來兩句話說清楚的事,往往因為穿插了很多條件檢查的語句使程式碼邏輯變得混亂。
不要一根筋似的看到異常就catch並吃掉(swallowing)
如下定義一個方法,請求url並返回響應:
import requests def get_json_response(url): try: r = requests.get(url) return r.json() except: print('Oops, something went wrong!') return None
如果這個函式給第三方呼叫,萬一出了什麼問題,使用者只會得到一個something went wrong提示,而只能猜出錯的原因,是GFW的問題呢,還是url地址格式錯誤呢。改成如下:
import requests def get_json_response(url): return requests.get(url).json()
把出錯的處理權力交給呼叫方。