讲师中心 微信公众号
AI工具推荐 视频效率加速

Python函数式编程指南(四):生成器详解

夜涛吖_6585

夜涛吖_6585

发布时间:2016-06-06 11:19:26

|

1567人浏览过

|

来源于php中文网

原创

4. 生成器(generator)

4.1. 生成器简介

首先请确信,生成器就是一种迭代器。生成器拥有next方法并且行为与迭代器完全相同,这意味着生成器也可以用于Python的for循环中。另外,对于生成器的特殊语法支持使得编写一个生成器比自定义一个常规的迭代器要简单不少,所以生成器也是最常用到的特性之一。

从Python 2.5开始,[PEP 342:通过增强生成器实现协同程序]的实现为生成器加入了更多的特性,这意味着生成器还可以完成更多的工作。这部分我们会在稍后的部分介绍。

4.2. 生成器函数

立即学习“Python免费学习笔记(深入)”;

4.2.1. 使用生成器函数定义生成器

如何获取一个生成器?首先来看一小段代码:

代码如下:


>>> def get_0_1_2():
...   yield 0
...   yield 1
...   yield 2
...
>>> get_0_1_2

我们定义了一个函数get_0_1_2,并且可以查看到这确实是函数类型。但与一般的函数不同的是,get_0_1_2的函数体内使用了关键字yield,这使得get_0_1_2成为了一个生成器函数。生成器函数的特性如下:

1.调用生成器函数将返回一个生成器;

代码如下:


>>> generator = get_0_1_2()
>>> generator


2.第一次调用生成器的next方法时,生成器才开始执行生成器函数(而不是构建生成器时),直到遇到yield时暂停执行(挂起),并且yield的参数将作为此次next方法的返回值;

代码如下:


>>> generator.next()
0


3.之后每次调用生成器的next方法,生成器将从上次暂停执行的位置恢复执行生成器函数,直到再次遇到yield时暂停,并且同样的,yield的参数将作为next方法的返回值;

代码如下:


>>> generator.next()
1
>>> generator.next()
2


4.如果当调用next方法时生成器函数结束(遇到空的return语句或是到达函数体末尾),则这次next方法的调用将抛出StopIteration异常(即for循环的终止条件);

代码如下:


>>> generator.next()
Traceback (most recent call last):
  File "", line 1, in
StopIteration

5.生成器函数在每次暂停执行时,函数体内的所有变量都将被封存(freeze)在生成器中,并将在恢复执行时还原,并且类似于闭包,即使是同一个生成器函数返回的生成器,封存的变量也是互相独立的。
我们的小例子中并没有用到变量,所以这里另外定义一个生成器来展示这个特点:

代码如下:


>>> def fibonacci():
...   a = b = 1
...   yield a
...   yield b
...   while True:
...     a, b = b, a+b
...     yield b
...
>>> for num in fibonacci():
...   if num > 100: break
...   print num,
...
1 1 2 3 5 8 13 21 34 55 89


看到while True可别太吃惊,因为生成器可以挂起,所以是延迟计算的,无限循环并没有关系。这个例子中我们定义了一个生成器用于获取斐波那契数列。

4.2.2. 生成器函数的FAQ
接下来我们来讨论一些关于生成器的有意思的话题。

1.你的例子里生成器函数都没有参数,那么生成器函数可以带参数吗?

当然可以啊亲,而且它支持函数的所有参数形式。要知道生成器函数也是函数的一种:)

代码如下:


>>> def counter(start=0):
...   while True:
...     yield start
...     start += 1
...


这是一个从指定数开始的计数器。

2.既然生成器函数也是函数,那么它可以使用return输出返回值吗?

不行的亲,是这样的,生成器函数已经有默认的返回值——生成器了,你不能再另外给一个返回值;对,即使是return None也不行。但是它可以使用空的return语句结束。如果你坚持要为它指定返回值,那么Python将在定义的位置赠送一个语法错误异常,就像这样:

代码如下:


>>> def i_wanna_return():
...   yield None
...   return None
...
  File "", line 3
SyntaxError: 'return' with argument inside generator

3.好吧,那人家需要确保释放资源,需要在try...finally中yield,这会是神马情况?(我就是想玩你)我在finally中还yield了一次!
Python会在真正离开try...finally时再执行finally中的代码,而这里遗憾地告诉你,暂停不算哦!所以结局你也能猜到吧!

代码如下:


>>> def play_u():
...   try:
...     yield 1
...     yield 2
...     yield 3
...   finally:
...     yield 0
...
>>> for val in play_u(): print val,
...
1 2 3 0


*这与return的情况不同。return是真正的离开代码块,所以会在return时立刻执行finally子句。
*另外,“在带有finally子句的try块中yield”定义在PEP 342中,这意味着只有Python 2.5以上版本才支持这个语法,在Python 2.4以下版本中会得到语法错误异常。

4.如果我需要在生成器的迭代过程中接入另一个生成器的迭代怎么办?写成下面这样好傻好天真。。

代码如下:


>>> def sub_generator():
...   yield 1
...   yield 2
...   for val in counter(10): yield val
...

这种情况的语法改进已经被定义在[PEP 380:委托至子生成器的语法]中,据说会在Python 3.3中实现,届时也可能回馈到2.x中。实现后,就可以这么写了:

代码如下:


>>> def sub_generator():
...   yield 1
...   yield 2
...   yield from counter(10)
  File "", line 4
    yield from counter(10)
             ^
SyntaxError: invalid syntax


看到语法错误木有?现在我们还是天真一点吧~

有更多问题?请回复此文:)

4.3. 协同程序(coroutine)

协同程序(协程)一般来说是指这样的函数:

Shadows Python Sensei
Shadows Python Sensei

Python 最佳实践助手——代码规范、设计模式、性能优化、测试与类型注解。适用于编写或审查 Python 代码。

下载

1.彼此间有不同的局部变量、指令指针,但仍共享全局变量;
2.可以方便地挂起、恢复,并且有多个入口点和出口点;
3.多个协同程序间表现为协作运行,如A的运行过程中需要B的结果才能继续执行。

协程的特点决定了同一时刻只能有一个协同程序正在运行(忽略多线程的情况)。得益于此,协程间可以直接传递对象而不需要考虑资源锁、或是直接唤醒其他协程而不需要主动休眠,就像是内置了锁的线程。在符合协程特点的应用场景,使用协程无疑比使用线程要更方便。

从另一方面说,协程无法并发其实也将它的应用场景限制在了一个很狭窄的范围,这个特点使得协程更多的被拿来与常规函数进行比较,而不是与线程。当然,线程比协程复杂许多,功能也更强大,所以我建议大家牢牢地掌握线程即可:Python线程指南

这一节里我也就不列举关于协程的例子了,以下介绍的方法了解即可。

Python 2.5对生成器的增强实现了协程的其他特点,在这个版本中,生成器加入了如下方法:

1.send(value):

send是除next外另一个恢复生成器的方法。Python 2.5中,yield语句变成了yield表达式,这意味着yield现在可以有一个值,而这个值就是在生成器的send方法被调用从而恢复执行时,调用send方法的参数。

代码如下:


>>> def repeater():
...   n = 0
...   while True:
...     n = (yield n)
...
>>> r = repeater()
>>> r.next()
0
>>> r.send(10)
10

*调用send传入非None值前,生成器必须处于挂起状态,否则将抛出异常。不过,未启动的生成器仍可以使用None作为参数调用send。
*如果使用next恢复生成器,yield表达式的值将是None。
2.close():
这个方法用于关闭生成器。对关闭的生成器后再次调用next或send将抛出StopIteration异常。
3.throw(type, value=None, traceback=None):
这个方法用于在生成器内部(生成器的当前挂起处,或未启动时在定义处)抛出一个异常。
*别为没见到协程的例子遗憾,协程最常见的用处其实就是生成器。

4.4. 一个有趣的库:pipe
这一节里我要向诸位简要介绍pipe。pipe并不是Python内置的库,如果你安装了easy_install,直接可以安装它,否则你需要自己下载它:http://pypi.python.org/pypi/pipe

之所以要介绍这个库,是因为它向我们展示了一种很有新意的使用迭代器和生成器的方式:流。pipe将可迭代的数据看成是流,类似于linux,pipe使用'|'传递数据流,并且定义了一系列的“流处理”函数用于接受并处理数据流,并最终再次输出数据流或者是将数据流归纳得到一个结果。我们来看一些例子。

第一个,非常简单的,使用add求和:

代码如下:


>>> from pipe import *
>>> range(5) | add
10


求偶数和需要使用到where,作用类似于内建函数filter,过滤出符合条件的元素:

代码如下:


>>> range(5) | where(lambda x: x % 2 == 0) | add
6

还记得我们定义的斐波那契数列生成器吗?求出数列中所有小于10000的偶数和需要用到take_while,与itertools的同名函数有类似的功能,截取元素直到条件不成立:

代码如下:


>>> fib = fibonacci
>>> fib() | where(lambda x: x % 2 == 0)\
...       | take_while(lambda x: x ...       | add
3382


需要对元素应用某个函数可以使用select,作用类似于内建函数map;需要得到一个列表,可以使用as_list:

代码如下:


>>> fib() | select(lambda x: x ** 2) | take_while(lambda x: x [1, 1, 4, 9, 25, 64]

pipe中还包括了更多的流处理函数。你甚至可以自己定义流处理函数,只需要定义一个生成器函数并加上修饰器Pipe。如下定义了一个获取元素直到索引不符合条件的流处理函数:

代码如下:


>>> @Pipe
... def take_while_idx(iterable, predicate):
...   for idx, x in enumerate(iterable):
...     if predicate(idx): yield x
...     else: return
...


使用这个流处理函数获取fib的前10个数字:

代码如下:


>>> fib() | take_while_idx(lambda x: x [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]


更多的函数就不在这里介绍了,你可以查看pipe的源文件,总共600行不到的文件其中有300行是文档,文档中包含了大量的示例。

pipe实现起来非常简单,使用Pipe装饰器,将普通的生成器函数(或者返回迭代器的函数)代理在一个实现了__ror__方法的普通类实例上即可,但是这种思路真的很有趣。

函数式编程指南全文到这里就全部结束了,希望这一系列文章能给你带来帮助。希望大家都能看到一些结构式编程之外的编程方式,并且能够熟练地在恰当的地方使用 :)

明天我会整理一个目录放上来方便查看,并且列出一些供参考的文章。遗憾的是这些文章几乎都是英文的,请努力学习英语吧 - -#

热门AI工具

更多
UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

讯飞绘文

讯飞绘文是一款由科大讯飞推出的一站式 AIGC 内容运营平台。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

相关专题

更多
Buffalo框架数据库开发全教程
Buffalo框架数据库开发全教程

本专题围绕Buffalo框架数据库开发,讲解database.yml多环境配置、soda与fizz迁移生成回滚、模型结构体标签、增删改查与条件查询、一对多与多对多关联、数据校验、回调钩子、事务处理及原生SQL执行能力。

20

2026.09.23

Buffalo框架路由与请求处理实操指南
Buffalo框架路由与请求处理实操指南

本专题讲解Buffalo框架路由与请求处理机制,涵盖路由注册与分组、资源路由、Handler编写规范、Context上下文方法、参数绑定、中间件编写挂载、Session与Cookie读写、Flash消息及错误页面定制方法。

0

2026.09.23

Buffalo框架零基础入门教程
Buffalo框架零基础入门教程

本专题整理Buffalo框架入门内容,涵盖Go环境准备、buffalo CLI安装、新项目生成、目录结构说明、dev热加载启动、数据库连接配置与常见报错排查,帮助新手按约定优于配置的思路跑通第一个Buffalo框架应用。

20

2026.09.23

Conan创建软件包配方指南
Conan创建软件包配方指南

本专题介绍通过conanfile.py创建软件包的方法,讲解包名、版本、依赖和构建设置等基础信息,以及source、build、package、package_info等常用方法的作用及编写思路。

0

2026.09.22

Conan二进制包配置指南
Conan二进制包配置指南

本专题介绍Conan根据操作系统、编译器、架构和构建类型生成二进制包的方法,讲解Profile、Settings、Options及Package ID的作用,帮助管理不同平台和编译环境下的包版本。

20

2026.09.22

Conan私有仓库搭建教程
Conan私有仓库搭建教程

本专题系统的讲解Conan私有仓库的搭建流程,涵盖仓库服务部署、存储目录配置、用户认证、权限划分和远程地址添加,并介绍内部C++依赖包的上传、下载及版本维护方法。

20

2026.09.22

loomy官网入口地址合集
loomy官网入口地址合集

本专题汇总了 Loomy 桌面 AI 助理的官方入口地址合集及使用指南。提供 macOS 与 Windows 客户端下载 。Loomy 是讯飞推出的桌面级 AI 工作搭子,支持文件整理、数据分析、网页操作及通过飞书/钉钉远程操控电脑,助你高效完成本地办公任务 。

20

2026.09.22

NumPy常见函数使用方法
NumPy常见函数使用方法

本专题整理 NumPy 常见函数使用方法相关教程,覆盖函数大全、参数用法、数组运算、统计聚合、排序处理、where 条件筛选、linspace 创建数列等常用场景,帮助读者快速掌握 NumPy 函数调用思路和实际数据处理技巧。

0

2026.09.22

NumPy性能优化版本更新与常见报错排查
NumPy性能优化版本更新与常见报错排查

本专题整理 NumPy 性能优化、版本更新与常见报错排查相关教程,覆盖向量化计算、广播性能、内存布局、NumPy 2.0 升级、版本兼容冲突、安装导入报错、dtype 溢出、矩阵运算异常和 broadcasting 报错修复,帮助读者系统掌握 NumPy 性能调优与问题定位方法。

20

2026.09.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn