0

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

In Python 2.7, consider I have the following code:

class Base(object):
    # Variant 1
    def __init__(self, records=[]):
        self._records = records

    # Variant 2
    # def __init__(self, records=[]):
    #     self._records = []
    #     if records:
    #         self._records = records

    def append(self, value):
        self._records.append(value)

class ChildA(Base):
    pass

class ChildB(Base):
    pass

a = ChildA()
b = ChildB()
a.append(100)
b.append(200)

print a._records
print b._records

If I use variant 1 to initialize my base class, self._records behaves like a class variable. Executing the code using variant 1 to initialize my base class, I get the ouput:

[100, 200]
[100, 200]

Using variant 2 to initialize my base class, self._records behaves like a instance variable (as expected). Executing the code using variant 2 to initialize my base class, I get the output:

[100]
[200]

What is the difference between these both variants? Why does variant 1 work different to variant 2? Thanks a lot for your help!

Community
  • 1
  • 1

3 Answers3

1

Your default argument is [], which is a common pitfall with Python. See more in the tutorial:

Important warning: The default value is evaluated only once. This makes a difference when the default is a mutable object such as a list, dictionary, or instances of most classes.

hochl
  • 12,524
  • 10
  • 53
  • 87
1

It has nothing to do with inheritance, class or instance variables. Consider the next code:

>>> def f(a=[]):
...     a.append(1)
...     print a
...
>>> f.func_defaults
([],)
>>> f()
[1]
>>> f()
[1, 1]
>>> f.func_defaults
([1, 1],)

Default values for function parameters are evaluated only ones and stored within function object. Each time f is called it operates with the same list. Right as in your case.

Roman Bodnarchuk
  • 29,461
  • 12
  • 59
  • 75
0

As the others had put - this has to do with using an empty list as default value, not with class inheritance behavior at all.

The correct way to do it is:

class Base(object):
    # Variant 1
    def __init__(self, records=None):
        if records is None:
            records = []
        self._records = records

Therefore ensuring a new list instance is created each time the Base class is instantiated. The way you put it in your code, a list object is instantiated when your class body is parsed by Python - and that one instance of list is used as the default parameter each time the __init__ method is run. As the objects hold a reference and change that same list, the change is visible in all other objects which share the Base class.

jsbueno
  • 99,910
  • 10
  • 151
  • 209