Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extending a list of lists in Python? [duplicate]

I might be missing something about the intended behavior of list extend, but why does the following happen?

x = [[],[]]
y = [[]] * 2

print x       # [[],[]]
print y       # [[],[]]
print x == y  # True

x[0].extend([1])
y[0].extend([1])

print x    # [[1],[]], which is what I'd expect
print y    # [[1],[1]], wtf?

I would guess that the * operator is doing something unexpected here, though I'm not exactly sure what. It seems like something is going on under the hood that's making the original x and y (prior to calling extend) not actually be equal even though the == operator and repr both would make it seem as though they were identical.

I only came across this because I wanted to pre-populate a list of empty lists of a size determined at runtime, and then realized that it wasn't working the way I imagined. I can find a better way to do the same thing, but now I'm curious as to why this didn't work. This is Python 2.5.2 BTW - I don't have a newer version installed so if this is a bug I'm not sure if it's already fixed.

like image 914
Paul D. Avatar asked Feb 16 '10 21:02

Paul D.


2 Answers

In the case of [something] * 2, python is simply making a reference-copy. Therefore, if the enclosed type(s) are mutable, changing them will be reflected anywhere the item is referenced.

In your example, y[0] and y[1] point to the same enclosed list object. You can verify this by doing y[0] is y[1] or alternately id(y[0]) == id(y[1]).

You can however re-assign list elements, so if you had done:

y[0] = [1]

You would've re-bound the first element to a new list containing the element "1", and you would've got your expected result.

Containers in python store references, and it's possible in most sequence containers to reference the same item multiple times. A list can actually reference itself as an element, though the usefulness of this is limited.

This issue wouldn't have come up if you had multiplied a list containing immutable types:

a = [0, 1] * 2

The above would give you the list [0, 1, 0, 1] and indeed both instances of 1 point to the same object, but since they are immutable, you cannot change the value of the int object containing "1", only reassign elements.

So doing: a[1] = 5 would result in a showing as [0, 5, 0, 1].

like image 131
Crast Avatar answered Oct 21 '22 15:10

Crast


The statement y = [[]] * 2 binds y to a list containing 2 copies of the same list. Use:

y = [[], []]

or

y = [[] for n in range(2)]
like image 30
Ignacio Vazquez-Abrams Avatar answered Oct 21 '22 16:10

Ignacio Vazquez-Abrams