“列表理解”和类似的意思是啥?它是如何工作的,我该如何使用它?

Posted

技术标签:

【中文标题】“列表理解”和类似的意思是啥?它是如何工作的,我该如何使用它?【英文标题】:What does "list comprehension" and similar mean? How does it work and how can I use it?“列表理解”和类似的意思是什么?它是如何工作的,我该如何使用它? 【发布时间】:2016-04-22 12:22:38 【问题描述】:

我有以下代码:

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

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

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

我已经搜索过,这似乎被称为 列表推导,类似地,似乎有 set/dict 推导和生成器表达式。但它是如何工作的呢?

【问题讨论】:

【参考方案1】:

From the documentation:

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


关于您的问题,列表理解与以下“普通”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]

基本上,我们可以用x 做任何事情。不仅x**2。比如运行x的方法:

>>> [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]

例如,我们还可以使用x 作为dict 对象的键。让我们看看:

>>> 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...else。例如,您只需要range(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]

要使用if...else 三元表达式,您需要将if ... else ... 放在x 之后,不是range(10) 之后:

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

你听说过nested list comprehension吗?您可以将两个或多个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 x 给出123456

警告:你总是需要把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

我们还有set comprehensionsdict comprehensionsgenerator expressions

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

>>> 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

dict comprehension 看起来像一个集合推导,但它使用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)) 是否提供 元组?不!,它是generator expression。它返回一个生成器

>>> (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

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

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

相关(关于生成器):Understanding Generators in Python。

【讨论】:

三元表达式x if cond else y 确实与列表推导没有任何特别的关系——任何有效的表达式都可以在列表压缩中使用——三元表达式是许多 python 表达式之一。跨度> @AChampion:是的,我在回答中提到了这一点,因为我在学习列表推导时尝试了[i for i in x if i else y],但它不起作用。经过一番研究,我明白我必须改用[i if i else y for i in x]。所以我想如果我在这里提到它,那么其他人可以避免我之前遇到的问题。【参考方案2】:

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

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

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

它们通常包括:

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

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

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

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

【讨论】:

这让我很开心:When done well, they have a high satisfaction quotient.【参考方案3】:

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

语法

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

设置生成器符号速成课程

以下是集合生成器符号的工作原理(非常基础):

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

混淆点

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

2) 集合生成器表示法中的谓词过滤器放在最后,列表推导中也是如此。(一些)初学者认为像[x &lt; 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],因为我们要求Python 为range(10) 中的所有 项评估x &lt; 5。没有谓词意味着我们从集合中获取所有内容(就像在集合构建器表示法中一样)。

如果您在使用列表推导时将集合构建器符号放在脑海中,它们会更容易接受。

HTH!

【讨论】:

【参考方案4】:

如果您更喜欢以更直观的方式了解正在发生的事情,那么这可能会有所帮助:

# 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]

【讨论】:

【参考方案5】:

简介

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

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

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

代码的两个 sn-ps 将产生 squares 等于 [0, 1, 4, 9, 16]

请注意,在第一个 sn-p 中,您键入的是声明您想要什么样的列表,而第二个是指定如何创建它。这就是为什么理解是高级和声明性的。

语法

[EXPRESSION for VARIABLE in SEQUENCE]

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

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

EXPRESSIONn ** 2 VARIABLEn SEQUENCErange(5)

请注意,如果您检查 squares 的类型,您会发现列表理解只是一个常规列表:

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

更多关于表达式

表达式可以是任何简化为值的东西:

算术表达式,例如n ** 2 + 3 * n + 1f(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']

过滤:

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

[EXPRESSION for VARIABLE in SEQUENCE if CONDITION]

CONDITION 是一个计算结果为TrueFalse 的表达式。从技术上讲,条件不必依赖于VARIABLE,但它通常使用它。

例子:

>>> [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 允许您编写除列表之外的其他类型的推导:

字典理解 设置理解

【讨论】:

以上是关于“列表理解”和类似的意思是啥?它是如何工作的,我该如何使用它?的主要内容,如果未能解决你的问题,请参考以下文章

Git (master|REBASE 1/1) 是啥意思?我该如何摆脱它?

XMLHttpRequest 中不同的就绪状态是啥意思,我该如何使用它们?

Ruby on Rails:Xcode:仪器:跟踪/BPT 陷阱?它是啥,我该如何解决?

Requires capabilities : [CAPABILITY_IAM] 是啥意思,我该如何激活它?

net::ERR_INCOMPLETE_CHUNKED_ENCODING 是啥意思,我该如何解决?

这个带有全称量词的表达式是啥意思?