2

Context:

This is a simplified version of the problem I came accross. I found a solution but still, I would like to find another one and fully determine the root cause of my mistake.

My problem:

This code creates a list of teams. Each team is implemented with a Team class which has an instance variable leader which is basically another class named Person. This Person class has a list of his/her family members (instances of Person Class).

The problem is that the list of family members points to the same object for different teams. My root cause analysis showed two possibilities:

  1. a list with a default value in the instance constructor (init) could probably put all the list in new Person instances to point the same list [].

  2. the list appending method is working with the same object (list) all the time.

The code with the bug:

class Team():
    def __init__(self, person):
        self.leader = person

    def __repr__(self):
        return self.leader

class Person():
    def __init__(self, name=None, family = []):    #notice here default value
        self.name = name
        self.family = family

    def __repr__(self):
        return "Name: {}, Family:\n\t ((- {} -))\n\t".format(self.name,self.family)

team = []

team.append(Team(Person('John')))
team[0].leader.family.append(Person('Jake'))

team.append(Team(Person('Lucy')))
team[1].leader.family.append(Person('Jesus'))

for i in team:
    print (i.leader)

Console Output 1:

As you can see, the appended family members Jake and Jesus, in the end, are wrongly assigned. In other words, they were added to the same list.

Name: John, Family:
     ((- [Name: Jake, Family:
     ((- [...] -))
    , Name: Jesus, Family:
     ((- [...] -))
    ] -))

Name: Lucy, Family:
     ((- [Name: Jake, Family:
     ((- [...] -))
    , Name: Jesus, Family:
     ((- [...] -))
    ] -))

My Solution: I removed the default value of the list (empty list by default) which now leaves me no option but to pass to the constructor an empty list every time I create a new Person Instance.

class Team():
    def __init__(self, person):
        self.leader = person

    def __repr__(self):
        return self.leader

class Person():
    def __init__(self, name, family):
        self.name = name
        self.family = family

    def __repr__(self):
        return "Name: {}, Family:\n\t((- {} -))\n\t".format(self.name,self.family)

team = []

team.append(Team(Person('John', [])))
team[0].leader.family.append(Person('Jake', []))

team.append(Team(Person('Lucy', [])))
team[1].leader.family.append(Person('Jesus', []))

for i in team:
    print (i.leader)

Console Output 2 (As expected):

Name: John, Family:
    ((- [Name: Jake, Family:
    ((- [] -))
    ] -))

Name: Lucy, Family:
    ((- [Name: Jesus, Family:
    ((- [] -))
    ] -))

Final Thoughts:

I would like to hear your comments and feedback. This is my first post so please excuse me for any inconvenience.

Thanks in advance for your help.

J. Reyes
  • 141
  • 2
  • 8
  • There are many discussions on the net regarding this problem, e.g. https://pythonconquerstheuniverse.wordpress.com/2012/02/15/mutable-default-arguments/ – rnso Feb 16 '18 at 17:41
  • 1
    This is a longstanding issue that I usually solve with `family=None` in the function args, and `self.family = family or []` in the function body. – Aaron Feb 16 '18 at 17:43
  • @Aran-Fey Thanks! very interesting! the mutable default trap :) – J. Reyes Feb 16 '18 at 18:11

0 Answers0