前言
文章有點標題黨,主要是分享一些Python好用的語法糖,用更少的程式碼實現同樣的功能,而且還很優雅。
兵器譜
- ifpython沒有三目運算子,我挺苦惱的,比如把兩個整數較大的那個複製給一個變數,有三目運算子的語言會這樣寫:
123a = 1b = 2c = a > b ? a : b
後來發現Python的if語句可以寫成一行完成上述功能:
1c = a if a > b else b - with我們通常以如下形式操作檔案:
12345try: f = open('/path/to/file', 'r')print f.read()finally:if f:f.close()
每次這樣寫太繁瑣,來試試with的威力:
12with open('/path/to/file', 'r') as f:print f.read()
程式碼更佳簡潔,並且不必呼叫f.close()方法。
with利用了上下文管理協議,這玩意說起來太複雜,直接上程式碼。
自定義一個支援上下文管理協議的類, 類中實現enter方法和exit方法。
12345678910111213141516171819202122class MyWith(object):def __enter__(self):print "Enter with"return self # 返回物件給as後的變數def __exit__(self, exc_type, exc_value, exc_traceback):#關閉資源等if exc_traceback is None:print "Exited without Exception"return Trueelse:print "Exited with Exception"return Falsedef test_with():with MyWith() as my_with:print "running my_with"print "------分割線-----"with MyWith() as my_with:print "running before Exception"raise Exceptionprint "running after Exception"if __name__ == '__main__':test_with()
輸出:
1234567Enter withrunning my_withExited without Exception------分割線-----Enter withrunning before ExceptionExited with Exception - map大多數的for迴圈可以用map來代替,用法是:
map(func,seq)
,對seq中的每個元素進行操作,具體什麼操作在func裡定義。
我們以前是這麼寫for迴圈的:
1234array = [1, 2, 3]square_array = []for i in array:square_array.append(i ** 2)
改用map:
12array = [1, 2, 3]square_array = map(lambda i: i ** 2, array)
map的第一個引數是lambda表示式,冒號前面的i作為形參,來自於array中的元素,冒號後面就是要返回的值。
當然你也可以使用列表推導式來代替:
12array = [1, 2, 3]square_array = [i ** 2 for i in array] - filter用法與map類似:
filter(func,seq)
,對seq中的元素進行過濾,返回符合條件的那些元素。
比如返回array = [1, 2, 3, 4]
中的所有奇數:
1print filter(lambda i: i % 2, array)
這裡是對2取餘,返回結果為True的元素。那麼什麼情況下結果為True?Python裡面不為0,None或者null都是True。所以結果就是,偶數是False,奇數是True,返回所有奇數。
列表推導式方案:
1print [i for i in array if i % 2 != 0] - reduce用法:
reduce(func,seq)
,對seq中的每個元素進行func操作,最後彙總返回一個值。- 求
array = [1, 2, 3]
所有元素的和:
1print reduce(lambda x, y: x + y, array)
reduce會先將array裡面的頭兩個數分別作為x和y,求它們的和,然後把它的結果和第三個相加,再把結果和第四個相加,直到最後一個元素。 - 求
array = [1, 2, 3]
中的最大值:
1print reduce(lambda x, y: x if x > y else y, array) - 求
strings = ["abc", "abcd", "def"]
中”abc”出現的總次數:
1print reduce(lambda count, str: count + str.count("abc"), strings, 0)
第三個引數0是count的初始值。
- 求
- eval執行一個字串表示式,並返回表示式的值。
12print eval("1 + 1")>> 2
再來個複雜點的:
12345678def init():return 1def func(num):return 2action = {"num": init, "func": func}expression = 'func(num)'print eval(expression, action)>> 2看不懂就算了,這玩意寫起來很飄逸,但是殺敵一千,自損八百。
- 裝飾器
設計模式的中的裝飾器模式還記得吧,可以動態擴充套件一個類的功能,但是又不會修改這個類的原始碼,Java IO包大量採用了裝飾器模式,我們來看看Python是怎麼玩的。
舉個簡單的例子吧,在一個函式執行前打日誌:1234567891011def log(func):def wrapper(*args, **kwargs):print('call %s()' % func.__name__)return func(*args, **kwargs)return wrapper@logdef func():print 'do something'func()輸出:
12call func()do something - 生成器yield是Python核心關鍵字,不懂生成器,基本上就是把Python當加強版的Shell在用。
迭代是在程式開發中常用的操作,對一個列表進行遍歷。可是如果列表資料過多,比如有上億條,就會遇到問題,因為記憶體空間有限。生成器應運而生,舉個斐波那契數列的例子:12345678def fib(n):a = b = 1for i in range(n):yield aa, b = b, a + bfor i in fib(10):print i,輸出:
11 1 2 3 5 8 13 21 34 55配合send、next函式,生成器可以實現協程的功能:
123456789def func():while True:n = yieldprint ngen = func()print gen.next()gen.send(2)gen.send(3)輸出:
123None23呼叫next函式後,程式碼執行到yield,因為後面沒有任何值,所以列印出來的結果是None,此時程式碼hold住,讓出CPU。呼叫send(2)後程式碼恢復執行,將2賦給n然後列印,yield自帶next函式功能,程式碼繼續執行到yield,周而復始。通過生成器在單執行緒的情況下實現了任務排程。
- for/else我們經常使用for迴圈來查詢元素,有兩個場景會使迴圈停下來:
- 元素被找到,觸發break。
- 迴圈結束。
但是我們並不知道是哪個原因導致迴圈結束,通常是設定一個標記,元素被找到,改變標記的值。for/else可以很優雅地解決這個問題:12345for i in range(10):if i > 10:print ielse:print("can't find result")