views:

106

answers:

3

I'm experiencing a (for me) very weird problem in Python.

I have a class called Menu: (snippet)

class Menu:
    """Shows a menu with the defined items"""
    menu_items = {}
    characters = map(chr, range(97, 123))

    def __init__(self, menu_items):
        self.init_menu(menu_items)

    def init_menu(self, menu_items):
        i = 0
        for item in menu_items:
            self.menu_items[self.characters[i]] = item
            i += 1

When I instantiate the class, I pass in a list of dictionaries. The dictionaries are created with this function:

def menu_item(description, action=None):
    if action == None:
        action = lambda : None
    return {"description": description, "action": action}

And then the lists are created like this:

t = [menu_item("abcd")]
m3 = menu.Menu(t)

a = [ menu_item("Test")]
m2 = menu.Menu(a)

b = [   menu_item("Update", m2.getAction),
                      menu_item("Add"),
                      menu_item("Delete")]
m = menu.Menu(b)

When I run my program, I everytime get the same menu items. I've run the program with PDB and found out as soon as another instance of a class is created, the menu_items of all previous classes are set to latest list. It seems as if the menu_items member is static member.

What am I overseeing here?

+15  A: 

The menu_items dict is a class attribute that's shared between all Menu instances. Initialize it like this instead, and you should be fine:

class Menu:
    """Shows a menu with the defined items"""
    characters = map(chr, range(97, 123))

    def __init__(self, menu_items):
        self.menu_items = {}
        self.init_menu(menu_items)

    [...]

Have a look at the Python tutorial section on classes for a more thorough discussion about the difference between class attributes and instance attributes.

Pär Wieslander
What is the reason they are shared this way?
Ikke
No, it's because you're defining `menu_items` as a class attribute (directly inside `class Menu`) rather than as a instance attribute (initialized inside a method of the class).
Pär Wieslander
The reason it works this way (much like static attributes in other languages) is that you sometimes want to be able to share data between instances. I added a link to the Python tutorial in my answer where you can read more about how class and instance attributes work.
Pär Wieslander
Okay, I incorrectly thought that those were instance members, but they are class members thus.
Ikke
+5  A: 

Since Pär answered your question here is some random advice: dict and zip are extremely useful functions :-)

class Menu:
    """Shows a menu with the defined items"""
    characters = map(chr, range(97, 123))

    def __init__(self, menu_items):
        self.menu_items = dict(zip(self.characters, menu_items))
THC4k
Thanks, I forgot about the zip function.
Ikke
A: 

Duplicate question: see also why-do-attribute-references-act-like-this-with-python-inheritance

Peter Hansen