最近有朋友問到python中的迭代器和生成器的區(qū)別和作用,以及如何定義和區(qū)分,今天在這里稍微總結(jié)一下滚局。
可迭代對象
定義:可迭代對象必須實現(xiàn) "_iter_()" 方法冲九。
python中的字符串、列表、元組、字典、集合檩电、文件對象等都是可迭代對象卓箫。
口說無憑魂挂,上bug...不對,上法器:可使用內(nèi)建函數(shù) isinstance 來判別。
from collections import deque
from collections.abc import Iterable, Iterator
s = "hello hmcf"
print("string is iterable? ", isinstance(s, Iterable))
print("string is iterator? ", isinstance(s, Iterator))
l = [1, 2, 3, 4]
print("list is iterable? ", isinstance(l, Iterable))
print("list is iterator? ", isinstance(l, Iterator))
d = {"city": "hangzhou", "age": 26}
print("dict is iterable? ", isinstance(d, Iterable))
print("dict is iterator? ", isinstance(d, Iterator))
t = (1, "a", 2.3, [5, 6])
print("tuple is iterable? ", isinstance(t, Iterable))
print("tuple is iterator? ", isinstance(t, Iterator))
de = deque("abcdefg")
print("deque is iterable?", isinstance(de, Iterable))
print("deque is iterator?", isinstance(de, Iterator))
結(jié)果如下:
string is iterable? True
string is iterator? False
list is iterable? True
list is iterator? False
dict is iterable? True
dict is iterator? False
tuple is iterable? True
tuple is iterator? False
deque is iterable? True
deque is iterator? False
可使用 dir() 函數(shù)來打印出來看看對象有沒有_iter_方法迫皱,也可以查看 builtins.pyi 源文件找到對應方法中的定義戏阅。
迭代器
迭代器是在可迭代對象的基礎上創(chuàng)建的訪問集合元素的一種方式。
迭代器是一個可以記住遍歷的位置的對象笆怠。
迭代器對象從集合的第一個元素開始訪問泡态,直到所有的元素被訪問完結(jié)束怔毛。迭代器只能往前不會后退,并且只會迭代一遍寄啼。
迭代器對象必須同時實現(xiàn) "_iter_()" 和 "_next_()" 方法才是迭代器副女。對于迭代器來說,"_iter_" 返回的是它自身 self,"_next_" 則是返回迭代器中的下一個值睛竣,最后沒有元素時猖吴。
字符串域仇,列表或元組對象都可用于創(chuàng)建迭代器挡逼。迭代器是可迭代對象惹骂,但可迭代對象不一定是迭代器矛物。
迭代器的優(yōu)點:每次只從對象中讀取一條數(shù)據(jù)被环,不會造成內(nèi)存的過大開銷筛欢。
至于迭代器的判斷方式,在上面的可迭代對象中已經(jīng)體現(xiàn)了么介。
for循環(huán)機制
為什么需要for循環(huán)機制壤短?因為需要去觸發(fā)、調(diào)用迭代器。字符串 、字典、列表在for循環(huán)的作用下,會生成迭代器悴侵,然后被使用逮刨。
x = 'hello'
for i in x: #iter_x=x.__iter__()
print(i) #iter_x.__next__()
iter_l =x.__iter__() #遵循迭代器協(xié)議,生成可迭代對象
print(iter_l.__next__()) #for循環(huán)和索引沒關系 基于迭代器機制
- 當對象本身就是迭代器時恢总,F(xiàn)or循環(huán)工作機制:
調(diào)用 _iter_方法迎罗,返回自身self,也就是返回迭代器。不斷地調(diào)用迭代器的next()方法片仿,每次按序返回迭代器中的一個值纹安。迭代到最后沒有元素時,就拋出異常 StopIteration
- 在可迭代對象中砂豌,for循環(huán)工作機制:
先判斷對象是否為可迭代對象(等價于判斷有沒有_iter_方法)厢岂,沒有的話直接報錯,拋出TypeError異常阳距。有的話塔粒,調(diào)用 _iter_方法,返回一個迭代器筐摘。在python內(nèi)部不斷地調(diào)用迭代器的_next_方法卒茬,每次按序返回迭代器中的一個值。迭代到最后沒有元素時咖熟,就拋出異常 StopIteration圃酵,這個異常 python 自己會處理,不會暴露給開發(fā)者馍管。
生成器
定義:邊循環(huán)邊計算的機制就稱為生成器(generator)(對象里面不是具體的內(nèi)容郭赐,而是一個產(chǎn)生數(shù)據(jù)的算法)
生成器類似于返回值為數(shù)組的一個函數(shù),這個函數(shù)可以接受參數(shù)确沸,可以被調(diào)用捌锭,但是俘陷,不同于一般的函數(shù)會一次性返回包括了所有數(shù)值的數(shù)組,生成器一次只能產(chǎn)生一個值舀锨,這樣消耗的內(nèi)存數(shù)量將大大減小岭洲,而且允許調(diào)用函數(shù)可以很快的處理前幾個返回值,因此生成器看起來像是一個函數(shù)坎匿,但是表現(xiàn)得卻像是迭代器盾剩。
python中生成器是迭代器的一種,可以被用作控制循環(huán)的迭代行為替蔬。
它有兩種形式:
- 生成器函數(shù)
使用yield返回值函數(shù)告私,每次調(diào)用yield會暫停,而可以使用next()函數(shù)和send()函數(shù)恢復生成器承桥。
import sys
def fibonacci(n): # 生成器函數(shù) - 斐波那契
a, b, counter = 0, 1, 0
while True:
if (counter > n):
return
yield a
a, b = b, a + b
counter += 1
f = fibonacci(10) # f 是一個迭代器驻粟,由生成器返回生成
while True:
try:
print (next(f), end=" ")
except StopIteration:
sys.exit()
- 生成器表達式
只要把一個列表生成式的[]中括號改為()小括號,就創(chuàng)建一個生成器凶异∈癯牛可以直接調(diào)用next()方法依次取出內(nèi)容(取到最后會爆出錯誤,不優(yōu)雅)剩彬,另外的就是可以使用for循環(huán)來調(diào)用酷麦。
generator = (x for x in [1,4,6,7,9] if x<5)
for n in generator:
print(n)