5

Suppose you have something like this:

class intlist:
        def __init__(self,l = []):
                self.l = l
        def add(self,a):
                self.l.append(a)

def appender(a):
        obj = intlist()
        obj.add(a)
        print obj.l

if __name__ == "__main__":
        for i in range(5):
                appender(i)

A function creates an instance of intlist and calls on this fresh instance the method append on the instance attribute l.

How comes the output of this code is:

[0]

[0, 1]

[0, 1, 2]

[0, 1, 2, 3]

[0, 1, 2, 3, 4]

? If i switch

obj = intlist()

with

obj = intlist(l=[])

I get the desired output

[0]

[1]

[2]

[3]

[4]

Why this happens?

Thanks

Mirko Rossini
  • 409
  • 2
  • 9

7 Answers7

14

Ah, you've hit one of the common Python gotchas: default values are computed once, then re-used. So, every time __init__ is called, the same list is being used.

This is the Pythonic way of doing what you want:

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

For a bit more information, check out the Python docs (especially about three paragraphs after that heading).

Edit: There is a much better description in another answer.

Community
  • 1
  • 1
David Wolever
  • 148,955
  • 89
  • 346
  • 502
  • 4
    Right. In general you probably do not want to use mutable types as default values. – prestomation Mar 08 '10 at 16:44
  • Just a little note: In my initial answer, I used `self.l = l or []`… Which is, in this case, probably correct, but it's a dangerous habit (for example, this: `my_list = []; intlist(my_list); my_list.append(4)` might not do what you want). – David Wolever Mar 08 '10 at 16:51
  • +1: Standard problem -- mutable objects as default values. See all of these for explanations: http://stackoverflow.com/search?q=%5Bpython%5D+mutable+default+values – S.Lott Mar 08 '10 at 17:06
4

When you set the default value of l=[] in __init__, you're actually using the same list each time. Instead, you could try something like:

class intlist:
    def __init__(self, l=None):
        if l is None:
            self.l = []
        else:
            self.l = l
Michael Williamson
  • 11,308
  • 4
  • 37
  • 33
  • 1
    +1, `is None` is exactly the right way to check here (not `== None` or checks simply based on the truth value of `l` as other answers suggest!). – Alex Martelli Mar 08 '10 at 16:44
3

The issue is that when you are saying

def __init__(self,l = []):

You are telling Python to use the same list, [], for each invocation of the constructor. So each time obj = intlist() is called the same list is appended to.

What you should do instead is set l to a default value of None, which is a scalar (so your code will work as expected if it is used multiple times). Then, if l is None, initialize a new class member as []. Otherwise just assign the member variable to l.

Justin Ethier
  • 131,333
  • 52
  • 229
  • 284
1

The behavior occurs because all calls to your __init__ method share the same default list.

Try:

class intlist:
        def __init__(self, l):
                self.l = l if (l is not None) else []
        def add(self,a):
                self.l.append(a)

EDIT: Use is not, per SilentGhost

Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539
1

obj = intlist() calls your __init__() function which uses the same array for every instance of the class.

obj = intlist(l=[]) creates a new array for every instance.

Nick Zig
  • 11
  • 3
1

For more information I suggest reading this: http://effbot.org/zone/default-values.htm

Tomasz Zieliński
  • 16,136
  • 7
  • 59
  • 83
0

Be careful with default parameters of types like lists and dicts. Each instance of intlist gets that same list object from the default parameter.

Corey D
  • 4,689
  • 4
  • 25
  • 33