0. 前言
- 参考资料:
- 《Python Cookbook》第四章
- 《流畅的Python》第十四章
- 《深入理解Python特性》第六章
- 迭代器和生成器属于同一概念,生成器可看作是简化版的迭代器。
1. 解析式
- 解析式类型:
- 列表解析式:
[]
- 集合解析式:
{}
- 字典解析式:
{}
- 列表解析式:
- 解析式都是语法糖,可以条件规律元素。
- 举例:
1
2
3squares = [x*x for x in range(10) if x % 2 == 0]
squares_set = {x*x for x in range(10)}
squares_dict = {x: x*x for x in range(5)}
2. 迭代器
2.1. 迭代器模式
- 迭代器模式可用来:
- 访问一个聚合对象的内容而无需暴露它的内部表示。
- 支持对聚合对象的多种遍历。
- 为遍历不同的聚合结构提供一个统一的接口(即支持多态迭代)。
2.2. 可迭代对象与迭代器
- 可迭代对象:
- 使用
iter
函数可以获取迭代器的对象。 - 抽象基类:
collections.abc.Iterable
。
- 使用
- 迭代器:
- 实现了无参数的
__next__
方法,返回序列中的下一个元素。如果没有元素,则抛出StopIteration
异常。 - 抽象基类:
collections.abc.Iterator
。
- 实现了无参数的
- 不建议可迭代对象和迭代器实现在一个类中:
- 这是常见的反模式。
2.3. iter函数
- 解释器需要迭代对象时,会自动调用
iter
函数。iter
函数的另外一种用法:iter(a, flag)
,第一个参数是可调用对象,第二个参数是哨兵符,当可调用对象返回这个哨兵符时,抛出异常,不返回值。
- 作用:
- 检查是否实现
__iter__
方法,如果实现了就调用,获取迭代器。 - 如果没有实现
__iter__
方法,但实现了__getitem__
方法,就会自动创建一个迭代器,尝试按顺序(从0开始)获取元素。 - 如果尝试失败,则抛出
TypeError
并提示XX Object is not iterable
。
- 检查是否实现
- 如果类实现了
__iter__
方法,则issubclass(Foo, abc.Iterable)
取值为True
。
2.4. for-in 循环在 Python 中的工作原理
如果Python程序如下:
1
2
3repeater = Repeater('Hello')
for item in repeater:
print(item)上面的程序等价于下面的程序
1
2
3
4
5repeater = Repeater('Hello')
iterator = repeater.__init__()
while True:
item = iterator.__next__()
print(item
3. 生成器
3.1. 基本生成器
- 在
__iter__
函数中使用yield
关键字。 - 只要在Python函数定义体中有
yield
关键字,该函数就是生成器函数。 - 调用生成器函数就会返回一个生成器对象。
- 生成器函数就是生成器工厂。
- 生成器函数的工作原理:
- 当第一次调用生成器时,从函数开头开始运行,到第一个
yield
结束。 - 当第二次(或多次)调用生成器时,从上一次结束的
yield
语句开始运行程序。
- 当第一次调用生成器时,从函数开头开始运行,到第一个
- 惰性实现:尽可能延后生成值,从而节约内存,避免无用处理。
3.2. 生成器表达式
- 可裂解为列表推导的惰性版本:不会迫切构建列表,而是返回一个生成器,按需惰性生成元素。
- 生成器表达式是语法糖,完全可以替换生成器函数。
- 生成器表达式与普通生成器的选择:
- 如果生成器表达式要分成多行,则尽量用生成器函数,以便提高可读性。
yield from
:生成器函数需要产生另一个生成器生成的值,可以通过嵌套for循环,也可以使用yield from
。
3.3. 标准库中的生成函数
itertools
compress
dropwhile
filterfalse
islice
takewhile
accumulate
startmap
- 内置:
filter
enumerate
map
- 举例:
- 通过
itertools.islice()
函数可以对迭代器做切片操作。 - 通过
itertools.dropwhile()
来跳过可迭代对象中的前一部分元素。 - 通过
itertools.permutations()
来获取所有可能的组合或排列。 - 通过
enumerate
获取索引+值的迭代序列。 - 通过
zip
同时迭代多个序列。 - 通过
itertools.chain
在不同容器中进行迭代。 - 通过
heapq.merge()
合并多个有序序列。
- 通过
4. Examples
4.1. 解析器与生成器表达式
1 | squares = [x*x for x in range(10) if x % 2 == 0] |
4.2. 迭代器
典型迭代器(《流畅的Python》示例14-4)
- 迭代器和可迭代对象分开。
- 这样比较清晰,符合设计模式,但很麻烦。
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
31import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(test)
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
18
19
20
21
22
23
24import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(test)
self.index = 0
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __next__(self):
try:
word = self.words[self.index]
except IndexError:
raise StopIteration()
self.index += 1
return word
def __iter__(self):
return self
4.3. 生成器
1 | def gen_123(): |