在列表推导式中将项目追加到列表中

42 投票
7 回答
123673 浏览
提问于 2025-04-15 20:47

我有一个列表,假设是 a = [[1,2],[3,4],[5,6]]

我想在这个列表中的每个项目后面加上字符串 'a'

当我使用:

a = [x.append('a') for x in a] 

结果是 [None,None,None]

但是如果我使用:

a1 = [x.append('a') for x in a]

那就出现了一些奇怪的情况。

结果是 a 变成了 [[1,2,'a'],[3,4,'a'],[5,6,'a']],但 a1 没有变化。

我不明白为什么第一次调用会返回 [None, None, None],也不明白为什么第二次会改变 a 而不是 a1

7 个回答

13

在第一个例子中,返回 [None, None, None] 的原因是因为 list.append 这个函数本身返回的是 None,所以它把这个 None 存储到了列表里。

在第二个例子中,原因是列表是可变的,每次你添加一个值的时候,原来的列表都会被修改。

你需要的是一个不会直接修改原列表的添加方式,比如用 +。也就是说,可以用 [x + ['a'] for x in a] 这样的写法。

31

正如其他人所说,append 是直接修改列表本身的,所以你不应该把它的结果赋值给一个变量。执行这个操作会改变列表的数据,实际上是更新了所有指向这个列表的地方。

但是,我有一个小技巧,当我想以一种函数式*的方式来处理现有对象(而不是创建新的对象,比如用 a=[x + ['a'] for x in a],或者具体的 x + ['a'])时,我会用到这个技巧。

所以,如果你够大胆的话,你也可以这样做:

>>> a=[[1,2],[3,4],[5,6]]
>>> a=[x.append('a') or x for x in a]
>>> a
[[1, 2, 'a'], [3, 4, 'a'], [5, 6, 'a']]

这样做是有效的,因为 append 返回的是 None,而 or 会继续寻找一个“真”的值,而 x 就是(它是一个至少包含了被添加内容的 list)。

我为什么需要这个呢?

假设你有一个列表,你想把其中的一些成员放到一个新列表里,并相应地更新引用:

比如你有一个列表 all

>>> all = [[], [], [], []]

其中的一部分被插入并更新到新列表 x 中:

>>> x = [i.append('x') or i for i in all[:2]]
>>> x
[['x'], ['x']]

还有一部分 all 的内容也被插入并更新到列表 y 中:

>>> y = [i.append('y') or i for i in all[1:3]]

此时 all 也被更新了:

>>> all
[['x'], ['x', 'y'], ['y'], []]

x 也随之更新:

>>> x
[['x'], ['x', 'y']]

最后,y 也按预期生成了:

>>> y
[['x', 'y'], ['y']]

总体来说,对于简单的任务,我建议使用 for 循环来显式更新。这被认为是符合 Python 风格的做法。

从技术上讲,如果你能访问到列表类,你可以把这个过程做成一个函数:

def more_functional_append(self, x):
    self.append(x)
    return self
  • 函数式编程 的核心是每个语句基本上只做一件事,并且没有副作用(也就是说,不修改原有数据并返回结果)。append 并不算很函数式,因为它会修改列表(纯粹的函数式编程只使用不可变对象),而且不返回结果供其他操作(函数)使用。使用函数式编程的概念,你可以写出很长的单行代码,没人能看懂,这也被称为“工作保障”或“糟糕的代码”。
45

list.append 是直接在原来的列表上添加内容,并且它不会返回任何东西,返回的是 None。而列表推导式是用来生成一个新列表的,这在你只想修改原来的列表时就不太合适了。

>>> x = [[1, 2], [3, 4], [5, 6]]
>>> for sublist in x:
...     sublist.append('a')
...
>>> x
[[1, 2, 'a'], [3, 4, 'a'], [5, 6, 'a']]

撰写回答