-1


I am trying to understand, even if in the comments is explained, what is the cause of the error in this code:

    """

    This program is part of an exercise in
    Think Python: An Introduction to Software Design
    Allen B. Downey

    WARNING: this program contains a NASTY bug.  I put
    it there on purpose as a debugging exercise, but
    you DO NOT want to emulate this example!

    """

    class Kangaroo(object):
        """a Kangaroo is a marsupial"""

        def __init__(self, contents=[]):
            """initialize the pouch contents; the default value is
            an empty list"""
            self.pouch_contents = contents

        def __str__(self):
            """return a string representaion of this Kangaroo and
            the contents of the pouch, with one item per line"""
            t = [ object.__str__(self) + ' with pouch contents:' ]
            for obj in self.pouch_contents:
                s = '    ' + object.__str__(obj)
                t.append(s)
            return '\n'.join(t)

        def put_in_pouch(self, item):
            """add a new item to the pouch contents"""
            self.pouch_contents.append(item)

    kanga = Kangaroo()
    roo = Kangaroo()
    kanga.put_in_pouch('wallet')
    kanga.put_in_pouch('car keys')
    kanga.put_in_pouch(roo)

    print kanga

    # If you run this program as is, it seems to work.
    # To see the problem, trying printing roo.

The explaination is reported here:

"""

This program is part of an exercise in
Think Python: An Introduction to Software Design
Allen B. Downey

This program explains and corrects a bug in BadKangaroo.py.
Before reading this, you should try to debug BadKangaroo.

"""

class Kangaroo(object):
    """a Kangaroo is a marsupial"""

    def __init__(self, contents=[]):
        # The problem is the default value for contents.
        # Default values get evaluated ONCE, when the function
        # is defined; they don't get evaluated again when the
        # function is called.

        # In this case that means that when __init__ is defined,
        # [] gets evaluated and contents gets a reference to
        # an empty list.

        # After that, every Kangaroo that gets the default
        # value get a reference to THE SAME list.  If any
        # Kangaroo modifies this shared list, they all see
        # the change.

        # The next version of __init__ shows an idiomatic way
        # to avoid this problem.
        self.pouch_contents = contents

    def __init__(self, contents=None):
        # In this version, the default value is None.  When
        # __init__ runs, it checks the value of contents and,
        # if necessary, creates a new empty list.  That way,
        # every Kangaroo that gets the default value get a
        # reference to a different list.

        # As a general rule, you should avoid using a mutable
        # object as a default value, unless you really know
        # what you are doing.
        if contents == None:
            contents = []
        self.pouch_contents = contents

    def __str__(self):
        """return a string representation of this Kangaroo and
        the contents of the pouch, with one item per line"""
        t = [ object.__str__(self) + ' with pouch contents:' ]
        for obj in self.pouch_contents:
            s = '    ' + object.__str__(obj)
            t.append(s)
        return '\n'.join(t)

    def put_in_pouch(self, item):
        """add a new item to the pouch contents"""
        self.pouch_contents.append(item)

kanga = Kangaroo()
roo = Kangaroo()
kanga.put_in_pouch('wallet')
kanga.put_in_pouch('car keys')
kanga.put_in_pouch(roo)

print kanga
print ''

print roo

but I, since I'm new in Python (and general) programming, yet don't understand it.

Thanks.

cerkiewny
  • 2,761
  • 18
  • 36
raffing
  • 1
  • 2

2 Answers2

1

The problem is that if you put the array in the member definition as a default, it is the same array for all instances of the class that do not pass one in. Thus, instead of each instance getting a new empty array, the contents are shared among all instances.

Alfred Rossi
  • 1,942
  • 15
  • 19
0

The problem is that a mutable object - the list is used in the init part of the class kangaroo. The reason this causes a problem is that mutable objects give you a reference to an object, and sometimes when you think you are making a new copy of the object, you are just making a new reference to the same object. The solution moves the definition so that it will be evaluated to a new list every time the class initialises, instead of just when the function init is defined as part of the kangaroo class.

micsthepick
  • 562
  • 7
  • 23
  • ok but why does it get evaluated as default the \_\_init\_\_ with the value contents = None? Is that a standard rule? – raffing May 08 '15 at 11:01
  • Its just a way of getting around the problem. It evaluates the default value as None, and then later on it tests if the value is none, and sets it to an empty list. The only problem is using a list as a default value in a function's variable. The list is only evaluated once, and so every time the function is called, it will point to the same list. – micsthepick May 09 '15 at 23:53
  • Ok it's clear now. I've done some exercise and I have unserstand it – raffing May 10 '15 at 07:13