在列表推导式中将项目追加到列表中
我有一个列表,假设是 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 个回答
在第一个例子中,返回 [None, None, None]
的原因是因为 list.append
这个函数本身返回的是 None
,所以它把这个 None
存储到了列表里。
在第二个例子中,原因是列表是可变的,每次你添加一个值的时候,原来的列表都会被修改。
你需要的是一个不会直接修改原列表的添加方式,比如用 +
。也就是说,可以用 [x + ['a'] for x in a]
这样的写法。
正如其他人所说,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
并不算很函数式,因为它会修改列表(纯粹的函数式编程只使用不可变对象),而且不返回结果供其他操作(函数)使用。使用函数式编程的概念,你可以写出很长的单行代码,没人能看懂,这也被称为“工作保障”或“糟糕的代码”。
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']]