views:

97

answers:

2

Hi. So I'm trying to generate a nested list in Python based on a width and a height. This is what I have so far:

    width = 4
    height = 5
    row = [None]*width
    map = [row]*height

Now, this obviously isn't quite right. When printed it looks fine:

[[None, None, None, None],
 [None, None, None, None],
 [None, None, None, None],
 [None, None, None, None],
 [None, None, None, None]]

But attempting to assign a value to a position like so:

map[2][3] = 'foo'

I get:

[[None, None, None, 'foo'],
 [None, None, None, 'foo'],
 [None, None, None, 'foo'],
 [None, None, None, 'foo'],
 [None, None, None, 'foo']]

Clearly this happens because each sublist is really just referencing the same object, row, so changing one, changes them all. So this is closest I've got!

How can I dynamically generate a nested list? Thanks!

+7  A: 

When you do [row]*height you end up with the same list object in each row. The row array reference is repeated in each row which means each row is actually pointing to the same list object. Hence modifying one row actually modifies all rows.

Take a look at what happens when you print the id() for each row. They're all the same!

>>> grid = [[None] * width] * height
>>> [id(row) for row in grid]
[148014860, 148014860, 148014860, 148014860, 148014860]

You can get python to generate separate-but-identical lists for each row by using a list comprehension. When you use [rowexpr for i in xrange(height)] then rowexpr will be evaluated once per row. The trick then is to use an expression that will result in a unique list each time it is evaluated.

This'll make more sense if you see it in action:

>>> grid = [[None] * width for i in xrange(height)]
>>> grid[2][3] = 'foo'
>>> grid
[[None, None, None, None],
 [None, None, None, None],
 [None, None, None, 'foo'],
 [None, None, None, None],
 [None, None, None, None]]

Each time [None] * width is evaluated it generates a new list.

>>> [id(row) for row in grid]
[148016172, 148015212, 148016236, 148016108, 148016332]
John Kugelman
A: 

I use something like this:

w = 5
h = 5

map = []

for i in range(h):
 row = []
 for j in range(w):
  row.append(None)
 map.append(row)

print map

map[2][3] = 'foo'

print map
lalli
A list comprehension is the __one__ right way to do this.
aaronasterling
I agree, list comprehension is cleaner, more readable, more 'Pythonic'. My way is just a way to solve the problem and also readable for people (like me) who are still evolving from C to python.
lalli
@lalli doing it with nested for loops doesn't help you or anyone else make the transition from C to python: It just gets in the way.
aaronasterling
@aaronasterling: point taken.. thanks
lalli