Re: Multi-dimensional list initialization

Discussion in 'Python' started by Chris Rebert, Nov 5, 2012.

  1. Chris Rebert

    Chris Rebert Guest

    On Sun, Nov 4, 2012 at 10:27 PM, Demian Brecht <> 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
     
    Chris Rebert, Nov 5, 2012
    #1
    1. Advertising

  2. On Monday, November 5, 2012 3:07:12 PM UTC+8, Chris Rebert wrote:
    > On Sun, Nov 4, 2012 at 10:27 PM, Demian Brecht <> 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

    This is not clear in a name binding objective
    programming language.

    b=[1,2,3,4]*4
    mb=[ b]*4

    # check the behaviors and usages of reference copies
    # and shadow value copies and deep-value copies





    >
    > >

    >
    > > 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
     
    88888 Dihedral, Nov 8, 2012
    #2
    1. Advertising

  3. On Monday, November 5, 2012 3:07:12 PM UTC+8, Chris Rebert wrote:
    > On Sun, Nov 4, 2012 at 10:27 PM, Demian Brecht <> 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

    This is not clear in a name binding objective
    programming language.

    b=[1,2,3,4]*4
    mb=[ b]*4

    # check the behaviors and usages of reference copies
    # and shadow value copies and deep-value copies





    >
    > >

    >
    > > 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
     
    88888 Dihedral, Nov 8, 2012
    #3
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    3
    Views:
    318
  2. Demian Brecht

    Multi-dimensional list initialization

    Demian Brecht, Nov 5, 2012, in forum: Python
    Replies:
    55
    Views:
    650
    Ethan Furman
    Nov 9, 2012
  3. Chris Angelico

    Re: Multi-dimensional list initialization

    Chris Angelico, Nov 5, 2012, in forum: Python
    Replies:
    0
    Views:
    202
    Chris Angelico
    Nov 5, 2012
  4. Andrew Robinson

    Re: Multi-dimensional list initialization

    Andrew Robinson, Nov 5, 2012, in forum: Python
    Replies:
    0
    Views:
    231
    Andrew Robinson
    Nov 5, 2012
  5. Demian Brecht

    Re: Multi-dimensional list initialization

    Demian Brecht, Nov 5, 2012, in forum: Python
    Replies:
    0
    Views:
    173
    Demian Brecht
    Nov 5, 2012
Loading...

Share This Page