The formatting of your code is a bit unusual : no line break between classes, line breaks in the middle of the functions, local variable names starting with an upper-case letter, etc. To be fair, your code does't even run on my setup because of the Fitness
variable.
A few tools exist to perform some checks on your Python code. Let's run then and see what they tell us :
pep8
fitness.py:11:1: E302 expected 2 blank lines, found 0
fitness.py:16:1: E302 expected 2 blank lines, found 0
fitness.py:30:18: W291 trailing whitespace
fitness.py:36:18: W291 trailing whitespace
fitness.py:40:5: E303 too many blank lines (2)
fitness.py:40:23: E231 missing whitespace after ','
fitness.py:42:41: W291 trailing whitespace
fitness.py:44:45: W291 trailing whitespace
fitness.py:53:35: W291 trailing whitespace
fitness.py:55:1: E302 expected 2 blank lines, found 1
fitness.py:66:5: E303 too many blank lines (2)
fitness.py:72:45:W291 trailing whitespace
pychecker
fitness.py:6: Imported module (os) not used
fitness.py:7: Imported module (sys) not used
fitness.py:8: Imported module (time) not used
fitness.py:21: Local variable (x) not used
fitness.py:44: Local variable (a) not used
fitness.py:58: Variable (Fitness) used before being set
fitness.py:59: Invalid arguments to (StartPopulation), got 2, expected
fitness.py:59: self is not first method argument
fitness.py:67: Invalid arguments to (FitnessMethod), got 1, expected 2 fitness.py:67: self is not first method argument
fitness.py:68: Invalid arguments to (Reproduce), got 1, expected 2 fitness.py:68: self is not first method argument
fitness.py:69: Invalid arguments to (Aging), got 1, expected 2
fitness.py:69: self is not first method argument
pyflakes
fitness.py:6: 'os' imported but unused
fitness.py:7: 'sys' imported but unused
fitness.py:8: 'time' imported but unused
fitness.py:9: 'call' imported but unused
fitness.py:58: local variable 'Fitness' (defined in enclosing scope on line 16) referenced before assignment
After fixing the different issues, your code looks like :
#!/usr/bin/python
"""Docstring for Python module"""
import random as r
class Entity(object):
def __init__(self, number):
self.number = number
self.chance = 10
self.age = 0
class Fitness(object):
def __init__(self, population):
self.population = population
def StartPopulation(self, pop, entities):
for _ in range(pop):
entity = Entity(r.randint(1, 10))
entities.append(entity)
def FitnessMethod(self, entities):
for x in entities:
if x.number == 5:
x.chance += 10
else:
x.chance += 0
if x.chance > 100:
x.chance = 100
else:
x.chance += 0
def Reproduce(self, entities):
x = entities[r.randint(0, (len(entities) - 1))]
if r.randint(0, 100) < x.chance:
random_children = r.randint(1, 3)
for _ in range(random_children):
entity = Entity(x.number)
entity.chance = x.chance
entities.append(entity)
x.age += 1
def Aging(self, entities):
for x in entities:
if x.age >= 1:
entities.remove(x)
def main():
pop = r.randint(5, 10)
entities = []
fitness = Fitness(pop)
fitness.StartPopulation(pop, entities)
for x in entities:
print x.number, x.chance
print "~~~~~~~~~~~~~\n"
raw_input()
for x in range(200):
fitness.FitnessMethod(entities)
fitness.Reproduce(entities)
fitness.Aging(entities)
for x in entities:
print x.number, x.age, x.chance
print "Generation_Mutation_Complete"
if __name__ == "__main__":
main()
Now, x.chance += 0
is probably not useful : let's get rid of it. Also, you can use min
if you want to be sure not to go further than 100:
def FitnessMethod(self, entities):
for x in entities:
if x.number == 5:
x.chance = min(100, x.chance+5)
Then, it seems like all method from Fitness
take entities
as a parameter. This is a good indicator that maybe it should be a member of the class. I also took this chance to move fitness_method + reproduce + aging
in a single method. Also, the StartPopulation
is a bit weird : maybe this should be in the __init__
method. Once integrated, one can easily realise that self.population
is never used.
Once re-simplified, your code becomes :
#!/usr/bin/python
"""Docstring for Python module"""
import random as r
class Entity(object):
def __init__(self, number):
self.number = number
self.chance = 10
self.age = 0
class Fitness(object):
def __init__(self, population):
self.entities = []
for _ in range(population):
self.entities.append(Entity(r.randint(1, 10)))
def fitness_method(self):
for x in self.entities:
if x.number == 5:
x.chance = min(100, x.chance+5)
def reproduce(self):
x = self.entities[r.randint(0, (len(self.entities) - 1))]
if r.randint(0, 100) < x.chance:
random_children = r.randint(1, 3)
for _ in range(random_children):
entity = Entity(x.number)
entity.chance = x.chance
self.entities.append(entity)
x.age += 1
def aging(self):
for x in self.entities:
if x.age >= 1:
self.entities.remove(x)
def run_step(self):
self.fitness_method()
self.reproduce()
self.aging()
def main():
fitness = Fitness(r.randint(5, 10))
for x in fitness.entities:
print x.number, x.chance
print "~~~~~~~~~~~~~\n"
raw_input()
for x in range(200):
fitness.run_step()
for x in fitness.entities:
print x.number, x.age, x.chance
print "Generation_Mutation_Complete"
if __name__ == "__main__":
main()
Now, a few points about list :
you shouldn't alter a container while looping over it. You'll find the explanation here and some information about the right way to do this here about what you should be doing in your aging
function. (Btw, aging
is not a very descriptive name for what the method does, the aging process is more the x.age += 1
part).
the constructor can be re-written using list comprehension
It would look like this :
def __init__(self, population):
self.entities = [Entity(r.randint(1, 10)) for _ in range(population)]
I have to go before I reach the interesting part but I am not convinced your code performs a proper evolutionnary algorithm. I suggest you have an additional look at ressources online and go back to your code.
Edit :
Anonymous' answer is probably the kind of answer you were expecting. However, I'll share with you a few other findings that I was about to post before the freeze.
You could add a default parameter chance
in the constructor of Entity
. This allows you to make Reproduce
more concise as it doesn't need to update chance
after the creation. You can even take this chance to use list comprehension again.
You do not need to keep track of the age of each entity : as soon as you were about to "make it older", you can remove it alltogether.
This is what you get afterwards :
class Entity(object):
def __init__(self, number, chance=10):
self.number = number
self.chance = chance
class Fitness(object):
def __init__(self, population):
self.entities = [Entity(r.randint(1, 10)) for _ in range(population)]
def fitness_method(self):
for x in self.entities:
if x.number == 5:
x.chance = min(100, x.chance+5)
def reproduce(self):
x = r.choice(self.entities)
if r.randint(0, 100) < x.chance:
# removing aging entity
self.entities.remove(x)
self.entities.extend(Entity(x.number, x.chance) for _ in range(r.randint(1, 3)))
def run_step(self):
self.fitness_method()
self.reproduce()