views:

130

answers:

3

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.

+3  A: 

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)]
Ignacio Vazquez-Abrams
Technically, it's two references to the same list, not copies.
Crast
+1  A: 

y contains two references to a single, mutable, list.

Jon-Eric
+10  A: 

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

Crast
Thanks, that makes sense.
Paul D.