Search code examples
pythonlistpointers

Why does using multiplication operator on list create list of pointers?


>>> rows = [['']*5]*5
>>> rows
[['', '', '', '', ''], ['', '', '', '', ''], ['', '', '', '', ''], ['', '', '', '', ''], ['', '', '', '', '']]
>>> rows[0][0] = 'x'

Naturally, I expect rows to become:

[['x', '', '', '', ''], ['', '', '', '', ''], ['', '', '', '', ''], ['', '', '', '', ''], ['', '', '', '', '']]

Instead, I get:

[['x', '', '', '', ''], ['x', '', '', '', ''], ['x', '', '', '', ''], ['x', '', '', '', ''], ['x', '', '', '', '']]

It seems that elements of rows list are pointers to the same old ['']*5 list. Why does it work this way and is this a Python feature?


Solution

  • The behaviour is not specific to the repetition operator (*). For example, if you concatenate two lists using +, the behaviour is the same:

    In [1]: a = [[1]]
    
    In [2]: b = a + a
    
    In [3]: b
    Out[3]: [[1], [1]]
    
    In [4]: b[0][0] = 10
    
    In [5]: b
    Out[5]: [[10], [10]]
    

    This has to do with the fact that lists are objects, and objects are stored by reference. When you use * et al, it is the reference that gets repeated, hence the behaviour that you're seeing.

    The following demonstrates that all elements of rows have the same identity (i.e. memory address in CPython):

    In [6]: rows = [['']*5]*5
    
    In [7]: for row in rows:
       ...:     print id(row)
       ...:     
       ...:     
    15975992
    15975992
    15975992
    15975992
    15975992
    

    The following is equivalent to your example except it creates five distinct lists for the rows:

    rows = [['']*5 for i in range(5)]