[Tutor] The trap of the year

Steven D'Aprano steve at pearwood.info
Wed Jan 26 00:33:34 CET 2011


Karim wrote:
> 
> Hello All,
> 
> Just to share on rageous bug I encounter where 2 lists which "should" to 
> be different (same id) because hold by different instances of the same 
> class are not in fact DIFFERENT, see below:

I think you are confused. If the two lists have the same ID, they should 
be the same, not different.


>  >>> class Device():
> ...     def __init__(self, parameters=[]):
> ...         self.parameters = parameters

This creates a list ONCE, when the method is created, and then uses that 
same list over and over again.

Consider this:

 >>> import time
 >>> now = time.ctime()
 >>> now
'Wed Jan 26 10:05:31 2011'
 >>> def test(t=now):
...     print(t)
...
 >>> test()
Wed Jan 26 10:05:31 2011
 >>> time.sleep(60); test()
Wed Jan 26 10:05:31 2011


Are you surprised that time.ctime() only gets called *once*? I hope not 
-- I would expect that you consider this example obvious and not surprising.

How about this instead?

 >>> def test2(t=time.ctime()):
...     print(t)
...
 >>> test2()
Wed Jan 26 10:09:10 2011
 >>> time.sleep(60); test2()
Wed Jan 26 10:09:10 2011


I hope that this also will not be surprising. time.ctime() is called 
once, when the function is created, and the result used as often as needed.

It is no different when the default value is a list like []. The list is 
created once, and used each time the function is called.


  > When I discovered that I was puzzled because at the prompt:
> 
>  >>> a = []
>  >>> b = []
>  >>> id(a)
> 140559202956496
>  >>> id(b)
> 140559202957000

But this test is completely different. You create TWO lists, not one. A 
better test would be:

 >>> a = []
 >>> b = a
 >>> id(a), id(b)
(3083146668, 3083146668)


> I am not really understanding why my init in the class made it refers to 
> the same list object.

Because it only creates one list, when the method is defined, not each 
time the method is called.

Only the *inside* of the method is executed each time the method is 
called, not the method definition. If you want something to be executed 
each time the method is called, you have to put it in the body of the 
method:


 >>> def test3(t=None):
...     if t is None: t = time.ctime()
...     print(t)
...
 >>> test3()
Wed Jan 26 10:18:33 2011
 >>> time.sleep(60); test3()
Wed Jan 26 10:19:37 2011



-- 
Steven



More information about the Tutor mailing list