Multi-dimensional list initialization

Chris Rebert clp2 at rebertia.com
Mon Nov 5 02:07:07 EST 2012


On Sun, Nov 4, 2012 at 10:27 PM, Demian Brecht <demianbrecht at gmail.com> wrote:
> So, here I was thinking "oh, this is a nice, easy way to initialize a 4D matrix" (running 2.7.3, non-core libs not allowed):
>
> m = [[None] * 4] * 4
>
> The way to get what I was after was:
>
> m = [[None] * 4, [None] * 4, [None] * 4, [None * 4]]
>
> (Obviously, I could have just hardcoded the initialization, but I'm too lazy to type all that out ;))
>
> The behaviour I encountered seems a little contradictory to me.
> [None] * 4 creates four distinct elements in a single array
> while [[None] * 4] * 4 creates one distinct array of four distinct elements, with three references to it:

Incorrect. In /both/ cases, the result is a list of length 4, whose
elements are 4 (references to) the exact same object as the original
list's element.
Put simply, the list multiplication operator never copies objects; it
just makes additional references to them.

However, unlike a list object (as in your latter example), the object
`None` is completely immutable (and what's more, a singleton value),
so you just-so-happen *not to be able to* run into the same problem of
mutating an object (assignment to an index of a list constitutes
mutation of that list) that is referenced in multiple places, for you
cannot mutate None in the first place!:
>>> x = None
>>> x.a = 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'a'
>>> # it doesn't overload any mutating operators:
>>> type(None).__dict__.keys()
['__hash__', '__repr__', '__doc__']
>>> # and it obviously has no instance variables,
>>> # so, we can't modify it in any way whatsoever!
(Lists, on the other hand, define item assignment, .pop(), .remove(),
and a few other mutator methods.)

>>>> a = [None] * 4
>>>> a[0] = 'a'
>>>> a
> ['a', None, None, None]
>
>>>> m = [[None] * 4] * 4
>>>> m[0][0] = 'm'
>>>> m
> [['m', None, None, None], ['m', None, None, None], ['m', None, None, None], ['m', None, None, None]]
>
> Is this expected behavior

Yes. It's also a FAQ:
http://docs.python.org/2/faq/programming.html#how-do-i-create-a-multidimensional-list

> and if so, why?

It's a general (albeit AFAIK unstated) principle that Python never
copies objects unless you explicitly ask it to. You have encountered
one example of this rule in action.

> In my mind either result makes sense, but the inconsistency is what throws me off.

It is perfectly consistent, once you understand what list
multiplication actually does.

Cheers,
Chris
--
http://rebertia.com



More information about the Python-list mailing list