Reputation: 561
This is going to look like class inheritance but I think it is not and there must be an easy way of doing the following. Take a look at this simple code:
class Land:
def __init__(self):
print "a new Land"
self.farms = []
def addfarm(self):
self.farms.append(Farm())
class Farm:
def __init__(self):
print "a new farm"
self.animals = []
def addanimal(self,name):
self.animals.append(Animal(name))
class Animal:
def __init__(self, name):
print "hi, I am %s" % name
self.name = name
USA = Land()
USA.addfarm()
USA.farms[0].addanimal('George')
USA.farms[0].addanimal('Martin')
USA.addfarm()
USA.farms[1].addanimal('Polly')
USA.farms[1].addanimal('Ralph')
Is there an easy way of getting all animals without doing?:
for eachfarm in USA.farms:
for each in eachfarm.animals:
print each.name
I am asking this because if for instance the user wants to add a new George to farm 0 I would like to quickly be able to say that name is taken. I would also be able to quickly run a function that gives me all animals in the land or all farms. Should I be writing functions for all that or Python got its own?
I am also interested on knowing if my nested class structure is not correct and could end up causing issues.
For instance, lets say I have a function that given an animal tells me the perfect food mix for it. I would like to be able to run that function on all my animals and write back into their object. If they are nested I am afraid the function may get confused!
Thanks!
Upvotes: 0
Views: 506
Reputation: 1473
Using nested classes like this is perfectly fine and is not about inheritance at all. However you may want to choose slightly different data structures.
You say that in each farm you only want to be able to have one animal of each name. However, you use a list to store them. A list allows you to have as many animals of the same name inside as you want to, so you'd need to perform that check yourself when you add another one.
However, you could use a dict
. A dict is an unordered data structure that links a key to a value. In your case you could use the name of the animal as the key and the Animal
object for the value. Checking if a key exists can be done in constant time (as compared to linear time with a loop), since internally a dict
is a hash table.
Example code might look like this:
class Land:
def __init__(self):
print "a new Land"
self.farms = []
def addfarm(self):
self.farms.append(Farm())
class Farm:
def __init__(self):
print "a new farm"
self.animals = {}
def addanimal(self,name):
if not name in self.animals:
self.animals[name] = Animal(name)
return True
return False
class Animal:
def __init__(self, name):
print "hi, I am %s" % name
self.name = name
USA = Land()
USA.addfarm()
USA.farms[0].addanimal('George')
USA.farms[0].addanimal('Martin')
USA.addfarm()
USA.farms[1].addanimal('Polly')
USA.farms[1].addanimal('Ralph')
This would prevent you from adding two animals of the same name to one farm, returning a boolean depending on whether the animal could be added to the farm or not.
To get all animals on all farms you will still need nested loops. But enabling iteration over the objects itself can be much nicer. If you do the following:
class Land(object):
def __init__(self):
print "a new Land"
self.farms = []
def addfarm(self):
self.farms.append(Farm())
def __iter__(self):
for farm in self.farms:
yield farm
class Farm(object):
def __init__(self):
print "a new farm"
self.animals = {}
def addanimal(self,name):
if not name in self.animals:
self.animals[name] = Animal(name)
return True
return False
def __iter__(self):
for name, animal in self.animals.iteritems():
yield animal
class Animal(object):
def __init__(self, name):
print "hi, I am %s" % name
self.name = name
You could then:
for farm in USA:
for animal in farm:
pass #do something here
According to your comment, you also want to be able to do land.getAllAnimals()
and farm.getAllAnimals()
. The latter is easily accomplished because farm
works as an iterator over all animals. If you want a list you can simply call list(farm)
.
For land.getAllAnimals()
there are two nice options. Both are to be added to the previous declaration.
class Land(object):
def getAllAnimals(self):
for farm in self:
for animal in farm:
yield animal
Option 2
from itertools import chain
class Land(object):
def getAllAnimals(self):
return chain(*self)
Both will return iterators over all animals. To cast these into a list, simply call list
on them. The former is easier to understand, but the latter is more concise and, in my opinion, nicer.
Upvotes: 2
Reputation: 1602
There is nothing wrong with nesting your loops, and it's just the way to do it. You might want to look into a more declarative approach, or you might want to store your data differently, but that's all just implementation detail and primarily a matter of taste.
Upvotes: 1