可迭代对象、迭代器、生成器的理解

全部的生成器都是迭代器python

关于迭代器和生成器的一种定义:迭代器用于从集合中取出元素;生成器用于凭空生成元素。函数

Python中,全部的集合都是能够迭代的,在Python语言内部,迭代器用于支持:测试

  • for 循环
  • 构建和扩展集合类型
  • 逐行遍历文本文件
  • 列表推导,字典推导,集合推导
  • 元组拆包
  • 调用函数时,使用*拆包实参

如同标题本文的标题同样,这边文章主要讲解三个方面,可迭代对象,迭代器,生成器,下面逐个开始理解spa

可迭代对象

先经过下面单词序列例子来理解:code

 1 import re
 2 import reprlib
 3 
 4 
 5 RE_WORD = re.compile('\w+')
 6 
 7 
 8 class Sentence(object):
 9     def __init__(self,text):
10         self.text = text
11         self.words = RE_WORD.findall(text)
12 
13     def __getitem__(self, index):
14         return self.words[index]
15 
16     def __len__(self):
17         return len(self.words)
18 
19     def __repr__(self):
20         """
21         用于打印实例化对象时,显示自定义内容,
22         reprlib.repr函数生成的字符换最多有30个字符,当超过怎会经过省略号显示
23         :return: 自定义内容格式
24         """
25         return 'Sentence(%s)' % reprlib.repr(self.text)
26 
27 s = Sentence('"the time has come," the Walrus said,')
28 print(s)
29 print(type(s))
30 for word in s:
31     print(word)
32 
33 print(list(s))

上面代码的运行结果:对象

首先从结果来看,咱们能够看出这个类的实例是能够迭代的,
而且咱们从打印print(s)的结果能够看出,显示的也是咱们定义的内容,若是咱们在类中没有经过__repr__自定义,打印结果将为:
<__main__.Sentence object at 0x102a08fd0>
同时这里的实例化对象也是一个序列,因此咱们能够经过s[0]这种方式来获取每一个元素
咱们都知道序列能够迭代,那么序列为啥能够迭代,继续深刻理解blog

序列能够迭代缘由

解释器须要迭代对象x时,会自动调用iter(x)索引

内置的iter函数做用:接口

  • 检查对象是否实现了__iter__方法,若是实现调用它,获取一个迭代器
  • 若是没有实现__iter__方法,可是实现了__getitem__方法,python会建立一个迭代器,尝试按顺序(从0开始)获取元素
  • 若是尝试失败,会抛出TypeError异常,一般会提示:“C object is not iterable”,其中C是目标对象所属的类

任何python序列能够迭代的缘由是,他们都实现了__getitem__方法,而且标准的序列也实现了__iter__方法。字符串

关于如何判断x对象是否为可迭代对象,有两种方法:iter(x)或者isinstance(x,abc.Iterable)
那么这两种判断法有什么区别么?
其实从Python3.4以后建议是经过iter(x)方法来进行判断,由于iter方法会考虑__getitem__方法,而abc.Iterable不会考虑,因此iter(x)的判断方法更加准确

就像我最开始写的那个例子,分别经过这两种方式来测试,能够看出,其实这个类是能够迭代的,可是经过abc.Iterable的方式来判断,确实不可迭代的

关于可迭代对象的一个小结:

  1. 使用iter内置函数能够获取迭代器的对象,若是对象实现了能返回迭代器的__iter__方法,那么对象就是可迭代的
  2. 序列均可以迭代
  3. 实现了__getitem__方法,并且其参数是从零开始的索引,这种对象也能够迭代

迭代器

首先咱们要明白可迭代的对象和迭代器之间的关系:
Python从可迭代的对象中获取迭代器

一个简单的例子,当咱们循环字符串的时候,字符串就是一个可迭代的对象,背后就是有迭代器,只不过咱们看不到,下面为代码例子:

 1 # 经过for循环方式
 2 s = "ABC"
 3 for i in s:
 4     print(i)
 5 
 6 
 7 print(''.center(50, '-'))
 8 
 9 # 经过while循环方式
10 it = iter(s)
11 
12 while True:
13     try:
14         print(next(it))
15     except StopIteration:
16         del it
17         break

这两种方式均可以获取可迭代对象里的内容,可是while循环的方式若是不经过try/except方式获取异常,最后就会提示StopIteration的错误,这是由于Python语言内部会处理for循环和其余迭代上下文(如列表推导,元组拆包等等)中的StopIteration

标准的迭代器接口有两个方法:

  • __next__:返回下一个可用的元素,若是没有元素了抛出StopIteration异常
  • __iter__:返回self,以便在应该使用迭代器的地方使用迭代器,例如for循环

由于迭代器只须要__next__和__iter__两个方法,因此除了调用next()方法,以及捕获StopIteration异常以外,没有办法检查是否还有遗留元素,而且没有办法还原迭代器,若是想要再次迭代,就须要调用iter(...)传入以前构建迭代器的可迭代对象

咱们把刚开始写的sentence类经过迭代器的方式来实现,要说的是这种写法不符合python的习惯作法,这里是为了更好的理解迭代器和可迭代对象之间的重要区别

 1 import re
 2 import reprlib
 3 from collections import abc
 4 
 5 
 6 RE_WORD = re.compile('\w+')
 7 
 8 
 9 class Sentence:
10 
11     def __init__(self,text):
12         self.text = text
13         self.words = RE_WORD.findall(text)
14 
15     def __repr__(self):
16         return "Sentence(%s)" % reprlib.repr(self.text)
17 
18     def __iter__(self):
19         return SentenceIterator(self.words)
20 
21 
22 class SentenceIterator:
23 
24     def __init__(self,words):
25         self.words = words
26         self.index = 0
27 
28     def __next__(self):
29         try:
30             word = self.words[self.index]
31         except IndexError:
32             raise StopIteration()
33         self.index += 1
34         return word
35 
36     def __iter__(self):
37         return self

这样咱们就能够很清楚的明白,咱们定义了一个SenteneIterator是一个迭代器,也实现了迭代器应该有的两种方法:__next__和__iter__方法,这样咱们经过 issubclass(SentenceIterator,abc.Iterator)检查
这里咱们还能看到可迭代对象和迭代器的区别:
可迭代对象有__iter__方法,每次都实例化一个新的迭代器
迭代器要实现__next__和__iter__两个方法,__next__用于获取下一个元素,__iter__方法用于迭代器自己,所以迭代器能够迭代,可是可迭代对象不是迭代器

有人确定在想在Sentence类中实现__next__方法,让Sentence类既是可迭代对象也是自身的迭代器,可是这种想法是不对的,这是也是常见的反模式。因此可迭代对象必定不能是自身的迭代器

生成器

先经过用生成器方式替换上个例子中SentenceIterator类,例子以下:

 1 import re
 2 import reprlib
 3 
 4 
 5 RE_WORD = re.compile('\w+')
 6 
 7 
 8 class Sentence:
 9 
10     def __init__(self,text):
11         self.text = text
12         self.words = RE_WORD.findall(text)
13 
14     def __repr__(self):
15         return 'Sentence(%s)' % reprlib.repr(self.text)
16 
17     def __iter__(self):
18         for word in self.words:
19             yield word

在上面这个代码中,咱们经过yield关键字,这里的__iter__函数其实就是生成器函数,迭代器实际上是生成器对象,每次调用__iter__方法,都会自动建立。

生成器的工做原理

Python函数定义体中有yield关键字,该函数就是生成器函数。

生成器函数会建立一个生成器对象,包装生成器函数的定义体,把生成器传给next(...)函数时,生成器函数会向前,执行函数定义体中的下一个yield语句,返回产出的值,并在函数定义体的当前位置暂停,最终,函数的定义体返回时,外层的生成器对象会抛出SotpIteration异常,这一点和迭代器协议一致。

下面是一个生成器的例子:

这里其实咱们要明白进行for循环的过程其实就是在隐式的调用next()函数
当咱们写了好几种Sentence类的时候,感受咱们经过生成器方式实现的挺简单了,其实还有更简单的方法的,代码例子以下,这里的finditer函数构建了一个迭代器:

 1 import re
 2 import reprlib
 3 
 4 
 5 RE_WORD = re.compile('\w+')
 6 
 7 
 8 
 9 class Sentence:
10 
11     def __init__(self,text):
12         self.text = text
13 
14     def __repr__(self):
15         return 'Sentence(%s)' % reprlib.repr(self.text)
16 
17     def __iter__(self):
18         for match in RE_WORD.finditer(self.text):
19             yield match.group()

关于生成器表达式

生成器表达式能够理解为列表推导的惰性版本,不会直接构成列表,而是返回一个生成器,按需惰性生成元素。
关于实现Sentence,还能够经过生成器表达式。

 1 import re
 2 import reprlib
 3 
 4 
 5 RE_WORD = re.compile('\w+')
 6 
 7 
 8 class Sentence:
 9 
10     def __init__(self,text):
11         self.text = text
12 
13     def __repr__(self):
14         return 'Sentence(%s)' % reprlib.repr(self.text)
15 
16     def __iter__(self):
17         return (match.group() for match in RE_WORD.finditer(self.text))
相关文章
相关标签/搜索