Python基础:08列表解析与生成器表达式
一:列表解析
列表解析(List comprehensions)来自函数式编程语言Haskell 。它可以用来动态地创建列表。它在 Python 2.0 中被加入。
列表解析的语法: [expr for iter_var in iterable]
这个语句的核心是 for 循环,它迭代 iterable 对象的所有条目。前边的 expr 应用于序列的每个成员,最后的结果值是该表达式产生的列表。
比如一个计算序列成员的平方的 lambda 函数表达式:
>>> map(lambda x: x ** 2, range(6))
[0,1,4,9,16,25]
可以使用下面这样的列表解析来替换它:
>>> [x ** 2 for x in range(6)]
[0,1,4,9,16,25]
在新语句中,只有一次函数调用( range() ),而先前的语句中有三次函数调用(range() ,map() ,以及 lambda )。列表解析的表达式的效率更高。
结合if语句,列表解析还提供了一个扩展版本的语法:[expr for iter_var in iterable if cond_expr]
这个语法在迭代时会过滤/捕获满足条件表达式 cond_expr 的序列成员。
比如用于判断一个数值对象是奇数还是偶数的函数(奇数返回 1 ,偶数返回 0 ):
def odd(n):
return n % 2
可以使用filter() 和 lambda 挑选出序列中的奇数:
>>> seq = [11, 10, 9, 9, 10, 10, 9,8, 23, 9, 7, 18, 12, 11, 12]
>>> filter(lambda x: x % 2, seq)
[11,9,9,9,23,9,7,11]
也可以使用列表解析来完成操作,获得想要的数字:
>>> [x for x in seq if x % 2]
[11,9,9,9,23,9,7,11]
更多的例子:
迭代一个有三行五列的矩阵么:
>>> [(x+1, y+1) for x in range(3) for y in range(5)]
[(1,1),(1,2),(1,3),(1,4),(1,5),(2,1),(2,2),(2,
3),(2,4),(2,5),(3,1),(3,2),(3,3),(3,4),(3,5)]
计算纯英文文本文件中,所有非空白字符的数目:
可以像这样计算单词个数:
>>> f = open('hhga.txt’, 'r')
>>> len([word for line in f for word in line.split()])
91
可以把每个单词的长度加起来,得到和:
>>> f.seek(0)
>>> sum([len(word) for line in f for word in line.split()])
408
一个清晰明了的列表解析完成了之前需要许多行代码才能完成的工作! 如你所见,列表解析支持多重嵌套for 循环以及多个 if 子句。完整的语法可以在官方文档中找到。也可以在 PEP 202 中找到更多关于列表解析的资料。
二:生成器表达式
生成器表达式是列表解析的一个扩展。
生成器是在 Python 版本 2.2 时加入的一个重要特性。生成器是特定的函数,允许你返回一个值,然后"暂停"代码的执行,稍后恢复。
列表解析的一个不足就是必须生成所有的数据,用以创建整个列表。这在处理有大量数据的迭代器时有负面效应。生成器表达式通过结合列表解析和生成器解决了这个问题。
生成器表达式在 Python 2.4 被引入,它与列表解析非常相似,而且它们的基本语法基本相同。不过它并不真正创建数字列表,而是返回一个生成器。这个生成器在每次计算出一个条目后,把这个条目“产生”(yield)出来。生成器表达式使用了"延迟计算"(lazy evaluation),所以它在使用内存上更有效。
来看看它和列表解析到底有多相似:
列表解析:[expr for iter_var in iterable if cond_expr]
生成器表达式:(expr for iter_var in iterable if cond_expr)
在前边列表解析一节,计算文本文件中非空白字符总和。最后的代码中,我们展示了如何使用一行列表解析代码做所有的事。但是如果这个文件的大小变得很大,那么这行代码的内存性能会很低,因为我们要创建一个很长的列表用于存放单词的长度。
为了避免创建庞大的列表,可以使用生成器表达式来完成求和操作。它会计算每个单词的长度然后传递给 sum() 函数(它的参数不仅可以是列表,还可以是可迭代对象,比如生成器表达式)。这样,可以得到优化后的代码(代码长度,还有执行效率都很高效):
>>> sum(len(word) for line in data for word in line.split())
408
生成器表达式就好像是懒惰的列表解析(这反而成了它主要的优势)。它还可以用来处理其他列表或生成器,例如这里的 rows 和 cols:
rows = [1,2,3,17] def cols(): # example of simple generator
yield 56
yield 2
yield 1
不需要创建新的列表,直接就可以创建配对。可以使用下面的生成器表达式:
x_product_pairs = ((i, j) for i in rows for j in cols())
现在我们可以循环 x_product_pairs ,它会懒惰地循环 rows 和 cols:
>>> for pair in x_product_pairs:
...print pair
... (1,56)
(1,2)
(1,1)
(2,56)
(2,2)
(2,1)
(3,56)
(3,2)
(3,1)
(17,56)
(17,2)
(17,1)
在举一个冗长的样例,从它可以感觉到 Python 代码在这些年来的变化,看看如何改进代码。一个寻找文件最长的行的例子来,在以前,我们这样读取文件:
f = open('/etc/motd', 'r')
longest = 0
while True:
linelen = len(f.readline().strip())
if not linelen:
break
if linelen > longest:
longest = linelen
f.close()
return longest
事实上,这还不够老。真正的旧版本 Python 代码中,布尔常量应该写是整数 1 ,而且应该使用 string 模块而不是字符串的 strip() 方法:
import string
...
len(string.strip(f.readline()))
从那时起,我们认识到如果读取了所有的行,那么应该尽早释放文件资源。如果这是一个很多进程都要用到的日志文件,那么理所当然我们不能一直拿着它的句柄不释放。
所以读取文件的行的首选方法应该是这样:
f = open('/etc/motd', 'r')
longest = 0
allLines = f.readlines()
f.close()
for line in allLines:
linelen = len(line.strip())
if linelen > longest:
longest = linelen
return longest
列表解析允许我们稍微简化我们代码,而且我们可以在得到行的集合前做一定的处理。
f = open('/etc/motd', 'r')
longest = 0
allLines = [x.strip() for x in f.readlines()]
f.close()
for line in allLines:
linelen = len(line)
if linelen > longest:
longest = linelen
return longest
前两个例子在处理大文件时候都有问题,因为 readlines() 会读取文件的所有行。后来有了迭代器,文件本身就成为了它自己的迭代器,不需要调用 readlines() 函数。
我们已经做到了这一步,为什么不去直接获得行长度的集合呢(之前我们得到的是行的集合)? 这样,我们就可以使用 max() 内建函数得到最长的字符串长度:
f = open('/etc/motd', 'r')
allLineLens = [len(x.strip()) for x in f]
f.close()
return max(allLineLens)
这里唯一的问题就是你一行一行迭代 f 的时候,列表解析需要文件的所有行读取到内存中,然后生成列表。可以进一步简化代码:使用生成器表达式替换列表解析,然后把它移到 max()函数里,这样,所有的核心部分只有一行:
f = open('/etc/motd' , 'r')
longest = max(len(x.strip()) for x in f)
f.close()
return longest
最后,我们可以去掉文件打开模式(默认为读取),然后让 Python 去处理打开的文件。当然,文件用于写入的时候不能这么做:
return max(len(x.strip()) for x in open('/etc/motd'))
注意,即便是这只有一行的 Python 程序也不是很晦涩。可以在 PEP 289 中找到更多生成器表达式相关内容。
Python基础:08列表解析与生成器表达式的更多相关文章
- Python中的列表解析和生成器表达式
Python中的列表解析和生成器表达式 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.列表解析案例 #!/usr/bin/env python #_*_coding:utf-8 ...
- Python基础(9)三元表达式、列表解析、生成器表达式
一.三元表达式 三元运算,是对简单的条件语句的缩写. # if条件语句 if x > f: print(x) else: print(y) # 条件成立左边,不成立右边 x if x > ...
- Python全栈day18(三元运算,列表解析,生成器表达式)
一,什么是生成器 可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他数据类型需要调用自己内置的__iter__方法),所以生成器是可迭代对象. 二,生成器分类在python中的表现形式 1 ...
- python的迭代器、生成器、三元运算、列表解析、生成器表达式
一 迭代的概念 迭代是Python最强大的功能之一,是访问集合元素的一种方式. 迭代器是一个可以记住遍历的位置的对象. 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前 ...
- python 中的列表解析和生成表达式 - 转
优雅.清晰和务实都是python的核心价值观,如果想通过操作和处理一个序列(或其他的可迭代对象)来创建一个新的列表时可以使用列表解析( List comprehensions)和生成表达式,通过这两 ...
- Day4 闭包、装饰器decorator、迭代器与生成器、面向过程编程、三元表达式、列表解析与生成器表达式、序列化与反序列化
一.装饰器 一.装饰器的知识储备 1.可变长参数 :*args和**kwargs def index(name,age): print(name,age) def wrapper(*args,**k ...
- 闭包、装饰器decorator、迭代器与生成器、面向过程编程、三元表达式、列表解析与生成器表达式
一.装饰器 一.装饰器的知识储备 不想修改函数的调用方式,但是还想在原来的函数前后添加功能 1.可变长参数 :*args和**kwargs def index(name,age): print(na ...
- Python 迭代器之列表解析与生成器
 [TOC] 1. 列表解析 1.1 列表解析基础 列表解析把任意一个表达式应用到一个迭代对象中的元素 Python内置ord函数会返回一个字符的ASCII整数编码(chr函数是它的逆过程, 它将A ...
- 3、Python迭代器、列表解析及生成器(0530)
1.动态语言 sys.getrefcount() //查看对象的引用计数 增加对象的引用计数场景 对象创建时:以赋值的方式,创建变量名的同时就会创建变量 将对象添加进容器时:类似list.app ...
随机推荐
- laravel--laravel的重定向类Redirector
laravel的重定向类Redirector 在laravel5中,重定向类可以直接通过redirect()方法直接获取,不需要声明,有几个常用的方法: redirect() -> to( “重 ...
- H5C3--transform实现任何元素居中对齐
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 面试Nginx的几个常见问题(
1.Nginx 服务器上的 Master 和 Worker 进程分别是什么 Master 进程:读取及评估配置和维持 Worker 进程:处理请求 2.怎么添加模块? Firstly, you h ...
- 手残,盘符前边多打一个空格导致的message d:\WEB_APP_QuChongFu\file\五月.xlsx (文件名、目录名或卷标语法不正确。)
尝试读取并解析一个excel文件,一直提示错误 但是有个原始数据,导入就没问题 对比了一下,好像也就是字母d的大小写有区别 我先把大写的D改成小写的试试,如果是大小写问题,那应该抛出异常 好吧,好像并 ...
- Oracle删除当前用户下所有的表的方法1
1.如果有plsql客户端,则可以使用该用户登录,选中所有表 右键Drop即可. 2.如果有删除用户的权限,则可以: ? 1 drop user user_name cascade; 加了cascad ...
- iOS播放器横竖屏切换
http://www.cocoachina.com/cms/wap.php?action=article&id=20292 http://feihu.me/blog/2015/how-to-h ...
- 【洛谷】P1880 石子合并
P1880 石子合并 题目描述 在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分. 试设计出1个算法,计 ...
- Leetcode665.Non-decreasing Array非递减数组
给定一个长度为 n 的整数数组,你的任务是判断在最多改变 1 个元素的情况下,该数组能否变成一个非递减数列. 我们是这样定义一个非递减数列的: 对于数组中所有的 i (1 <= i < n ...
- Etag 和 If-None-Match
ETag是HTTP1.1中才加入的一个属性,用来帮助服务器控制Web端的缓存验证. 它的原理是这样的,当浏览器请求服务器的某项资源(A)时, 服务器根据A算出一个哈希值(3f ...
- Linux操作系统各版本ISO镜像下载(包括oracle linux\redhat\centos\u
Linux操作系统各版本ISO镜像下载(包括oracle linux\redhat\centos\ubuntu\debian等) 1.Oracle Linux(下载地址) (1)OracleLinux ...