# Life and death model in python

The basic idea of the code is that agents come into the world, they age and then die. I want to keep track of who is dead and who is alive. Eventually, they will do more things but I think there should be a lot of things I am not doing best practice on. I assume I'm doing something wrong with "Global" as well.

# Classes

class Agent(object):
def __init__(self, identification, productivity, wealth, preferences, age, health, alive, sex):
self.identification = identification
self.productivity = productivity
self.wealth = wealth
self.preferences = preferences
self.age = age
self.health = health
self.alive = alive
self.sex = sex
def description(self):
print("My id is", self.identification)
print("My age is", self.age)

def Create_agents():
global id
alive.append(Agent(id,100,20,"blue",30,0,True,True ))
id += 1

# functions
def Initialize():
for x in range(3):
Create_agents()

def Death():
temp = alive.copy()
for agent in temp:
if agent.age>30:
agent.alive = False
print("Death of", agent.identification)
alive.remove(agent)

def Time_skip():
print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%SKIPPING TIME%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%")
global year
year += 1
print("The new year is", year)
for agent in alive:
agent.age +=1
Create_agents()
Death()

# Values
alive = []
id = 0
year = 0
Initialize()

# Testing
for agent in alive:
agent.description()

Time_skip()

for agent in alive:
agent.description()


The output is:

My id is 0
My age is 30
My id is 1
My age is 30
My id is 2
My age is 30
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%SKIPPING TIME%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The new year is 1
Death of 0
Death of 1
Death of 2
My id is 3
My age is 30

• Does this code work as expected? – pacmaninbw May 16 '20 at 17:57
• @pacmaninbw Yes, note the temp = alive.copy() change. – Peilonrayz May 16 '20 at 18:01

• Python has a standardized style guide called PEP 8. There is also Google's Python style guide which AFAIK is very similar to PEP 8.

Any code that goes largely against PEP 8 normally doesn't look Pythonic.

• Python has a naming scheme to allow developers to know the variables type by it's name only.

• Classes are in CamelCase,
• Functions, methods and variables are in snake_case, and
• Constants are in UPPER_SNAKE_CASE.
• There should be a single space after commas.

• Lines should be limited to 79 characters.
• There should be one space between all methods, and two between top level classes and functions.

• class Agent(object) is a relic from Python 2. In Python 2 classes didn't inherit from object by default. These are now called old style classes. However in Python 3 everything inherits from object by default - making all classes in Python 3 new style classes.

• Your code would benefit from another class.

I'm reluctant to suggest this as the code has an over-reliance on global variables. In the future you should try to solve the problem without globals and then see if it can be made cleaner by using a class.

And so we can make a Simulation class. I would include all the global functions you have in it except initialize.

• I would use an if __name__ == "__main__": guard to prevent your code from running if it's been imported accidentally.

This gets the following. There's some more changes that can be made, but the base of your code is good.

class Agent:
def __init__(self, identification, productivity, wealth, preferences, age, health, alive, sex):
self.identification = identification
self.productivity = productivity
self.wealth = wealth
self.preferences = preferences
self.age = age
self.health = health
self.alive = alive
self.sex = sex

def description(self):
print("My id is", self.identification)
print("My age is", self.age)

class Simulation:
def __init__(self):
self.alive = []
self._id = 0
self.year = 0

def create_agent(self):
self.alive.append(Agent(self._id, 100, 20, "blue", 30, 0, True, True))
self._id += 1

def deaths(self):
for agent in self.alive.copy():
if agent.age > 30:
agent.alive = False
print("Death of", agent.identification)
self.alive.remove(agent)

def time_skip(self):
print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%SKIPPING TIME%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%")
self.year += 1
print("The new year is", self.year)
for agent in self.alive:
agent.age += 1
self.create_agent()
self.deaths()

if __name__ == '__main__':
simulation = Simulation()
for _ in range(3):
simulation.create_agent()

# Testing
for agent in simulation.alive:
agent.description()

simulation.time_skip()

for agent in simulation.alive:
agent.description()

• Thanks, looks good. 1)What is the advantage of making the lists inside the class simulation? And what is the function of " name == 'main'"(i'm aware of what you wrote, i'm just hoping for more detail)? – Dio May 16 '20 at 18:40
• Actually, when i run it, there is an error: AttributeError: 'Simulation' object has no attribute 'id' – Dio May 16 '20 at 18:46
• @Dio 1) The benefit is that you're not relying on globals. It's hard to explain with the code that you have here as there isn't really a problem that the class solves. However globals lead to poor design choices. By wrapping it in a class you're preventing these bad choices prematurely. I understand it's not much of an explanation. You'll likely only understand it once you've been bitten by it once. 2) If the file is called foo.py you can run import foo from another file, with the if the code doesn't run. Without the if the code does. – Peilonrayz May 16 '20 at 18:50
• @Dio I made a couple of mistakes when converting to a class. They're now fixed. – Peilonrayz May 16 '20 at 18:53
• Great! thanks a bunch, @Peilonrayz you've been very helpful :) – Dio May 16 '20 at 18:57

Instead of using either global variables or another class to manage the collections of agents, you could use class variables that do that for you. Consider this (reduced) example of an Agent class:

class Agent:
id_ = 0
alive = set()

def __init__(self):
self.id = Agent.id_
Agent.id_ += 1
self.alive = True

def die(self):
self.alive = False
Agent.alive.remove(self)

def __repr__(self):
return f"Agent(id={self.id})"


which you can use like this:

agents = [Agent() for _ in range(3)]
print(Agent.alive)
# {Agent(id=1), Agent(id=0), Agent(id=2)}
agents[1].die()
print(Agent.alive)
# {Agent(id=0), Agent(id=2)}