1

i'm learning python and hit a wall. I am trying to define a 2d list which i can later use to append values. This corresponding to a grid of width *height

I tried using [] to initialize the empty lists but then the wid is ignored. I tried using None as a placeholder but then i can't append

wid = 3
hgt = 3
l1 = [[]*wid ] * hgt
l = [[None]*wid ] * hgt
l[1][1].append("something")

Result

l1: [[], [], []]

l: [[None, None, None], [None, None, None], [None, None, None]]

Error:

append: AttributeError: 'NoneType' object has no attribute 'append'

desired result: [[[], [], []], [[], [], []], [[], [], []]]

tobias_k
  • 81,265
  • 12
  • 120
  • 179
Jop
  • 29
  • 1
  • 4

4 Answers4

5

Try using a list comprehension within a list comprehension:

>>> [ [ [] for i in range(wid) ] for i in range(hgt) ]
[[[], [], []], [[], [], []], [[], [], []]]

Note this is preferred to list multiplication because each of these lists is unique. Compare:

>>> x = [ [[] for i in range(wid)] for i in range(hgt) ]
>>> x[1][1].append('a')
>>> x
[[[], [], []], [[], ['a'], []], [[], [], []]]

vs.

>>> y = [ [[]] * wid for i in range(hgt) ]
>>> y[1][1].append('a')
>>> y
[[[], [], []], [['a'], ['a'], ['a']], [[], [], []]]

vs.

>>> z = [ [[]] * wid ] * hgt
>>> z[1][1].append('a')
>>> z
[[['a'], ['a'], ['a']], [['a'], ['a'], ['a']], [['a'], ['a'], ['a']]]

Where, in the second and third cases, 'a' appears in multiple cells! And using None does not avoid this problem:

>>> m = [ [None] * wid ] * hgt
>>> m
[[None, None, None], [None, None, None], [None, None, None]]
>>> if m[1][1] is None:
...     m[1][1] = ['a']
... else:
...     m[1][1].append('a')
...
>>> m
[[None, ['a'], None], [None, ['a'], None], [None, ['a'], None]]

tl;dr - use the double list comprehension. In my opinion, it's the most readable option anyway.

chris
  • 1,915
  • 2
  • 13
  • 18
  • And refer to [this](https://www.pythonforbeginners.com/basics/list-comprehensions-in-python) guy! List comprehension is considerably important in Python and I highly recommend you to search a little more about it :) – Pedro Martins de Souza Feb 13 '19 at 15:40
  • This answers only part of the question (OP might still try to `append` to those `None` entries) – tobias_k Feb 13 '19 at 15:40
  • 1
    Thanks for your solution and explenation! exactly what i wanted to know. – Jop Feb 14 '19 at 10:44
2

There's two ways to do it

  1. use None as placeholder value, and replace it with l[1][1] = 5
  2. use empty lists and append new elements to them, with l[1].append(5)

Currently, you are mixing the two ways. the l[1][1] returns a None value, not a list, and you are trying to call append on it.

Regardless, there is a common python trap here. When you write

mylist = [[None]*2]*3

this does two things

  1. creates a list [None, None]
  2. uses this list 3 times to create another list

The key is that the outer list will consist of 3 copies of the inner list, not of 3 different by identical lists. so, everytime you modify one of them, the rest will be modified too. So, you need to make copies of the inner list.

For a 2D array however, the best way would be to use numpy

x = np.zeros(m, n)
x[1, 2] = 5
blue_note
  • 27,712
  • 9
  • 72
  • 90
  • 1
    This answers only part of the question (the sublists are still all the same instance) – tobias_k Feb 13 '19 at 15:40
  • @tobias_k: correct. the question was badly formatted when I read it. editing answer – blue_note Feb 13 '19 at 15:41
  • Thanks for the help! I still get confused sometimes when python copies or references to the same object. This helps a lot! Would performace wise numpy be faster then lists? – Jop Feb 14 '19 at 10:42
  • @Jop: yes, definitely better performance. but, if you care about performance, you should do matrix operations, avoid for loops – blue_note Feb 14 '19 at 11:48
0
>>> x = 5
>>> y = 5
>>> [[None for _ in range(x)] for _ in range(y)]
[[None, None, None, None, None], [None, None, None, None, None], [None, None, None, None, None], [None, None, None, None, None], [None, None, None, None, None]]

Don't use [[None]*x]*y unless you really know that you just created shared references.

Benoît P
  • 3,179
  • 13
  • 31
0

if you are dealing with matrices you can use numpy:

import numpy as np

my_matrix = np.zeros((rows, columns) # with 2 dimentions
kederrac
  • 16,819
  • 6
  • 32
  • 55