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 ...
随机推荐
- Unknown command: crawl
Use "scrapy" to see available commands 1.使用命令行方式cmd,是因为没有cd到项目的根目录,crawl会去搜索cmd目录下的scrapy. ...
- java并发系列(六)-----Java并发:volatile关键字解析
在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性.可见性和有序性.只要有一条原则没有被保证,就有可能会导致程序运行不正确.volatile关键字 被用来保证可见性 ...
- mysql 主从复制 配置
mysql 的 默认配置文件在 /etc/my.cnf 1 修改主库 配置文件: 设置 服务id,并且开启二进制日志文件. server-id=1 log-bin=mysql-bin 2重启服务:se ...
- java-多线程安全-锁
一 同步函数 1.1 一般的方法 同步的另一种体现形式:同步函数. 同步函数使用的锁是哪个?经过分析:大概猜的是this,因为函数必须被对象调用. 验证:写一个同步代码块,写一个同步函数,如果同步代码 ...
- day38 07-Spring框架Bean的时候方式
Spring是自动帮我们创建对象的,有几种创建Bean的方式呢? 构造方法实例化:(默认无参数)其实就是反射new Instance(). 静态工厂实例化: 实例工厂实例化: 一般不会改变它实例化的方 ...
- 【洛谷】P1880 石子合并
P1880 石子合并 题目描述 在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分. 试设计出1个算法,计 ...
- 如何查看MySQL执行计划呢?
覆盖索引: MySQL可以利用索引返回select列表中的字段,而不必根据索引再次读取数据文件 包含所有满足查询需要的数据的索引称为 覆盖索引(Covering Index) 如果要使用覆盖索引,一定 ...
- tomcat标准化安装
操作系统说明: 操作系统 版本 linux red hat release 6.4 关键软件包说明: 软件包 版本 目录 运行用户 jdk-7u79-linux-x64.gz 1.7 /usr/loc ...
- Maven常用命令备忘
1. 修改版本号 mvn versions:set -DnewVersion=1.0.1-SNAPSHOT 2. <relativePath>的默认值是../pom.xml,如果没有配置, ...
- Django 配置MySQL数据库 mysql
Django 配置MySQL数据库 在settings.py中配置 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # ...