Even more memory
An example: ecology simulator
Imagine we’re doing a simulation of animal behavior patterns, as in this Python file. We have a couple of classes:
@dataclass class Habitat: food: int # could have other fields here--location, temperature, etc. @dataclass class Animal: time_since_food: int food_consumption: int habitat: Habitat
Each habitat is going to support multiple animals, who will consume the food in that habitat via this function:
def eat(animal: Animal): animal.habitat.food = animal.habitat.food - animal.food_consumption animal.time_since_food = 0
Let’s say we wanted to create a couple of animals sharing the same habitat, which will start with 50 food. How would we do it?
Option 1:
> animal1 = Animal(0, 10, Habitat(50)) > animal2 = Animal(0, 20, Habitat(50))
Option 2:
> animal1 = Animal(0, 10, Habitat(50)) > animal2 = Animal(0, 20, animal1.habitat)
Option 3:
> habitat = Habitat(50) > animal1 = Animal(0, 10, habitat) > animal2 = Animal(0, 20, habitat)
Option 4:
> food = 50 > animal1 = Animal(0, 10, Habitat(food)) > animal2 = Animal(0, 20, Habitat(food))
See the lecture capture for the details.
Circular references
Let’s say we want a Habitat
to have a list
of all of the animals in that
habitat:
@dataclass class Habitat: food: int animals: list
We could try to set this up as follows:
habitat = Habitat(50, [Animal(0, 10,
We get stuck here–what should we list as the habitat of the animal we’re building?
We can do something like this:
habitat = Habitat(50, [])
habitat.animals.append(Animal(0, 10, habitat))
habitat.animals.append(Animal(0, 20, habitat))
We have circular references here–the habitat references each animal, and each animal references the habitat. It can be helpful to draw arrows in memory to keep track of this (see the lecture capture).
We could write a function to do this:
def animal_in_habitat(habitat: Habitat, food_consumption: int) -> Animal: animal = Animal(0, food_consumption, habitat) h.animals.append(animal) return animal
How would we test this function?
def test_animal_in_habitat(): h = Habitat(50, []) a = animal_in_habitat(h, 10) #test("habitat correct", h, Habitat(50, [Animal(0, 10, ...)])) test("habitat has animal", h.animals. [a]) test("animal has habitat", a.habitat, h)
Dictionaries in memory
In memory, dictionaries work very much like lists: when we build a new dictionary we add it to memory, and its keys/values work much like individual indexes in a list.
Going back to our Coord
example, what happens when we execute the following
code?
@dataclass class Coord: x: int y: int objects = { "tree": Coord(1, 2), "rock": Coord(4, 3) } objects["rock"].x = 5 objects["bird"] = objects["tree"]