Python adding to unintended list in a dictionary

Viewed 59
class Animal:
    def __init__(self, name='', child=[]):
        self.name=name
        self.child=child

val = []


dog = Animal(name="Foo")
val.append(dog.__dict__)

cat = Animal(name='John')
cat.child.append('Doe')
val.append(cat.__dict__)

print(val)

Result:

[{'name': 'Foo', 'child': ['Doe']}, {'name': 'John', 'child': ['Doe']}]

Expected:

[{'name': 'Foo', 'child': []}, {'name': 'John', 'child': ['Doe']}]

I am assuming it's something to do with memory allocation but can't work around my head how it works.

2 Answers

dictionaries are reference type variables. A reference is a name that refers to the specific location in memory of a value (object). it means after you instantiate a dict it always point to the same location in memory. no matter if you edit your dict. so an easy solution to you code is:

class Animal:

    def __init__(self, name='', child=None):
        self.name=name
        self.child=child
        if self.child is None:
            self.child = []
    

val = []

dog = Animal(name="Foo")
print(dog.__dict__)
val.append(dog.__dict__)

cat = Animal(name='John')
cat.child.append('Doe')
val.append(cat.__dict__)

print(val)

this way every time you create an object from Animal class, it reserves a new place in memory.

You can use dataclasses and set the default factory for lists

from dataclasses import dataclass, field, asdict
from typing import List

@dataclass
class Animal:
    name : str 
    children : List[str] = field(default_factory=list)

a=Animal(name="Foo")
b=Animal(name='John')
b.children.append('Doe')

val = [
    asdict(x) for x in [a,b]
]

print(val)
Related