1. fixture——yield介紹
fixture的teardown操作并不是獨立的函數,用yield
關鍵字呼喚teardown操作。前面通過fixture實現了在每個用例之前執行初始化操作,那么用例執行完之后,如需要清除數據(或還原)操作,可以使用yield
來實現。
yield
是一個關鍵字,它不是單獨存在的,要寫在fixture標記的固件中。我們在聲明的固件fixture中加入yield
關鍵字,在它下面寫測試用例執行后想要運行的代碼;其他有關于固件的使用沒有任何差別。需要說明的一點是我們在pytest主函數中增加一個參數-setup-show
,它會顯示出固件的執行情況。
自動執行清理邏輯:無論測試是否通過、是否拋出異常,yield
后的代碼始終會被執行。這確保資源(如文件、網絡連接)被正確釋放。但是如果固件中的yield
之前的代碼也是相當于setup部分的代碼,出現錯誤或斷言失敗,那么yield
后的代碼將不會再執行,當然測試用例中的代碼也不會執行。
2. 基本原理:通過 yield 分割執行階段
在 fixture 函數中,yield
語句將代碼分為兩部分:
- 前置操作:
yield
之前的代碼在測試執行前運行(類似于 setup)。 - 返回值:
yield
后的值作為 fixture 的返回值傳遞給測試函數。 - 后置操作:
yield
之后的代碼在測試執行后運行(類似于 teardown),無論測試是否通過。
用法示例
#!/usr/bin/env python
# encoding: utf-8
'''
@Author : 草木零
@Software: PyCharm
@File : test_yield.py
@Time : 2023/8/23 0:50
@desc : fixture裝飾的函數里,yield的代碼,是pytest用例執行完后再執行的
'''
import pytest@pytest.fixture
def fix1():print('\nfix1,用例前置')yieldprint('\nfix1,用例執行完成,收尾')# 測試用例
def test_yield(fix1):print('fixture + yield的測試用例')if __name__ == '__main__':pytest.main(['-s', 'test_yield.py'])
控制臺:
3. 處理異常的清理
若yield
前的代碼拋出異常,yield
后的清理代碼不會執行。需使用try/finally
包裹:
@pytest.fixture
def safe_setup():resource = Nonetry:resource = acquire_resource() # 可能拋出異常yield resourcefinally:if resource:resource.release() # 確保資源被釋放
4. yield詳解
在 Python 中,yield
是創建生成器(Generator)的核心關鍵字。生成器是一種特殊的迭代器,它允許你在迭代過程中動態生成值,而不是一次性生成所有值。這種特性讓生成器在處理大數據、優化內存和實現異步編程時非常有用。
yield: return
yield: return + generator 的一部分
PS:帶yield的函數才是完整的generator
(1) 生成器的本質:按需生成的迭代器
生成器是 Python 中實現惰性計算的主要方式,它有兩種形式:
a. 生成器函數(Generator Function)
使用yield
關鍵字的函數,調用時返回一個生成器對象:
def count_up_to(n):i = 0while i <= n:yield i # 暫停執行并返回當前值i += 1# 創建生成器對象(不會立即執行函數體)
gen = count_up_to(3)# 通過迭代獲取值
print(next(gen)) # 輸出: 0
print(next(gen)) # 輸出: 1
print(next(gen)) # 輸出: 2
print(next(gen)) # 輸出: 3
print(next(gen)) # 拋出 StopIteration 異常
b. 生成器表達式(Generator Expression)
類似列表推導式,但使用圓括號:
gen = (x**2 for x in range(3)) # 創建生成器print(next(gen)) # 輸出: 0
print(next(gen)) # 輸出: 1
print(next(gen)) # 輸出: 4
(2) yield 的核心作用:暫停與恢復執行
當生成器函數被調用時,函數體不會立即執行,而是返回一個生成器對象。每次調用next()
時:
- 生成器從上次暫停的位置繼續執行(或從函數起始處開始)。
- 執行到
yield
語句時,生成一個值并暫停執行。 - 下一次調用
next()
時,繼續執行yield
之后的代碼。
總結:yield和return的關系和區別
帶yield
的函數是一個生成器,而不是一個函數了。這個生成器有一個函數就是next
函數,next
就相當于“下一步”生成哪個數,這一次的next
開始的地方是接著上一次的next
停止的地方執行的。所以調用next
的時候,生成器并不會從yield_demo
函數的開始執行,而是接著上一步開始,然后遇到yield
后,return
出要生成的數,此步就結束。
#!/usr/bin/env python
# encoding: utf-8
'''
@Author : 草木零
@Software: PyCharm
@File : yield.py
@Time : 2023/8/23 17:28
@desc : yield詳解
1.程序開始執行以后,因為yield_demo函數終有yield關鍵字,
所以test函數并不會真正的執行,而是先得到一個生成器y
2.直到調用next方法,yield_demo函數正式開始執行,先執行yield_demo函數中的print方法,然后進入while循環
3.程序遇到yield關鍵字,然后把yield想成是return,return了一個8之后,程序停止,
并沒有執行賦值給a操作,此時next(y)語句執行完成,所以輸出前面兩行(第一行是while上面的print的結果,第二行是return出來的結果)
4.程序執行print("**********************")
5.又開始執行下面的print(next(y)),這個時候是從剛才next程序停止的地方開始執行,
也就是要執行a的賦值操作,這個時候因為賦值操作的右邊是沒有值的,已經被return出去了
這個時候a的值是none,所以接下來的輸出是a:none
6.程序會繼續在whili里執行,又一次碰到yield,這個時候同樣return出8,然后程序停止。
print函數輸出的8就是這次return出的8
'''def yield_demo():print('begin================')while True:a = yield 8print('a', a)y = yield_demo() # 這句不會打印出東西,因為test函數終有yield關鍵字,所以yield_demo函數并不會真正的執行,而是先得到一個生成器y
print(next(y))
print('*******************************')
print(next(y))
print('*******************************')
控制器: