服务器之家

服务器之家 > 正文

详解Python3中的迭代器和生成器及其区别

时间:2021-04-06 00:15     来源/作者:miuric

介绍

本篇将介绍python3中的迭代器生成器,描述可迭代与迭代器关系,并实现自定义类的迭代器模式。

迭代的概念

上一次输出的结果为下一次输入的初始值,重复的过程称为迭代,每次重复即一次迭代,并且每次迭代的结果是下一次迭代的初始值

注:循环不是迭代

?
1
2
while true: #只满足重复,因而不是迭代
print('====>')

 迭代器

1.为什么要有迭代器?

对于没有索引的数据类型,必须提供一种不依赖索引的迭代方式。

2.迭代器定义:

迭代器:可迭代对象执行__iter__方法,得到的结果就是迭代器,迭代器对象有__next__方法

它是一个带状态的对象,他能在你调用next()方法的时候返回容器中的下一个值,任何实现了__iter__和__next__()方法的对象都是迭代器,__iter__返回迭代器自身,__next__返回容器中的下一个值,如果容器中没有更多元素了,则抛出stopiteration异常

可迭代的(iterable)

python标准库中存在着一些可迭代对象,例如:list, tuple, dict, set, str等。

可以对这些迭代对象,进行for-in等迭代操作,例如:

?
1
2
for s in "helloworld":
  print(s)

编译器若想迭代一个对象a,则会自动调用iter(a)获取该对象的迭代器(iterator),如果iter(a)抛出异常,则对象a不可迭代。

判断对象是否可迭代

原生函数iter(instance) 可以判断某个对象是否可迭代,它的工作流程大概分为以下3个步骤:

  • 检查对象instance是否实现了__iter__方法,并调用它获取返回的迭代器(iterator)。
  • 如果对象没有实现__iter__方法,但是实现了__getitem__方法,python会生成一个迭代器。
  • 如果上述都失败,则编译器则抛出typeerror错误,‘xxx' object is not iterable。

自定义类实现__iter__方法

根据第一条,我们自定义类iter1实现__iter__方法使该类的对象可迭代。

?
1
2
3
4
5
6
7
8
9
10
class iter1:
  def __init__(self, text):
    self.text = text
 
  def __iter__(self):
    return iter(self.text)
 
iter1 = iter1("hello")
for s in iter1:
  print(s)

iter1类实现了__iter__方法,通过iter()调用,得到可迭代对象text的迭代器并返回,实现了迭代器协议,因此可以通过for-in等方式对该对象进行迭代。

第二条通常都是针对python中的序列(sequence)而定义,例如list,为了实现sequence协议,需要实现__getitem__方法。

?
1
2
3
4
5
6
7
8
9
10
11
class iter2:
  def __init__(self, sequence):
    self.sequence = sequence
 
  def __getitem__(self, item):
    return self.sequence[item]
 
 
iter2 = iter2([1, 2, 3, 4])
for s in iter2:
  print(s)

实际上,为了避免版本后序改动,python标准库中的序列除了实现了__getitem__方法,也实现了__iter__方法,因此我们在定义序列时也应实现__iter__。

综上,如果显示判断某个对象是否可迭代,应该调用iter(instance)是否抛出异常,因为只实现了__getitem__的序列也是可迭代的(例子中iter2的对象是可迭代的,但isinstance(iter2, abc.iterator)返回结果是false)。同时,如果在调用iter后进行迭代操作不必显示判断,可以用try/except方式包装代码块。

iterable vs iterator(可迭代vs迭代器)

iterable定义

任何可以由原生函数iter获取到迭代器的对象
任何实现了__iter__方法并返回迭代器的对象
所有的序列(实现了__getitem__)

python通过获取到可迭代对象的迭代器(iterator)实现迭代,例如for-in的实现其实是在内部获取到了迭代器进行操作。for-in机制可以理解为下述代码:

?
1
2
3
4
5
6
7
8
s = 'hello'
it = iter(s)
while (true):
  try:
    print(next(it))
  except stopiteration:
    del it
    break

stopiteration异常将在迭代器耗尽后被抛出,for-in、生成式(comprehension)、元组解压(tuple unpacking)等迭代操作都会处理并这个异常。

迭代器是个迭代值生产工厂,它保存迭代状态,并通过next()函数产生下一个迭代值。实现迭代器需要实现以下两个方法:

__iter__
返回self

__next__
返回下一个可用的元素,如果无可用元素则抛出stopiteration异常

迭代器实现__iter__,因此所有的迭代器都是可迭代的,下图展示了iterable和iterator的结构。

详解Python3中的迭代器和生成器及其区别

迭代器模式

实现一个自定义的迭代器模式需要两个类,分别为实现了__iter__方法的类和通过__iter__返回的迭代器实例类(实现了__iter__和__next__方法)。下面例子简单实现了上述功能。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class itertext:
  def __init__(self, text):
    self.text = text
 
  def __iter__(self):
    return iteratortext(self.text)
 
 
class iteratortext:
  def __init__(self, text):
    self.text = text
    self.index = 0
 
  def __iter__(self):
    return self
 
  def __next__(self):
    try:
      letter = self.text[self.index]
    except indexerror:
      raise stopiteration
    self.index += 1
    return letter
 
text = itertext("hey")
for l in text:
  print(l)

可迭代的itertext实现了__iter__方法,返回了迭代器iteratortext实例。iteratortext实现了__next__方法返回下一个迭代元素直到抛出异常,同时iteratortext实现了__iter__方法返回自身对象用于迭代。
这里的itertext和iteratortext很容易混淆,如果在itertext中实现了__next__方法并将__iter__中返回自身实例self也可以实现上述功能,但通常可迭代对象和迭代器应当分开,这样在可迭代对象中的__iter__中可以返回不同的迭代器对象,使功能独立。

生成器(generator)

通过上述文章说明,迭代器通过next()不断产出下一个元素直到迭代器耗尽,而python中的生成器可以理解为一个更优雅的迭代器(不需要实现__iter__和__next__方法),实现了迭代器协议,它也可以通过next()产出元素。
python中的生成器主要分为两种类型:

生成器函数(generator function)返回得到的生成器:

包含yield关键字的函数称为生成器函数

?
1
2
3
4
5
def gen_func():
  yield 1
  yield 2
  yield 3
g = gen_func()

生成器表达式(generator expression)返回得到的生成器

?
1
g = (i for i in (1, 2, 3))

我们可以利用生成器进行迭代操作:

?
1
2
3
4
5
6
7
for e in g:
  print(e)
  
## 生成器g已被耗尽,如果需要重新迭代需要重新获得新的生成器对象
g = gen_func()
for e in g:
  print(e)

利用生成器代替可迭代中的__iter__迭代器

在迭代器模式章节中,我们在可迭代itertext中的__iter__返回迭代器iteratortext实例,然而使用生成器的方式会使代码更加优雅。

?
1
2
3
4
5
6
7
class itertext:
  def __init__(self, text):
    self.text = text
 
  def __iter__(self):
    for letter in self.text:
      yield letter

因为yield存在于__iter__,因此__iter__变成了生成器函数,调用它测返回一个生成器,同时生成器又实现了迭代器协议,因此itertext满足了可迭代的需求。

总结

本篇介绍了python中的可迭代(iterable)、迭代器(iterator)以及它们的关系,并讲述了迭代器模式的实现,同时通过python中的生成器完善了迭代器模式。希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://segmentfault.com/a/1190000016620127

相关文章

热门资讯

2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
yue是什么意思 网络流行语yue了是什么梗
yue是什么意思 网络流行语yue了是什么梗 2020-10-11
背刺什么意思 网络词语背刺是什么梗
背刺什么意思 网络词语背刺是什么梗 2020-05-22
Intellij idea2020永久破解,亲测可用!!!
Intellij idea2020永久破解,亲测可用!!! 2020-07-29
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总 2020-11-13
返回顶部