Fluent_Python_14

流畅的Python

可迭代的对象、迭代器和生成器

python2.2开始引入yield关键字

迭代器从集合中取出元素,生成器用于“凭空”生成元素。

解释器需要迭代对象x时,会自动调用iter(x)。

  1. 检查对象是否实现了__iter__()方法,如果实现就调用它产生一个迭代器
  2. 如果实现了__getitem__()方法,Python就创建一个迭代器,尝试按顺序(从索引0开始)按顺序获取
  3. TypeError。“C object is not iterable”,C是对象所属的类。

Python3.4开始,检验对象x是否可以迭代:iter(x)

Python从可迭代对象中获取迭代器。

迭代器到头会抛出StopIteration异常,Python内置的已经处理了。如果是自己写迭代器话,需要处理这个异常。也只有这个办法,不能检查是否有遗留元素。

1
2
3
4
5
6
7
8
s = 'Test'
it = iter(x)
while True:
try:
print(next(it))
except StopIteration:
del it
break
标准迭代器接口:
  1. __next__()方法:

    返回下一个可用的元素,如果没有就抛出StopIteration。

  2. __iter__()方法:

    返回self。

迭代器可以迭代,但是可迭代对象不是迭代器。
迭代器模式
  • 访问一个聚合对象的内容而无需暴露它的内部表示

  • 支持对聚合对象的多种遍历

  • 为遍历不同的聚合结构提供一个统一的接口(多态迭代)。

    必须能从同一个可迭代的实例中获取多个独立的迭代器,而且每个迭代器要能维护自身的内部状态。

但是,用一个新的类太不Python了,所以常用生成器函数。

生成器函数:有yield关键字。

调用生成器函数时,会返回一个生成器对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def gen_a():
print("good morning")
yield '10:00'
print("good afternoon")
yield '12:30'
print("good night")
yield "18:30"
for c in gen_a():
print('the time is:', c)
---
output:
good morning
the time is: 10:00
good afternoon
the time is: 12:30
good night
the time is: 18:30
lazy evaluation and eager evalution

`懒惰求值和及早求值。

re.finditer函数是re.findall`的惰性版本。

1
2
3
def __iter__(self):
for match in RE_WORD.finditer(self.text):
yield match.group()
生成器表达式(语法糖

使用列表推导式的时候,它迫不及待地推导出结果。而生成器表达式只在要生成的时候才生成。

1
2
def __iter(self):
return (match.group() for match in RE_WORD.finditer(self.text))

如何判断:如果是生成器表达式需要分行写,就倾向于生成器函数。另外生成器函数可以复用。

@优雅

1
2
3
4
5
# begin和step的type可能是不同的!
result = type(begin + step)(begin)
index += 1
# 防止浮点误差的积累效应
result = begin + index * step
itertools模块
1
2
3
4
5
import itertools
# 会无限生成
gen = itertools.count(1, .5)
# 在条件为False的时候就会停止。
gen2 = itertools.takewhile(lambda n: n < 3, count(1, .5))
标准库中的生成器函数