0

Okay so this was driving me nuts all day.

Why does this happen:

class Foo:
    def __init__(self, bla = {}):
        self.task_defs = bla
    def __str__(self):
        return ''.join(str(self.task_defs))

a = Foo()
b = Foo()
a.task_defs['BAR'] = 1
print 'B is ==> %s' % str(b)
print 'A is ==> %s' % str(a)

Gives me the output:

B is ==> {'BAR': 1}
A is ==> {'BAR': 1}

I know it has to do with python passing everything by reference.

But why does this happen? This was literally making me go insane all day, basically causing me to tear my stuff apart. Shouldn't python be smart enough to deal with something like this?

Nosredna
  • 83,000
  • 15
  • 95
  • 122
UberJumper
  • 20,245
  • 19
  • 69
  • 87
  • Should this be considered a dupe? There's been lots of questions/problems people have had based on using a default mutable data type. – Mark Roddy Jul 15 '09 at 19:11
  • Here is one, just an hour ago: http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument – Lennart Regebro Jul 15 '09 at 19:12
  • Just an aside: there's no need to `''.join` a string (you create a single string with the `str` call). – cdleary Jul 15 '09 at 19:14
  • I voted to close the question, sorry i didn't realize others existed. I read the other topic and it makes more sense :) – UberJumper Jul 15 '09 at 19:15
  • @uberjumper no worries, it's VERY common problem that people run into. – Mark Roddy Jul 15 '09 at 19:17
  • Though I want to note that I'm not 100% committed to marking dupe. There are a lot of different questions that all have the same answer so I'm not sure whether or not that should count as a dupe. I just wanted to discuss to see what other people think. – Mark Roddy Jul 15 '09 at 19:44

1 Answers1

6

Since you have bla initially set to a mutable type (in this case a dict) in the arguments, it gets shared since bla doesn't get reinitialized to a new dict instance for each instance created for Foo. Here, try this instead:

class Foo:
    def __init__(self, bla=None):
        if bla is None:
            bla = {}
        self.task_defs = bla
    def __str__(self):
        return ''.join(str(self.task_defs))

a = Foo()
b = Foo()
a.task_defs['BAR'] = 1
print 'B is ==> %s' % str(b)
print 'A is ==> %s' % str(a)
Evan Fosmark
  • 98,895
  • 36
  • 105
  • 117
  • Yeah i know but that becomes very messy when dealing with constructors that require multiple optional arguments. I don't know i am just more or less frustrated that i wasted so much time on something like this. :( – UberJumper Jul 15 '09 at 19:11
  • It's nothing about multiple optional arguments. The problem is that you are using a mutable default, in this case the dictionary. – Lennart Regebro Jul 15 '09 at 19:14
  • No but if i have multiple optional arguments, lets say 5 optional arguments, that take lists/maps, then this becomes a fairly big pain. I think python should just fix it :) – UberJumper Jul 15 '09 at 19:21