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 ...
随机推荐
- 跑步进入全站 HTTPS ,这些经验值得你看看
随着国内网络环境的持续恶化,各种篡改和劫持层出不穷,越来越多的网站选择了全站 HTTPS.就在前几天,免费提供证书服务的 Let’s Encrypt 项目也正式开放测试,HTTPS 很快就会成为 WE ...
- Apache-Tika解析Excell文档
通常在使用爬虫时,爬取到网上的文章都是各式各样的格式处理起来比较麻烦,这里我们使用Apache-Tika来处理Excell格式的文章,如下: package com.mengyao.tika.app; ...
- Mongodb query查询
Query.All("name", "a", "b");//通过多个元素来匹配数组Query.And(Query.EQ("name ...
- flume-ng 使用spool source 传输文件到hdfs
Flume 1.4.0 User Guide 地址:http://archive.cloudera.com/cdh4/cdh/4/flume-ng-1.4.0-cdh4.6.0/FlumeUserGu ...
- python获取本机IP、mac地址、计算机名
在python中获取ip地址和在php中有很大不同,在php中往往比较简单.那再python中怎么做呢? 我们先来看一下python 获得本机MAC地址: 1 2 3 4 import uuid de ...
- Web日程管理FullCalendar
fullcalendar是一款jQuery日程管理控件,提供了丰富的属性设置和方法调用,官网下载地址http://fullcalendar.io/download,眼下最新版本号是2.3.2. 仅仅要 ...
- 《火球——UML大战需求分析》(第1章 大话UML)——1.2 结构型的UML(Structure Diagram)
说明: <火球——UML大战需求分析>是我撰写的一本关于需求分析及UML方面的书,我将会在CSDN上为大家分享前面几章的内容,总字数在几万以上,图片有数十张.欢迎你按文章的序号顺序阅读,谢 ...
- javascript 鼠標拖動功能
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- Reso | The Linux Command Line 的中文版
http://book.haoduoshipin.com/tlcl/book/zh/ 本书是 The Linux Command Line 的中文版, 为大家提供了多种不同的阅读方式. 中英文双语版- ...
- 新版本ButterKnife的配置
新版本的ButterKnife的添加方式发生了变化,然后摸索着记录了一下. 按照ButterKnife的官网描述,使用ButterKnife需要在Gradle中添加如下依赖: compile 'com ...