7 - 列表解析式-生成器

1 解析式

        從一個問題來看解析式,現有以下需求:生成一個列表,元素0-9,對每個元素自增1後求平方返回新列表。app

lst = list(range(10))
lst2 = []
for value in lst:
    lst2.append((value + 1) ** 2)
print(lst2)

        看起來很容易理解,可是這種需求居然用了5行代碼!下面來看一下列表解析式的寫法。函數

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

看起來很是簡潔,屬於Python的風格!哈哈性能

        再來看一下,什麼是列表解析式?在Python中列表解析式是一種語法糖,雖然對看似複雜的代碼進行了簡寫,可是編譯器會進行優化,不會由於簡寫而影響效率,反而由於優化提升了效率。另外還介紹了代碼量,減小了出錯的機會,還簡化了代碼,增長了代碼可讀性。學習

2 列表解析式

        列表解析式的基本語法是以下優化

[ 返回值 for 元素 in 可迭代對象 if 條件]
  • 使用中括號將表達式(推導式)括起來
  • 內部是for循環,if條件可選,能夠是多個可是不支持elif語句
  • 返回一個新的列表

有這樣的賦值語句 newlist = [ print(i) for i in range(10) ],請問newlist打印出來是什麼?code

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

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

爲何是None?由於表達式只會將函數的返回值做爲結果,進行添加,因此當返回值是一個函數操做的對象時,必定要注意函數的返回值!對象

2.1 列表解析式進階

        有的時候咱們的代碼須要進行兩個或多個循環,列表解析式進階版本能夠知足這種需求哦。它的語法是:ip

[ 返回值 for 元素 in 可迭代對象 if 條件表達式1 if 條件表達式2 ... ]
等同於:
for 元素 in 可迭代對象:
    if 條件表達式1:
        if 條件表達式2:
返回值

[ 返回值 for 元素 in 可迭代對象1 for 元素 in 可迭代對象2 ... ]
等同於:
for 元素1 in 可迭代對象1:
    for 元素2 in 可迭代對象2:
        # if 也能夠加條件判斷
        返回值[1個或多個]
  • 條件表達式能夠是多個,可是不能是elif,多個if是而且的關係
  • 多個循環條件等同於循環嵌套,時間複雜度是O(n*內層循環個數)

例子:內存

20之內,既能被2整除,又能被3整除的列表
[ i for i in range(20) if i % 2 == 0 if i % 3 == 0]


[(i,j) for i in range(10) for j in range(20) if i < 3 if j > 18]
表示當i小於3時,j大於18時,組成一個元素返回

3 其餘解析式

        除了列表解析式之外,Python中還存在集合解析式字典解析式'元組解析式'

可不是什麼元組解析式,這行小字你看不到,可不怪我哦。

3.1 集合表達式

  • 語法:{ 返回值 for 元素 in 可迭代對象 if 條件 }
  • 列表解析式的中括號換成大括號{}便可
  • 一樣是當即返回一個集合
20之內,既能被2整除,又能被3整除的集合
{ i for i in range(20) if i % 2 == 0 if i % 3 == 0}

注意集合的特性,若是生成了不可hash的元素好比list,那麼是不能生成集合的哦,若是元素重複,集合會去重的哦

3.2 字典解析式

  • 語法:{ 返回值(key:value) for 元素 in 可迭代對象 if 條件 }
  • 列表解析式的中括號換成大括號{}便可
  • 請使用key:value格式
  • 當即返回一個字典
生成一個key爲abcded的字典
{x:y for x in 'abcdef' for y in range(10)}

注意字典的key相同時,後面的賦值會把以前的值覆蓋哦,因此結果是{'a': 9, 'b': 9, 'c': 9, 'd': 9, 'e': 9, 'f': 9}

4 生成器表達式

        若是你是從上倒下看的,那麼你可能會奇怪,說好的元組表達式呢?若是你是直接跳轉過來的,那麼請忽略前面這句話。那什麼是生成器表達式呢?
        生成器表達式是按需計算(或者惰性求值、延遲計算)的,只有須要的時候才計算值,而列表解析式是直接返回一個新的列表,生成器是一個可迭代對象迭代器。在使用type命令判斷對象類型時,generator 就表示一個生成器對象

  • 語法:( 返回值 for 元素 in 可迭代對象 if 條件表達式 )
  • 列表解析式的中括號換成大括號()便可
  • 延遲計算(惰性計算)
  • 只能迭代一次,不能回頭
g = ((i,j) for i in range(10) for j in range(20) if i < 3 if j > 18)
print(g)  # <generator object <genexpr> at 0x7fca2552b258>
for i in g:
    print(i) # 只能迭代一次,迭代完畢生成器就爲空了哦,

4.1 特色

        沒錯,用括號括起來的並非元組表達式,而變成了生成器表達式,它自己因爲惰性計算的特性和其餘解析式有不少不一樣的特性

  1. 計算方式
            生成器表達式延遲計算(惰性計算),只有你去向它要,它纔會給你計算,而列表解析式在你執行後,會直接給你生成一個新的列表。
  2. 內存佔用
            生成器沒有數據,內存佔用極少,它是使用時一個一個返回數據,若是將這些返回的數據合起來佔用的空間也和列表解析式差很少,可是它不是當即須要這麼多空間
  3. 計算速度
            單看計算時間來看,生成器表達式耗時很是短,列表解析式時長,由於生成器自己並無任何返回值,只是返回了一個生成器對象,列表解析式構造並返回了一個新的列表,因此看起來更耗時了
  4. 遍歷
            當咱們須要對數據進行遍歷時,因爲生成器是遍歷一次計算一個返給你,而列表解析式執行完畢後直接返回一個新的列表不須要計算,因此性能要優於生成器表達式。

    4.2 next函數

    除了遍歷咱們還能夠經過next方法來一次一次的獲取生成器的數據

In [8]: g = ((i,j) for i in range(10) for j in range(20) if i < 3 if j > 18)
In [9]: next(g) 
Out[9]: (0, 19)
In [10]: next(g)
Out[10]: (1, 19)
In [11]: next(g)
Out[11]: (2, 19)
In [12]: next(g)   
---------------------------------------------------------------------------
StopIteration     Traceback (most recent call last)
<ipython-input-12-e734f8aca5ac> in <module>
----> 1 next(g)

StopIteration: 
In [13]:

next()能夠理解爲向生成器要一次數據(撥一下生成器),當生成器爲空時,就會提示StopIteration異常,for循環幫咱們對StopIteration異常作了處理,尚未學習異常處理的咱們,該怎麼辦呢?其實next方法爲咱們提供了默認值參數,即從生成器中拿不到數據,就返回指定的默認: next(g[, default])

In [13]: g = ((i,j) for i in range(10) for j in range(20) if i < 3 if j > 18)           
In [14]: next(g, 'None')    
Out[14]: (0, 19)
In [15]: next(g, 'None')    
Out[15]: (1, 19)
In [16]: next(g, 'None')    
Out[16]: (2, 19)
In [17]: next(g, 'None')     # 生成器空了,就返回default指定的默認值 
Out[17]: 'None'
In [18]: next(g, 'None')    
Out[18]: 'None'

5 總結

        Python2 引入列表解析式,Python2.4引入生成器表達式,Python3 引入集合、字典解析式,並遷移到了Python 2.7,通常來講,應該多用解析式,簡短、高效不過還須要注意的是:

  • 若是一個解析式很是複雜,難以讀懂,能夠考慮拆成for循環,不必非要網列表解析式上靠
  • 生成器和迭代器是不一樣的對象,但都是可迭代對象
  • 可迭代對象範圍更大,均可以使用for循環遍歷

從是否可迭代來看生成器、迭代器、可迭代對象的關係是以下
生成器

相關文章
相關標籤/搜索