[Tutor] Issues with initializing lists

Tom Jenkins tjenkins@devis.com
Fri, 10 Aug 2001 13:33:38 -0400


Hello,
Yep this one bites _everyone_ at some point.  Lets take a look...

Leona Euler wrote:
> Hi,
> 
> As an example:
> 
>>>>l1=[[None]*3]*4
>>>>l1
>>>>
> [[None, None, None], [None, None, None], [None, None, None], [None, None,
> None]]

What is happening here is that 4 copies of the _same_ list are being 
generated.  We can confirm this in the interpreter:
 >>> l1 = [[None]*3]*4
 >>> print l1
[[None, None, None], [None, None, None], [None, None, None], [None, 
None, None]]
 >>> for sub in l1:
... 	print id(sub)
...
17189420
17189420
17189420
17189420
 >>>

Note the print id(sub) call... lets see what the docs say:
 >>> id.__doc__
"id(object) -> integer\n\nReturn the identity of an object.  This is 
guaranteed to be unique among\nsimultaneously existing objects.  (Hint: 
it's the object's memory address.)"
 >>>

Ok so we see that they all point to the same memory location.

[snip]
> 
> I could do
> 
> l1=[]
> 
>>>>for i in range(4):
>>>>
> ... 	l1.append([])
> ... 	for j in range(3):
> ... 		l1[i].append(None)
> ... 
> l1[1][1]='hello'
> 
>>>>l1
>>>>
> [[None, None, None], [None, 'hello', None], [None, None, None], [None, None,
> None]]
> 

Lets look at this one in the interpreter:
 >>> l2 = []
 >>> for i in range(4):
... 	l2.append([])
... 	for j in range(3):
... 		l2[i].append(None)
... 		
 >>> print l2
[[None, None, None], [None, None, None], [None, None, None], [None, 
None, None]]
 >>> for sub in l2:
... 	print id(sub)
... 	
15857188
15854956
15852996
15858828
 >>>

Ok different numbers, so different memory locations, so different objects.

So now we know _why_ it was working / not working in the previous two 
algorithms.

> This looks fine. But this approach really seems excessive. 
> 

Lets see if we can tighten this up.

 >>> l3 = []
 >>> for i in range(4):
... 	l3.append([None]*3)
... 	
 >>> print l3
[[None, None, None], [None, None, None], [None, None, None], [None, 
None, None]]
 >>> for sub in l3:
... 	print id(sub)
... 	
17236724
15244756
17238524
17238444
 >>>

ok, cool that one is shorter and gives us different ids.

Now lets see if there is another way.  Basically anytime you see a 
simple loop like above, you should be able to replace it with a list 
comprehension (on python 2 and above).  So lets try that...

 >>> l4 = [[None]*3 for x in range(4)]
 >>> print l4
[[None, None, None], [None, None, None], [None, None, None], [None, 
None, None]]
 >>> for sub in l4:
... 	print id(sub)
... 	
17182676
17182004
17182380
17236980
 >>>
 >>> l4[1][1] = 'hello'
 >>> print l4
[[None, None, None], [None, 'hello', None], [None, None, None], [None, 
None, None]]
 >>>

Viola!

-- 
Tom Jenkins
devIS - Development Infostructure
http://www.devis.com