0

Can someone please explain what's going on in the following? Why on earth does object b have the value of object a's list?

class Test:

    def __init__(self, A = []):
        self.A = A

    def __str__(self):
        return str(self.A)

def mutate_A():
    a = Test()
    a.A.append(1)

    return a

def problem_here():
    a = mutate_A()
    b = Test()

    print b  # why does this print [1] in python 2.7

problem_here()

Please let me know if I am being unclear or if more information is needed. Thank you.

rookie
  • 2,783
  • 5
  • 29
  • 43

1 Answers1

1

Because in python, the default arguments are evaluated only once (when the function is defined). Therefore, all instances of the class use the same list A

If however, you want each instance to have its own list, then you should do:

def __init__(self):
    self.A = []

>>> a = mutate_A()
>>> b = Test()
>>> print b
[]
sshashank124
  • 31,495
  • 9
  • 67
  • 76
  • Should mutable default arguments be avoided for this reason? How would you suggest fixing this problem? – rookie Apr 08 '14 at 03:03
  • 1
    This isn't quite right. The argument is evaluated when the function is defined, not when the function is called for the first time. – Ismail Badawi Apr 08 '14 at 03:04
  • @gjdanis, You shouls avoid it unless you specifically want that feature. How to fix it? I updated my answer – sshashank124 Apr 08 '14 at 03:04
  • @IsmailBadawi, Oops sorry, Thought to English mistranslation. Updated. Thanks – sshashank124 Apr 08 '14 at 03:05
  • @sshashank124: Thank you. This is quite astonishing... – rookie Apr 08 '14 at 03:06
  • @gjdanis, If my answer was helpful, would you mind accepting it? Thank you – sshashank124 Apr 08 '14 at 03:06
  • The usual fix if you want an argument with a default value that is mutable, but you want to avoid this oddity, is to use `None` as a sentinel value for the default and then use `self.A = A or []` in the body of `__init__` – Carl Meyer Apr 08 '14 at 03:07
  • @sshashank124: Yes, in 8 minutes. – rookie Apr 08 '14 at 03:07
  • @CarlMeyer, Yes. That is what I have done in my answer. Thank you. – sshashank124 Apr 08 '14 at 03:08
  • @sshashank124 Actually that isn't quite what you've done in your answer. You removed the argument entirely so it is no longer possible to pass in a value for `A`. – Carl Meyer Apr 08 '14 at 03:09
  • @CarlMeyer it is better to use something like `self.A = A if A is not None else []`, because there are other falsey things that could be passed in. – lvc Apr 08 '14 at 03:12
  • @lvc I think it's good to be aware that my boolean version will convert anything falsy to an empty list; I also think that most of the time in practice that is just fine (presuming the only valid inputs for that argument are lists). – Carl Meyer Apr 08 '14 at 03:18