python 递归展开嵌套的序列(生成器用法)
任何使用yield语句的函数都称为生成器。调用生成器函数将创建一个对象,该对象通过连续调用next()方法(在python3中是__next__())生成结果序列。
next()调用使生成器函数一直运行到下一条yield语句为止。此时next()将返回值传递给yield,而且函数将暂时中止执行。再次调用next()时,函数将继续执行yield之后的语句。此过程持续到函数返回为止。
通常不会在生成器上直接调用next()方法,而是在for语句、sum()或一些使用序列的其他操作中使用它。
生成器函数完成的标志是返回或引发StopIteration异常,这标志着迭代的结束。如果生成器在完成时返回None之外的值,都是不合法的。
生成器是由两部分组成:生成器的函数和生成器的迭代器。
生成器的函数是用def 语句定义的,包含yield的部分;生成器的迭代器是这个函数返回的部分。按一种不是很准确的说法,两个实体经常被当作一个,合起来叫做生成器。
>>> def simple_generator():
... yield 1
...
>>> simple_generator
<function simple_generator at 0x16eb398>
>>> simple_generator()
<generator object simple_generator at 0x16cfc30>
>>>
生成器的函数返回的迭代器可以像其他迭代器那样使用。
首先请确信,生成器就是一种迭代器。生成器拥有next方法并且行为与迭代器完全相同,这意味着生成器也可以用于Python的for循环中。另外,对于生成器的特殊语法支持使得编写一个生成器比自定义一个常规的迭代器要简单不少,所以生成器也是最常用到的特性之一。
从Python 2.5开始,[PEP 342:通过增强生成器实现协同程序]的实现为生成器加入了更多的特性,这意味着生成器还可以完成更多的工作。这部分我们会在稍后的部分介绍。
生成器函数
使用生成器函数定义生成器
如何获取一个生成器?首先来看一小段代码:
1
2
3
4
5
6
7
|
>>> def get_0_1_2(): ... yield 0 ... yield 1 ... yield 2 ... >>> get_0_1_2 <function get_0_1_2 at 0x00B2CB70 > |
我们定义了一个函数get_0_1_2,并且可以查看到这确实是函数类型。但与一般的函数不同的是,get_0_1_2的函数体内使用了关键字yield,这使得get_0_1_2成为了一个生成器函数。生成器函数的特性如下:
- 调用生成器函数将返回一个生成器;
123
>>> generator
=
get_0_1_2()
>>> generator
<generator
object
get_0_1_2 at
0x00B1C7D8
>
- 第一次调用生成器的next方法时,生成器才开始执行生成器函数(而不是构建生成器时),直到遇到yield时暂停执行(挂起),并且yield的参数将作为此次next方法的返回值;
12
>>> generator.
next
()
0
- 之后每次调用生成器的next方法,生成器将从上次暂停执行的位置恢复执行生成器函数,直到再次遇到yield时暂停,并且同样的,yield的参数将作为next方法的返回值;
1234
>>> generator.
next
()
1
>>> generator.
next
()
2
- 如果当调用next方法时生成器函数结束(遇到空的return语句或是到达函数体末尾),则这次next方法的调用将抛出StopIteration异常(即for循环的终止条件);
1234
>>> generator.
next
()
Traceback (most recent call last):
File
"<stdin>"
, line
1
,
in
<module>
StopIteration
- 生成器函数在每次暂停执行时,函数体内的所有变量都将被封存(freeze)在生成器中,并将在恢复执行时还原,并且类似于闭包,即使是同一个生成器函数返回的生成器,封存的变量也是互相独立的。
我们的小例子中并没有用到变量,所以这里另外定义一个生成器来展示这个特点:12345678910111213>>>
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可别太吃惊,因为生成器可以挂起,所以是延迟计算的,无限循环并没有关系。这个例子中我们定义了一个生成器用于获取斐波那契数列。
生成器注意事项:
既然生成器函数也是函数,那么它可以使用return输出返回值吗?
不行的亲,是这样的,生成器函数已经有默认的返回值——生成器了,你不能再另外给一个返回值;对,即使是return None也不行。但是它可以使用空的return语句结束。如果你坚持要为它指定返回值,那么Python将在定义的位置赠送一个语法错误异常,就像这样:
>>> def i_wanna_return():
... yield None
... return None
...
File "<stdin>", line 3
SyntaxError: 'return' with argument inside generator
- 如果我需要在生成器的迭代过程中接入另一个生成器的迭代怎么办?写成下面这样好傻好天真。。
12345
>>>
def
sub_generator():
...
yield
1
...
yield
2
...
for
val
in
counter(
10
):
yield
val
...
这种情况的语法改进已经被定义在[PEP 380:委托至子生成器的语法]中,据说会在Python 3.3中实现,届时也可能回馈到2.x中。实现后,就可以这么写了:
12345678>>>
def
sub_generator():
...
yield
1
...
yield
2
...
yield
from
counter(
10
)
File
"<stdin>"
, line
4
yield
from
counter(
10
)
^
SyntaxError: invalid syntax
看到语法错误木有?现在我们还是天真一点吧~更多:http://www.cnblogs.com/huxi/archive/2011/07/14/2106863.html
任务
序列中的子项可能是序列,子序列的子项仍可能是序列,以此类推,则序列嵌套可以达到任意的深度。需要循环遍历一个序列,将其中所有的子序列展开成一个单一的、只具有基本子项的序列。(一个基本子项或者原子,可以是任何非序列的对象-或者说叶子,假如你认为嵌套序列是一棵树。)
解决方案
我们需要能够判断哪些我们正在处理的子项是需要被展开的,哪些是原子。为了获得通用性,我们使用了一个断定来作为参数,由它来判断子项是否是可以展开的。(断定[predicate]是一个函数,每当我们处理一个元素时就将其应用于该元素并返回一个布尔值:在这里,如果元素是一个需要展开的子序列就返回True,否则返回False。)我们假定每一个列表或者元组都是需要被展开的,而其他类型则不是。那么最简单的解决方法就是提供一个递归的生成器:
def list_or_tuple(x):
return isinstance(x, (list, tuple))
def flatten(sequence, to_expand=list_or_tuple):
for item in sequence:
if to_expand(item):
for subitem in flatten(item, to_expand):
yield subitem
else:
yield item
- for x in flatten([1, 2, [3, [ ], 4, [5, 6], 7, [8,], ], 9]):
- print x,
- 输出1 2 3 4 5 6 7 8 9。
我们也可以写一个非递归版本的flatten。这种写法可以超越Python的递归层次的极限,一般不超过几千层。实现无递归遍历的要点是,采用一个明确的后进先出(LIFO)栈。在这个例子中,我们可以用迭代器的列表实现:
def flatten(sequence, to_expand=list_or_tuple):
iterators = [ iter(sequence) ]
while iterators:
# 循环当前的最深嵌套(最后)的迭代器
for item in iterators[-1]:
if to_expand(item):
# 找到了子序列,循环子序列的迭代器
iterators.append(iter(item))
break
else:
yield item
else:
# 最深嵌套的迭代器耗尽,回过头来循环它的父迭代器
iterators.pop( )
转自:http://book.51cto.com/art/201005/198629.htm
python 递归展开嵌套的序列(生成器用法)的更多相关文章
- python 展开嵌套的序列
将一个多层嵌套的序列展开成一个单层列表 可以写一个包含yield from 语句的递归生成器来轻松解决这个问题. from collections import Iterable def flatte ...
- Python展开一个嵌套的序列
摘自<Python Cookbook> 4.6 任务 序列中的子序列可能是序列,子序列的子项仍有可能是序列,以此类推,则序列嵌套可以达到任意的深度.需要循环遍历一个序列,将其所有的子序列展 ...
- 递归遍历嵌套结构(多层List)中的元素 ------Python
读Python基础教程(第二版)后看到了这么一个东西,就是利用递归遍历嵌套结构中的元素. 上代码: #encoding:UTF-8 def flatten(nested): try: #不要迭代类似字 ...
- Python代码阅读(第11篇):展开嵌套列表
Python 代码阅读合集介绍:为什么不推荐Python初学者直接看项目源码 本篇阅读的代码实现了展开嵌套列表的功能,将一个嵌套的list展开成一个一维list(不改变原有列表的顺序). 本篇阅读的代 ...
- Python之旅Day5 列表生成式 生成器 迭代器 装饰器
装饰器 器即函数,装饰即修饰,意指为其他函数添加新功能 装饰器定义:本质就是函数,功能是为其他函数添加新功能 装饰器涉及的知识点= 高阶函数+函数嵌套+闭包 在遵循下面两个原则的前提下为被装饰者新功能 ...
- Python 迭代器之列表解析与生成器
 [TOC] 1. 列表解析 1.1 列表解析基础 列表解析把任意一个表达式应用到一个迭代对象中的元素 Python内置ord函数会返回一个字符的ASCII整数编码(chr函数是它的逆过程, 它将A ...
- 提高你的python:解释 yield 和 Generators(生成器)
转自:http://www.oschina.net/translate/improve-your-python-yield-and-generators-explained 原文:http://www ...
- python基础之 迭代器回顾,生成器,推导式
1.迭代器回顾 可迭代对象:Iterable 可以直接作用于for循环的对象统称为可迭代对象:Iterable.因为可迭代对象里面存在可迭代协议,所以才会被迭代 可迭代对象包括: 列表(list) 元 ...
- Python开发——函数【迭代器、生成器、三元表达式、列表解析】
递归和迭代 小明问路篇解释说明 递归:小明——>小红——>小于——>小东:小东——>小于——>小红——>小明 小明向小红问路,因小红不知道,所以向小于问路,因小于不 ...
随机推荐
- CTO这点事(技术,业务,管理,情商,周期,趋势)转
几乎整个互联网行业都缺 CTO,特别是一些草根背景的创业者,这个问题更加显著.从我自己的感受,身边各种朋友委托我找 CTO 的需求,嗯,算下来超过两位数了,光最近一个月就有 3 个,而且这三家都是刚拿 ...
- Source insight添加工具自动排版
当在网上找了一些别人的程序拿来学习,用Source insight来看时,会不会因为代码太乱看了义愤填膺呢? 有很多集成的开发环境可以自动排版,但source insight却不行!不过,有工具和配置 ...
- RFC3261--sip
本文转载自 http://www.ietf.org/rfc/rfc3261.txt 中文翻译可参考 http://wenku.baidu.com/view/3e59517b1711cc7931b716 ...
- Java中ThreadLocal无锁化线程封闭实现原理
虽然现在可以说很多程序员会用ThreadLocal,但是我相信大多数程序员还不知道ThreadLocal,而使用ThreadLocal的程序员大多只是知道其然而不知其所以然,因此,使用ThreadLo ...
- windows media player 中播放pls的方法
windows media player目前只能播放 wpl 和 asm格式的列表文件.而linux下mplayer和vlc支持的pls,很遗憾没法支持. 不过,老外写了个“open pls in w ...
- iOS更新之DFU模式和恢复模式
DFU模式和恢复模式的区别:DFU模式是在iPhone固件引导启动之前进行恢复的模式.所以用DFU模式刷机一般比较干净,不会有任何垃圾文件.想当于电脑重新格式化之后再安装系统. DFU模式进入方法:1 ...
- PC-CSS-默认字体样式
字体样式:Arial:字体大小:12px;行高:1.5倍:
- linux svn安装和配置
linux svn安装和配置,不结合apache http://blog.51yip.com/server/901.html 张映 发表于 2010-07-07 分类目录: 服务器相关 今天有个同事在 ...
- gcc基本用法
GCC基本用法 GCC最基本的用法是: gcc [option] filenames option:编译器所需要的编译选项 filenames:要编译的文件名 gcc编译流程 都以 hello.c 为 ...
- JS数组追加数组采用push.apply的坑(转)
JS数组追加数组没有现成的函数,这么多年我已经习惯了a.push.apply(a, b);这种自以为很酷的,不需要写for循环的写法,一直也没遇到什么问题,直到今天我要append的b是个很大的数组时 ...