鐵樂學python_day13_迭代器生成器

来源:https://www.cnblogs.com/tielemao/archive/2018/04/04/8719221.html
-Advertisement-
Play Games

目前知道的迭代器有兩種: 一種是調用方法直接返回的; 一種是可迭代對象通過執行iter方法得到的。 迭代器的好處是可以節省記憶體。 如果在某些情況下,我們也需要節省記憶體,就只能自己寫。自己寫的這個能實現迭代器功能的東西就叫生成器。 ...


一、【可迭代對象Iterable】

粗略判斷的話,我們可以說能被for迴圈進行遍歷的對象就是可迭代對象,如str,list,tuple,dict(key),set,range。
(open file 中的文件句柄屬於迭代器的一種。)

如果想要更直觀的判斷的話,在這裡我們使用dir()方法查看一下對象所有的可操作方法:

s ='hello,wutiele'
print(dir(s))
顯示:

['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

同樣,dir列表,元祖,字典等類型的對象時也可以查看到有'iter'操作方法。
當然,上面的方法查看太麻煩,我們可以使用in的方法判斷,返回布爾值為真的就是操作方法中有'iter'的,如:

l1 = ['lv1', 'lv2', 'lv3', 'lv4', 'lv5']
print('__iter__' in dir(l1))

顯示:
True

所以我們又可以說,內部含有'__iter__'方法的對象是可迭代對象。
可迭代對象遵循可迭代協議,可迭代協議的定義非常簡單,就是內部實現了__iter__方法。
還有一種方法可以直觀判斷,那就是isinstance:
from collections import Iterable
t1 = ('槍兵', '射手', '劍士', '僧侶')
print(isinstance(t1, tuple))
print(isinstance(t1, Iterable))

顯示:
True
True

isinstance同樣作為判斷類型的方法,它比type方法判斷面更廣,且返回的值是布爾值。

二、【迭代器Iterator】

迭代器英文是iterator。

迭代器比起可迭代對象來說,其實它只多包含了一個操作方法,那就是'__next__'方法。

下麵使用__iter__將可迭代對象轉化成迭代器,然後使用__next__方法操作一番,可以看到這個方法是做什麼用的:
from collections import Iterable
t1 = ('槍兵', '射手', '劍士', '僧侶')

t1_obj = t1.__iter__()
print(t1_obj)
print(type(t1_obj))
print(isinstance(t1_obj, Iterator))
print(isinstance(t1_obj, tuple))

輸出結果
<tuple_iterator object at 0x0000000000D90DA0>
<class 'tuple_iterator'>
True
False

可迭代對象轉化成迭代器:可迭代對象.__iter__()
上例使用print和type查看從tuple轉化的迭代器可以看到類型己經變成了tuple_iterator。

迭代器.__next__()
t1 = ('槍兵', '射手', '劍士', '僧侶')
t1_obj = t1.__iter__()

print(t1_obj.__next__())
print(t1_obj.__next__())
print(t1_obj.__next__())
print(t1_obj.__next__())

# 返回
槍兵
射手
劍士
僧侶

如果再操作多一次.__next__會怎樣?從頭迴圈開始嗎?答案是:
報錯,StopIteration異常。

    print(t1_obj.__next__())
StopIteration

__next__方法每一次只會取出迭代器中的一個元素,是不是和for迴圈時的操作有點像?
其實for迴圈一個可迭代對像時,用到的就有__next__操作方法。

迭代器遵循迭代器協議:對象不僅含有__iter__方法,同時還含有__next__方法。

for迴圈,能遍歷一個可迭代對象,他的內部到底進行了什麼?
 將可迭代對象轉化成迭代器。(可迭代對象.__iter__())
 內部使用__next__方法,一個一個取值。
 加了異常處理功能,取值到底後自動停止。

【使用while模擬for迴圈機制】
li = [i for i in range(1, 100)]
# 轉換成迭代器
l1_obj = li.__iter__()
while True:
    # 異常處理,嘗試進行try塊區的語句,如果報錯就執行except塊區語句處理異常。
    try:
        j = l1_obj.__next__()
        print(j)
    # 當出現StopIteration異常時,不報錯而是執行break中斷while迴圈。
    except StopIteration:
        break

那麼,使用for迴圈相比while,也就是迭代器它有什麼好處呢?
首先沒有迭代器的話,while只能處理有下標的列表,字元串和元祖,字典,集合,文件就不方便了;
其次,對大數據來說,使用迭代器比起直接使用列表(比如一百萬個元素的列表),字元串(超大文本的日誌)等節省記憶體空間多了,因為它的惰性機制,它生成後,只會在記憶體空間占用一條元素的空間;
滿足惰性機制,取一個值才輸出一個值;
同樣由於怠惰,迭代器也不能反覆取值(不能逆向或取到盡時重頭又來),它內部有一個指針,取一個就指向到下一個,一直向前,不會反覆。
迭代器最大的好處就是節省記憶體空間。

三、【生成器Generator】

生成器本質上也是迭代器(自帶了__iter__方法和__next__方法),特點是惰性運算,開發者自定義。

目前知道的迭代器有兩種:
一種是調用方法直接返回的;
一種是可迭代對象通過執行iter方法得到的。
迭代器的好處是可以節省記憶體。
如果在某些情況下,我們也需要節省記憶體,就只能自己寫。自己寫的這個能實現迭代器功能的東西就叫生成器。

生成器產生迭代器的方式:
1)生成器函數構造;
函數體中使用yield語句而不是return語句返回結果。
也就是說你只要在一個函數的函數體中看到有yield,你就知道它不是一個常規函數,而是一個生成器了。
yield語句一次返回一個結果,在每個結果中間,掛起函數的狀態,以便下次重它離開的地方繼續執行。
2)用生成器推導式構造;
3)數據類型的轉化。

【生成器函數】

一個包含yield關鍵字的函數就是一個生成器函數。yield可以為我們從函數中返回值,但是yield又不同於return,return的執行意味著程式的結束,調用生成器函數不會得到返回的具體的值,而是得到一個可迭代的對象。每一次獲取這個可迭代對象的值,就能推動函數的執行,獲取新的返回值。直到函數執行結束。

【next】

例:next()取出生成器的值:

import time
def genrator_fun():
    lv1 = '槍兵*14'
    print('人族可以產出1級兵了!')
    yield lv1
    lv2 = '弓箭手*8'
    print('人族可以產出2級兵了!')
    yield lv2

g = genrator_fun()
print('g:', g) # 列印g輸出生成器類型
print('-'*12, '我是華麗的分割線', '-'*12)
print(next(g)) # 使用next方法取出g生成器的第一個值(yield)
time.sleep(1)
print(next(g)) # 第二次執行取的是第二個值

g: <generator object genrator_fun at 0x0000000000DB86D0>
------------ 我是華麗的分割線 ------------
人族可以產出1級兵了!
槍兵*14
人族可以產出2級兵了!
弓箭手*82:指針會順著取出的值執行下去
def dead_sold():
    # 可怕的亡靈大軍,己經累積到1萬個骷髏兵了!
    for i in range(1, 10001):
        yield '可怕的亡靈大軍,己經累積到第 %s 個骷髏兵了!' % i
d = dead_sold() # 將函數賦值給一個變數,不然直接在下麵的式子用會反覆只取到第1個骷髏
for i in range(50):
    print(d.__next__())
print('華麗分割線'.center(30, '-'))
for i in range(150):
    print(d.__next__())

可怕的亡靈大軍,己經累積到第 47 個骷髏兵了!
可怕的亡靈大軍,己經累積到第 48 個骷髏兵了!
可怕的亡靈大軍,己經累積到第 49 個骷髏兵了!
可怕的亡靈大軍,己經累積到第 50 個骷髏兵了!
------------華麗分割線-------------
可怕的亡靈大軍,己經累積到第 51 個骷髏兵了!
可怕的亡靈大軍,己經累積到第 52 個骷髏兵了!
可怕的亡靈大軍,己經累積到第 53 個骷髏兵了!

上例中,取到第50個值時,再執行取值的語句時不會從第1個起執行,而是順著51開始。

【send】

send和__next__()一樣,都是執行下一個yield,不同的是,send還可以給上一個yield賦值。
當然,函數體中第一個取值語句不能為send,因為第一個之前並沒有yield可給它進行賦值。
同樣,最後一個yield不能接受send的賦值,因為最後一個後面並不能再取出值了。
send是取值和賦值同時進行的,且賦的是上一個yield的值。
def generator():
    content = yield 1
    print('=======', content, '=======') # 列印上一個yield的值
    yield 2
    yield 3

g = generator()
ret = g.__next__()
print('***', ret)
ret = g.send('hello')   #send賦值給上一個yield
print('***', ret)

*** 1
======= hello =======
*** 2

end
2018-4-4


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

-Advertisement-
Play Games
更多相關文章
  • <html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>引入外部樣式</title><link rel=" ...
  • 模板方法模式使用繼承來實現模式的功能,在基類使用一個方法來定義演算法的各個步驟,這些步驟(方法)的具體實現會放到子類中,通過這樣來實現不同演算法對象的演算法拼合,完成該對象整體演算法的實現。 作用 模板方法中定義了具體操作中所使用演算法的各個步驟,並將其實現交由子類完成,從而實現多種不同的功能; 類視圖 實現 ...
  • 一、前言 BIM:Building Information Modeling 建築信息模型,就是將建築的相關信息附著於模型中,以管理該建築在設計、算量、施工、運維全生命周期的情況。創建模型的主要主流軟體有Autodesk(歐特克)的Revit、Bentley的microstation、達索的CATI ...
  • 策略模式將不同演算法的邏輯抽象介面封裝到一個類中,通過組合和多態結合的方式來進行不同演算法具體的實現。 作用 策略模式是一種定義一系列演算法的方法,Strategy類層次為Context定義了一系列的可重用的演算法或行為, 所有的演算法以相同的方式進行調用,減少了演算法類之間的耦合 類視圖 實現 Strateg ...
  • 面向對象這門課,寫了3次作業,在這裡分享一下自己的設計思路、分析總結等,歡迎一起探討~ ...
  • 版權聲明:本文為博主原創文章,未經博主允許不得轉載。 一. 下載並安裝Nginx 去Nginx官網下載 我這裡選取nginx/Windows-1.10.3版本,下載後解壓出來即可,解壓出來的路徑不能含有中文 我解壓後將其放置的路徑如下 二、開始運行 在當前目錄下按住shift+滑鼠右鍵,選擇“在此處 ...
  • 這篇文章主要介紹了python sort、sorted高級排序技巧,本文講解了基礎排序、升序和降序、排序的穩定性和複雜排序、cmp函數排序法等內容,需要的朋友可以參考下 Python list內置sort()方法用來排序,也可以用python內置的全局sorted()方法來對可迭代的序列排序生成新的 ...
  • ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...