您可能聽說過,帶有 yield 的函數在 Python 中被稱為 generator(生成器),何謂 generator ?
我們先拋開 generator,以一個常見的程式設計題目來展示 yield 的概念。
如何產生斐波那契數列
斐波那契(Fibonacci)數列是一個非常簡單的遞歸數列,除第一個和第二個數外,任一個數都可由前兩個數相加得到。用電腦程式輸出斐波那契數列的前N 個數是一個非常簡單的問題,許多初學者都可以輕易寫出如下函數:
清單1. 簡單輸出斐波那契數列前N 個數
deffab(max): n, a, b =0, 0, 1 whilen < max: printb a, b =b, a +b n =n +1
執行fab(5),我們可以得到以下輸出:
>>> fab(5)
結果沒有問題,但有經驗的開發者會指出,直接在fab 函數中用print 列印數字會導致該函數可重複使用性較差,因為fab 函數傳回None,其他函數無法取得該函數產生的數列。
要提高 fab 函數的可重複使用性,最好不要直接列印出數列,而是傳回一個 List。以下是fab 函數改寫後的第二個版本:
清單2. 輸出斐波那契數列前N 個數第二版
deffab(max): n, a, b =0, 0, 1 L =[] whilen < max: L.append(b) a, b =b, a +b n =n +1 returnL
可以使用以下方式列印出fab 函數傳回的List:
>> forn infab(5):
... printn
...
改寫後的fab 函數透過返回List 能滿足復用性的要求,但是更有經驗的開發者會指出,該函數在運行中佔用的記憶體會隨著參數max 的增加而增加,如果要控制記憶體佔用,最好不要用List
來保存中間結果,而是透過iterable 物件來迭代。例如,在Python2.x 中,程式碼:
清單3. 透過iterable 物件來迭代
fori inrange(1000): pass
會導致產生一個1000 個元素的List,而程式碼:
會導致產生一個1000 個元素的List,而程式碼:): pass
則不會產生一個1000 個元素的List,而是在每次迭代中傳回下一個數值,記憶體空間佔用量很小。因為 xrange 不回傳 List,而是回傳一個 iterable 物件。
利用iterable 我們可以把fab 函數改寫為一個支援iterable 的class,以下是第三個版本的Fab:
清單4. 第三個版本
classFab(object): def__init__(self, max): self.max=max self.n, self.a, self.b =0, 0, 1 def__iter__(self): returnself defnext(self): ifself.n < self.max: r =self.b self.a, self.b =self.b, self.a +self.b self.n =self.n +1 returnr raiseStopIteration()清單4. 第三個版本
deffab(max): n, a, b =0, 0, 1 whilen < max: yieldb # print b a, b =b, a +b n =n +1
清單4. 第三個版本
defread_file(fpath): BLOCK_SIZE =1024 with open(fpath, 'rb') as f: whileTrue: block =f.read(BLOCK_SIZE) ifblock: yieldblock else: return
Fab 類通過下一個列數數,記憶體佔用總是常數:
>>> forn inFab(5):
... printn
...
然而,使用class 改寫的這個版本,程式碼遠沒有第一版的程式碼遠函數來得簡潔。如果我們想要保持第一版fab 函數的簡潔性,同時又要獲得iterable 的效果,yield 就派上用場了:
清單5. 使用yield 的第四版
rrreee清單5. 使用yield 的第四版
rrreee第四個版本的fab 和第四個版本的fab 和第四個版本第一版相比,光是把print b 改為了yield b,就在保持簡潔性的同時獲得了iterable 的效果。
調用第四版的fab 和第二版的fab 完全一致:
>>> forn infab(5):
... printn
一個函數變成一個generator,帶有yield 的函數不再是一個普通函數,Python 解釋器會將其視為一個generator,呼叫fab(5) 不會執行fab 函數,而是傳回一個iterable 物件!當for 迴圈執行時,每次迴圈都會執行fab 函數內部的程式碼,執行到yield b 時,fab 函數會傳回一個迭代值,下次迭代時,程式碼從yield b 的下一語句繼續執行,而函數的本地變數看起來和上次中斷執行前是完全一樣的,所以函數繼續執行,直到再次遇到yield。
也可以手動呼叫fab(5) 的next() 方法(因為fab(5) 是一個generator 對象,該對象具有next() 方法),這樣我們就可以更清楚地看到fab 的執行流程:
清單6. 執行流程
>>> f =fab(5)
>>> f.next()
>>> f.next()
>>> f.n
>>> f.next()>>> f.next()> >> f.next()>>> f.next()>>> f.next()Traceback (most recent call last):File"", line 1, in StopStopIteration當函數執行結束時,generator 會自動拋出StopIteration 異常,表示迭代完成。在 for 迴圈裡,無需處理 StopIteration 異常,迴圈會正常結束。 🎜🎜 我們可以得出以下結論:🎜🎜 一個帶有yield 的函數就是一個generator,它和普通函數不同,產生一個generator 看起來像函數調用,但不會執行任何函數代碼,直到對其調用next( )(在for 迴圈中會自動呼叫next())才開始執行。雖然執行流程仍按函數的流程執行,但每執行到一個 yield 語句就會中斷,並傳回一個迭代值,下次執行時從 yield 的下一個語句繼續執行。看起來好像一個函數在正常執行的過程中被 yield 中斷了數次,每次中斷都會透過 yield 傳回目前的迭代值。 🎜yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。
如何判断一个函数是否是一个特殊的 generator 函数?可以利用 isgeneratorfunction 判断:
清单 7. 使用 isgeneratorfunction 判断
>>> frominspect importisgeneratorfunction
>>> isgeneratorfunction(fab)
True
要注意区分 fab 和 fab(5),fab 是一个 generator function,而 fab(5) 是调用 fab 返回的一个 generator,好比类的定义和类的实例的区别:
清单 8. 类的定义和类的实例
>>> importtypes
>>> isinstance(fab, types.GeneratorType)
False
>>> isinstance(fab(5), types.GeneratorType)
True
fab 是无法迭代的,而 fab(5) 是可迭代的:
>>> fromcollections importIterable
>>> isinstance(fab, Iterable)
False
>>> isinstance(fab(5), Iterable)
True
每次调用 fab 函数都会生成一个新的 generator 实例,各实例互不影响:
>>> f1 =fab(3)
>>> f2 =fab(5)
>>> print'f1:', f1.next()
f1: 1
>>> print'f2:', f2.next()
f2: 1
>>> print'f1:', f1.next()
f1: 1
>>> print'f2:', f2.next()
f2: 1
>>> print'f1:', f1.next()
f1: 2
>>> print'f2:', f2.next()
f2: 2
>>> print'f2:', f2.next()
f2: 3
>>> print'f2:', f2.next()
f2: 5
return 的作用
在一个 generator function 中,如果没有 return,则默认执行至函数完毕,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。
另一个例子
另一个 yield 的例子来源于文件读取。如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取:
清单 9. 另一个 yield 的例子
defread_file(fpath): BLOCK_SIZE =1024 with open(fpath, 'rb') as f: whileTrue: block =f.read(BLOCK_SIZE) ifblock: yieldblock else: return
以上仅仅简单介绍了 yield 的基本概念和用法,yield 在 Python 3 中还有更强大的用法,我们会在后续文章中讨论。
注:本文的代码均在 Python 2.7 中调试通过

Python更易學且易用,C 則更強大但複雜。 1.Python語法簡潔,適合初學者,動態類型和自動內存管理使其易用,但可能導致運行時錯誤。 2.C 提供低級控制和高級特性,適合高性能應用,但學習門檻高,需手動管理內存和類型安全。

Python和C 在内存管理和控制方面的差异显著。1.Python使用自动内存管理,基于引用计数和垃圾回收,简化了程序员的工作。2.C 则要求手动管理内存,提供更多控制权但增加了复杂性和出错风险。选择哪种语言应基于项目需求和团队技术栈。

Python在科學計算中的應用包括數據分析、機器學習、數值模擬和可視化。 1.Numpy提供高效的多維數組和數學函數。 2.SciPy擴展Numpy功能,提供優化和線性代數工具。 3.Pandas用於數據處理和分析。 4.Matplotlib用於生成各種圖表和可視化結果。

選擇Python還是C 取決於項目需求:1)Python適合快速開發、數據科學和腳本編寫,因其簡潔語法和豐富庫;2)C 適用於需要高性能和底層控制的場景,如係統編程和遊戲開發,因其編譯型和手動內存管理。

Python在數據科學和機器學習中的應用廣泛,主要依賴於其簡潔性和強大的庫生態系統。 1)Pandas用於數據處理和分析,2)Numpy提供高效的數值計算,3)Scikit-learn用於機器學習模型構建和優化,這些庫讓Python成為數據科學和機器學習的理想工具。

每天學習Python兩個小時是否足夠?這取決於你的目標和學習方法。 1)制定清晰的學習計劃,2)選擇合適的學習資源和方法,3)動手實踐和復習鞏固,可以在這段時間內逐步掌握Python的基本知識和高級功能。

Python在Web開發中的關鍵應用包括使用Django和Flask框架、API開發、數據分析與可視化、機器學習與AI、以及性能優化。 1.Django和Flask框架:Django適合快速開發複雜應用,Flask適用於小型或高度自定義項目。 2.API開發:使用Flask或DjangoRESTFramework構建RESTfulAPI。 3.數據分析與可視化:利用Python處理數據並通過Web界面展示。 4.機器學習與AI:Python用於構建智能Web應用。 5.性能優化:通過異步編程、緩存和代碼優

Python在開發效率上優於C ,但C 在執行性能上更高。 1.Python的簡潔語法和豐富庫提高開發效率。 2.C 的編譯型特性和硬件控制提升執行性能。選擇時需根據項目需求權衡開發速度與執行效率。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

SublimeText3漢化版
中文版,非常好用

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具