測開之資料型別· 第3篇《列表推導式、字典推導式、2種方式建立生成器》
阿新 • • 發佈:2020-12-18
## 堅持原創輸出,點選藍字關注我吧
![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217214736.png)
作者:清菡
部落格:oschina、雲+社群、知乎等各大平臺都有。
# 目錄
- 一、列表推導式
- 二、字典推導式
- 三、2種方式建立生成器
- 1.生成器表示式
- 2.函式裡面,通過 yield 定義生成器
## 一、列表推導式
推導式可以幫助我們快速建立列表、建立字典。比如現在要建立一個列表。
做自動化測試的時候,比如建立個 url 列表,url 列表裡面可能是儲存了網站的頁數:
![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217070428.png)
一直到 100,生成 100 個頁面,但是這 100 個頁面有規律,url 地址,前面這一部分是不變的,只有後面的 1,2,3,4 這部分的變化。
如果去生成這樣一個列表,不用列表推導式,用之前的方法的話,可以這樣做,先定義一個空列表:
`urls = []`
然後來個 for 迴圈 set 100 個:
`for i in range(1,101):`
前面字串這部分是確定的,比如說一個 page,後面這部分不確定,就來個`format()`給它填進去。
`url = 'page{}'.format(i)`
通過`append()`把 url 加進去。
```PYTHON
# url = ['page1','page2']
urls = []
for i in range(1,101):
url = 'page{}'.format(i)
urls.append(url)
print(urls)
```
能夠生成 1-100 個頁面。
![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217073010.png)
推導式有個優勢,一行就能解決。推導式可以看成 for 迴圈的一個解體。
寫起來特別簡單,同樣的功能,推導式可以這樣寫:
![圖片中應為推導式不是倒,字打錯了,圖是我截得](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217074931.png)
```PYTHON
# 列表推導式
urls1 = [i for i in range(1,101)]
print(urls1)
```
**這段列表推導式程式碼解釋是:**
`for` 迴圈,`i` 從 `range` 裡面迴圈,迴圈出來拿出一個 `i`,然後往前面放到這個列表裡面。
再拿出一個 `i` 放到這個列表裡面,這樣重複(拿出一個 `i` 放到列表裡面),直到把 `for` 迴圈遍歷完。
將裡面所有的元素都拿出來放到列表裡面,最後生成一個新的列表,這就是列表推導式。
**裡面是 1-100 個數字:**
![圖片中應為推導式不是倒,字打錯了,圖是我截得](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217080347.png)
如果用列表推導式生成這個 page1,到 100 頁。程式碼就修改成這樣:
```PYTHON
urls1 = ['page{}'.format(i) for i in range(1,101)]
print(urls1)
```
`'page{}'.format(i)`
`format()`格式化字串的函式。
![圖片中應為推導式不是倒,字打錯了,圖是我截得](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217092254.png)
```PYTHON
# 列表推導式
urls1 = ['page{}'.format(i) for i in range(1,101)]
print(urls1)
```
![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217095421.png)
簡而言之,就是遍歷出來的元素放到這個前面就行了。然後在前面,你可以做其它操作。
以上,這就是用列表推導式快速生成一個列表。
## 二、字典推導式
字典推導式和列表推導式,它的原理是一樣的。都用 for 迴圈去遍歷,然後拿出對應的值在前面,生成對應的值。
每遍歷一輪,會把前面你寫的內容放到字典裡面去。前面寫個鍵,鍵就是遍歷出來的`i`,對應的值就是`i+1`。
![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217111405.png)
```PYTHON
dict1 = {i:i+1 for i in range(10)}
print(dict1)
```
鍵就是遍歷出來的`i`,值就是鍵的基礎上加 1。每迴圈遍歷一輪,這個就生成一個鍵值對。
推導式可以推匯出字典,也可以推匯出列表。大括號、中括號、花括號都可以。
`推導式改成小括號後是什麼?`
中括號是列表,花括號是字典,小括號是元組。
**推導式改成小括號後,不再是個元組了,是個生成器。**
```PYTHON
# () 生成器表示式
tu = (i for i in range(10)) #生成器物件
print(tu)
```
## 三、2種方式建立生成器
### 1.生成器表示式
#### 1.1 什麼是生成器?
這裡有很多資料,可以把它裝到一個 “就像自動取筷盒,拿出一雙筷子,自動下來一雙筷子”,就是你要用的時候,它給你生成一個出來。
生成器不像列表,比如建立個列表,比如列表裡面有一千個元素,建立列表的時候,那麼這一千個元素已經被建立好放在列表裡面了。生成器不是這樣,它內部只保留了一個生成器計算的規則。
#### 1.2 使用生成器的好處
##### 生成器要生成一千個元素,這樣:
`tu = [i for i in range(1000)]#生成器物件`
直接生成一千個元素的列表。改成生成器,這個生成器物件裡面儲存的是一個計算公式,並沒有儲存這一千條資料啊。
使用生成器來儲存這些資料的話,相對於列表的優勢是:**不那麼佔記憶體。**
一千條資料可能看不出效果,如果是一千萬條資料往列表裡面一放,那得佔用多大的記憶體啊。如果是個生成器,裡面就是個計算的規則,就是個生成的規則,沒有那麼多資料,節約記憶體,可以提高程式碼的效能。
#### 1.3 拿生成器裡面的資料,也可以一個一個得拿,怎麼拿呢?
生成器表示式,打印出來是個生成器。
![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217121033.png)
當然,可以通過`list`把它轉換成一個列表。
```PYTHON
tu = (i for i in range(1000))#生成器物件
print(list(tu))
```
![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217121635.png)
它可以把生成器裡面所有的元素都拿出來轉換成列表。
`通過生成器表示式來定義生成器,一次想拿一個元素,怎麼拿呢?`
Python 裡面有個內建的函式,叫做`next()`。把生成器物件放進去,得到一個結果:
```PYTHON
# () 生成器表示式
tu = (i for i in range(1000))#生成器物件
a = next(tu)
print(a)
print(next(tu))
```
![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217125707.png)
##### 互動環境中可以看到:
![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217132304.png)
它依次生成,要的時候,從生成器裡面拿一個出來就行了。你要用的時候就去拿,它就一直生成,它就把裡面所有的元素都取出來。
#### 1.4 所有的元素都取出來之後,我又拿了一次,它會出現什麼情況呢?
![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217132939.png)
會報錯。
生成器可以用來節約記憶體,提高程式碼效能。**生成器在於你什麼時候用,你什麼時候去取值。**
### 2.函式裡面,通過 yield 定義生成器
除了生成器表示式可以建立生成器,還有另外一個方式。Python 關鍵字裡面有個`yield`引數。
`yield`這個關鍵字是用在函式裡面的,這個關鍵字只能在函式裡面用。
函式定義完之後,只要在函式裡面呼叫函式,那就會執行函式裡面的程式碼。
```PYTHON
def gen_fun():
print('清菡 加油')
gen_fun()
```
如果當一個函式裡面,有`yield`這個關鍵字:
```PYTHON
def gen_fun():
yield
print('清菡 加油')
gen_fun()
```
這個時候再去執行這個函式,這個函式不會立即執行。
#### 2.1 為什麼不會立即執行呢?
這個函式執行的時候,預設是沒有寫`return`的。
```PYTHON
def gen_fun():
# yield
print('清菡 加油')
res = gen_fun()
print(res)
```
![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217143002.png)
如果函式裡面出現了`yield`這個關鍵字,這個時候再看下。
函式沒有寫`return`,呼叫函式,它裡面,程式碼沒有執行,但是有返回結果,返回的結果是:
![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217150648.png)
返回的是一個生成器。
通過`yield`定義出來的這個函式,是個生成器函式。
呼叫這個函式的時候,它會給你返回一個生成器物件。既然它是一個生成器物件,那麼就可以通過`next()`來對它進行取值。
##### 執行結果如下:
```PYTHON
# 通過yield定義生成器
def gen_fun():
yield
print('清菡 加油')
res = gen_fun() #返回生成器物件
print(next(res))
```
你看到輸出結果是:None
![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217150509.png)
#### 2.2 為什麼是 None 呢?
生成器生成的元素在`yield`關鍵字後面。
```PYTHON
# 通過yield定義生成器
def gen_fun():
yield 100
print('清菡 加油')
res = gen_fun() #返回生成器物件
print(next(res))
```
![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217151133.png)
再寫 2 個`yield`:
```PYTHON
# 通過yield定義生成器
def gen_fun():
yield 100
print('清菡 加油')
yield 1000
yield 100100
res = gen_fun() #返回生成器物件
print(next(res))
```
![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217170106.png)
**生成器函式:** 只有通過`next()`取值的時候,它才會執行函式裡面的程式碼。
`next()`一次,就執行到第一個`yield`這裡,把這個結果返回出來。然後到這個地方,暫停了不動了,不會往下走了。
如果在下面再`next()`,**從生成器裡面再獲取一個元素:**
`print(next(res))`
![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217153046.png)
直到等到下一個`next()`取值。當你下一次從生成器函式裡面取值的時候,才會觸發下一個`yield`。
```PYTHON
# 通過yield定義生成器
def gen_fun():
yield 100
print('清菡 加油')
yield 1000
yield 100100
res = gen_fun() #返回生成器物件
print(next(res))
print(next(res))
print(next(res))
```
![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217153621.png)
但是如果全部都生成完了,再去取一次,就會報錯:
![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217153819.png)
因為裡面已經沒有元素了。
以上,生成器只有通過這 2 種方式定義。
---
公眾號 **「清菡軟體測試」** 首發,更多原創文章:**清菡軟體測試 108+原創文章**,歡迎關注、交流,禁止第三方擅自