Python生成器

来源:https://www.cnblogs.com/lucky-zhangcd/archive/2018/05/23/9080031.html
-Advertisement-
Play Games

通過列表生成式,我們可以直接創建一個列表。但是,受到記憶體限制,列表容量肯定是有限的。而且,如果創建一個包含100萬個元素的列表,不僅占用很大的存儲空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素占用的空間都白白浪費了。 如果列表元素可以按照某種演算法推算出來,可以在迴圈的過程中不斷推算出後續 ...


  通過列表生成式,我們可以直接創建一個列表。但是,受到記憶體限制,列表容量肯定是有限的。而且,如果創建一個包含100萬個元素的列表,不僅占用很大的存儲空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素占用的空間都白白浪費了。

  如果列表元素可以按照某種演算法推算出來,可以在迴圈的過程中不斷推算出後續的元素,這樣就不必創建完整的list,從而節省大量的空間。在Python中,這種一邊迴圈一邊計算的機制,稱為生成器:generator。

  要創建一個generator,有很多種方法。第一種方法很簡單,只要把一個列表生成式的[]改成(),就創建了一個generator:

>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x000002B3BA7D79E8>

  創建L和g的區別僅在於最外層的[]和(),L是一個list,而g是一個generator。

  我們可以直接列印出list的每一個元素,如果要列印出generator的每一個元素,可以通過next()函數獲得generator的下一個返回值。generator保存的是演算法,每次調用next(g),就計算出g的下一個元素的值,直到計算到最後一個元素,沒有更多的元素時,拋出StopIteration的錯誤。也可以用for迴圈:

>>> for n in g:
...     print(n)
...
0
1
4
9
16
25
36
49
64
81

  當我們創建了一個generator後,基本上永遠不會調用next(),而是通過for迴圈來迭代它,並且不需要關心StopIteration的錯誤。

 

  如果推算的演算法比較複雜,用類似列表生成式的for迴圈無法實現的時候,還可以用函數來實現。

  比如,斐波拉契數列(Fibonacci),除第一個和第二個數外,任意一個數都可以由前兩個數相加得到:1,1,2,3,5,8,13,21,34,……。斐波拉契數列用列表生成式寫不出來,但是用函數把它列印出來卻很容易:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n = n + 1
    return 'done'
# 註意,a, b = b, a + b這條語句相當於:
t = (b, a + b)  # t是一個tuple
a = t[0]
b = t[1]

 

  可以看出,fib函數實際上定義了斐波拉契數列的推算規則,可以從第一個元素開始,推算出後續任意的元素,這種邏輯其實非常類似generator。也就是說,上面的函數和generator僅一步之遙。要把fib函數變成generator,只需要把print(b)改為yield b就可以了:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'

  這就是定義generator的另一種方法。如果一個函數定義中包含yield關鍵字,那麼這個函數就不再是一個普通函數,而是一個generator:

>>> f = fib(6)
>>> f
<generator object fib at 0x000002B3BA96F150>

  在這裡,generator和函數的執行流程不一樣。函數是順序執行,遇到return語句或者最後一行函數語句就會返回。而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行。可以用一個簡單的例子展示如下:

  調用該generator時,首先生成一個generator對象,然後用next()函數不斷獲得下一個返回值:

>>> def odd():
...     print('step 1')
...     yield 1
...     print('step 2')
...     yield 3
...     print('step 1')
...     yield 5
...
>>> o = odd()
>>> next(o)
step 1
1
>>> next(o)
step 2
3
>>> next(o)
step 1
5
>>> next(o)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

 

  可以看到,odd不是普通函數,而是generator,在執行過程中,遇到yield就中斷,下次又繼續執行。執行3次yield後,已經沒有yield可以執行了,所以,第4次調用next(o)就報錯。

  同樣的,把函數改成generator後,我們基本上從來不會用next()來獲取下一個返回值,而是直接用for迴圈來迭代:

>>> for n in fib(6):
...     print(n)
...
1
1
2
3
5
8

  但是用for迴圈調用generator時,發現拿不到generator的return語句的返回值。如果想要拿到返回值,必須捕獲StopIteration錯誤,返回值包含在StopIteration的value中。

>>> g = fib(6)
>>> while True:
...     try:
...             x = next(g)
...             print('g:', x)
...     except StopIteration as e:
...             print('Generator return value:', e.value)
...             break
...
g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
Generator return value: done 

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 解釋器模式-Interpreter 目前沒有想到其他更好的例子, 以後本文肯定重寫,或者儘力介紹得更到位....目前只是copy了iluwatar大神的代碼, 作為練習, 也方便自己以後查找本段代碼. Expression介面 表達式的統一定義. PlusExpression類 加法表達式. Min ...
  • 編寫可供用戶查詢的員工信息表 要求: 1.需要用戶認證 2.員工信息表文件內容: ID Name Department Phone 3.認證成功後查詢正確信息 代碼: 效果預覽: ...
  • 這幾天藉著學習的機會,開通了雲伺服器、完成了實名認證、註冊了微信公眾平臺,準備動手實踐實踐,把整個過程記錄下來和大家交流學習,可以分3個步驟: 1.準備雲伺服器,因為微信在做接入的時候要填寫公網的IP地址 2.配置web伺服器,在伺服器端用nginx做了一個代理,把請求轉發給後端 3.後端服務採用的 ...
  • 解決方案: 1. 查看pom包中是否包含如下依賴 2. 如果已經包含,查看<scope>provided</scope>配置的是否為compile(推薦先註釋掉) 原因:provided:這個scope假定對應的依賴會由運行這個應用的JDK或者容器來提供。最好的例子就是servlet API。任何在 ...
  • Task 1: Open countries.xml, compose the following XQueries: 1. Return the area of Mongolia. 2. Return the names of all cities that have the same name ...
  • Python是一門解釋器語言,代碼想運行,必須通過解釋器執行,Python存在多種解釋器,分別基於不同語言開發,每個解釋器有不同的特點,但都能正常運行Python代碼,以下是常用的五種Python解釋器: CPython 當 從Python官方網站下載並安裝好Python2.7後,就直接獲得了一個官 ...
  • Github https://github.com/gongluck/Opencv3.4 study.git ...
  • 前面兩行引入相應的庫,真正的代碼就4行,夠簡單吧。第1行甚至可以不寫,它定義了圖的大小。第2行我們創建一個地圖,第3行把海岸線畫上,第4行顯示這個地圖,就是這樣: 你用 Java 的 4 行代碼畫一個地圖出來? 然後我們開始畫上國家,又是1行代碼: 就變成了這樣: 看上去有點變形,這是因為我們沒有添 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...