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

72

我有以下的代码:

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

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

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

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

我搜索过后发现,这种语法结构被称为列表推导式,类似的还有集合/字典推导式和生成器表达式。但是它们是如何运作的呢?


2
请注意:对于重复的问题,请考虑以下两个链接,以了解如何编写列表推导式:https://dev59.com/JF8f5IYBdhLWcg3wEPEq(用于映射)和https://dev59.com/1W455IYBdhLWcg3wAvRI(用于过滤)。 - Karl Knechtel
2
有关(显式)嵌套列表推导和带有多个子句的列表推导,请参见https://dev59.com/NmMl5IYBdhLWcg3w16B3。 - Karl Knechtel
肯定有一个2008年的规范问题。 - Peter Mortensen
5个回答

115

来自文档

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


关于你的问题,列表推导式与以下“普通”的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]

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

>>> [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 i in x之前放置for x in [[1, 2, 3], [4, 5, 6]]
>>> [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

注意:如果您在函数内部使用列表推导式,并且该函数可以循环生成器,则不需要[]。例如,sum()
>>> sum(i**2 for i in range(5))
30

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


三元表达式 x if cond else y 实际上与列表推导式没有任何特定关系 - 任何有效的表达式都可以在列表推导式中使用 - 三元表达式只是 Python 表达式的众多之一。 - AChampion
5
@AChampion:是的,我在我的答案中提到了这一点,因为我在学习列表推导时尝试过 [i for i in x if i else y],但它并不起作用。经过一些研究,我明白我必须使用 [i if i else y for i in x]。所以我认为如果我在这里提到它,那么其他人就可以避免我之前遇到的问题。 - Remi Guan

8

Python中有列表、字典和集合推导式,但没有元组推导式(不过可以尝试“生成器表达式”)。

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

它们并非解决所有问题的万能药,并且可以重写为传统循环。当需要在迭代之间维护和更新状态时,它们变得笨拙。

它们通常由以下部分组成:

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

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

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

当做得好时,它们具有很高的满意度。


6
这让我很开心:“当做得好时,它们有很高的满意度。” - Sohaib Farooqi

6

如果你更喜欢通过视觉方式了解正在发生的事情,那么也许这会有所帮助:

# 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
我最近看到很多混淆(在其他SO问题和同事那里)关于列表推导式的工作原理。一点数学教育可以帮助理解为什么语法是这样的,以及列表推导式的真正含义。
语法
最好将列表推导式视为对集合的谓词,就像我们在数学中使用集合构建符号表示一样。实际上,这种符号对我来说感觉非常自然,因为我拥有数学本科学位。但是请忘记我,Python创始人Guido van Rossum拥有数学硕士学位并具备数学背景。
集合构建符号简介
下面是集合构建符号的基本原理:

enter image description here

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

困惑点

1)在集合构建符号中,谓词过滤器只指定我们想要保留的项,列表推导谓词也是同样的道理。您不必包含特殊逻辑来省略项目,除非谓词包括该项目,否则它们将被省略。空谓词(即末尾没有条件)会包括给定集合中的所有项。

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

如果你在使用列表推导式时将集合构建符号记在脑海中,它们会更容易理解。

希望有所帮助!


2

介绍

列表推导式是在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 语言中的任何表达式,但通常情况下要使用一些变量。这个变量在 VARIABLE 字段中被声明。 SEQUENCE 定义了变量枚举的值的来源。

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

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

请注意,如果检查 squares 的类型,您会发现列表推导只是一个普通的列表:

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

关于表达式

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

  • 算术表达式,例如n ** 2 + 3 * n + 1
  • 使用变量n的函数调用,如f(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 允许您编写除列表之外的其他类型的推导式:

  • 字典推导式
  • 集合推导式

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接