《Python高级编程》(三)生成器

定义

生成器是一个函数,它并不执行并返回一个单一值,而是按照顺序返回一个或多个值

生成器的语法

生成器函数的特征就是在函数内部有一个或多个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

猜你喜欢

转载自blog.csdn.net/zhuix7788/article/details/86012514
今日推荐