Python展开一个嵌套的序列
摘自《Python Cookbook》 4.6
任务
序列中的子序列可能是序列,子序列的子项仍有可能是序列,以此类推,则序列嵌套可以达到任意的深度。需要循环遍历一个序列,将其所有的子序列展开成一个单一的,只具有基本子序列的序列。(一个基本子项或者原子,可以是任何非序列的对象-或者说叶子,假如你认为序列是一棵树)
解决方案
我们需要能够判断哪些我们正在处理的子项是需要被展开的,那些是原子。为了获得通用性,我们使用了一个断定来作为参数,由它来判断子项是否可以展开。(断定[predicate]是一个函数,每当我们处理一个元素时就将其运用于该元素并返回一个布尔值;在这里,如果元素是一个需要展开的子序列就返沪True,否则返回False。)我们假定每一个列表或者原组都是需要被展开的,而其他类型不是。那么简单的解决方法就是提供一个递归的生成器
def list_or_update(x):
return isinstance(x, (list, tuple)) def flatten(sequence, to_expand=list_or_update):
for item in sequence:
if to_expand(item):
for subitem in flatten(item, to_expand):
yield subitem
else:
yield item
讨论
展开一个嵌套的序列,或者等价地,按照顺序“遍历”一棵树的所有叶子,是在各种运用中很常见的任务。如果有一个嵌套的结构,元素都被组织成序列或者子序列,而且,基于某些理由,你并不关心结构本省,需要的只是一个接一个的处理所有的元素。举个例子:
l = [['a', 'b'], 'c', ['d', ['e', ['f'], 'g']], 'h'] for i in flatten(l):
print i,
这个任务唯一的问题是,怎样在尽量通用的尺度下,判断什么是需要展开的,什么是需要被当作原子的,这其实没看上其那么简单。所以,我绕开直接的判断,把这个工作交给一个可调用的判定参数,调用者可以将其传递给 flatten,如果调用者满足于flatten简单的默认行为方法,即指展开原组和列表。
在flatten所在的模块中,我们还需要提供另一个调用者可能需要用到的判定——它将展开任何非字符串(无论是普通字符串还是Unicode)的可迭代对象。字符串是可迭代,但是绝大多数运用程序还是想把他们当成原子,而不是子序列。
至于判断对象是否可迭代,我们只需要对该对象调用内建的iter函数;若该对象不可迭代,此函数将抛出TypeError异常。为了判断对象是否是类字符串,我们则简单第检查它是否是 basestring 的实例,当obj是basestring的任何子类的实例时, isinstance(obj, basestring)的返回值将是True——这意味着任何类字符串类型。因此,这样的一个判定并不难写:
def nonstring_iterable(obj):
try:
iter(obj)
except TypeError:
return False
else:
return not isinstance(obj, basestring)
当具体的需求展开任何可迭代非字符串对象时,调用者可以调用flatten(seq, nonstring_iterable)。无疑,不把nonstring_iterable 断定作为flattern的默认选项是一个更好的选择:在简单的需求中,如我们前面展示的示例代码片段,使用nonstring_iterable会比使用list_or_tuple慢3倍以上。
我们也可以写一个非递归版本的flattern。这种写法可以超越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()
其中 if 语句块的 if 子句会展开一个我们需要展开的元素——即我们需要循环遍历的子序列;所以我们该在子句中,我们将那个子序列的迭代器压入栈的末尾,在通过break打断for的执行,回到外层的while,外层while会针对我们刚刚压入的新的迭代器执行一个新的for语句。else子句则用于处理那些不需要展开的元素,它直接产生元素本身。
如果for循环未被打断,for语句块所属的else子句将得以执行——换句话说,当for循环完全执行完毕,说明它已经遍历完当前的最新的迭代器。所以,在else子句中,我们移除了已经耗尽的嵌套最深(最近)的迭代器,之后外层的while循环继续执行,如果栈已经空了,则中止循环,如果栈中还有迭代器,则执行一个新的for循环来处理之+正好是上次执行中断的地方,本质上,迭代器的任务就是记忆迭代的状态。
flatten的非递归实现产生的结果和前面的简单一些的递归版本的结果完全一致。如果你认为非递归实现会比递归方式快,那么你可能会失望;我采用一系列的测试用例进行观察测量,发现非递归版本比递归版本慢约10%。
Python展开一个嵌套的序列的更多相关文章
- python 展开嵌套的序列
将一个多层嵌套的序列展开成一个单层列表 可以写一个包含yield from 语句的递归生成器来轻松解决这个问题. from collections import Iterable def flatte ...
- 【原创】从 列表的重复 到 用sum展开二层嵌套列表将子元素合并
转载请注明出处:https://www.cnblogs.com/oceanicstar/p/9517159.html ★像R语言里头有rep函数可以让向量的值重复,在python里面可以直 ...
- 使用python检测一个设备是否ping的通
使用python检测一个设备是否ping的通 一,subprocess以及常用的封装函数 运行python的时候,我们都是在创建并运行一个进程.像Linux进程那样,一个进程可以fork一个子进程,并 ...
- python数据结构(一)------序列
数据结构是通过某种方式(例如对元素进行编号)组织在一起的数据元素的集合:在Python中,最基本的数据结构是序列(sequence),序列中的每个元素被分配一个序列号--即元素的位置,也称为索引. p ...
- python基础—函数嵌套与闭包
python基础-函数嵌套与闭包 1.名称空间与作用域 1 名称空间分为: 1 内置名称空间 内置在解释器中的名称 2 全局名称空间 顶头写的名称 3 局部名称空间 2 找一个名称的查找顺序: ...
- python中函数嵌套、函数作为变量以及闭包的原理
嵌套函数: python允许创建嵌套函数.也就是说我们可以在函数里面定义函数,而且现有的作用域和变量生存周期依旧不变. 例子: #encoding=utf-8 def outer(): name ...
- python教程(四)·序列
距离上次的小项目已经休息了很长一段时间,是时候来继续本系列教程了.这一节开始我们将深入python中的数据结构. 序列的概念 在python中,最基本的数据结构是序列,序列包含一个或多个元素,每个元素 ...
- Python决定一个变量时局部的,还是全局的,是在编译期
Python中的变量名是在编译时就解析好的,换句话说,在编译时(也就是在交互控制台输入代码是或者import文件时),Python就已经决定一个变量应该是局部变量,还是全局变量.来看下面的例子: &g ...
- 孤荷凌寒自学python第三天 初识序列
孤荷凌寒自学python第三天 初识序列 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) Python的序列非常让我着迷,之前学习的其它编程语言中没有非常特别关注过序列这种类型的对象,而pyt ...
随机推荐
- jSP的3种方式实现radio ,checkBox,select的默认选择值。
jSP的3种方式实现radio ,checkBox,select的默认选择值.以radiao 为例:第一种方式:在jsp中使用java 脚本,这个方法最直接,不过脚本太多,不容易维护<%Stri ...
- MySQL ERROR 1045错误解决办法
今天在安装MySQL数据库时,有安装过程中报ERROR 1045错误,网上查了一下,解决方法如下: 1.对于安装过程中该错选择"skip"继续向下安装,完成安装: 2.在MySQL ...
- Atom 编辑器 前端基本插件
Atom 编辑器插件 这个编辑器是github出品,现在处于免费试用期:如果是初学者,可以使用这个编辑器,插件安装很方便,只需要点菜单栏的File-Settings-Install,在搜索框中输入想要 ...
- A_star poj2449 k短路
赛后填坑系列QAQ 贴代码呀 #include<iostream> #include<algorithm> #include<cstdio> #include< ...
- Git客户端(Windows系统)的使用
本文环境: 操作系统:Windows XP SP3 Git客户端:TortoiseGit-1.8.5.0-32bit 一.安装Git客户端 全部安装均采用默认! 1. 安装支撑软件 msysgit: ...
- BAT染指影视制作 欲全面撬开互联网粉丝经济
预測: 或靠"用户"模式盈利 除了内容制作,电影发行也在遭遇互联网模式的冲击. 除了给片方支付高额保底以外,随着市场竞争激烈.新进入者都在争夺好片的发行权. 业内预測.再往后,发行 ...
- 开源实时视频码流分析软件:VideoEye
本文介绍一个自己做的码流分析软件:VideoEye.为什么要起这个名字呢?感觉这个软件的主要功能就是对"视频"进行"分析".而分析是要用眼睛来看的,因此取了&q ...
- Linux cpuinfo 详解
在Linux系统中,如何详细了解CPU的信息呢? 当然是通过cat /proc/cpuinfo来检查了,但是比如几个物理CPU/几核/几线程,这些问题怎么确定呢? 经过查看,我的开发机器是1个物理C ...
- 使用 Java 配置进行 Spring bean 管理--转
概述 众所周知,Spring 框架是控制反转 (IOC) 或依赖性注入 (DI) 模式的推动因素,而这种推动是通过基于容器的配置实现的.过去,Spring 允许开发人员使用基于 XML 的配置,通过利 ...
- 微信支付bug
1.最基本的操作就是检查各项参数正确2.确保将测试微信号加入测试白名单 3.目录正确:发起授权请求的页面必须是在授权目录下的页面,而不能是存在与子目录中.否则会返回错误,Android返回“Syste ...