Python騷操作從列表推導和生成器表示式開始
阿新 • • 發佈:2021-01-31
# 序列
序列是指一組資料,按存放型別分為容器序列與扁平序列,按能否被修改分為不可變序列與可變序列。
**容器序列與扁平序列**
容器序列存放的是物件的引用,包括`list`、`tuple`、`collections.deque`。
扁平序列存放的是物件的值,包括`str`、`bytes`、`bytearray`、`memoryview`和`array.array`。
> 扁平序列的值是字元、位元組和數值這種基礎型別。
**不可變序列與可變序列**
不可變序列,包括`tuple`、`str`、`bytes`。
可變序列,包括`list`、`bytearray`、`array.array`、`collection.deque`、`memoryview`。
下圖左邊是父類,右邊是子類,可以看出可變序列是從不可變序列繼承來的,擴充套件了可變方法:
![](https://img2020.cnblogs.com/blog/1629545/202101/1629545-20210131174928290-671543225.png)
# 列表推導
Python語言魅力在於簡潔,這能從最常見的建立列表體現出來,比如我們想把字串`"abc"`轉換成新列表`["a", "b", "c"]`,常規寫法:
```python
symbols = "abc"
codes = []
for symbol in symbols:
codes.append(symbol)
print(codes) # ["a", "b", "c"]
```
用到了for迴圈和列表append方法。實際上可以不用append方法,直接:
```python
symbols = "abc"
codes = [symbol for symbol in symbols]
```
這叫做列表推導,是更加Pythonic的寫法。
無論是編寫效率還是可閱讀性,列表推導都更勝一籌,可以說是構建列表的快捷方式。但是不能濫用,通用原則是,如果列表推導的程式碼超過了兩行,就要考慮用append了。這不是規定,完全可以憑藉自我喜好來選擇。
笛卡爾積是指多個序列中元素所有組合,我們用列表推導來實現笛卡爾積:
```python
colors = ["black", "white"]
sizes = ["S", "M", "L"]
tshirts = [(color, size) for color in colors for size in sizes]
```
一行程式碼搞定!Life is short,use Python,list comprehension is wonderful,amazing。
注意這行程式碼有兩個for迴圈,等價於:
```python
for color in colors:
for size in sizes:
```
執行結果是:
```python
[('black', 'S'), ('black', 'M'), ('black', 'L'), ('white', 'S'), ('white', 'M'), ('white', 'L')]
```
如果換一下順序:
```python
[(color, size) for color in colors for size in sizes]
```
等價於:
```python
for size in sizes:
for color in colors:
```
執行結果是不同的,觀察第2個元素:
```python
[('black', 'S'), ('white', 'S'), ('black', 'M'), ('white', 'M'), ('black', 'L'), ('white', 'L')]
```
# 生成器表示式
一般接觸到生成器時,都要講`yield`關鍵字,看似有點複雜,然而卻很簡單,生成器就像列表推導一樣,只不過是用來生成其他型別序列的,比如元組:
```python
symbols = "abc"
codes = (symbol for symbol in symbols)
```
它的語法非常簡單,把列表推導的中括號`[]`換成小括號`()`,就可以了。
語法相似,本質上卻有很大區別,我們試著用生成器表示式來實現笛卡爾積,看看會有什麼變化:
```python
colors = ["black", "white"]
sizes = ["S", "M", "L"]
tshirts = ((color, size) for color in colors for size in sizes)
```
執行結果是:
```python
at 0x000001FD57D2DB30>
```
`generator object`,結果是一個生成器物件。因為生成器表示式在每次迭代時才會逐個產出元素,所以這裡的結果並不是已經建立好的元組。列表推導才會一次性產生新列表所有元素。
通過迭代把生成器表示式結果輸出:
```python
for tshirt in tshirts:
print(tshirt)
```
```python
('black', 'S')
('white', 'S')
('black', 'M')
('white', 'M')
('black', 'L')
('white', 'L')
```
生成器表示式可以提升程式效能,比如要計算兩個各有1000個元素的列表的笛卡爾積,生成器表示式可以幫忙省掉執行for迴圈的開銷,即一個包含100萬個元素的列表。
> yield作用和return差不多,後面會講到。
# Tips
本小節內容是我看《流暢的Python》第一遍時記錄的知識點:
1. Python標準庫用C實現了豐富的序列型別。
2. 列表推導,就是指`a = [x for x in something]`這種寫法。
3. 生成器表示式用於生成列表外的其他型別的序列,它跟列表推導的區別僅僅在於方括號換成圓括號,如`b = tuple(x for x in something)` 。
4. `array.array('I', x for x in something) `,array構造方法的第一個引數指定了陣列中數字的儲存方式。
5. `for tshirt in [c, s for c in colors for s in sizes]`,列表推導會一次性生成這個列表,儲存在記憶體中,佔用資源。`for tshirt in ('%s %s' for c in colors for s in sizes)`,生成器表示式只在迴圈時逐個產出元素,避免額外的記憶體佔用,省掉了執行for迴圈的開銷。
# 小結
本文首先介紹了序列的概念,然後演示了Python常規騷操作——列表推導,最後引出了生成器表示式這個看似複雜實則簡單的語法。列表是可變的,它有個不可變的孿生兄弟,元組。
> 參考資料:
>
> 《流暢的P