1

Possible Duplicate:
“Least Astonishment” in Python: The Mutable Default Argument
dictionary shared between objects for no reason?

     class Player():
        zones = {}
        def __init__(self):
            self.zones['hand'] = []
        def InitHand(self):
            for a in range(5):
                self.zones['hand'].append(a)
lst = []
lst.append(Player())
lst.append(Player())
lst[0].InitHand()
print lst[1].zones['hand']

This prints "[0, 1, 2, 3, 4]", but I only initialized the 0th element... Changing them to arrays as below fixes the problem, but for the life of me I can't figure out why this happens.

    class Player2():
        zones = []
        def __init__(self):
            self.zones = []
        def InitHand(self):
            for a in range(5):
                self.zones.append(a)
lst = []
lst.append(Player2())
lst.append(Player2())
lst[0].InitHand()
print lst[1].zones

This prints "[]" as expected

Community
  • 1
  • 1
Andy Ross
  • 15
  • 3
  • Related / dupe: ["Least Astonishment" in Python: The Mutable Default Argument](http://stackoverflow.com/q/1132941) – Martijn Pieters Jan 22 '13 at 17:53
  • @MartijnPieters I don't see any mutable default arguments in this question. – Mark Ransom Jan 22 '13 at 17:54
  • 3
    @MarkRansom: That's why I said 'related'. The principle is the same, the mutable attribute is shared between instances instead of function invocations in this case. – Martijn Pieters Jan 22 '13 at 17:55
  • In the dict version, `self.zones['hand']` is a *lookup* of `self.zones`, which finds the class variable, and then sets the `hand` dict key. In the array version, `self.zones = []` is *setting* a new instance variable, which then *overrides* the class variable. In this context, lookups cascade to outer scopes, but attribute-setting creates new variables in the inner scope. – andrewdotn Jan 22 '13 at 18:08

1 Answers1

3

In your code, all players share the same zones dictionary. Anything set in the class scope is a class attribute, not an instance attribute.

class Player():
    def __init__(self):
        self.zones = {}
        self.zones['hand'] = []
    def InitHand(self):
        for a in range(5):
            self.zones['hand'].append(a)
Eric
  • 95,302
  • 53
  • 242
  • 374
  • 1
    +1, but I would change that to, "anything set in the class scope is a class attribute," since instance attributes can be set in any context where instances may exist (including a class or static method). – Silas Ray Jan 22 '13 at 18:00