[Tutor] multiple class instances

Corran Webster cwebster@math.tamu.edu
Mon, 10 May 1999 15:33:00 -0500 (CDT)


> Hi All -
> 
> and thanks Corran for the that 'beautyful' help. I mean it wasn't that
> abstract as your last article - so was able to get most of it ;-).
> Realy, thank very much!

You're welcome - and sorry if my first reply was a bit cryptic - I
wasn't sure just where you were at in your knowledge of python.

>> Which leads to the infamous default method gotcha:
                                       ^^^^^^
Urg - this should really be "default argument gotcha".  Nothing
particularly special about methods, that's just where it come up most
often.

>> class Bla:
>>   def __init__(self, mylist = []):
>>     self.mylist = mylist
>> 
>> (note: can be any method, not just __init__)
>>
>> For the tutees: the default argument [] is actually an anonymous class
>> variable - so this construct means that for each instance the variable
>> x.mylist refers to the same mutable object
>> 
>> >>> x = Bla()
>> >>> y = Bla()
>> >>> print y.mylist
>> []
>> >>> x.mylist.append('a')
>> >>> print y.mylist
>> ['a']

[snip]
 
> But z = Bla(['my_own']) get's it's own list object (not only reference).
> From this point on it's sort of independent ... (right).

Precisely - for z, at least.  Other instances can still have the
original weird behaviour.

> Do I have to know that the empty list '[]' in the above example is an
> anonymous class variable (of class 'list'?) because it's following the
> python philosophy or do you know that because you had a look in the
> implementation of python's list class?

It's more because it's following python philosophy, plus a bit of
folklore which I picked up in c.l.py that python evaluates default
arguments when it defines the function (which is the natural time to do
it).  This behaviour isn't restricted to lists - you can get it
happening with any mutable object such as a dictionary or class
instance.

Let's have a closer look at the code and see what's going on:

class Bla:
  def __init__(self, mylist = []):
    self.mylist = mylist

When Python rund this, it hits the method definition and says "Aha!
mylist has a default value - it's the list object [] - I'd better
squirrel away an internal reference to this in case I need it later"

>>> x = Bla()

When Python sees this is says "Whoa - not enough arguments... but wait,
there's a default for mylist - it's this empty list that I held onto
earlier.  Good thing I remembered."  But then it creates x.mylist (aka
self.mylist) to point to the same empty list (BTW, there can be more
than one different copy of the empty list, there's nothing canonical
about it).

>>> y = Bla()

Same thing happens here, creating y.mylist.

So now Python has three references to the single list object - the
anonymous internal reference held by Bla.__init__, x.mylist and
y.mylist.

Use any one of these to mutate the list - by appending or whatever -
and all three will appear to change.  Such as:

x.mylist.append('a')

As a side note, the object is not completely anonymous - if you want to
see it, have a look at Bla.__init__.im_func.func_defaults and you'll see
it's a tuple with the list object as one of it's entries.

>>> Bla.__init__.im_func.func_defaults
(['a'],)

> So python programmers have to be aware of class variables (especially
> when they're anonymous). Huh, that's realy hard to get but I think it'll
> become better when writing some python progs :-) .

I'm not sure how much you need to be aware of them - all you need to
know is that you should avoid the sort of default variable as above. 
It's not really even something to do with class variables.  Consider
the following:

Python 1.5.2 (#2, Apr 15 1999, 13:12:41)  [GCC 2.7.2] on sunos5
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> def f(x = []):
...   x.append('a') 
...   print x
... 
>>> f()   
['a']
>>> f()
['a', 'a']
>>> f(['wibble'])
['wibble', 'a']
>>> f()
['a', 'a', 'a']

Same sort of thing is happening here, this time with an anonymous global
list (or if you prefer, it's not anonymous, it's hiding in
f.func_defaults).

You don't really need to understand what's going on here to avoid
problems, rather you just need to remember the rule:

"Avoid mutable default values in functions and methods"

I can't think of a situation where this sort of default argument
behaviour would be useful, although I'm sure Tim Peters could come up
with one <wink>.

Corran