Blog

Python 列表推导式

Python 列表推导式的语法、过滤、嵌套,字典推导式和集合推导式的写法,以及什么时候该改用生成器表达式。

推导式用一个表达式就能从循环里构建出列表。它不只是更短,也比在循环里调用 append 更快,因为追加元素这一步是在 C 里完成的。

各部分的顺序永远不变:先是表达式,然后是 for,最后是可选的 if

循环和推导式

下面两种写法得到的列表完全一样。

# the loop and the comprehension do the same thing
squares = []
for n in range(6):
    squares.append(n * n)
print(squares)

print([n * n for n in range(6)])

输出:

[0, 1, 4, 9, 16, 25]
[0, 1, 4, 9, 16, 25]

过滤

末尾加一个 if,只留下你想要的元素。

# filtering with if
print([n for n in range(20) if n % 3 == 0])

输出:

[0, 3, 6, 9, 12, 15, 18]

选择值

if/else 是表达式的一部分,所以要放在前面。很多人在这里栽跟头:两种写法长得像,位置却不同。

# if/else goes before the for
print(['even' if n % 2 == 0 else 'odd' for n in range(5)])

输出:

['even', 'odd', 'even', 'odd', 'even']

记住一条:末尾的 if 负责过滤,前面的 if/else 负责选值。

字典推导式和集合推导式

用花括号时,写键值对得到字典,写单个值得到集合。

# dict and set comprehensions
words = ['apple', 'bread', 'apple', 'milk']
print({w: len(w) for w in words})
print({len(w) for w in words})

输出:

{'apple': 5, 'bread': 5, 'milk': 4}
{4, 5}

集合版本去掉了重复的长度,集合本来就是这样。

两个 for 子句

多个 for 子句从左往右读,顺序和写嵌套循环时一样。展平嵌套列表就是这么写的。

# two for clauses read top to bottom
pairs = [(x, y) for x in 'ab' for y in (1, 2)]
print(pairs)

grid = [[1, 2], [3, 4], [5, 6]]
print([cell for row in grid for cell in row])

输出:

[('a', 1), ('a', 2), ('b', 1), ('b', 2)]
[1, 2, 3, 4, 5, 6]

生成器表达式

换成圆括号,得到的就是生成器。它按需产生值,不会把整个列表放在内存里。

# a generator expression does not build the list
import sys

listcomp = [n * n for n in range(100000)]
genexp = (n * n for n in range(100000))
print('list bytes:', sys.getsizeof(listcomp))
print('generator bytes:', sys.getsizeof(genexp))
print('sum is the same:', sum(listcomp) == sum(genexp))

输出:

list bytes: 800984
generator bytes: 200
sum is the same: True

结果一样,内存却少了四千倍。如果结果直接交给只消费一次的东西,比如 sumany 或者循环,就用生成器。

循环变量不会跑出来

在 Python 3 里,推导式有自己的作用域,不会覆盖你之前已有的变量。

# the loop variable does not leak
n = 'untouched'
result = [n for n in range(3)]
print(result, n)

输出:

[0, 1, 2] untouched

要点

  • 顺序是:表达式、for、然后 if
  • 末尾的 if 负责过滤。if/else 写在 for 前面。
  • 花括号构建字典和集合,圆括号构建生成器。
  • 循环变量不会泄漏到外层作用域。

如果推导式需要两个以上的子句,或者一行写不下,就老老实实写循环。一旦得靠解读才能看懂,推导式就不划算了。

这篇文章对你有帮助吗?

点一颗爱心来评分!

平均评分 0 / 5. 投票总数: 0

还没有人投票。来做第一个评分的人吧。