python中的for、迭代器和生成器的理解

上图的这些模式,都是以list对象和操作符[]来获取元素,而索引只是作为参数。这是一种迭代模式。

有没有一种模式,元素的提取只和下标打交道,而和可迭代对象无关。这样的一种设计模式,就是迭代器模式

 for i in [1, 2, 3, 4]:

  print(i)

for c in "python":

   print(c)

for k in {"x": 1, "y": 2}:

  print(k)

for line in open("a.txt"):

  print(line, end="")

这些例子中,字符串、列表、字典、文件都可以使用for循环,这些类型都是可迭代对象(字符串、列表、字典、文件)。

 

迭代器模式特别适合于以下情形:

1、不关心元素的随机访问(不需要干涉遍历的过程,只需要取出来元素)

2、元素的个数不可提前预测(元素个数未知,不像列表、字典、文件那样有固定大小,可以提前加载到内存中)。

那是如何实现迭代器的?

 1、返回当前『位置』的元素,当前位置是可迭代对象的起始位置

2、将『位置』向后递增

3、如果到达可迭代对象的末尾,即没有元素可以提取,则抛出StopIteration异常

 

Python的iter函数和next函数的内部实现:

 

def iter(obj):
    return obj.__iter__()

#Python 2.X
def next(obj):
    return obj.next()

#Python 3.X
def next(obj):
    return obj.__next__()

 iter()和next()方法:

iter()参数是可迭代对象,返回值返回一个迭代器。

next()参数是迭代器,返回值是迭代器中的下一个元素,如果没有下一个元素,则抛出StopIteration异常。

1)可迭代对象包含迭代器。
2)如果一个对象拥有__iter__方法,其是可迭代对象;如果一个对象拥有next方法,其是迭代器。
3)定义可迭代对象,必须实现__iter__方法;定义迭代器,必须实现__iter__和next方法。

可迭代对象、迭代器、生成器之间的关系:

结论3与结论2是不是有一点矛盾?既然一个对象拥有了next方法就是迭代器,那为什么迭代器必须同时实现两方法呢?

因为结论1,迭代器也是可迭代对象,因此迭代器必须也实现__iter__方法。

而一个生成器是一个迭代器。

可迭代对象是指:只要具有__iter__或__getitem__的object就是可迭代对象。

而同时具有__iter__和__next__方法的的对象为Iterator迭代器。

而判断一个对象是可迭代对象或者迭代器的方法:

1、利用dir(类型)或者hasattr(类型)去检查是否含有__iter__、__getitem__和__next__这些方法,来判断是一个可迭代对象还是迭代器。

2、isinstance(对象,Iterable),isinstance(对象,Iterator)

 

例如:

list对象有__iter__以及__getitem__但没有__next__,

所以是list列表是可迭代对象Iterable,但不是迭代器Iterator。

但是,可以通过调用 iter(list)函数,(其实就是call list.__iter__()),

这样会返回一个同时带有__iter__和__next__的新的对象,这个返回值就是一个迭代器Iterator了。

 

而for循环中 的对象,不需要一定是 迭代器Iterator,只要是可迭代对象就行了。

yield作为及物动词表示产出;作为不及物动词,它表示让步或撤回。python在生成器函数中都体现了这个功能。

 

1、yield只能出现在function里,而调用带有yield功能的函数会回返回一个Generator生成器对象。
2、Generatoro对象是一个Iterator,带有__iter__和__next__属性。
3、第一次调用next(generator)执行内容等价于将原功能执行到第一次出现yield处,返回yield后的值,并“暂停”执行。
4、第二次之后每次调用next(generator)都会不断从上次「暂停」处继续执行,直到功能全部执行完毕并执行StopIteration。因为yield没有将原功能从调用堆栈中移除,故能暂停并重新回到上次暂停处继续执行。这逻辑也是yield和return最核心不同之处,return会直接将原函数从调用堆栈中移除,终止函数,无论return后面是还是还有其他程序代码。
5、yeild除了可传出值外,也可以接受由外部输入的值,利用generator.send()可以传出和传入值。此设计,让Generator对象可和外部进行双向沟通,可以传出,也可以设定值。
6、关于Generator生成器对象创建的两种语法:一是在function内加入yield”,二是形如x = (i for i in y)的方式。

其实大家常用的产生列表的其中一种写法x = [i for i in range(10)]就是创建一个Generator对象的变形。

 

 

迭代器的实现类似于类,其中有两个方法,一个是__iter__,一个是__next__

class  iteror:

  def __init__(self, n):
    self.i = 0
    self.n = n

 

  def __iter__(self):  #参数是一个可迭代对象
    return self    #返回值是一个迭代器

 

  def __next__(self):  #参数是一个迭代器
    if self.i < self.n:
      i = self.i
      self.i += 1
      return i    #返回下一个元素
    else:
      raise StopIteration()  #没有下一个元素,抛出异常

 参考文章:https://medium.com/citycoddee/python%E9%80%B2%E9%9A%8E%E6%8A%80%E5%B7%A7-6-%E8%BF%AD%E4%BB%A3%E9%82%A3%E4%BB%B6%E5%B0%8F%E4%BA%8B-%E6%B7%B1%E5%85%A5%E4%BA%86%E8%A7%A3-iteration-iterable-iterator-iter-getitem-next-fac5b4542cf4

posted @ 2020-06-18 15:19  一日学一日功  阅读(665)  评论(0编辑  收藏  举报