patternpythonMinor
Simulating a river ecosystem
Viewed 0 times
riversimulatingecosystem
Problem
I'm currently working my way through a textbook to enhance my beginner Python skills (Goodrich, Data Structures and Algorithms in Python). The first, somewhat in-depth problem involving OOP was the following:
Write a Python program to simulate an ecosystem containing bears and
fish. The ecosystem consists of a river, which is modeled as a list.
Each element should be a Bear object, a Fish object, or None. Animals
have attributes gender and strength. In each time step, each animal
randomly either moves to an adjacent tile or stays where it is. If a
fish encounters a bear, the fish gets eaten. If two animals of the
same type encounter each other, a new instance of that animal type is
generated if they are the same gender. Otherwise, they fight and the
one with larger strength survives.
I would really appreciate some feedback on my code. I am not doing this as part of a class, so it's hard to tell whether I'm doing well and where I need improvement.
```
#!/usr/bin/env python3
"""
Simulation of river environment containing bears and fish.
"""
import random
class Animal:
"""Animals are the inhabitants of the river"""
def __init__(self, gender = None, strength = None):
"""Initialize animal
gender gender of the animal (M, F) determines mating or fighting (default random)
strength stregth of animal, determines winner of fight (default random)
"""
if not gender:
self._gender = random.choice(['M','F'])
else:
self._gender = gender
if not strength:
self._strength = random.randint(0,9)
else:
self._strength = strength
def get_gender(self):
"""Return the animals gender"""
return self._gender
def get_stren(self):
"""Return the animals strength"""
return self._strength
class Bear(Animal):
def __init__(self, gender = None, strength = None):
super().__init__(gender, strength)
cla
Write a Python program to simulate an ecosystem containing bears and
fish. The ecosystem consists of a river, which is modeled as a list.
Each element should be a Bear object, a Fish object, or None. Animals
have attributes gender and strength. In each time step, each animal
randomly either moves to an adjacent tile or stays where it is. If a
fish encounters a bear, the fish gets eaten. If two animals of the
same type encounter each other, a new instance of that animal type is
generated if they are the same gender. Otherwise, they fight and the
one with larger strength survives.
I would really appreciate some feedback on my code. I am not doing this as part of a class, so it's hard to tell whether I'm doing well and where I need improvement.
```
#!/usr/bin/env python3
"""
Simulation of river environment containing bears and fish.
"""
import random
class Animal:
"""Animals are the inhabitants of the river"""
def __init__(self, gender = None, strength = None):
"""Initialize animal
gender gender of the animal (M, F) determines mating or fighting (default random)
strength stregth of animal, determines winner of fight (default random)
"""
if not gender:
self._gender = random.choice(['M','F'])
else:
self._gender = gender
if not strength:
self._strength = random.randint(0,9)
else:
self._strength = strength
def get_gender(self):
"""Return the animals gender"""
return self._gender
def get_stren(self):
"""Return the animals strength"""
return self._strength
class Bear(Animal):
def __init__(self, gender = None, strength = None):
super().__init__(gender, strength)
cla
Solution
Don't compare against
Don't compare against
Use
Use
Instead of getters like this:
It's more natural to use properties:
When you use this, instead of
I also dropped the redundant comment.
Generalize when possible
The current code has many hard-wired elements for Bear and Fish,
for example:
If later you want to add one more animal type,
you will have to edit the code in many places,
and it can be quite troublesome.
You can make the above example easier to extend like this:
Another opportunity for generalization is when you print letter symbols depending on the animal type:
It would be better to add a
And then you could use simply
Other simplifications
Since strings are iterables,
instead of the tedious:
You can write simpler:
Follow PEP8
PEP8 is the official Python coding style guide. Please follow it.
For example, instead of:
Write like this:
NoneDon't compare against
None with == or !=.Use
is None and is not None.Use
@property instead of gettersInstead of getters like this:
def get_stren(self):
"""Return the animals strength"""
return self._strengthIt's more natural to use properties:
@property
def stren(self):
return self._strengthWhen you use this, instead of
animal.get_stren(), you will have to write animal.stren (without parentheses).I also dropped the redundant comment.
Generalize when possible
The current code has many hard-wired elements for Bear and Fish,
for example:
for i in range(self._length):
rval = random.randint(1,3)
if rval == 1:
self._contents.append(Bear())
elif rval == 2:
self._contents.append(Fish())
else:
self._contents.append(None)If later you want to add one more animal type,
you will have to edit the code in many places,
and it can be quite troublesome.
You can make the above example easier to extend like this:
animal_types = (Bear, Fish)
len_animal_types = len(animal_types)
for _ in range(self._length):
rval = random.randint(0, len_animal_types)
if rval == len_animal_types:
self._contents.append(None)
else:
self._contents.append(animal_types[rval]())Another opportunity for generalization is when you print letter symbols depending on the animal type:
if type(x) == Bear:
s += 'B'
elif type(x) == Fish:
s += 'F'It would be better to add a
@property in Animal:@property
def symbol(self):
return self.__class__.__name__[0]And then you could use simply
x.symbol to get the initial letter of the class name of the Animal instance.Other simplifications
Since strings are iterables,
instead of the tedious:
self._gender = random.choice(['M', 'F'])You can write simpler:
self._gender = random.choice('MF')Follow PEP8
PEP8 is the official Python coding style guide. Please follow it.
For example, instead of:
def __init__(self, gender = None, strength = None):Write like this:
def __init__(self, gender=None, strength=None):Code Snippets
def get_stren(self):
"""Return the animals strength"""
return self._strength@property
def stren(self):
return self._strengthfor i in range(self._length):
rval = random.randint(1,3)
if rval == 1:
self._contents.append(Bear())
elif rval == 2:
self._contents.append(Fish())
else:
self._contents.append(None)animal_types = (Bear, Fish)
len_animal_types = len(animal_types)
for _ in range(self._length):
rval = random.randint(0, len_animal_types)
if rval == len_animal_types:
self._contents.append(None)
else:
self._contents.append(animal_types[rval]())if type(x) == Bear:
s += 'B'
elif type(x) == Fish:
s += 'F'Context
StackExchange Code Review Q#88520, answer score: 6
Revisions (0)
No revisions yet.