0

I learned Python last year, and was just getting back into it with a new project. I've hit a snag right at the beginning, and after a bunch of poring through documentation I'm still stumped. I have to believe it's something simple, but I can't find it. Here's the issue:

I have the following set up in a file:

class Wrapper(object):
    def __init__(self, title=None):
        self.header = Thing("HEADER")
        if title is not None:
            self.header.content.append( Thing("TITLE", content=[str(title)]) )
        self.body = Thing("BODY")
        self.content = Thing("COMPLETE", content=[self.header, self.body])

class Thing(object):
    def __init__(self, name, content=[]):
        self.name = name
        self.content = content

Then from an interactive prompt I do:

>>> import things
>>> a = things.Wrapper("This is a title")

Now, I would expect at this point that the body attribute of a would be a Thing instance with the name "BODY" and a content consisting of an empty list. What I'm surprised to find is that its content is actually a list containing the same "TITLE" instance that a.header.content holds.

>>> a.header.name
'HEADER'
>>> a.header.content
[<test.Thing object at 0xb752290c>]
>>> a.body.name
'BODY'
>>> a.body.content
[<test.Thing object at 0xb752290c>]
>>> a.body.content[0].name
'TITLE'
>>> a.body.content[0].content
['This is a title']

I can't for the life of me figure out how a.body.content got assigned that way. Can anyone shed some light on this?

glibdud
  • 7,550
  • 4
  • 27
  • 37
  • 4
    Please view this answer / explanation: ["Least Astonishment" in Python: The Mutable Default Argument](http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument) – g.d.d.c Aug 08 '12 at 15:39

1 Answers1

1

Try this

class Thing(object):
    def __init__(self, name, content=None):
        if content is None:
            content = []
        self.name = name
        self.content = content

The issue with [] as a default is that the default parameters are only evaluated once, when the interpreter first creates the function. For an int or other immutable type this is fine, but for mutable types (lists, dictionaries, anything that can be changed in place) this causes problems. What winds up happening is that all instances of Thing share the same content.

This way, the empty list is created each time you call the constructor, creating a new list every time.

Ryan Haining
  • 35,360
  • 15
  • 114
  • 174