41

I have a list, let's say, a = [[1,2],[3,4],[5,6]]

I want to add the string 'a' to each item in the list a.

When I use:

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

it returns [None,None,None].

But if I use:

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

then it does something odd.

a, but not a1 is [[1,2,'a'],[3,4,'a'],[5,6,'a']].

I don't understand why the first call returns [None, None, None] nor why the second changes on a instead of a1.

FBruzzesi
  • 6,385
  • 3
  • 15
  • 37
ariel
  • 2,637
  • 4
  • 20
  • 12

7 Answers7

45

list.append mutates the list itself and returns None. List comprehensions are for storing the result, which isn't what you want in this case if you want to just change the original lists.

>>> x = [[1, 2], [3, 4], [5, 6]]
>>> for sublist in x:
...     sublist.append('a')
...
>>> x
[[1, 2, 'a'], [3, 4, 'a'], [5, 6, 'a']]
Mike Graham
  • 73,987
  • 14
  • 101
  • 130
30

As others have said, append mutates the list itself and you shouldn't assign it to a variable. Executing it changes it's data, effectively updating everyone pointing at it.

But, there's a trick I use when I want to do stuff in a functional* way while mutating existing objects (rather than constructing new ones, in this case using a=[x + ['a'] for x in a], or specifically the x + ['a']).

So, if you're brave enough you can also do this:

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

This works because append returns None, and the or continues on to search for a truth-y value, which x is (it's a list with at least what was appended to it).

Why do I even need this?

Say you have a list and you want to insert some of it's members to a new list, and update the references accordingly:

So you have the list all:

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

Some of it is inserted and updated to a new list x:

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

Some of all is also inserted and updated to a list y:

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

all is updated:

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

But x is also updated:

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

And y is generated as expected:

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

Overall, for simple tasks, I'd recommend using a for loop updating explicitly. This is what's considered pythonic.

Technically speaking, if you had access to the list class, you could make this a function:

def more_functional_append(self, x):
    self.append(x)
    return self
  • functional programming is based on every statement doing essentially one thing, and not having side effects (so, not mutating and returning). append is not very functional since it mutates a list (pure functional programming has only immutable objects) and does not return a result to pass to other actions (functions). Using functional programming concepts you can create great big one-liners no one can read, also known as "job security" or "bad code".
Reut Sharabani
  • 30,449
  • 6
  • 70
  • 88
  • 1
    I have a different view on this answer nowdays so if you are reading this please comment so I can update it. There is a missing core feature for functional programming here which is immutabiliity and I should expand on that. – Reut Sharabani Jan 23 '18 at 07:45
  • I am curious about your new insights. – Jan Dec 18 '18 at 15:56
  • @Jan my new insights are that without immutability its just too complex. The default should be immutable (and python has some solutions there like `frozenset` and `tuple`) - but its **really** lacking not having a `frozendict` and not having the immutable versions as defaults. I really don't like this answer since its so alien to python. – Reut Sharabani Dec 18 '18 at 15:58
  • It is very strange to use a list comprehension to update the outer list out-of-place, when you are also updating the inner lists in-place. Code which uses the function will likely expect it to be in-place or out-of-place, not some combination of the two; this can lead to bugs that are difficult to track down. – kaya3 Aug 19 '20 at 09:52
13

For the first case, the reason it returns [None, None, None] is because the list.append function returns None, and that's what it stores in the list.

In the second case, it's because the list is mutable, and each time you append the value, the original list is modified.

What you need is a non-in-place append operator, such as +. i.e. [x + ['a'] for x in a].

sykora
  • 96,888
  • 11
  • 64
  • 71
7

You can use list addition within a list comprehension, like the following:

a = [x + ['a'] for x in a] 

This gives the desired result for a. One could make it more efficient in this case by assigning ['a'] to a variable name before the loop, but it depends what you want to do.

Nafeez Quraishi
  • 5,380
  • 2
  • 27
  • 34
Bud
  • 311
  • 4
  • 6
5

(This is a combination of the answers by Mike Graham and sykora):

If you merely want to change the values in-place, try a regular for-loop, and not a list comprehension:

for sublist in a:
    sublist.append('a')

If you want to leave a alone, and put the result in a1:

a1 = [sublist + ['a'] for sublist in a]

As they explained, append modifies the list, but returns None, while + leaves the list alone, but returns a new, appended list.

Oddthinking
  • 24,359
  • 19
  • 83
  • 121
0

In the first value assignment of your list comprehension an Attribute Error, 'NoneType' object has no attribute 'append', helps explain why your list, a, will be loaded with None(s). To get my console to throw the error, I used x as a variable for the list comprehension and also as the iterator.

Traceback (most recent call last):
x = [x.append('a') for x in a]
AttributeError: 'NoneType' object has no attribute 'append'

Then, I reverted back to a for x and it threw the same error.

Traceback (most recent call last):
a = [x.append('a') for x in a]
AttributeError: 'NoneType' object has no attribute 'append'
Rider
  • 39
  • 6
-1

leave the a = and use the side effect on a:

[x.append('a') for x in a] 
print a
Ruggero Turra
  • 16,929
  • 16
  • 85
  • 141
  • 7
    Please, no. Throwing away a list comprehension just to utilize its side effects is bad form; your suggestion works, but it's frowned upon just like similar `map` usage has been frowned upon. Explicit is better than implicit; use a loop rather than a list comprehension. – tzot Mar 27 '10 at 01:04