Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python base classes share attributes? [duplicate]

Tags:

python

Code in test.py:

class Base(object):
    def __init__(self, l=[]):
        self.l = l

    def add(self, num):
        self.l.append(num)

    def remove(self, num):
        self.l.remove(num)

class Derived(Base):
    def __init__(self, l=[]):
        super(Derived, self).__init__(l)

Python shell session:

Python 2.6.5 (r265:79063, Apr  1 2010, 05:22:20) 
[GCC 4.4.3 20100316 (prerelease)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import test
>>> a = test.Derived()
>>> b = test.Derived()
>>> a.l
[]
>>> b.l
[]
>>> a.add(1)
>>> a.l
[1]
>>> b.l
[1]
>>> c = test.Derived()
>>> c.l
[1]

I was expecting "C++-like" behavior, in which each derived object contains its own instance of the base class. Is this still the case? Why does each object appear to share the same list instance?

like image 926
tad Avatar asked May 21 '10 04:05

tad


2 Answers

You're making a common Python newcomer mistake.

See my answer here: How should I declare default values for instance variables in Python?

Briefly explained, Python interprets the class definitions only once. That means everything declared in the __init__() method is only created once. Or, in another words, your [] list default argument is only made once.

Then self.l = l assigns a reference to the same instance every time you create a new class, hence the behaviour you weren't expecting.

The Pythonic way is this (partial code):

def __init__(self, arg=None):
    if arg is None:
        arg = []
    self.arg = arg

Also, you should consider using a better naming convention than l, which is hard to read and might be mistaken as 1 or |.

like image 58
Xavier Ho Avatar answered Oct 22 '22 21:10

Xavier Ho


This is called the mutable default argument bug that is commonly made by people new to Python. When you give a mutable as a default argument, the same object gets used across instances when the default argument is required to be used. The get a better understand check the Important warning section in http://docs.python.org/tutorial/controlflow.html#default-argument-values

In your code, the instance a used the mutable default argument (a empty list object) in it's init call and when you created the instance of b, which in turn called Base's init method, again used the very same object that a used in it's init. On simpler words a.l and b.l point to the same list object.

A very similar discussion - "Least Astonishment" and the Mutable Default Argument

like image 44
Parthan Avatar answered Oct 22 '22 20:10

Parthan