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 ...
随机推荐
- bzoj 2662 [BeiJing wc2012]冻结——分层图
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2662 这种的都是分层图. #include<iostream> #include ...
- GitBook的使用方法
---恢复内容开始--- 由于近期工作中使用gitbook编写讲义,现把出现的问题总结下: 1 . gitbook的安装 Gitbook与word等办公软件类似,能够编写文档,Gitbook中编写文档 ...
- json字符串和对象的相互转换
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,是理想的数据交换格式. 同时,JSON是 JavaScript 原生格式,这 ...
- mysql日常sql
重置表 truncate table david_account; 触发器 /* 商户资料更新时更新终端 */ DELIMITER | CREATE TRIGGER bankChange AFTER ...
- JS简单实现:根据奖品权重计算中奖概率实现抽奖的方法
本文主要介绍:使用 JS 根据奖品权重计算中奖概率实现抽奖的方法. 一.示例场景 1.1.设置抽奖活动的奖项名称 奖项名称:["一等奖", "二等奖", &qu ...
- jnhs-java实体类的有参构造器 无参构造器Could not instantiate bean class 实体类No default constructor found
new一个对象的时候要用到构造函数, 例如Hello hello = new Hello();这时调用的是Hello的无参数构造方法; Hello hello = new Hello("hi ...
- MyBatis连接Neo4j问题记录:mapper参数传递(节点标签作为参数)
MyBatis与Neo4j的连接我在上一篇做了,这是链接:https://blog.csdn.net/qq_34233510/article/details/82496101 上一篇中UserMapp ...
- 2019.9.29 csp-s模拟测试55 反思总结
不咕咕咕是一种美德[大雾] 头一次体会到爆肝写题解??? 这次考试我们没赶上,是后来掐着时间每个人自己考的.我最后的分数能拿到152…熟悉的一题AC两题爆炸. 强烈吐槽出题人起名走心 T1联: 发现每 ...
- 助力深度学习!阿里开源可插拔 GPU 共享调度工具
根据 Gartner 对全球 CIO 的调查结果显示,人工智能将成为 2019 年组织革命的颠覆性力量.对于人工智能来说,算力即正义,成本即能力,利用 Docker 和 Kubernetes 代表云原 ...
- Django项目:CRM(客户关系管理系统)--12--05PerfectCRM实现King_admin注册功能获取内存01
#base_admin.py #Django admin 注册功能的形式 # sites = { # 'crm':{ # 'customers':CustomerAdmin, # 'customerf ...