views:

107

answers:

7

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

+1  A: 

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
`is` is an idiomatic testing for `None`
SilentGhost
+12  A: 

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.

David Wolever
Right. In general you probably do not want to use mutable types as default values.
prestomation
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
+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
+3  A: 

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
A: 

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
+3  A: 

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
+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
+1  A: 

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
+1  A: 

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

Tomasz Zielinski