網站首頁 編程語言 正文
一、迭代器(Iterator)
1.1 可迭代對象(Iterable)
可迭代對象,可以簡單理解為可遍歷對象,即能夠使用 for 循環遍歷的對象。Python中常見的可迭代對象有:列表、元組、字符串、集合、range、字典等。
迭代器和生成器都是可迭代對象。
對于Python中的任意對象,只要它定義了可以返回一個迭代器的 __iter__ 方法,或者定義了可以支持下標索引的 __getitem__ 方法,那么它就是一個可迭代對象。
對可迭代對象使用 __iter__ 方法后,會返回一個迭代器。
如何判斷一個對象是否為可迭代對象呢?請看下例。
from collections.abc import Iterable isinstance([1, 2, 3], Iterable) ?# True isinstance((1, 2, 3), Iterable) ?# True isinstance('123', Iterable) ?# True isinstance({1, 2, 3}, Iterable) ?# True isinstance(range(3), Iterable) ?# True isinstance({'key': 'value'}, Iterable) ?# True isinstance(123, Iterable) ?# False
可以看出,我們只需要使用 isinstance(object, Iterable) 即可判斷給定的 object 是否為可迭代對象。
嚴格來講,isinstance() 只會將有 __iter__ 方法的對象判斷為 Iterable。
換言之,僅用 __getitem__ 方法實現的可迭代對象會被 isinstance() 誤判為不可迭代對象。
最正確的做法是直接嘗試 iter(object),如果沒有報錯,則說明 object 是可迭代對象。
1.2 將可迭代對象轉化為迭代器
我們可以將現有的可迭代對象轉化為可迭代器:
s = '12345' myiter = iter(s) myiter # <str_iterator at 0x25e6f40d130>
不斷調用 next 方法來依次獲取迭代器的元素:
next(myiter) # '1' next(myiter) # '2' next(myiter) # '3' next(myiter) # '4' next(myiter) # '5' next(myiter) # StopIteration:?
可見迭代器執行到最后時會拋出一個 StopIteration 異常。
為避免這種異常,我們完全可以用更簡單的 for 循環去遍歷:
for e in myiter: ? ? print(e) # 1 # 2 # 3 # 4 # 5
1.3 構造迭代器
構造一個迭代器只需要在自定義的類中實現兩個方法:__iter__ 和 __next__ 。
- 迭代器是一個可以記住遍歷位置的對象。
- 迭代器對象會從第一個元素開始訪問,直到所有元素都被訪問為止,且只能前進不能后退。
當我們構造類時,必須要有一個名為 __init__() 的函數,該函數可以在實例化時進行一些初始化。
-
__iter__()
方法的行為類似,可以執行操作(初始化等),但必須始終返回迭代器對象本身。 -
__next__()
方法還允許你進行其他操作,并且必須返回序列中的下一項。
class MyIter: ? ? def __iter__(self): ? ? ? ? self.count = 1 ? ? ? ? return self ? ?? ? ? def __next__(self): ? ? ? ? x = self.count ? ? ? ? self.count += 1 ? ? ? ? return x
我們創建了一個返回數字的迭代器,每次序列的數值都將 +1。
myiter = iter(MyIter()) next(myiter) # 1 next(myiter) # 2 next(myiter) # 3
如果我們一直調用 next() 的方法,則序列的值將會無限遞增下去。即如果我們使用 for 循環去遍歷上述迭代器,循環將永遠進行下去…
myiter = iter(MyIter()) for e in myiter: ? ? print(e) # 循環將一直進行下去...
為了防止迭代永遠進行下去,我們可以在迭代次數達到一定值時拋出 StopIteration 異常。
class MyIter: ? ? def __iter__(self): ? ? ? ? self.count = 1 ? ? ? ? return self ? ?? ? ? def __next__(self): ? ? ? ? if self.count <= 5: ? ? ? ? ? ? x = self.count ? ? ? ? ? ? self.count += 1 ? ? ? ? ? ? return x ? ? ? ? else: ? ? ? ? ? ? raise StopIteration
這樣再執行 for 循環就不會一直進行下去了:
myiter = iter(MyIter()) for e in myiter: ? ? print(e) # 1 # 2 # 3 # 4 # 5
二、生成器(Generator)
在Python中,一邊迭代(循環)一邊計算的機制,稱為生成器。生成器能夠迭代的關鍵是因為它有一個 __next__ 方法。
為什么要有生成器呢?我們知道,列表中的所有數據都存儲在內存中,如果有海量數據的話將會非常消耗內存。很多時候,我們只需要訪問列表中前面的元素,這樣一來后面的元素所占用的空間就白白浪費了。
如果列表元素能夠按照某種算法推算出來,那我們就可以在循環的過程中不斷推算出后續的元素,這樣就不必創建完整的列表,從而節省了大量的空間(即用多少就生成多少)。
有以下兩種常用方法來創建生成器:
將列表解析式中的 [] 改為 ()。在自定義的函數中使用 yield 關鍵字。此時這個函數就不再是一個普通函數,而是一個生成器,調用該函數就是創建了一個生成器對象。
2.1 使用 () 構造生成器
比較以下兩段代碼:
a = [x for x in range(3)] type(a) # list
a = (x for x in range(3)) type(a) # generator
我們還可以比較列表解析式和生成器的耗時:
tic = time.time() a = sum([x for x in range(10000000)]) toc = time.time() print(toc - tic) # 0.9081981182098389
tic = time.time() a = sum((x for x in range(10000000))) toc = time.time() print(toc - tic) # 0.6906485557556152
我們當然可以對生成器使用 next() 方法:
next(a) # 0 next(a) # 1 next(a) # 2 next(a) # StopIteration:?
但一般我們不會用 next() 來獲取下一個返回值,而是直接使用 for 循環來迭代。
2.2 使用帶有 yield 關鍵字的函數構造生成器
帶有 yield 的函數不再是一個普通函數,而是一個生成器。
yield 相當于return一個值,并且記住這個返回的位置,下次迭代時,代碼從 yield 的下一條語句開始執行。
我們可以通過下面的例子先來理解一下:
def num(): ? ? print('開始執行') ? ? for i in range(5): ? ? ? ? yield i ? ? ? ? print('繼續執行') mygen = num() type(mygen) # generator
由此,我們成功創建了一個生成器對象。接下來調用 next 方法觀察這個生成器是如何工作的:
next(mygen) # 開始執行 # 0 next(mygen) # 繼續執行 # 1 next(mygen) # 繼續執行 # 2 next(mygen) # 繼續執行 # 3 next(mygen) # 繼續執行 # 4 next(mygen) # StopIteration:
當然我們也可以使用 for 循環來遍歷這個生成器:
for step in mygen: ? ? print(step) # 開始執行 # 0 # 繼續執行 # 1 # 繼續執行 # 2 # 繼續執行 # 3 # 繼續執行 # 4 # 繼續執行
總結
原文鏈接:https://raelum.blog.csdn.net/article/details/124573424
相關推薦
- 2023-01-21 詳解Go語言如何使用標準庫sort對切片進行排序_Golang
- 2023-02-12 React實現錨點跳轉組件附帶吸頂效果的示例代碼_React
- 2023-07-08 keycloak更新token調用updateToken函數無效,解決辦法
- 2022-10-25 記一次生產環境死鎖問題分析
- 2022-04-20 Django學習之靜態文件與模板詳解_python
- 2023-04-18 C#?TabControl手動觸發DrawItem的實現_C#教程
- 2022-08-21 Android自定義view貝塞爾曲線_Android
- 2022-04-11 python文件讀寫操作小結_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支