详解Python列表推导式

列表推导式,也叫列表解析式,英文名称为list comprehension,可以使用非常简洁的方式来快速生成满足特定需求的列表,代码具有非常强的可读性。另外,Python的内部实现对列表推导式做了大量优化,可以保证很快的运行速度。列表推导式的语法形式为:

[表达式 for 变量 in 序列或迭代对象 if 条件表达式]

列表推导式在逻辑上相当于一个循环,只是形式更加简洁,例如:

>>> aList = [x*x for x in range(10)]

相当于

>>> aList = []

>>> for x in range(10):

aList.append(x*x)

当然也等价于

>>> aList = list(map(lambda x: x*x, range(10)))

>>> freshfruit = [' banana', ' loganberry ', 'passion fruit ']

>>> aList = [w.strip() for w in freshfruit]

则等价于下面的代码

>>> freshfruit = [' banana', ' loganberry ', 'passion fruit ']

>>> aList = []

>>> for item in freshfruit:

aList.append(item.strip())

也等价于

>>> freshfruit = [' banana', ' loganberry ', 'passion fruit ']

>>> aList = list(map(lambda x: x.strip(), freshfruit))

>>> freshfruit = [' banana', ' loganberry ', 'passion fruit ']

>>> aList = list(map(str.strip, freshfruit))

大家应该看过一个故事,说是阿凡提(也有的说是阿基米德,这不是重点)与国王比赛下棋,国王说要是自己输了的话阿凡提想要什么他都可以拿得出来。阿凡提说那就要点米吧,棋盘一共64个小格子,在第一个格子里放1粒米,第二个格子里放2粒米,第三个格子里放4粒米,第四个格子里放8粒米,以此类推,后面每个格子里的米都是前一个格子里的2倍,一直把64个格子都放满。结果可想而知,最后国王没有办法拿出那么多米。那么到底需要多少粒米呢,其实使用列表推导式再结合内置函数sum()就很容易知道答案。

>>> sum([2**i for i in range(64)])

18446744073709551615

接下来再通过几个示例来进一步展示列表推导式的强大功能。

(1)使用列表推导式实现嵌套列表的平铺

>>> vec = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

>>> [num for elem in vec for num in elem]

[1, 2, 3, 4, 5, 6, 7, 8, 9]

在这个列表推导式中有2个循环,其中第一个循环可以看做是外循环,执行的慢;而第二个循环可以看做是内循环,执行的快。上面代码的执行过程等价于下面的写法:

>>> vec = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

>>> result = []

>>> for elem in vec:

for num in elem:

result.append(num)

>>> result

[1, 2, 3, 4, 5, 6, 7, 8, 9]

(2)过滤不符合条件的元素

在列表推导式中可以使用if子句来进行筛选,只在结果列表中保留符合条件的元素。例如下面的代码可以列出当前文件夹下所有Python源文件:

>>> import os

>>> [filename for filename in os.listdir('.') if filename.endswith('.py')]

下面的代码用于从列表中选择符合条件的元素组成新的列表:

>>> aList = [-1, -4, 6, 7.5, -2.3, 9, -11]

>>> [i for i in aList if i>0]

[6, 7.5, 9]

下面的代码使用列表推导式查找列表中最大元素的位置:

>>> from random import randint

>>> x = [randint(1, 10) for i in range(20)]

>>> m = max(x)

>>> m

10

>>> [index for index, value in enumerate(x) if value == m]

[0, 5, 6, 10]

>>> x

[10, 2, 3, 4, 5, 10, 10, 9, 2, 4, 10, 8, 2, 2, 9, 7, 6, 2, 5, 6]

(3)在列表推导式中使用多个循环,实现多序列元素的任意组合,并且可以结合条件语句过滤特定元素

>>> [(x, y) for x in [1, 2, 3] for y in [3, 1, 4] if x != y]

[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

注意:对于包含多个循环的列表推导式,一定要清楚多个循环的执行顺序或“嵌套关系”。例如,上面的代码等价于

>>> result = []

>>> for x in [1, 2, 3]:

for y in [3, 1, 4]:

if x != y:

result.append((x,y))

>>> result

[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

(4)使用列表推导式实现矩阵转置

>>> matrix = [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]

>>> [[row[i] for row in matrix] for i in range(4)]

[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

或者,也可以使用内置函数zip()和list()来实现矩阵转置:

>>> list(map(list,zip(*matrix)))

[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

注意:对于嵌套了列表推导式的列表推导式,一定要清楚其执行顺序。例如,上面列表推导式的执行过程等价于下面的代码,可以看出,使用列表推导式更加简洁,代码可读性更强。

>>> matrix = [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]

>>> result = []

>>> for i in range(len(matrix)):

temp = []

for row in matrix:

temp.append(row[i])

result.append(temp)

>>> result

[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

(5)列表推导式中可以使用函数或复杂表达式

>>> def f(v):

if v%2 == 0:

v = v**2

else:

v = v+1

return v

>>> print([f(v) for v in [2, 3, 4, -1] if v>0])

[4, 4, 16]

>>> print([v**2 if v%2 == 0 else v+1 for v in [2, 3, 4, -1] if v>0])

[4, 4, 16]

(6)列表推导式支持文件对象迭代

>>> fp = open('C:\install.log', 'r')

>>> print([line for line in fp])

>>> fp.close()

(7)使用列表推导式生成100以内的所有素数

>>> [ p for p in range(2, 100) if 0 not in [ p% d for d in range(2, int(p**0.5)+1)] ]

[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

本文节选自(内容略有改动)《Python程序设计》(第2版),董付国编著,清华大学出版社,2016年6月出版,2016年11月第二次印刷。

原文发布于微信公众号 - Python小屋(Python_xiaowu)

原文发表时间:2016-11-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏CVer

刷题笔记 | 剑指Offer 03 二维数组中的查找

本文主要讲解《剑指Offer》中第03题"二维数组中的查找",介绍题目、解决思路、解题步骤,并分别以C++和Python编程语言解答此题。

1473
来自专栏desperate633

LintCode 最小调整代价题目分析代码

给一个整数数组,调整每个数的大小,使得相邻的两个数的差小于一个给定的整数target,调整每个数的代价为调整前后的差的绝对值,求调整代价之和最小是多少。

831
来自专栏desperate633

LintCode 搜索插入位置题目分析代码

给定一个排序数组和一个目标值,如果在数组中找到目标值则返回索引。如果没有,返回到它将会被按顺序插入的位置。 你可以假设在数组中无重复元素。

752
来自专栏杂七杂八

numpy.nonzero()函数

官方文档如下: numpy.nonzero(a) Return the indices of the elements that are non-zero....

2353
来自专栏机器之心

入门 | 数据科学初学者必知的NumPy基础知识

选自TowardsDataScience 作者:Ehi Aigiomawu 机器之心编译 参与:李诗萌、路 本文介绍了一些 NumPy 基础知识,适合数据科学初...

2773
来自专栏C语言及其他语言

【每日一题】问题 1472: 矩阵乘法

关注我们 题目描述 给定一个N阶矩阵A,输出A的M次幂(M是非负整数) 例如: A = 1 2 3 4 A的2次幂 7 10 ...

31110
来自专栏Python小屋

Python花式编程案例锦集(3)

严格来说,本文的2个代码不算花式编程,在Python中就应该是这样写。 1、生成包含20个随机数的列表,然后删除其中的所有奇数。 from random imp...

35313
来自专栏和蔼的张星的图像处理专栏

138. 子数组之和 map存储加规律

给定一个整数数组,找到和为零的子数组。你的代码应该返回满足要求的子数组的起始位置和结束位置。 假定一定存在这样的字数组。 样例 给出 [-3, 1, 2,...

1151
来自专栏尾尾部落

[剑指offer] 调整数组顺序使奇数位于偶数前面

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相...

962
来自专栏C/C++基础

C++11新特性——range for

很多编程语言都有range for语法功能,自C++11起,终于将这个重要功能加入C++标准中。range for语句,可以方便的遍历给定序列中的每个元素并对其...

1012

扫码关注云+社区

领取腾讯云代金券