python 生成器(五):生成器实例(一)创建数据处理管道
问题
你想以数据管道(类似Unix管道)的方式迭代处理数据。 比如,你有个大量的数据需要处理,但是不能将它们一次性放入内存中。
解决方案
生成器函数是一个实现管道机制的好办法。 为了演示,假定你要处理一个非常大的日志文件目录:
foo/
access-log-012007.gz
access-log-022007.gz
access-log-032007.gz
...
access-log-012008
bar/
access-log-092007.bz2
...
access-log-022008
假设每个日志文件包含这样的数据:
124.115.6.12 - - [10/Jul/2012:00:18:50 -0500] "GET /robots.txt ..." 200 71
210.212.209.67 - - [10/Jul/2012:00:18:51 -0500] "GET /ply/ ..." 200 11875
210.212.209.67 - - [10/Jul/2012:00:18:51 -0500] "GET /favicon.ico ..." 404 369
61.135.216.105 - - [10/Jul/2012:00:20:04 -0500] "GET /blog/atom.xml ..." 304 -
...
为了处理这些文件,你可以定义一个由多个执行特定任务独立任务的简单生成器函数组成的容器。就像这样:
import os
import fnmatch
import gzip
import bz2
import re def gen_find(filepat, top):
'''
Find all filenames in a directory tree that match a shell wildcard pattern
'''
for path, dirlist, filelist in os.walk(top):
for name in fnmatch.filter(filelist, filepat):
yield os.path.join(path,name) def gen_opener(filenames):
'''
Open a sequence of filenames one at a time producing a file object.
The file is closed immediately when proceeding to the next iteration.
'''
for filename in filenames:
if filename.endswith('.gz'):
f = gzip.open(filename, 'rt')
elif filename.endswith('.bz2'):
f = bz2.open(filename, 'rt')
else:
f = open(filename, 'rt')
yield f
f.close() def gen_concatenate(iterators):
'''
Chain a sequence of iterators together into a single sequence.
'''
for it in iterators:
yield from it def gen_grep(pattern, lines):
'''
Look for a regex pattern in a sequence of lines
'''
pat = re.compile(pattern)
for line in lines:
if pat.search(line):
yield line
现在你可以很容易的将这些函数连起来创建一个处理管道。 比如,为了查找包含单词python的所有日志行,你可以这样做:
lognames = gen_find('access-log*', 'www')
files = gen_opener(lognames)
lines = gen_concatenate(files)
pylines = gen_grep('(?i)python', lines)
for line in pylines:
print(line)
如果将来的时候你想扩展管道,你甚至可以在生成器表达式中包装数据。 比如,下面这个版本计算出传输的字节数并计算其总和。
lognames = gen_find('access-log*', 'www')
files = gen_opener(lognames)
lines = gen_concatenate(files)
pylines = gen_grep('(?i)python', lines)
bytecolumn = (line.rsplit(None,1)[1] for line in pylines)
bytes = (int(x) for x in bytecolumn if x != '-')
print('Total', sum(bytes))
讨论
以管道方式处理数据可以用来解决各类其他问题,包括解析,读取实时数据,定时轮询等。
为了理解上述代码,重点是要明白 yield 语句作为数据的生产者而 for 循环语句作为数据的消费者。 当这些生成器被连在一起后,每个 yield 会将一个单独的数据元素传递给迭代处理管道的下一阶段。 在例子最后部分, sum() 函数是最终的程序驱动者,每次从生成器管道中提取出一个元素。
这种方式一个非常好的特点是每个生成器函数很小并且都是独立的。这样的话就很容易编写和维护它们了。 很多时候,这些函数如果比较通用的话可以在其他场景重复使用。 并且最终将这些组件组合起来的代码看上去非常简单,也很容易理解。
使用这种方式的内存效率也不得不提。上述代码即便是在一个超大型文件目录中也能工作的很好。 事实上,由于使用了迭代方式处理,代码运行过程中只需要很小很小的内存。
在调用 gen_concatenate() 函数的时候你可能会有些不太明白。 这个函数的目的是将输入序列拼接成一个很长的行序列。 itertools.chain() 函数同样有类似的功能,但是它需要将所有可迭代对象作为参数传入。 在上面这个例子中,你可能会写类似这样的语句 lines = itertools.chain(*files) , 这将导致 gen_opener() 生成器被提前全部消费掉。 但由于 gen_opener() 生成器每次生成一个打开过的文件, 等到下一个迭代步骤时文件就关闭了,因此 chain() 在这里不能这样使用。 上面的方案可以避免这种情况。
gen_concatenate() 函数中出现过 yield from 语句,它将 yield 操作代理到父生成器上去。 语句 yield from it 简单的返回生成器 it 所产生的所有值。 关于这个我们在4.14小节会有更进一步的描述。
最后还有一点需要注意的是,管道方式并不是万能的。 有时候你想立即处理所有数据。 然而,即便是这种情况,使用生成器管道也可以将这类问题从逻辑上变为工作流的处理方式。
David Beazley 在他的 Generator Tricks for Systems Programmers 教程中对于这种技术有非常深入的讲解。可以参考这个教程获取更多的信息。
python 生成器(五):生成器实例(一)创建数据处理管道的更多相关文章
- python高级之生成器&迭代器
python高级之生成器&迭代器 本机内容 概念梳理 容器 可迭代对象 迭代器 for循环内部实现 生成器 1.概念梳理 容器(container):多个元素组织在一起的数据结构 可迭代对象( ...
- 【python】迭代器&生成器
源Link:http://www.cnblogs.com/huxi/archive/2011/07/01/2095931.html 迭代器 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素 ...
- 第三篇:python高级之生成器&迭代器
python高级之生成器&迭代器 python高级之生成器&迭代器 本机内容 概念梳理 容器 可迭代对象 迭代器 for循环内部实现 生成器 1.概念梳理 容器(container ...
- Python - 迭代器与生成器 - 第十三天
Python 迭代器与生成器 迭代器 迭代是Python最强大的功能之一,是访问集合元素的一种方式. 迭代器是一个可以记住遍历的位置的对象. 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问 ...
- Python三大器之生成器
Python三大器之生成器 生成器初识 什么是生成器 生成器本身属于迭代器.继承了迭代器的特性,惰性求值,占用内存空间极小. 为什么要有生成器 我们想使用迭代器本身惰性求值的特点创建出一个可以容纳百万 ...
- 『无为则无心』Python基础 — 63、Python中的生成器
目录 1.为什么要有生成器 2.创建生成器 (1)简单创建生成器 (2)生成器的使用 3.yield关键词 (1)yield关键词说明 (2)send()方法说明 4.使用yield实现斐波那契数列 ...
- python迭代器和生成器(3元运算,列表生成式,生成器表达式,生成器函数)
1.1迭代器 什么是迭代器: 迭代器是一个可以记住遍历的位置对象 迭代器对象从集合的第一个元素元素开始访问,直到所有元素被访问完结束,迭代器只能往前不会后退. 迭代器有两个基本方法:iter ,nex ...
- Python基础 (yield生成器)
如果在一个函数中使用了yield,那么这个函数实际上生成的是一个生成器函数 ,返回的是一个generator object.生成器是实现迭代的一种方式 特点: 其实返回的就是可以的迭代对象 和迭代的方 ...
- python 基础——generate生成器
通过列表表达式可以直接生成列表,不过列表一旦生成就需要为所有元素分配内存,有时候会很消耗资源. 所以,如果列表元素可以按照某种算法推算出来,这样就不必创建完整的list,从而节省大量的内存空间. 在P ...
随机推荐
- (二)用testng的groups管理用例
原文链接:https://www.cnblogs.com/Jourly/p/7002096.html 一.需求: 测试时经常有两种场景,第一种是冒烟测试的小部分用例:一类是全部用例. 二.针对第一种运 ...
- 【JMeter_12】JMeter逻辑控制器__包括控制器<Include Controller>
包括控制器<Include Controller> 业务逻辑: 通过Filename的路径和文件名引用外部的jmx文件,然后对齐执行,若外部文件中没有可执行范围内的脚本内容,则跳过该逻辑控 ...
- 06 . Jenkins分布式构建和Pipline
Pipline简介 pipline 是帮助 Jenkins 实现 CI 到 CD 转变的重要角色,是运行在 jenkins 2.X 版本的核心插件,简单来 说 Pipline 就是一套运行于 Jenk ...
- Latex文件本机能正常编译,但在另一台电脑不能编译的解决方法
问题:同样的文件在台式机能编译出正常的PDF文件,但发现在另一个电脑上不能编译出PDF文件. \documentclass[preprint,10pt,5p,times,twocolumn]{elsa ...
- docker部署dubbo怎么实现外部主机访问服务?
dubbo在分布式项目中太常见了,docker也是现在热门的项目,然而docker的网络配置也是非常麻烦的一件事情,这里给大家介绍一下dubbo实现跨服务器访问服务配置 docker-compose. ...
- Windows 安装RabbitMQ后,启动服务就自动停止
在做SpringCloud消息总线的时候,需要用到RabbitMQ,于是在windows上下载安装了一个,erlang的安装包不是官网下载的,而是朋友分享给我的,没注意它的版本(9.3). 安装完成后 ...
- Java | 静态嵌套类(Static Nested Class)
前言 本文内容主要来自 Java 官方教程中的<嵌套类>章节. 本文提供的是 JDK 14 的示例代码. 定义 静态嵌套类(Static Nested Class),是 Java 中对类的 ...
- VulnHub CengBox2靶机渗透
本文首发于微信公众号:VulnHub CengBox2靶机渗透,未经授权,禁止转载. 难度评级:☆☆☆☆官网地址:https://download.vulnhub.com/cengbox/CengB ...
- SpringBoot——项目启动时读取配置及初始化资源
介绍 在开发过程中,我们有时候会遇到非接口调用而出发程序执行任务的一些场景,比如我们使用quartz定时框架通过配置文件来启动定时任务时,或者一些初始化资源场景等触发的任务执行场景. 方法一:注解 ...
- Python3-算法-冒泡排序
冒泡排序 它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来,走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成,这个算法的名字由来是因为越大的元素 ...