高級語法
除了像上面介紹的 [x ** 2 for x in L]
這種基本語法之外,列表推導式還有一些高級的擴展。
4.1. 帶有if語句
我們可以在 for
語句后面跟上一個 if
判斷語句,用於過濾掉那些不滿足條件的結果項。
例如,我想去除列表中所有的偶數項,保留奇數項,可以這么寫:
>>> L = [1, 2, 3, 4, 5, 6] >>> L = [x for x in L if x % 2 != 0] >>> L [1, 3, 5]
4.2. 帶有for嵌套
在復雜一點的列表推導式中,可以嵌套有多個 for
語句。按照從左至右的順序,分別是外層循環到內層循環。
例如:
>>> [x + y for x in 'ab' for y in 'jk'] ['aj', 'ak', 'bj', 'bk']
4.3. 既有if語句又有for嵌套
列表推導式可以帶任意數量的嵌套 for
循環,並且每一個 for
循環后面都有可選的 if
語句。
通用語法:
[ expression for x in X [if condition] for y in Y [if condition] ... for n in N [if condition] ]
例如,下面的代碼輸出了0~4之間的偶數和奇數的組合。
>>> [(x, y) for x in range(5) if x % 2 == 0 for y in range(5) if y % 2 == 1] [(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]
等價於下面的一般 for
循環:
>>> L = [] >>> for x in range(5): ... if x % 2 == 0: ... for y in range(5): ... if y % 2 == 1: ... L.append((x, y)) >>> L [(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]
4.4. 列表推導式生成矩陣
生成矩陣的方式有多種,例如手動賦值、一般for循環,還有就是列表推導式。如果我們要用列表推導式生成下面的矩陣,可以怎么寫?
>>> M = [[1, 2, 3], ... [4, 5, 6], ... [7, 8, 9]]
一種方法是:
>>> M = [[x, x+1, x+2] for x in [1, 4, 7]] >>> M [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
矩陣的列數少時可以使用這種方法。
如果矩陣的列數較多,我們可以使用另外一種方式:在循環變量的表達式中使用列表推導式。
具體代碼如下:
>>> M = [[y for y in range(x, x+3)] for x in [1, 4, 7]] >>> M [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
與之前帶 for
嵌套的語法不同,這個例子中,實際使用的是最基本的 [expression for x in L]
語法,只有一個 for
語句。
復雜的地方在於前面的變量表達式 expression
不再是簡單的變量運算,而是一個列表推導式,在這個例子中就是 [y for y in range(x, x+3)]
。
內層的列表推導式返回一個行向量,而這些行向量經由外層的列表推導式,最終形成一個二維列表,也就是我們想要的矩陣。
當然,在實際的應用中不能單純追求代碼的簡潔,還要考慮到代碼的可讀性和維護成本。
如果代碼變得過於復雜,不易於理解,我們寧可多寫幾行代碼來增加它的可讀性。
5. 生成器表達式
生成器表達式與列表推導式的語法相同,區別在於生成器表達式的外面使用圓括號,而列表推導式使用方括號。
有關生成器的介紹,請參考這篇文章:《Python高級編程之初識生成器》
6. 集合推導式和字典推導式
注意:集合推導式和字典推導式只有在Python2.7以及之后的版本中才有,Python2.7之前的版本不支持這兩種推導式。
集合推導式的語法與列表推導式相同,只需要把外面的方括號改成花括號即可。
例如,我們可以通過以下方式來生成一個集合:
>>> {x ** 2 for x in [1, 2, 2]} {1, 4}
字典推導式的外面也是使用花括號,不過花括號的內部需要包含鍵值兩部分。
在值不重復的情況下,我們可以通過字典推導式快速交換鍵值對:
>>> D = {'a':1, 'b':2, 'c':3} >>> D = {value: key for key, value in D.items()} >>> D {1: 'a', 2: 'b', 3: 'c'}
from:http://www.codebelief.com/article/2017/02/python-advanced-programming-list-comprehensions/
lix = [];
for x in range(1, 101): lix.push(x ** 2) 執行結果:lix = [1,4,9,16,25.....]
在列表構建器的表達式中,可以添加簡單的條件處理
lix = [x * x for x in range(1, 101) if x % 2 == 0] 執行結果:lix = [4,16,36.....]
也可以在循環過程中,來使用多層循環嵌套,實現更加復雜的效果
lix = [x + y for x in "abc" for y in "xyz"] 執行結果:['ax', 'ay', 'az', 'bx', 'by', 'bz', 'cx', 'cy', 'cz']
列表動態構建器
但是我們通過前面的學習已經知道,這些數據都是加載到內存中的,如果列表中的數據量比較大的情況下,內存消耗是比較嚴重的
在某些情況下,我們只需要使用列表中的一部分數據,后面的數據並不是特別關心,如:通過列表來記錄一個符合某種規則的序列,每次我們只是關心下一個數據,並不關心后面的N條數據,應該怎么做呢?比如我們需要一個奇數列表
# 常規構建器的做法 lix = [2*x + 1 for x in range(1, 101)] # 執行結果:[1,3,5,7,9,11,13,15,17.....] # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # 常規構建器可以直接構建生成 # 但是存在問題,如果一次構建的數據量太大,會嚴重占用內存 # 我們在使用該列表的時候,有可能只是使用前10項 # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # 使用列表動態構建器 lix = (2 * x - 1 for x in range(1, 101)) # 執行結果:print (lix) --> <generator object <genexpr> at 0x7f232e462048> next(lix) # 執行結果:1 next(lix) # 執行結果:3 next(lix) # 執行結果:5 next(lix) # 執行結果:7 # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # 列表動態構建器 # 和構建器基本沒有區別,創建的時候列表中是沒有數據的 # 必須通過next()函數來獲取列表中的下一條數據 # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
from:https://www.jianshu.com/p/fa3fda487f15
[]改成()
,就創建了一個generator:
>>> L = [x * x for x in range(10)] >>> L [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> g = (x * x for x in range(10)) >>> g <generator object <genexpr> at 0x1022ef630>
L和g
的區別僅在於最外層的[]
和()
,L
是一個list,而g
是一個generator。
如果要一個一個打印出來,可以通過next()
函數獲得generator的下一個返回值:
>>> next(g) 0 >>> next(g) 1 >>> next(g) 4 >>> next(g) 9 >>> next(g)
next(g)實在是太變態了,正確的方法是使用for
循環,因為generator也是可迭代對象:
>>> g = (x * x for x in range(10)) >>> for n in g: ... print(n) ... 0 1 4 9
要把fib
函數變成generator,只需要把print(b)
改為yield b
就可以了:
def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 return 'done'
yield關鍵字,那么這個函數就不再是一個普通函數,而是一個generator:
>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>
return語句或者最后一行函數語句就返回。而變成generator的函數,在每次調用next()
的時候執行,遇到yield
語句返回,再次執行時從上次返回的yield
語句處繼續執行。