20

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

I'm very confused about the behavior of dictionaries as class instance variables in Python 3. The way I understand it, instance variables in Python have per-instance storage, unlike class variables which are per-class (similar to what some other languages call "static").

And this seems to hold true, except when the instance variable is a dictionary created from a default parameter. For example:

class Foo:
    def __init__(self, values = dict()):
        self.values = values

f1 = Foo()
f1.values["hello"] = "world"

f2 = Foo()
print(f2.values)

This program outputs:

{'hello': 'world'}

Huh? Why does the instance f2 have the same dictionary instance as f1?

I get the expected behavior if I don't pass in an empty dictionary as a default parameter, and just assign self.values to an empty dictionary explicitly:

class Foo:
    def __init__(self):
        self.values = dict()

But I can't see why this should make any difference.

Community
  • 1
  • 1
Channel72
  • 24,139
  • 32
  • 108
  • 180
  • 1
    It might be that the default parameters are only evaluated once, when the class is loaded. That way you're merely assigning the same reference as a default parameter. – Platinum Azure May 16 '11 at 17:28
  • Stack overflow has a nice "FAQ" function per tag. Here is the FAQ for the Python tag: http://stackoverflow.com/questions/tagged/python?sort=faq&pagesize=30 Your question is answered in question number 4. – Lennart Regebro May 16 '11 at 18:30

2 Answers2

18

This is a well known surprise in Python. The default parameters are evaluated when the function is defined, not when it is called. So your default parameter is a reference to a common dict. It has nothing to do with assigning it to class/instance variables.

If you want to use a default parameter, use None, and check it:

if values is None:
    self.values = {}
else:
    self.values = values
Thomas K
  • 39,200
  • 7
  • 84
  • 86
3

Default values are only evaluated once. You want something like this:

 class Foo:
     def __init__(self, values = None):
         self.values = values or dict()

If you supply a values, that'll get used. If not, values is evaluated as FALSE by the or operator and instantiates a new dict.

eduffy
  • 39,140
  • 13
  • 95
  • 92
  • 3
    This failes if anything falsy is passed as values - e.g. an empty instance of a custom `dict` subclass. –  May 16 '11 at 18:02