Python_7_列表解析式-生成器

1. 解析式

??從一個(gè)問(wèn)題來(lái)看解析式,現(xiàn)有如下需求:生成一個(gè)列表,元素 0-9,對(duì)每一個(gè)元素自增 1 后求平方返回新列表。

lst = list(range(10))
lst2 =[]
for value in lst:
    lst2.append((value + 1) ** 2)
print(lst2)
# 打印結(jié)果:[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

??看起來(lái)很容易理解,但是這種需求竟然用了 5 行代碼!下面來(lái)看一下列表解析式的寫(xiě)法。

[(x+1)**2 for x in range(10)]

上面的代碼功能一樣,但看起來(lái)非常簡(jiǎn)潔,屬于 Python 的風(fēng)格 (Pythonic)!

??再來(lái)看一下,什么是列表解析式?在 Python 中列表解析式是一種語(yǔ)法糖,雖然對(duì)看似復(fù)雜的代碼進(jìn)行了簡(jiǎn)寫(xiě),但是編譯器會(huì)進(jìn)行優(yōu)化,不會(huì)因?yàn)楹?jiǎn)寫(xiě)而影響效率,反而因?yàn)閮?yōu)化提高了效率。另外還節(jié)省了代碼量,減少了出錯(cuò)的機(jī)會(huì),簡(jiǎn)化代碼的同時(shí)增加了代碼可讀性。

2. 列表解析式

??列表解析式的基本語(yǔ)法如下:

[返回值 for 元素 in 可迭代對(duì)象 if 條件]
  • 使用中括號(hào) [] 將表達(dá)式(推導(dǎo)式)括起來(lái)
  • 內(nèi)部是 for 循環(huán),if 條件可選,可以有多個(gè) if 但是不支持 elif 語(yǔ)句
  • 返回一個(gè)新的列表

??有這樣的賦值語(yǔ)句 newlist = [print(i) for i in range(10)],請(qǐng)問(wèn) newlist 打印出來(lái)是什么?

In : newlist = [print(i) for i in range(10)]
0
1
2
3
4
5
6
7
8
9

In : newlist 
Out:[None, None, None, None, None, None, None, None, None, None]

??為什么是 None?因?yàn)楸磉_(dá)式只會(huì)將函數(shù)的返回值作為結(jié)果,進(jìn)行添加,所以當(dāng)返回值是一個(gè)函數(shù)操作的對(duì)象時(shí),一定要注意函數(shù)的返回值!

2.1. 列表解析式進(jìn)階

??有的時(shí)候我們的代碼需要進(jìn)行兩個(gè)或多個(gè)循環(huán),列表解析式進(jìn)階版本可以滿(mǎn)足這種需求。它的語(yǔ)法是:

# 語(yǔ)法一:
[返回值 for 元素 in 可迭代對(duì)象 if 條件表達(dá)式1 if 條件表達(dá)式2 ...]
# 等同于:
for 元素 in 可迭代對(duì)象:
    if 條件表達(dá)式1:
        if 條件表達(dá)式2:
返回值

# 語(yǔ)法二:
[返回值 for 元素1 in 可迭代對(duì)象1 for 元素2 in 可迭代對(duì)象2 ...]
# 等同于:
for 元素1 in 可迭代對(duì)象1:
    for 元素2 in 可迭代對(duì)象2:
        # if 也可以加條件判斷
        返回值[1 個(gè)或多個(gè)]
  • 條件表達(dá)式可以是多個(gè),但是不能是 elif,多個(gè) if 是并且的關(guān)系
  • 多個(gè)循環(huán)條件等同于循環(huán)嵌套,時(shí)間復(fù)雜度是 O(n * 內(nèi)層循環(huán)個(gè)數(shù))

??例子:

# 20 以?xún)?nèi),既能被 2 整除,又能被 3 整除的列表
In : [i for i in range(20) if i % 2 == 0 if i % 3 == 0]
Out: [0, 6, 12, 18]

# 20 以?xún)?nèi),當(dāng) i 小于 3 時(shí),j 大于 18 時(shí),組成一個(gè)元組返回
In : [(i,j) for i in range(20) for j in range(20) if i < 3 if j > 18]
Out: [(0, 19), (1, 19), (2, 19)]

3. 其他解析式

??除了列表解析式以外,Python 中還存在 集合解析式字典解析式、生成器表達(dá)式。

3.1. 集合表達(dá)式

  • 語(yǔ)法:{返回值 for 元素 in 可迭代對(duì)象 if 條件}
  • 列表解析式的中括號(hào)換成大括號(hào) {} 即可
  • 同樣是立即返回一個(gè)集合
# 20 以?xún)?nèi),既能被 2 整除,又能被 3 整除的集合
In : {i for i in range(20) if i % 2 == 0 if i % 3 == 0}
Out: {0, 6, 12, 18}

??注意集合的特性,如果生成了不可 hash 的元素比如 list,那么是不能生成集合的哦,如果元素重復(fù),集合會(huì)去重的。

3.2. 字典解析式

  • 語(yǔ)法:{返回鍵值對(duì)(key:value) for 元素 in 可迭代對(duì)象 if 條件 }
  • 列表解析式的中括號(hào)換成大括號(hào) {} 即可
  • 請(qǐng)使用 key:value 鍵值對(duì)格式
  • 立即返回一個(gè)字典
# 生成一個(gè) key 為 abcded 的字典
In : {x:y for x in 'abcdef' for y in range(10)}
Out: {'a': 9, 'b': 9, 'c': 9, 'd': 9, 'e': 9, 'f': 9}

??注意字典的 key 相同時(shí),后面的賦值會(huì)把之前的值覆蓋哦,所以結(jié)果是{'a': 9, 'b': 9, 'c': 9, 'd': 9, 'e': 9, 'f': 9}

4. 生成器表達(dá)式

??為什么沒(méi)有元組表達(dá)式呢?因?yàn)樾±ㄌ?hào)給了一個(gè)更重要的表達(dá)式使用,那就是生成器表達(dá)式,什么是生成器表達(dá)式呢?
??生成器表達(dá)式是按需計(jì)算(或者惰性求值、延遲計(jì)算)的,只有需要的時(shí)候才計(jì)算值,而列表解析式是直接返回一個(gè)新的列表,生成器是一個(gè) 可迭代對(duì)象迭代器。在使用 type 命令判斷對(duì)象類(lèi)型時(shí),generator 就表示一個(gè)生成器對(duì)象。

  • 語(yǔ)法:(返回值 for 元素 in 可迭代對(duì)象 if 條件表達(dá)式)
  • 列表解析式的中括號(hào)換成大括號(hào) () 即可
  • 延遲計(jì)算(惰性計(jì)算)
  • 只能迭代一次,不能回頭
In : g =((i,j) for i in range(10) for j in range(20) if i<3 if j>18)

In : print(g, type(g))
<generator object <genexpr> at 0x7fe4d04a0c50> <class 'generator'>

In : for i in g: 
   ...:     print(i) 
   ...:
(0, 19)
(1, 19)
(2, 19)
# 只能迭代一次,迭代完畢生成器就為空了哦,

4.1. 特點(diǎn)

??注意,用括號(hào)括起來(lái)的并不是元組表達(dá)式,而變成了 生成器表達(dá)式,它本身由于惰性計(jì)算的特性和其他解析式有很多不同的特性

  1. 計(jì)算方式
    ??生成器表達(dá)式延遲計(jì)算(惰性計(jì)算),只有你去向它要,它才會(huì)給你計(jì)算,而列表解析式在你執(zhí)行后,會(huì)直接給你生成一個(gè)新的列表。
  2. 內(nèi)存占用
    ??生成器沒(méi)有數(shù)據(jù),內(nèi)存占用極少,它在使用時(shí)一個(gè)一個(gè)地返回?cái)?shù)據(jù),如果將這些返回的數(shù)據(jù)合起來(lái)占用的空間也和列表解析式差不多,但是它不是立即需要這么多空間。
  3. 計(jì)算速度
    ??單從計(jì)算時(shí)間來(lái)看,生成器表達(dá)式耗時(shí)非常短,列表解析式時(shí)長(zhǎng),因?yàn)樯善鞅旧聿](méi)有任何返回值,只是返回了一個(gè)生成器對(duì)象,列表解析式構(gòu)造并返回了一個(gè)新的列表,所以看起來(lái)更耗時(shí)了
  4. 遍歷
    ??當(dāng)我們需要對(duì)數(shù)據(jù)進(jìn)行遍歷時(shí),由于生成器是遍歷一次計(jì)算一個(gè)返給你,而列表解析式執(zhí)行完畢后直接返回一個(gè)新的列表不需要計(jì)算,所以性能要優(yōu)于生成器表達(dá)式。

4.2. next 函數(shù)

??除了遍歷,我們還可以通過(guò) next 方法 來(lái)一次一次地獲取生成器的數(shù)據(jù)

In : g = ((i,j) for i in range(10) for j in range(20) if i<3 if j>18)

In : next(g) 
Out:(0, 19)

In : next(g)
Out:(1, 19)

In : next(g)
Out:(2, 19)

In : next(g)
---------------------------------------------------------------------------
StopIteration     Traceback(most recent call last)
<ipython-input-12-e734f8aca5ac> in <module>
----> 1 next(g)

StopIteration: 

In :

??next()可以理解為向生成器要一次數(shù)據(jù)(撥一下生成器),當(dāng)生成器為空時(shí),就會(huì)提示 StopIteration 異常,for 循環(huán)幫我們對(duì) StopIteration 異常做了處理,還沒(méi)有學(xué)習(xí)異常處理的我們,該怎么辦呢?其實(shí)next 方法為我們提供了默認(rèn)值參數(shù),即從生成器中拿不到數(shù)據(jù),就返回指定的默認(rèn)值:next(g[, default])。

In : g =((i,j) for i in range(10) for j in range(20) if i <3 if j> 18)

In : next(g, 'None')
Out:(0, 19)

In : next(g, 'None')
Out:(1, 19)

In : next(g, 'None')
Out:(2, 19)

In : next(g, 'None')     # 生成器空了,就返回 default 指定的默認(rèn)值 
Out: 'None'

In : next(g, 'None')
Out: 'None'

5. 總結(jié)

??Python2 引入列表解析式,Python2.4 引入生成器表達(dá)式,Python3 引入集合、字典解析式,并遷移到了 Python 2.7,一般來(lái)說(shuō),應(yīng)該多用解析式,簡(jiǎn)短、高效,不過(guò)還需要注意的是:

  • 如果一個(gè)解析式非常復(fù)雜,難以讀懂,可以考慮拆成 for 循環(huán),沒(méi)必要非要往列表解析式上靠
  • 生成器和迭代器是不同的對(duì)象,但都是可迭代對(duì)象
  • 可迭代對(duì)象范圍更大,都可以使用 for 循環(huán)遍歷

??從是否可迭代來(lái)看生成器、迭代器、可迭代對(duì)象的關(guān)系是如下

Iterable.png

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容