解析式
舉例場景:對一個列表所有的數值求平方
- 普通用法
In [15]: ret = []
...: for x in range(10):
...: ret.append(x ** 2)
...: print(ret)
...:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
- 解析式用法
In [12]: [x ** 2 for x in list(range(10))]
Out[12]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
結論:解析式是比普通語句寫法:性能更高,代碼更簡潔、可讀性更強
的一種表達式。如果用起來就有如上優點、如果不用也可以用更麻煩的方式實現
解析式與普通用法法的性能及優勢
- 列表解析的效率要高於普通用法
- 列表解析的代碼更簡潔、可讀性強
例:列表解析與普通用法法的性能及優勢
In [21]: import timeit
In [22]: %%timeit
...: ret = [ x ** 2 for x in range(10)]
...:
100000 loops, best of 3: 3.5 µs per loop
In [23]: %%timeit
...: ret = []
...: for x in range(10):
...: ret.append(x ** 2)
...:
100000 loops, best of 3: 3.88 µs per loop
總結:
列表解析的效率要高於普通用法
列表解析的代碼更簡潔、可讀性強
解析式有如下幾種
- 列表解析:[expr for e in iterator]
- 生成器解析:(expr for e in iterator)
- 集合解析:{expr for e in iterator}
- 字典解析:{keys: expr for e in iterator}
它們都支持:
- 帶if字句的列表解析:[expr for e in iterator if cond]
- 帶單個if的列表解析:[expr for e in iterator if cond]
- 帶任意多個的if字句:[ x for x in range(10) if x > 0 if x < 5 if x % 2 == 0]
列表解析
列表解析返回的都是列表,輸入對象是所有可迭代對象。
- 帶if字句的列表解析:[expr for e in iterator if cond]
- 帶單個if的列表解析:[expr for e in iterator if cond]
- 帶任意多個的if字句:[ x for x in range(10) if x > 0 if x < 5 if x % 2 == 0]
- 帶for語句的列表解析
- 帶單個for的列表解析
- 帶多個for的列表解析
- 帶if和for的列表解析
- 帶單個if和for的列表解析
- 帶多個if和for的列表解析
- 嵌套解析式(不建議使用)
帶if字句的列表解析
解析式:[expr for e in iterator if cond]
例:帶單個if的列表解析
In [25]: ret = []
...: for x in range(10):
...: if x % 2 == 0:
...: ret.append(x)
...: print(ret)
...:
[0, 2, 4, 6, 8]
In [26]: [x for x in range(10) if x % 2 == 0]
Out[26]: [0, 2, 4, 6, 8]
例:帶任意多個的if字句
In [27]: [ x for x in range(10) if x > 0 if x < 5 if x % 2 == 0]
Out[27]: [2, 4]
帶多個if語句的, 都可以轉換爲條件的邏輯運算, 所以一般來說, 不會帶多個if語句。
帶for語句的列表解析
例:可以有多個for語句, 相當於逐層嵌套
In [30]: [(x, y) for x in range(3) for y in range(5, 8)]
Out[30]: [(0, 5), (0, 6), (0, 7), (1, 5), (1, 6), (1, 7), (2, 5), (2, 6), (2, 7)]
In [32]: [(x, y, z) for x in range(2) for y in range(5, 7) for z in range(10, 12)]
Out[32]:
[(0, 5, 10),
(0, 5, 11),
(0, 6, 10),
(0, 6, 11),
(1, 5, 10),
(1, 5, 11),
(1, 6, 10),
(1, 6, 11)]
帶多個if語句和for語句
例:多個for語句及if語句
In [33]: [(x, y) for x in range(3) if x > 0 for y in range(5, 8)]
Out[33]: [(1, 5), (1, 6), (1, 7), (2, 5), (2, 6), (2, 7)]
嵌套的列表解析
例:嵌套的列表解析
In [11]: [ x for x in [y for y in range(10)]]
Out[11]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
難以預測結果, 所以一般不用
使用列表解析爲了讓代碼更簡潔
什麼時候用代碼解析式, 什麼時候不用代碼解析式, 跟着感覺走。什麼時候不用代碼解析式能簡潔代碼就什麼時候不用。
能一下子看出輸出結果時, 就用, 如果一眼看不出結果的解析式的結果就不要用了。
偶數求平方, 奇數求立方
例:偶數求平方, 奇數求立方
普通用法:
In [36]: ret = []
...: for x in range(10):
...: if x % 2 == 0:
...: ret.append(x ** 2)
...: else:
...: ret.append(x ** 3)
...: print(ret)
...:
[0, 1, 4, 27, 16, 125, 36, 343, 64, 729]
列表解析式用法
In [37]: [ x ** 2 if x % 2 == 0 else x ** 3 for x in range(10) ]
Out[37]: [0, 1, 4, 27, 16, 125, 36, 343, 64, 729]
列表解析的表達式
In [3]: x = 3
In [4]: x ** 2 if x % 2 == 0 else x ** 3
Out[4]: 27
x if cond else y # 當條件滿足時返回x, 當條件不滿足時返回y
In [15]: x ** 2 if x % 2 == 0 else x ** 3
Out[15]: 27
python 2.7 已經支持表達解析式
列表解析得到的結果是列表, 輸入對象是所有可迭代對象。
生成器解析
生成器是什麼:生成器是一次生成一個值的特殊類型函數。
Python3 迭代器與生成器
在 Python 中,使用了 yield 的函數被稱爲生成器(generator)。
跟普通函數不同的是,生成器是一個返回迭代器的函數,只能用於迭代操作,更簡單點理解生成器就是一個迭代器。
在調用生成器運行的過程中,每次遇到 yield 時函數會暫停並保存當前所有的運行信息,返回 yield 的值, 並在下一次執行 next() 方法時從當前位置繼續運行。
調用一個生成器函數,返回的是一個迭代器對象。
- 列表解析的中括號變成小括號就是生成器解析了
- 生成器解析比列表解析的優勢在於,當要生成的數特別大時列表解析會佔用非常大的內存,而生成器解析不用
- 什麼時候用列表解析, 什麼時候用生成器解析?
- 需要用下標訪問的時候, 用列表解析, 只需要對結果迭代的時候, 優先使用生成器解析。
例:生成器解析與列表解析的區別
In [16]: range(10000) # 當要生成的數特別大時
Out[16]: range(0, 10000)
In [17]: [ x ** 2 for x in range(10000)] # 會佔用很多內存
In [18]: (x ** 2 for x in range(10000))
Out[18]: <generator object <genexpr> at 0x7fb8f0ed8f68> # 返回一個generator, 並且沒有佔用多少內存。
In [19]: type((x ** 2 for x in range(10000)))
Out[19]: generator
In [20]: g = (x ** 2 for x in range(10000))
In [21]: next(g) # 生成器解析調用的方法
Out[21]: 0
In [22]: next(g)
Out[22]: 1
總結:
列表解析的中括號變成小括號就是生成器解析了
生成器解析式返回的是一個生成器
例:表達式在取值的時候纔開始計算
In [23]: def fn(x):
...: print('executed')
...: return x
...:
In [24]: g = (fn(x) for x in range(10))
In [25]: next(g)
executed
Out[25]: 0
In [26]: next(g)
executed
Out[26]: 1
In [27]: next(g)
executed
Out[27]: 2
什麼時候用列表解析, 什麼時候用生成器解析?
需要用下標訪問的時候, 用列表解析, 只需要對結果迭代的時候, 優先使用生成器解析。
集合解析
- 集合解析返回的是集合
- 集合解析式用大括號
In [1]: {x for x in range(10)}
Out[1]: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
In [2]: s = {x for x in range(10)}
In [3]: type(s)
Out[3]: set
總結:
集合解析返回的是集合
集合解析式用大括號
字典解析
- 字典解析式:key的表達式和value的表達式組成
例:字典解析表達
In [4]: {str(x): x for x in range(10)}
Out[4]:
{'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9}
{str(x): x for x in range(10)} # : 冒號之前是key的表達式, 冒號之後的是value的表達式
例:普通表達式
In [8]: d = {}
...: for x in range(10):
...: d[str(x)] = x
...:
In [9]: d
Out[9]:
{'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9}
例:字典解析
In [82]: {x:y for y, x in {'1': '2', '3': '4'}.items()}
Out[82]: {'2': '1', '4': '3'}
字典解析式:key的表達式和value的表達式組成