0

0

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

php中文网

php中文网

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

|

1547人浏览过

|

来源于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)

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

PathFinder
PathFinder

AI驱动的销售漏斗分析工具

下载

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工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

1

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

39

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

140

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

47

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

90

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

102

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

226

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

506

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

170

2026.03.04

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 22.5万人学习

Django 教程
Django 教程

共28课时 | 5万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.9万人学习

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

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号