可迭代、迭代器和生成器
可迭代的英文是iterable
迭代器的英文是iterator
生成器的英文是generator
三者的关系
iterable、iterator、generator的关系由官方给定的抽象基类关系表格就可以看出 https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes
Iterator继承自Iterable,Generator继承自Iterator
可迭代和迭代器
-
实现了__iter__(self)方法的类是可迭代iterable的,iter(A)实际上是返回了A.__iter__(self)的内容,后者返回的必须是迭代器对象iterator,否则会抛异常。
-
迭代器iterator对象必须是实现了__next__(self)方法的类对象,next(A)返回了A.__next__(self)的内容,可以多次调用直到抛出StopIteration异常
接下来我们根据__next__和__iter__是否实现分为四种情况
next iter都实现
from collections.abc import Iterable,Iterator
class AIter():
def __init__(self):
self.x=0
def __iter__(self):
return self
def __next__(self):
self.x+=1
if self.x>10:
raise StopIteration
return self.x
ai=AIter()
print(isinstance(ai,Iterable)) #true
print(isinstance(ai,Iterator))#true
print(next(ai))
for j in ai:
print(j)
# 1 2 3 4 5 6 7 8 9 10
ai既是可迭代又是迭代器,因为两个方法都实现了,可以用于for in迭代 关于for in句式在文章末尾有说明
实现iter不实现next
from collections.abc import Iterable,Iterator
class BIter():
def __init__(self):
self.x=0
def __iter__(self):
return self
# def __next__(self):
# self.x+=1
# if self.x>10:
# raise StopIteration
# return self.x
bi=BIter()
print(isinstance(bi,Iterable)) #true
print(isinstance(bi,Iterator))#false
print(next(bi)) # TypeError: 'BIter' object is not an iterator
for j in bi: # TypeError: iter() returned non-iterator of type 'BIter'
print(j)
BIter是可迭代Iterable,因为实现了iter方法。不是迭代器Iterator,因为没有实现next方法,所以也就不能作为next()的参数,也不能被for in句式迭代,我们从异常信息可以看出,是因为BIter的iter方法返回的self(即Biter自身类型实例)不是迭代器Itertor,关于for in句式在文章末尾有说明
不实现iter实现next
from collections.abc import Iterable,Iterator
class CIter():
def __init__(self):
self.x=0
# def __iter__(self):
# return self
def __next__(self):
self.x+=1
if self.x>10:
raise StopIteration
return self.x
ci=CIter()
print(isinstance(ci,Iterable)) #false
print(isinstance(ci,Iterator))#false
print(next(ci)) # 1
for j in ci: # TypeError: 'CIter' object is not iterable
print(j)
在抽象基类里可以看出迭代器Iterator是可迭代Iterable的子类,所以Citer不是可迭代Iterable,那肯定也不是迭代器Iterator
for in句式需要对象时iterable的,所以会报错
iter next都不实现
from collections.abc import Iterable,Iterator
class DIter():
def __init__(self):
self.x = 0
di = DIter()
print(isinstance(di, Iterable)) # false
print(isinstance(di, Iterator)) # false
print(next(di)) # TypeError: 'DIter' object is not an iterator
for j in di: # TypeError: 'DIter' object is not iterable
print(j)
- 可迭代Iterable的对象返回的迭代器不一定是self,比如list是可迭代Iterable对象,不是迭代器Iterator,所以__iter__返回的对象类型是list_iterator,而不是self
str也是类似的道理
from collections.abc import Iterable,Iterator
l=[1,2,3,4]
print(isinstance(l,Iterable)) #true
print(isinstance(l,Iterator))#false
print(type(iter(l))) # <class 'list_iterator'>
print(isinstance(iter(l),Iterator)) # true
s='1234'
print(isinstance(s,Iterable))#true
print(isinstance(s,Iterator))#false
print(type(iter(s))) #<class 'str_iterator'>
print(isinstance(iter(s),Iterator)) # true
for in句式的原理
for x in A
A必须是一个可迭代iterable的对象,即必须实现__iter__,获取到A的迭代器B后,不断调用B.__next__(self) 直到抛出StopIteration 异常就结束
A B可以相同也可以不同
具体可以看上面四个代码用例中for in句式的执行异常信息
序列类型可迭代的原因
内置的 iter 函数有以下作用。
(1) 检查对象是否实现了 iter 方法,如果实现了就调用它,获取一个迭代器。
(2) 如果没有实现 iter 方法,但是实现了 getitem 方法,Python 会创建一个迭代
器,尝试按顺序(从索引 0 开始)获取元素。
(3) 如果尝试失败,Python 抛出 TypeError 异常,通常会提示“C object is not iterable”(C
对象不可迭代),其中 C 是目标对象所属的类。
序列类型都实现了__getItem__函数。所以虽然有的序列类型没有实现__iter__,也可以作为iter()的参数,但用isinstance和collection.abc.Itertable判断的话会返回False,
所以对于实现了__getitem__没有实现__iter__的序列类型来说,其是否是可迭代的,我觉得就是见仁见智了
from collections.abc import Iterable
class A():
def __getitem__(self, item):
return 44
a=A()
print(iter(a)) # <iterator object at 0x000001CD4823D208>
print(isinstance(A(),Iterable)) #False
生成器
一个函数通过yield返回值就成为一个生成器函数,返回值就是一个生成器
通过生成器表达式也可以返回生成器,例如 (i for i in range(10)),下面只讨论生成器函数返回的生成器
要想获取return的值而不是yield的值,在yield不再返回值后就会返回return的值,在调用的地方捕获StopIteration可以拿到return值
生成器本身也是一个迭代器,因为它可以作为next()的参数,说明内部实现了__next__方法
next跟send方法对比,send可以传递参数给迭代器,见下面第二组代码
from collections.abc import Iterable,Iterator,Generator
def fun():
arr=[1,2,3,4,5,6]
for i in arr:
yield i
return 666
gen=fun()
print(gen) # <generator object fun at 0x000001D886B17360>
print(next(gen)) #1
print(next(gen)) #2
for i in gen:
print(i) #3 4 5 6
new_gen=fun()
print(new_gen) # <generator object fun at 0x000001D886B10468>
print(next(new_gen)) # 1
print(next(new_gen)) # 2
while True:
try:
print(next(new_gen)) #3 4 5 6
except StopIteration as e:
print(e.value) #666
break
import time
def consumer():
r = ''
while True:
n = yield r
if not n:
return
print('[CONSUMER] Consuming %s...' % n)
time.sleep(1)
r = '200 OK'
def produce(c):
c.next()
n = 0
while n < 5:
n = n + 1
print('[PRODUCER] Producing %s...' % n)
r = c.send(n)
print('[PRODUCER] Consumer return: %s' % r)
c.close()
if __name__=='__main__':
c = consumer()
produce(c)