(轉)python高級:列表解析和生成表達式


一、語法糖的概念

“糖”,可以理解為簡單、簡潔,“語法糖”使我們可以更加簡潔、快速的實現這些功能。 只是Python解釋器會把這些特定格式的語法翻譯成原本那樣復雜的代碼邏輯

 

 

我們使用的語法糖有:

  • if...else 三元表達式: 可以簡化分支判斷語句,如 x = y.lower() if isinstance(y, str) else y
  • with語句: 用於文件操作時,可以幫我們自動關閉文件對象,使代碼變得簡潔;
  • 裝飾器: 可以在不改變函數代碼及函數調用方式的前提下,為函數增加增強性功能;
  • 列表生成式: 用於生成一個新的列表
  • 生成器: 用於“惰性”地生成一個無限序列

 

 

 

生成器表達式、列表解析式對比

 

列表解析式  在需要改變列表而不是需要新建某列表時,可以使用列表解析無返回值

 

  1.  
    >>> L= [(x+1,y+1) for x in range(3) for y in range(5)]
  2.  
    >>> L
  3.  
    [( 1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5)]
  4.  
    >>> N=[x+10 for x in range(10) if x>5]
  5.  
    >>> N
  6.  
    [ 16, 17, 18, 19]



 

 

生成器表達式  有返回值

當序列過長, 而每次只需要獲取一個元素時,應當考慮使用生成器表達式而不是列表解析。生成器表達式的語法和列表解析一樣只不過生成器表達式是被()括起來的,而不是[],如下:

 

  1.  
    >>> L= (i + 1 for i in range(10) if i % 2)
  2.  
    >>> L
  3.  
    <generator object <genexpr> at 0xb749a52c>
  4.  
    >>> L1=[]
  5.  
    >>> for i in L:
  6.  
    ... L1.append(i)
  7.  
    ...
  8.  
    >>> L1
  9.  
    [ 2, 4, 6, 8, 10]

 

 

1. 當需要只是執行一個循環的時候盡量使用循環而不是列表解析,這樣更符合python提倡的直觀性。

 

  1.  
    for item in sequence:
  2.  
    process(item)

2. 當有內建的操作或者類型能夠以更直接的方式實現的,不要使用列表解析。例如復制一個列表時,使用:L1=list(L)即可,不必使用: 

 

 

L1=[x for x in L] 
 

3.如果需要對每個元素都調用並且返回結果時,應使用L1=map(f,L), 而不是 L1=[f(x) for x in L]

對比:

計算方式

生成器表達式延遲計算,列表解析式立即計算
 

 

內存占用

從返回值來說,生成器省內存,列表解析式返回新的列表
 
生成器沒有數據,內存占用極少,但是使用的時候,雖然一個個返回數據,但是合起來占用的內存也差不多
 
列表解析式構造新的列表需要占用內存
 

 

計算速度

單看計算時間,生成器表達式耗時非常短,列表解析式耗時長
 
但是生成器本身並沒有返回任何值,只返回了一個生成器對象
 
列表解析式構造並返回了一個新的列表
 

 

 

 

列表解析實例展示

 

  1.  
    要求:列出 1~10中大於等於4的數字的平方
  2.  
    ####################################################
  3.  
    1、普通方法:
  4.  
    >>> L = []
  5.  
    >>> for i in range(1,11):
  6.  
    ...     if i >= 4:
  7.  
    ...         L.append(i**2)
  8.  
    ... 
  9.  
    >>> print L
  10.  
    [ 16, 25, 36, 49, 64, 81, 100]
  11.  
    ####################################################
  12.  
    2、列表解析
  13.  
    >>>L = [ i** 2 for i in range(1,11) if i >= 4 ]
  14.  
    >>> print L
  15.  
    [ 16, 25, 36, 49, 64, 81, 100]
 
[python] view plain copy
 
 
print?
  1. <code class="language-python">要求:列出1~10所有數字的平方除以2的值  
  2. ####################################################  
  3. 1、普通方法  
  4. >>> L = []  
  5. >>> for i in range(1,11):  
  6. ...     L.append(i**2/2)  
  7. ...   
  8. >>> print L  
  9. [0, 2, 4, 8, 12, 18, 24, 32, 40, 50]  
  10. ####################################################  
  11. 2、列表解析  
  12. >>> L = [i**2/for i in range(1,11) ]  
  13. >>> print L  
  14. [0, 2, 4, 8, 12, 18, 24, 32, 40, 50]</code>  
  1.  
    要求:列出 1~10所有數字的平方除以2的值
  2.  
    ####################################################
  3.  
    1、普通方法
  4.  
    >>> L = []
  5.  
    >>> for i in range(1,11):
  6.  
    ... L.append(i**2/2)
  7.  
    ...
  8.  
    >>> print L
  9.  
    [ 0, 2, 4, 8, 12, 18, 24, 32, 40, 50]
  10.  
    ####################################################
  11.  
    2、列表解析
  12.  
    >>> L = [i**2/2 for i in range(1,11) ]
  13.  
    >>> print L
  14.  
    [ 0, 2, 4, 8, 12, 18, 24, 32, 40, 50]
  1.  
    要求:列出 "/var/log"中所有已'.log'結尾的文件
  2.  
    ##################################################
  3.  
    1、普通方法
  4.  
    >>> import os
  5.  
    >>>file = []
  6.  
    >>> for file in os.listdir('/var/log'):
  7.  
    ...     if file.endswith('.log'):
  8.  
    ...         file.append(file)
  9.  
    ... 
  10.  
    >>> print file
  11.  
    [ 'anaconda.ifcfg.log', 'Xorg.0.log', 'anaconda.storage.log', 'Xorg.9.log', 'yum.log', 'anaconda.log', 'dracut.log', 'pm-powersave.log', 'anaconda.yum.log', 'wpa_supplicant.log', 'boot.log', 'spice-vdagent.log', 'anaconda.program.log']
  12.  
    ##################################################
  13.  
    2.列表解析
  14.  
    >>> import os
  15.  
    >>> file = [ file for file in os.listdir('/var/log') if file.endswith('.log') ]
  16.  
    >>> print file
  17.  
    [ 'anaconda.ifcfg.log', 'Xorg.0.log', 'anaconda.storage.log', 'Xorg.9.log', 'yum.log', 'anaconda.log', 'dracut.log', 'pm-powersave.log', 'anaconda.yum.log', 'wpa_supplicant.log', 'boot.log', 'spice-vdagent.log', 'anaconda.program.log']
  1.  
    要求:實現兩個列表中的元素逐一配對。
  2.  
    1、普通方法:
  3.  
    >>> L1 = ['x','y','z']
  4.  
    >>> L2 = [1,2,3]
  5.  
    >>> L3 = []
  6.  
    >>> for a in L1:
  7.  
    ... for b in L2:
  8.  
    ... L3.append((a,b))
  9.  
    ...
  10.  
    >>> print L3
  11.  
    [( 'x', 1), ('x', 2), ('x', 3), ('y', 1), ('y', 2), ('y', 3), ('z', 1), ('z', 2), ('z', 3)]
  12.  
    ####################################################
  13.  
    2、列表解析:
  14.  
    >>> L1 = ['x','y','z']
  15.  
    >>> L2 = [1,2,3]
  16.  
    L3 = [ (a,b) for a in L1 for b in L2 ]
  17.  
    >>> print L3
  18.  
    [( 'x', 1), ('x', 2), ('x', 3), ('y', 1), ('y', 2), ('y', 3), ('z', 1), ('z', 2), ('z', 3)]
  1.  
    1 使用列表解析生成 9*9 乘法表
  2.  
    2
  3.  
    3 print('\n'.join([''.join(['%s*%s=%-2s '%(y,x,x*y)for y in range(1,x+1)])for x in range(1,10)]))
說明:以上實例,使用列表解析比使用普通方法的速度幾乎可以快1倍。因此推薦使用列表解析。 
 

 

 

列表生成式與map()、filter()等高階函數功能對比

[python] view plain copy
 
 
print?
  1. <code class="language-python">要求:把一個列表中所有的字符串轉換成小寫,非字符串元素原樣保留  
  2. L = ['TOM', 'Peter', 10, 'Jerry']  
  3. # 用列表生成式實現  
  4. list1 = [x.lower() if isinstance(x, str) else x for x in L]  
  5.   
  6. # 用map()函數實現  
  7. list2 = list(map(lambda x: x.lower() if isinstance(x, str) else x,  L))</code>  
  1.  
    要求:把一個列表中所有的字符串轉換成小寫,非字符串元素原樣保留
  2.  
    L = [ 'TOM', 'Peter', 10, 'Jerry']
  3.  
    # 用列表生成式實現
  4.  
    list1 = [x.lower() if isinstance(x, str) else x for x in L]
  5.  
     
  6.  
    # 用map()函數實現
  7.  
    list2 = list(map( lambda x: x.lower() if isinstance(x, str) else x, L))
 
[python] view plain copy
 
 
print?
  1. <code class="language-python">要求:把一個列表中所有的字符串轉換成小寫,非字符串元素移除  
  2. L = ['TOM', 'Peter', 10, 'Jerry']  
  3. # 用列表生成式實現  
  4. list3 = [x.lower() for x in L if isinstance(x, str)]  
  5.   
  6. # 用map()和filter()函數實現  
  7. list4 = list(map(lambda x: x.lower(), filter(lambda x: isinstance(x, str), L)))</code>  
  1.  
    要求:把一個列表中所有的字符串轉換成小寫,非字符串元素移除
  2.  
    L = [ 'TOM', 'Peter', 10, 'Jerry']
  3.  
    # 用列表生成式實現
  4.  
    list3 = [x.lower() for x in L if isinstance(x, str)]
  5.  
     
  6.  
    # 用map()和filter()函數實現
  7.  
    list4 = list(map( lambda x: x.lower(), filter(lambda x: isinstance(x, str), L)))

 

 

生成器(Generator)

1. 生成器的作用

按照某種算法不斷生成新的數據,直到滿足某一個指定的條件結束。

2. 生成器的構造方式

構造生成器的兩種方式:

  • 使用類似列表生成式的方式生成 (2*n + 1 for n in range(3, 11))
  • 使用包含yield的函數來生成

如果計算過程比較簡單,可以直接把列表生成式改成generator;但是,如果計算過程比較復雜,就只能通過包含yield的函數來構造generator。

  1.  
    # 使用類似列表生成式的方式構造生成器
  2.  
    g1 = ( 2*n + 1 for n in range(3, 6))
  3.  
     
  4.  
    # 使用包含yield的函數構造生成器
  5.  
    def my_range(start, end):
  6.  
    for n in range(start, end):
  7.  
    yield 2*n + 1
  8.  
     
  9.  
    g2 = my_range( 3, 6)
  10.  
    print(type(g1))
  11.  
    print(type(g2))
  1.  
    輸出結果:
  2.  
     
  3.  
    < class 'generator'>
  4.  
    <class 'generator'>


既然通過列表生成式就可以直接創建一個新的list,那么為什么還要有生成器存在呢?

   生成器中的元素是按照指定的算法推算出來的,只有調用時才生成相應的數據。這樣就不必一次性地把所有數據都生成,從而節省了大量的內存空間,這使得其生成的元素個數幾乎是沒有限制的,並且操作的返回時間也是非常快速的(僅僅是創建一個變量而已)

 

生成器 VS 列表推導式

列表推導式是一次性創建所有數據的,而生成器是生成對象,需要我們調用next方法逐一獲取元素。假設我創建的數據很大很大(比如一千萬),列表推導式會一次性創建,需要分配很大的內存空間存放這一千萬數據,而生成器不會,只是創建了一個生成器對象,所以生成器更節省內存。

 

 

 

可迭代對象(Iterable)

可直接用於for循環的對象統稱為可迭代對象(Iterable)

 

我們已經知道的可迭代(可用於for循環)的數據類型有:

  • 集合數據類型:如list、tuple、dict、set、str等
  • 生成器(Generator)

 

迭代器(Iterator)

Python中的Iterator對象表示的是一個數據流,Iterator可以被next()函數調用被不斷返回下一個數據,直到沒有數據可以返回時拋出StopIteration異常錯誤

 

 

Iterable、Iterator與Generator之間的關系

生成器對象既是可迭代對象,也是迭代器

 

 

迭代器對象一定是可迭代對象,反之則不一定: 例如list、dict、str等集合數據類型是可迭代對象,但不是迭代器,但是它們可以通過iter()函數生成一個迭代器對象

 

也就是說:迭代器、生成器和可迭代對象都可以用for循環去迭代,生成器和迭代器還可以被next()方函數調用並返回下一個值。

 

字典解析

字典的形式是: {key: val},所以字典解析式也是用花括號括起來的

  1.  
    # 快速生成值是鍵二倍的字典
  2.  
    ndict = {x: x* 2 for x in range(5)}
  3.  
    print ndict
  4.  
    { 0: 0, 1: 2, 2: 4, 3: 6, 4: 8}

 

集合解析

python中集合也是用花括號括起來的,所以集合解析式: {x for x in iter}

  1.  
    # 快速生成1-10的集合
  2.  
    nset = {x for x in range(1, 11)}
  3.  
    print nset
  4.  
    { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}


--------------------- 本文來自 努力一點點堅持一點點 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/sinat_23880167/article/details/78906819?utm_source=copy 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM