“列表推导”和类似是什么意思?它是如何工作的,我该如何使用它?

What does "list comprehension" and similar mean? How does it work and how can I use it?

提问人:Remi Guan 提问时间:1/17/2016 最后编辑:MisterMiyagiRemi Guan 更新时间:1/27/2022 访问量:13886

问:

我有以下代码:

[x ** 2 for x in range(10)]

当我在 Python shell 中运行它时,它会返回:

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

我搜索了一下,这似乎被称为列表推导,同样似乎有设置/字典导和生成器表达式。但它是如何工作的呢?

python 列表 列表推导

评论

2赞 Karl Knechtel 7/14/2022
注意 对于重复关闭器:对于专门询问如何编写列表理解的问题,还可以考虑映射 stackoverflow.com/questions/25082410 和过滤 stackoverflow.com/questions/4587915
2赞 Karl Knechtel 8/3/2022
另请参阅 stackoverflow.com/questions/18072759,了解有关(显式)嵌套列表推导式和具有多个子句的列表推导式的问题。
0赞 Peter Mortensen 12/15/2022
当然,2008 年一定有一个规范问题(?),
0赞 Peter Mortensen 12/15/2022
相关:列表推导与 for 循环相比有什么优势?、列表推导与映射、创建带有推导的字典为什么在 Python 中这样称呼列表推导?和理解列表推导的语法

答:

115赞 Remi Guan 1/17/2016 #1

文档中

列表推导式提供了一种创建列表的简洁方法。常见的应用是创建新列表,其中每个元素都是应用于另一个序列或可迭代对象的每个成员的某些操作的结果,或者创建满足特定条件的这些元素的子序列。


关于您的问题,列表推导与以下“普通”Python 代码的作用相同:

>>> l = [] 
>>> for x in range(10):
...     l.append(x**2)
>>> l
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

你怎么用一行写?嗯。。。我们可以...可能。。。将 map()lambda 结合使用:

>>> list(map(lambda x: x**2, range(10)))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

但是,只使用列表理解不是更清晰、更简单吗?

>>> [x**2 for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

基本上,我们可以用 .不但。例如,运行以下方法:xx**2x

>>> [x.strip() for x in ('foo\n', 'bar\n', 'baz\n')]
['foo', 'bar', 'baz']

或者用作另一个函数的参数:x

>>> [int(x) for x in ('1', '2', '3')]
[1, 2, 3]

例如,我们也可以用作对象的键。我看看:xdict

>>> d = {'foo': '10', 'bar': '20', 'baz': '30'}
>>> [d[x] for x in ['foo', 'baz']]
['10', '30']

组合怎么样?

>>> d = {'foo': '10', 'bar': '20', 'baz': '30'}
>>> [int(d[x].rstrip('0')) for x in ['foo', 'baz']]
[1, 3]

等等。


您还可以在列表推导式中使用 或。例如,您只需要 中的奇数。您可以执行以下操作:ifif...elserange(10)

>>> l = []
>>> for x in range(10):
...     if x%2:
...         l.append(x)
>>> l
[1, 3, 5, 7, 9]

啊,这太复杂了。以下版本呢?

>>> [x for x in range(10) if x%2]
[1, 3, 5, 7, 9]

要使用三元表达式,您需要将 after ,而不是 after :if...elseif ... else ...xrange(10)

>>> [i if i%2 != 0 else None for i in range(10)]
[None, 1, None, 3, None, 5, None, 7, None, 9]

你听说过嵌套列表推导吗?您可以在一个列表推导中放置两个或多个 fors。例如:

>>> [i for x in [[1, 2, 3], [4, 5, 6]] for i in x]
[1, 2, 3, 4, 5, 6]

>>> [j for x in [[[1, 2], [3]], [[4, 5], [6]]] for i in x for j in i]
[1, 2, 3, 4, 5, 6]

让我们谈谈第一部分,它给出了 和 .然后,给出 、 和 、 、 。for x in [[1, 2, 3], [4, 5, 6]][1, 2, 3][4, 5, 6]for i in x123456

警告:你总是需要放在for x in [[1, 2, 3], [4, 5, 6]]for i in x

>>> [j for j in x for x in [[1, 2, 3], [4, 5, 6]]]
Traceback (most recent call last):
  File "<input>", line 1, in <module>
NameError: name 'x' is not defined

我们还有设置推导字典推导生成器表达式

集合推导式和列表推导式基本相同,但前者返回的是集合而不是列表

>>> {x for x in [1, 1, 2, 3, 3, 1]}
{1, 2, 3}

它与以下内容相同:

>>> set([i for i in [1, 1, 2, 3, 3, 1]])
{1, 2, 3}

字典推导看起来像一个集合推导,但它使用 或 代替 .{key: value for key, value in ...}{i: i for i in ...}{i for i in ...}

例如:

>>> {i: i**2 for i in range(5)}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

它等于:

>>> d = {}
>>> for i in range(5):
...     d[i] = i**2
>>> d
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

是否给出元组?不!,这是一个生成器表达式。这将返回一个生成器(i for i in range(5))

>>> (i for i in range(5))
<generator object <genexpr> at 0x7f52703fbca8>

它与以下内容相同:

>>> def gen():
...     for i in range(5):
...         yield i
>>> gen()
<generator object gen at 0x7f5270380db0>

您可以将其用作生成器:

>>> gen = (i for i in range(5))
>>> next(gen)
0
>>> next(gen)
1
>>> list(gen)
[2, 3, 4]
>>> next(gen)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
StopIteration

注意:如果在函数中使用列表推导,则不需要 if 该函数可以循环生成器。例如,sum()[]

>>> sum(i**2 for i in range(5))
30

相关(关于生成器):了解 Python 中的生成器

评论

0赞 AChampion 1/17/2016
三元表达式实际上与列表推导式没有任何特别的关系 - 任何有效的表达式都可以在列表压缩中使用 - 三元表达式是许多 python 表达式之一。x if cond else y
5赞 Remi Guan 1/17/2016
@AChampion:是的,我在回答中提到了这一点,因为我在学习列表推导时尝试过,但它不起作用。经过一番研究,我明白我必须改用。所以我认为,如果我在这里提到它,那么其他人可以避免我之前遇到的问题。[i for i in x if i else y][i if i else y for i in x]
8赞 cdlane 1/22/2016 #2

有列表、字典和集合推导式,但没有元组推导式(尽管确实探索了“生成器表达式”)。

它们解决了 Python 中的传统循环是语句(不返回任何内容)而不是返回值的表达式的问题。

它们不是所有问题的解决方案,可以重写为传统循环。当需要在迭代之间维护和更新状态时,它们会变得尴尬。

它们通常包括:

[<output expr> <loop expr <input expr>> <optional predicate expr>]

但可以以许多有趣和奇怪的方式扭曲。

它们可以类似于 Python 中仍然存在并继续使用的传统和操作。map()filter()

如果做得好,他们的满意度很高。

评论

6赞 Sohaib Farooqi 1/9/2018
这让我的一天:When done well, they have a high satisfaction quotient.
6赞 Dave Rove 9/11/2018 #3

如果您更喜欢一种更直观的方式来弄清楚正在发生的事情,那么这可能会有所帮助:

# for the example in the question...

y = []
for x in range(10):
    y += [x**2]

# is equivalent to...

y = [x**2 for x in range(10)]

# for a slightly more complex example, it is useful
# to visualize  where the various x's end up...

a = [1,2,3,4]
b = [3,4,5,6]
c = []

for x in a:
          if x in b:
                  c += [x]
#   \         \        /
#    \    _____\______/
#     \  /      \
#      \/        \
#      /\         \
#     /  \         \
#    /    \         \
c = [x for x in a if x in b]

print(c)

...产生输出[3, 4]

6赞 Matt Messersmith 9/21/2018 #4

我最近看到很多关于列表推导如何工作的困惑(关于其他 SO 问题和同事)。一点点数学教育可以帮助理解为什么语法是这样的,以及列表推导的真正含义。

语法

最好将列表推导式视为集合/集合上的谓词,就像我们在数学中使用集合构建器表示法一样。这个符号对我来说实际上感觉很自然,因为我拥有数学本科学位。但是忘了我,Guido van Rossum(Python 的发明者)拥有数学硕士学位并具有数学背景。

设置构建器符号速成课程

以下是集合生成器表示法如何工作的(非常基本的知识):

enter image description here

因此,这个集合构建器表示法表示严格为正数的一组数字(即 )。[1,2,3,4,...]

混淆点

1)集合构建器表示法中的谓词过滤器仅指定我们要保留哪些项,而列表推导谓词也做同样的事情。您不必包含省略项的特殊逻辑,除非谓词包含,否则它们将被省略。空谓词(即末尾没有条件)包括给定集合中的所有项。

2)集合构建器表示法中的谓词过滤器在末尾,在列表推导式中也是如此。 (一些) 初学者认为类似的东西会给他们列表,而实际上它输出。我们之所以得到输出,是因为我们要求 Python 评估 中的所有项目。没有谓词意味着我们从集合中获取所有内容(就像在集合构建器表示法中一样)。[x < 5 for x in range(10)][0,1,2,3,4][True, True, True, True, True, False, False, False, False, False][True, True, True, True, True, False, False, False, False, False]x < 5range(10)

如果您在使用列表推导式时将 set builder 符号牢记在心,它们会更容易理解。

呵呵!

2赞 lmiguelvargasf 9/12/2019 #5

介绍

列表推导式是在 Python 中创建列表的一种高级声明性方法。理解的主要好处是可读性和可维护性。很多人发现它们非常可读,即使是以前从未见过它们的开发人员通常也可以正确猜出它的含义。

# Snippet 1
squares = [n ** 2 for n in range(5)]

# Snippet 2
squares = []
for n in range(5):
    squares.append(n ** 2)

两个代码片段将生成等于 。squares[0, 1, 4, 9, 16]

请注意,在第一个代码段中,您键入的内容是声明所需的列表类型,而第二个代码段是指定如何创建它。这就是为什么理解是高级的和陈述性的。

语法

[EXPRESSION for VARIABLE in SEQUENCE]

EXPRESSION是任何 Python 表达式,但通常其中包含一些变量。此变量在字段中说明。 定义变量枚举的值的来源。VARIABLESEQUENCE

考虑片段 1, :[n ** 2 for n in range(5)]

  • EXPRESSIONn ** 2
  • VARIABLEn
  • SEQUENCErange(5)

请注意,如果您检查类型,您将得到列表推导式只是一个常规列表:squares

>>> type(squares)
<class 'list'>

更多关于 EXPRESSION

表达式可以是任何简化为值的内容:

  • 算术表达式,例如n ** 2 + 3 * n + 1
  • 函数调用,如使用 as 变量f(n)n
  • 切片操作,如s[::-1]
  • 方法调用bar.foo()
  • ...

一些例子:

>>> [2 * x + 3 for x in range(5)]
[3, 5, 7, 9, 11]
>>> [abs(num) for num in range(-5, 5)]
[5, 4, 3, 2, 1, 0, 1, 2, 3, 4]
>>> animals = ['dog', 'cat', 'lion', 'tiger']
>>> [animal.upper() for animal in animals]
['DOG', 'CAT', 'LION', 'TIGER']

滤波:

最终列表中元素的顺序由 的顺序决定。但是,您可以过滤掉添加子句的元素:SEQUENCEif

[EXPRESSION for VARIABLE in SEQUENCE if CONDITION]

CONDITION是计算结果为 或 的表达式。从技术上讲,该条件不必依赖于 ,但它通常会使用它。TrueFalseVARIABLE

例子:

>>> [n ** 2 for n in range(5) if n % 2 == 0]
[0, 4, 16]
>>> animals = ['dog', 'cat', 'lion', 'tiger']
>>> [animal for animal in animals if len(animal) == 3]
['dog', 'cat']

另外,请记住,Python 允许您编写除列表之外的其他类型的推导式:

  • 词典推导
  • 设置推导式