1

Possible Duplicate:
“Least Astonishment” in Python: The Mutable Default Argument

I'm obviously missing something here: can anyone explain why t1 "mysteriously" acquires the self.thelist value of t2? What am I doing wrong?

>>> class ThingyWithAList(object):
...     def __init__(self, alist=[]):
...         super(ThingyWithAList, self).__init__()
...         self.thelist = alist
... 
>>> t1 = ThingyWithAList()
>>> t1.thelist.append('foo')
>>> t1.thelist.append('bar')
>>> print t1, t1.thelist
<__main__.ThingyWithAList object at 0x1004a8350> ['foo', 'bar']
>>> 
>>> t2 = ThingyWithAList()
>>> print t2, t2.thelist
<__main__.ThingyWithAList object at 0x1004a8210> ['foo', 'bar']
Community
  • 1
  • 1
Simon Whitaker
  • 20,506
  • 4
  • 62
  • 79
  • 4
    check this anwser : http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument – mouad Jul 26 '11 at 11:35
  • @mouad - excellent, thanks! I did poke around on SO before posting this question but didn't spot this one. – Simon Whitaker Jul 26 '11 at 11:47

3 Answers3

3

Because the default argument 'alist=[]', creates only one list, once, when the module is read. This single list becomes the default argument for that __init__", and is shared by all your Thingys.

Try using None as a dummy symbol meaning "make a new empty list here". E.g.

def __init__(self, alist=None):
     super(ThingyWithAList, self).__init__()
     self.thelist = [] if alist is None else alist
Adrian Ratnapala
  • 5,485
  • 2
  • 29
  • 39
2

If I get it right, the object reference created with [] in the constructor remains the same. Try this instead:

>>> class ThingyWithAList(object):
...     def __init__(self, alist=None):
...         super(ThingyWithAList, self).__init__()
...         self.thelist = alist or []
... 
>>> t1 = ThingyWithAList()
>>> t1.thelist.append('foo')
>>> t1.thelist.append('bar')
>>> print t1, t1.thelist
<__main__.ThingyWithAList object at 0xb75099ac> ['foo', 'bar']
>>> 
>>> t2 = ThingyWithAList()
>>> print t2, t2.thelist
<__main__.ThingyWithAList object at 0xb7509a6c> []
igor
  • 2,090
  • 20
  • 32
  • Your "alist or []" better than my "[] if alist is None else alist". In Perl I automatically think of that kind of syntax, in Python it seems like a Perlism. – Adrian Ratnapala Jul 26 '11 at 11:42
  • I guess I’ve been too long into Perl to do it any other way. ;-) – igor Jul 26 '11 at 11:44
  • Excellent, thanks! (It's OK, I still think in Perl from time to time. :-)) – Simon Whitaker Jul 26 '11 at 11:45
  • 1
    It's very unlikely it matters, but this will result in thelist being set to a new, empty list if it is passed an empty container. So you can't set thelist to be a container you already have a reference to (such as one that is an attribute of another object) this way. It's better to check against `None` since that's what you really mean. – agf Jul 26 '11 at 12:47
2

"Special cases aren't special enough", so checking conditionally for None rubs me the wrong way. If it's important that .thelist really is a list, then the class ought to enforce that itself, too.

def __init__(self, alist=()):
    super(ThingyWithAList, self).__init__()
    self.thelist = list(alist)

Note that there is no danger of modifying the default arg now, because tuples are immutable.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153