09-02解析式

解析式

舉例場景:對一個列表所有的數值求平方

  • 普通用法
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的表達式組成
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章