深入理解Python之Iterable,Iterators and Generators

概述

Python使用yield关键字来构造生成器,并以迭代器的方式工作。每个生成器都是一个迭代器,生成器实现迭代器接口。

本文主要内容

  • 迭代器的内部实现方式
  • Python中实现传统迭代器模式
  • 生成器的实现方式
  • 传统迭代器如何被生成器替代
  • 利用标准库里的通用生成器
  • 使用yield合并生成器
  • 生成器和协程很像,但实际上差距很大

单词序列

通过实现一个Sentence类来解释迭代

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
RE_WORD = re.compile('\w+')

class Sentence:

def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)

def __getitem__(self, index):
return self.words[index]

def __len__(self):
return len(self.words)

def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)

为什么Sentence是可迭代的

当解释器需要迭代一个对象时,其会调用内置方法__iter__,调用顺序

  1. 检查对象是否实现了__iter__,如果实现了,获取迭代器
  2. 如果没有实现__iter__,但是实现了__getitem__,Python会创建一个迭代器,然后会按顺序获取值
  3. 如果都没有,那么会抛出异常

这就是Sentence能迭代的原因,其实现了__getitem__。事实上,标准库序列都实心了__iter__。对于__getietm__的支持有可能会被去掉。Python3.4检查是否可迭代的最准确的方式是iter(x)

可迭代和迭代器

  • 可迭代,对象内置iter能够获取一个迭代器。Python从可迭代对象中获取迭代器。

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

  1. __next__: 返回下一个值,如果没有更多值了,抛出StopIteration异常
  2. __iter__: 返回迭代器自身
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

class Iterator(Iterable):
__slots__ = ()

@abstractmethod
def __next__(self):
'Return the next item from the iterator. When exhausted, raise StopIteration'
raise StopIteration

def __iter__(self):
return self

@classmethod
def __subclasshook__(cls, C):
if cls is Iterator:
if any("__next__" in B.__dict__ for B in C.__mro__) and any("_iter__" in B.__dict__ for B in C.__mro__):
return True

return NotImplemented

传统迭代器

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
28
29
30
31
RE_WORD = re.compile('\w+')


class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)

def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)

def __iter__(self):
return SentenceIterator(self.words)


class SentenceIterator:
def __init__(self, words):
self.words = words
self.index = 0

def __next__(self):
try:
word = self.words[self.index]
except IndexError:
raise StopIteration()
self.index += 1
return word

def __iter__(self):
return self

生成器方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

RE_WORD = re.compile('\w+')


class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)

def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)

def __iter__(self):
for word in self.words:
yield word
return

生成器如何工作

当调用yield时,返回一个生成器对象,换句话说,生成器方法是个生成器工厂

1
2
3
4
5
6

def gen_123():
yield 1
yield 2
yield 3

更偷懒的实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
RE_WORD = re.compile('\w+')


class Sentence:
def __init__(self, text):
self.text = text

def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)

def __iter__(self):
for match in RE_WORD.finditer(self.text):
yield match.group()

生成器表达式


RE_WORD = re.compile('\w+')


class Sentence:
    def __init__(self, text):
        self.text = text

    def __repr__(self):
        return 'Sentence(%s)' % reprlib.repr(self.text)

    def __iter__(self):
        return (match.group() for match in RE_WORD.finditer(self.text))

迭代器和协程

Python2.5加入了协程

生成器产生数据
协程消耗数据