理解嵌套列表推导式

75 投票
2 回答
81436 浏览
提问于 2025-04-17 05:51

我想了解嵌套列表推导式。下面,我列出了一个列表推导式的表达式以及它对应的for循环写法。
我想知道我对这些理解是否正确。

举个例子,

[(min([row[i] for row in rows]),max([row[i] for row in rows])) 
for i in range(len(rows[0]))]

等价于

result=[]
for i in range(len(rows[0])):
  innerResult=[]
  for row in rows:
    innerResult.append(row[i])
  innerResult2=[]
  for row in rows:
    innerResult2.append(row[i])
  tuple=(min(innerResult), max(innerResult2))
  result.append(tuple)

如果我可以概括一下,我猜

[exp2([exp1 for x in xSet]) for y in ySet]

这种形式可以翻译成以下内容。(我希望我说得对)

result=[]
for y in ySet:
  innerResult =[]
  for x in xSet:
    innerResult.append(exp1)
  exp2Result = exp2(innerResult)
  result.append(exp2Result)

对于更简单的情况,

[exp1 for x in xSet for y in ySet] 

等于

result=[] 
for x in xSet:
  for y in ySet: 
    result.append(exp1)

而且,

[[exp1 for x in xSet] for y in ySet]

等于

result=[]
for y in ySet:
  innerResult=[]
  for x in xSet:
    innerResult.append(exp1)
  result.append(innerResult)

我在关于复杂列表推导式的等效for循环表达式上问过类似的问题。
那里的回答在理解内部工作原理后重构了这种形式。
我想系统地了解它是如何运作的,这样我就可以把这个概念应用到其他稍微不同的例子中。

2 个回答

78

没错,你说得对。这在Python语言参考的表达式部分中有详细说明。

特别要注意的是,在一个列表推导式中,多个for的嵌套顺序总是从左到右:

>>> matrix = [[1, 2], [3, 4]]
>>> [item for item in row for row in matrix] # oops!
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    [item for item in row for row in matrix]
NameError: name 'row' is not defined
>>> [item for row in matrix for item in row] # nesting is in left-to-right order
[1, 2, 3, 4]
75

简单来说:是的,你的理解是正确的

不过有一点需要注意:你通常在Python代码中使用嵌套列表推导式的方式是为了处理多维数组。

一个典型的例子就是处理矩阵:

>>> matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> [[el - 1 for el in row] for row in matrix]
[[0, 1, 2], [3, 4, 5], [6, 7, 8]]

如你所见,这种“嵌套”是通过操作矩阵的每个维度来实现的。

在你提供的例子中,ySet(这个名字不太好,因为集合是Python提供的一种类型)只是一个通用的计数器,这让理解背后的逻辑变得有些困难。

至于你的第一个例子:

>>> rows = ([1, 2, 3], [10, 20, 30])
>>> [(min([row[i] for row in rows]),max([row[i] for row in rows])) for i in range(len(rows[0]))]
[(1, 10), (2, 20), (3, 30)]

你可能想看看zip这个内置函数:

>>> zip(rows[0], rows[1])
[(1, 10), (2, 20), (3, 30)]

或者为了更简洁和优雅:

>>> zip(*rows)
[(1, 10), (2, 20), (3, 30)]

希望对你有帮助!

撰写回答