可迭代、迭代器和生成器

可迭代、迭代器和生成器

可迭代的英文是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)
发布了51 篇原创文章 · 获赞 7 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_36267931/article/details/102954056
今日推荐