定义
生成器是一个函数,它并不执行并返回一个单一值,而是按照顺序返回一个或多个值
生成器的语法
生成器函数的特征就是在函数内部有一个或多个yield语句。
Python 2中,yield和return不能共存;python 3中可同时存在。
yield语句:命令函数返回一个值给调用者,但不会终止函数的执行。执行会暂时停顿直到调用代码重新恢复生成器,在停止的地方再次开始执行。
生成器案例
import time
def fib():
numbers = []
while True:
if len(numbers) < 2:
numbers.append(1)
else:
numbers.append(sum(numbers))
numbers.pop(0)
yield numbers[-1]
f = fib()
for i in f:
print i
time.sleep(1)
- 注意:f = fib()语句执行后,函数代码并没有运行,解释器唯一完成的就是识别生成器的出现并返回一个generator对象,该对象在每运行一次代码时就请求一个值。
- 可以使用内置的next函数请求第一个值
- 函数只是存储了最新的两个数字,没有把庞大的数列存储在内存
- TopIterator异常:迭代生成器抛出StopIteration时,标志着生成器迭代完成并已退出
- python3中消除了yield和return不能在一个函数中共存的限制。但return等同于raise StopIteration
- yield语句实际上是一个表达式,有返回值
生成器之间的交互
一个求平凡的生成器
def squares():
cursor = 1
while True:
yield cursor ** 2
cursor += 1
sq = squares()
print sq.next() # 1
print sq.next() # 4
该生成器是单向的,send方法允许生成器反向沟通
def squares1(cursor=1):
while True:
response = yield cursor ** 2
if response:
cursor = int(response)
else:
cursor += 1
sq = squares1()
print sq.next() # 1
print sq.next() # 4
print sq.send(7) # 49;值7发送给生成器,赋给cursor变量
print sq.next() # 64
- send方法:允许生成器反向沟通,因为yield语句实际上就是一个表达式
- send的目的是提供一个与生成器双向交互的机制,确定是否(如何)处理发送给生成器的值是生成器的责任
迭代对象和迭代器
- 迭代器 是指包含__next__方法的任何对象。生成器是一种迭代器。range是迭代器,但不是生成器
- 迭代对象 是指任何定义了__iter__方法的对象。可迭代对象的__iter__方法负责返回一个迭代器
print next(range(5)) #是迭代器,但不是生成器
TypeError: list object is not an iterator
print range(5).__iter__()
<listiterator object at 0x01662BB0>
标准库中的生成器
- range(前一节)
- dict.items及其家族
- python 2中:iterkeys,itervalues,iteritems
- python 3中:keys,values,items
- 如果在迭代期间试图修改字典,则会报RuntimeError。因为items迭代器是一个仅从引用的字典中读取数据的生成器,如果运行时字典表发生了变化,它将不知道自己应该做什么,因此抛出异常。
- 案例
dictionary = {'foo':'bar', 'baz':'bacon'}
iterator = iter(dictionary.items())
print next(iterator)
print next(iterator)
运行结果:
(‘foo’, ‘bar’)
(‘baz’, ‘bacon’)
- zip:使用zip的目的是在不同的结构中输出其迭代对象的返回成员,一次输出一个集合。缓解了对内存的需求。
>>> z = zip(['a','b','c','d'], ['x','y','z'])
>>> next(z)
('a', 'x')
>>> next(z)
('b', 'y')
>>> next(z)
('c', 'z')
>>> next(z)
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
next(z)
StopIteration
- map:该函数将一个能接受N个参数和N个迭代对象的函数作为参数,并且计算每个迭代对象的序列成员的函数结果
函数定义:map()是 Python 内置的高阶函数,它接收一个函数 f 和一个 list,并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list 并返回。
>>> m = map(lambda x,y: max([x,y]), [4,1,7], [3,4,5])
>>> m
<map object at 0x0208CB50>
>>> next(m)
4
>>> next(m)
4
>>> next(m)
7
>>> next(m)
Traceback (most recent call last):
File "<pyshell#15>", line 1, in <module>
next(m)
StopIteration
- 文件对象:readline方法可以一次读取一行
>>> f = open('aa.txt') #open函数返回的结果对象除了其他身份外,还是个生成器
>>> next(f)
'one line\n'
>>> next(f)
'two line\n'
>>> next(f)
Traceback (most recent call last):
File "<pyshell#23>", line 1, in <module>
next(f)
StopIteration
何时编写生成器
- 原则:只有当需要值时才会确定这个值,而不是提前准备好;即使需要所有数据,但是如果不需要一次性处理所有数据,仍然可以仅存储需要的数据。
- 两个理由:
- 分块访问数据:需要涵盖必须分块访问数据的情况,但是这种情况没必要存储整个副本(例如readline和dict.items)
- 分块计算数据:仅在需要它时计算数据(例如range和fibonacci函数)
生成器单例模式
很多生成器是单例模式;简单的生成器函数不是单例模式
class Fabonacci(object):
def __init__(self):
self.numbers = []
def __iter__(self):
return self
def __next__(self):
if len(self.numbers) < 2:
self.numbers.append(1)
else:
self.numbers.append(sum(self.numbers))
self.numbers.pop(0)
return self.numbers[-1]
def send(self, value):
pass
# For python2 compatibility
next = __next__
f = Fabonacci()
i1 = iter(f)
print next(i1)
print next(i1)
i2 = iter(f)
print next(i2)
运行结果:
1
1
2
- 这是一个Fabonacci类,实现了生成器的协议,它也是一个迭代对象,并且将自己作为参数响应iter,即每个Fabonacci对象只有一个迭代器:它自己
- 注意:弄明白一个迭代对象是否允许有多个迭代器:有些迭代对象可以有多个迭代器,有些迭代对象则不可以
生成器内部的生成器
- 生成器委托:yield from 为生成器提供一种调用其他生成器的直接方式。
- 案例:python 3.3之前,将子生成器组合成一个生成器的方法是显式地迭代它们,即:full_gen
>>> def gen1():
yield 'foo'
yield 'bar'
>>> def gen2():
yield 'spam'
yield 'eggs'
>>> def full_gen():
for word in gen1():
yield word
for word in gen2():
yield word
>>> def fuu_gen2(): #仅python3.3以上版本支持
yield from gen1() #生成器委托
yield from gen2()
>>> f =full_gen()
>>> for i in f:
print i
SyntaxError: Missing parentheses in call to 'print'. Did you mean print(i)?
>>> for i in f:
print (i)
foo
bar
spam
eggs
>>> f = fuu_gen2()
>>> for i in f:
print (i)
foo
bar
spam
eggs