Unexpected behavior of list of list

Steven D'Aprano steve at REMOVE.THIS.cybersource.com.au
Fri Mar 9 21:58:59 EST 2007


On Fri, 09 Mar 2007 17:30:09 -0800, kghose wrote:

> Hi,
> 
> The following code
> 
> listoflists = [[]]*2
> listoflists[0].append(1)
>
> appends(1) to both listoflists[0] and listoflists[1] (which I did not
> expect)

I think you will find the same question raised barely a few hours ago.

Hint:

>>> list_of_lists = [[]]*2
>>> id(list_of_lists[0])
-1211466452
>>> id(list_of_lists[1])
-1211466452

Notice that both inner lists have the same ID? That tells you that they
are the same list.

Like about what [obj]*2 does. Does it create multiple copies of obj? No it
does not. Python never makes copies of objects unless you explicitly tell
it to.

[] on it's own creates a new empty list, so [[], []] creates a list with
two DIFFERENT empty lists in it. [[]] creates a list with one empty list
in it; *2 makes a second reference (not a copy!) of that empty list.


> while
> 
> listoflists = [[]]*2
> listoflists[0] = [1]
> listoflists[0].append(2)
> 
> works as expected.i.e. only listoflists[0] gets 2 appended to it and
> any further operations work as expected.

>>> list_of_lists[0] = [] # creates a new empty list
>>> id(list_of_lists[0])
-1211466228
>>> id(list_of_lists[1]) # same ID as before
-1211466452




You know, now that Python has a warnings module, it would be really good
if list-of-lists*int raised a warning. Does anyone know if that's feasible
or possible? It needn't catch every imaginable Gotcha, just the common
case. I'm thinking something like this:

# pseudo-code
class list:
    def __mul__(self, count):
        for item in self:
            if isinstance(item, list):
                warn("this duplicates references, not copies")
                break
        ...do the rest




-- 
Steven.




More information about the Python-list mailing list