0

I've got a list of objects of type Person. Each person in the list has a report field. I want to iterate over my list of people, and as I do I want to individually update each person's report. The code for this is as follows:

class People:
    def __init__(self, name, age, report = list()):
        self.name = name
        self.age = age
        self.report = report

def get_all_people():
    p = []
    p.append(People('Eve', 25))
    p.append(People('Fred', 19))
    p.append(People('Gale', 104))
    p.append(People('Harry', 39))
    return p
    
def list_of_people():
    friends = ['alice', 'bob', 'carla', 'dave']
    people_list = get_all_people()
    for person in people_list:
        person.report.append(f'This is the first line I added for {person.name}')
        for i, friend in enumerate(friends):
            person.report.append(f'{friend} is {person.name}s {str(i)} friend')
        print(f'FRIEND REPORT FOR: {person.name}\n {person.report}')


if __name__ == '__main__':
    list_of_people()

However, when I run this I get the following output:

FRIEND REPORT FOR: Eve
 ['This is the first line I added for Eve', 'alice is Eves 0 friend', 'bob is Eves 1 friend', 'carla is Eves 2 friend', 'dave is Eves 3 friend']

FRIEND REPORT FOR: Fred
 ['This is the first line I added for Eve', 'alice is Eves 0 friend', 'bob is Eves 1 friend', 'carla is Eves 2 friend', 'dave is Eves 3 friend', 'This is the first line I added for Fred', 'alice is Freds 0 friend', 'bob is Freds 1 friend', 'carla is Freds 2 friend', 'dave is Freds 3 friend']

FRIEND REPORT FOR: Gale
 ['This is the first line I added for Eve', 'alice is Eves 0 friend', 'bob is Eves 1 friend', 'carla is Eves 2 friend', 'dave is Eves 3 friend', 'This is the first line I added for Fred', 'alice is Freds 0 friend', 'bob is Freds 1 friend', 'carla is Freds 2 friend', 'dave is Freds 3 friend', 'This is the first line I added for Gale', 'alice is Gales 0 friend', 'bob is Gales 1 friend', 'carla is Gales 2 friend', 'dave is Gales 3 friend']

FRIEND REPORT FOR: Harry
 ['This is the first line I added for Eve', 'alice is Eves 0 friend', 'bob is Eves 1 friend', 'carla is Eves 2 friend', 'dave is Eves 3 friend', 'This is the first line I added for Fred', 'alice is Freds 0 friend', 'bob is Freds 1 friend', 'carla is Freds 2 friend', 'dave is Freds 3 friend', 'This is the first line I added for Gale', 'alice is Gales 0 friend', 'bob is Gales 1 friend', 'carla is Gales 2 friend', 'dave is Gales 3 friend', 'This is the first line I added for Harry', 'alice is Harrys 0 friend', 'bob is Harrys 1 friend', 'carla is Harrys 2 friend', 'dave is Harrys 3 friend']

From looking at this you can see that each report seems to start where the last one left off. Why is the compiler doing this? And, importantly, what do I need to do to my code to make it update each list individually?

Thanks!

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
JMcK
  • 100
  • 1
  • 9
  • 1
    Does this answer your question? ["Least Astonishment" and the Mutable Default Argument](https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument) – ForceBru May 06 '21 at 19:50
  • sort of, but its not clear how to modify the code to get around the issue? The data I'm actually working with is pulled from a database so I'm pulling them, turning them into objects and then passing them iterating through. One solution might be to handle them as Dictionaries but that seems quite messy. any ideas? – JMcK May 06 '21 at 20:01
  • 2
    [This answer provides a way around this](https://stackoverflow.com/a/11416002/4354477) – ForceBru May 06 '21 at 20:08
  • @ForceBru brilliant, thanks! so I changed the default from `List` to `None` in the class definition and tested to see if it was `None` and set it to a list. If you leave an answer here I'll mark it as the answer. I think it would be helpful for anyone who has the same issue. The link you initially share has a lot to information to process before you get to the solution. – JMcK May 06 '21 at 20:16

1 Answers1

1

I'm posting this just in case anyone else has the same issue. I got the solution from ForceBru's comment, which pointed me to this Stackoverflow question on “Least Astonishment” and the Mutable Default Argument. The answer is basically to modify the Person Class changing the default value of the report variable from list() to None. Then checking if the value is None and if so setting it to a list. The code is below.

class Person:
    def __init__(self, name, age, report = None):
        self.name = name
        self.age = age
        if report is None:
            self.report = []

def get_all_people():
    p = []
    p.append(Person('Eve', 25))
    p.append(Person('Fred', 19))
    p.append(Person('Gale', 104))
    p.append(Person('Harry', 39))
    return p
    
def list_of_clients():
    friends = ['alice', 'bob', 'carla', 'dave']
    people_list = get_all_people()
    for person in people_list:
        person.report.append(f'This is the first line I added for {person.name}')
        for i, friend in enumerate(friends):
            person.report.append(f'{friend} is {person.name}s {str(i)} friend')
        print(f'FRIEND REPORT FOR: {person.name}\n {person.report}')


if __name__ == '__main__':
    list_of_clients()
JMcK
  • 100
  • 1
  • 9