Cleaner Alternative to Nested If/Else - python

I'm mainly focused on an alternative to if/else's in create_animal. If there is a more professional way to handle this.
In this case, it's a classifier based on a variable number of traits needed to figure out what the animal is. Since humans are the only animals that speak English, that property is sufficient. But if they roar instead Bear and Lion need an additional property of habitat to figure it out. I know I could group those conditionals more succinctly, but that's not what I'm trying to illustrate.
class Human:
self.family = 'Hominidae'
self.order = 'Primate'
class Bear:
self.family = 'Ursidae'
self.order = 'Carnivora'
class Lion:
self.family = 'Felidae'
self.order = 'Carnivora'
def create_animal(language, roars, habitat):
if language == 'English':
return Human()
elif roars == True:
if habitat == 'forest':
return Bear()
elif habitat == 'savannah':
return Lion()
animal1 = create_animal(None, roars=True,habitat='forest') # Will be a bear
animal2 = create_animal(language = 'English', roars=False,habitat='town') # Will be a human
animal3 = create_animal(language = None, roars=True,habitat='savannah') # Will be a lion
This will work, but for some real world complexity, I don't like how nasty the nested if/else's are getting and I figure there must a good way to do it with a classification map like this, but I'm not sure how to approach it.
species_classification_map = {
'speaks_english':Human(),
'roars':{
'forest':Bear(),
'savannah':Lion()
}}

One option that at least standardizes your function inputs is to have each animal classified by it's language and habitat and store it in a dictionary
class Human():
def __init__(self):
self.family = 'Hominidae'
self.order = 'Primate'
class Bear():
def __init__(self):
self.family = 'Ursidae'
self.order = 'Carnivora'
class Lion():
def __init__(self):
self.family = 'Felidae'
self.order = 'Carnivora'
def create_animal(language, habitat):
#dict of language, habitat
animals={('english','civilization'):Human(),
('roars','forest'):Bear(),
('roars','savannah'):Lion()}
#human habitat could also be None for simplicity
return animals[(language,habitat)]
b=create_animal('roars','forest')
b
<__main__.Bear at 0x210c8795460>
some_dude=create_animal('english','civilization')
some_dude
<__main__.Human at 0x210c87953a0>

You can define a matrix as a pandas.DataFrame object, whose columns are your animal characteristics, including your animal's name and each row is a record of animal species. Then, when you need to create a new animal with some characteristics, you can easily locate the columns that have a positive value.
Does this satisfy your requirements?

Related

Python: how to utilize instances of a class

New to OOP and python, I am struggling enormously to grasp what good classes actually are for. I tried to ask help from a lecturer who said "oh, then you should read about general methods to classes". Been putting in a days work but get no where.
I get it that a class allow you to collect an instance structure and methods to it, like this:
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
def show_list(self):
print(self.item_id, self.item_name)
idA = Items("idA", "A")
idA.show_list()
But what is even the point of a class if there were not MANY instances you would classify? If I have a method within the class, I must hard code the actual instance to call the class for. What if you want a user to search and select an instance, to then do operations to (e.g. print, compute or whatever)??
I thought of doing it like this:
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
def show_list(self):
print(self.item_id, self.item_name)
idA = Items("idA", "A")
idB = Items("idB", "B")
select_item = input("enter item id")
select_item.show_list()
Replacing hard coded variable with input variable doesn't work, probably logically. I then played with the idea of doing it like this:
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
iL = [Items('idA', 'A'), Items('idB', 'B')]
selected_item = input("enter item id")
for selected_item in iL:
print(f'{selected_item.item_id} {selected_item.item_name}')
Now all are called thanks to making it a list instead of separate instances, but how do I actually apply code to filter and only use one instance in the list (dynamically, based on input)?
I would love the one who brought me sense to classes. You guys who work interactively with large data sets must do something what I today believe exist in another dimension.
See examples above^^
It seems you want to find all the instances of a certain element within a class.
This is as simple as:
print([x for x in iL if x.item_id == selected_item])
Now, you may ask why you can't just store the elements of iL as tuples instead of classes. The answer is, you can, but
("idA", "A")
is much less descriptive than:
item_id = "idA"
item_name = "A"
Any code you write with classes, you should in theory be able to write without classes. Classes are for the benefit of the coder, not the end-user of the program. They serve to make the program more readable, which I'm sure you'll find is a desirable property.
Your point here is to lookup for Items instances based on their item_id attribute.
That's a thing to create instances of a class.
It's a completely different thing to search for items objects stored in memory - that is not directly linked to the concept of OOP, classes and instances.
You could use dictionary to store references of your objects and then lookup in your dictionary.
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
def show_list(self):
print(self.item_id, self.item_name)
idA = Items("idA", "A")
idB = Items("idB", "B")
lookup_dict = {"idA": idA, "idB": idB}
select_item = input("enter item id")
found_item = lookup_dict.get(select_item)
if found_item:
found_item.show_list()
else:
print(f"item {select_item} not found")

How do I list out all the instances of a subclass?

So I'm looking for a way to list out all the instances of a subclass either through a regular method or a classmethod.
For example:
class Player:
def __init__(self,role, abilities, visiting, unique,Type)
self.role = role
self.abilities = abilities
self.unique = unique
self.visiting = visiting
self.Type= Type
class Town(Player):
def __init__(self,role,abilities,visiting,unique,Type):
super().__init__(role,abilities,visiting,unique,Type)
Bodyguard= Town('Bodyguard', 'Choose someone to protect','Yes','No', 'Town Protective')
Crusader = Town('Crusader','Protect someone each night','Yes','No','Town Protective')
.
.
.
I want to be able to group up all the Type='Town Protective' and print out a list of them. for example
print(Town_Protectives)
Displays:
['Bodyguard','Crusader'....]
This is just a little project used to help me learn Python so it's nothing serious. Thanks for all of your help!
Bodyguard= Town('Bodyguard', 'Choose someone to protect','Yes','No', 'Town Protective')
Crusader = Town('Crusader','Protect someone each night','Yes','No','Town Protective')
Town_Protectives = [Bodyguard,Crusader]
print([p.role for p in Town_Protectives if p.Type == 'Town Protective'])
One clean way to do that is to have a class-attribute that will keep an explicit reference to each object created on that class, and then either accessing that straight, or having a classmethod to filter the isntances you want.
class Player:
_register = []
def __init__(self,role, abilities, visiting, unique,Type)
self.role = role
self.abilities = abilities
self.unique = unique
self.visiting = visiting
self.Type= Type
self.__class__._register.append(self) # the ".__class__." is not strictly needed;
#classmethod
def list(cls, Type=None):
results = []
for instance in self._results:
if isinstance(instance, cls) and (Type is None or Type == instance.Type):
results.append(instance)
return results

Pass enum type as constructor argument

I am new to python and I would like to pass an enum as an argument to a constructor, within a function.
EDIT: I am working on a program with a class that has to organize different types of data, but most of these data types can be treated the same way. This data won't be all be added at the same time or in a foreseeable order. I would therefore like to keep the same functions, and just change the way the constructor stores the data. Let's consider this simpler example:
Say I have an enum
from enum import Enum, auto
class HouseThing(Enum):
people = auto()
pets = auto()
furniture = auto()
And I have a class House that can contain some or all of those things
class House():
def __init__(self, address, people = None, pets = None,
furniture = None):
self.address = address,
if self.people is not None:
self.people = people
etc....
And now I want to have a function that makes new furbished houses, but I want to use a function that could be used for any house:
house_things = HouseThing.furniture
def make_house_with_some_house_things(neighborhood, house_things):
neighborhood.append(House(house_things.name = house_things.name))
Is there a way to do this without first testing what kind of HouseThing house_things is first? house_things.name passes a string, but I would like it to be able to use it as a keyword.
I'm not sure exactly what you are trying to achieve here, but for the sake of solving the puzzle:
First, change House to determine what it has been passed:
class House():
def __init__(self, address, *house_things):
self.address = address
for ht in house_things:
if ht is HouseThings.people:
self.people = ht
elif ht is HouseThings.pets:
self.pets = ht
elif ht is HouseThings.furniture:
self.furniture = ht
else:
raise ValueError('unknown house thing: %r' % (ht, ))
Then, change make_house_with_some_house_things to just pass the house things it was given:
def make_house_with_some_house_things(neighborhood, house_things):
neighborhood.append(House(house_things))

Python Dictionary Manipulation in rpg

I am making a basic python RPG for my daughter and looking for the best way to store character stats that can be added a and subtracted from. Right now, I am using a dictionary so that she can view a list of her stats; however, I can't see a way to automatically add or subtract from objects in the list.
For example, I have
CatAbilities = {'speed': 5, 'claw': 3}
etc. And I want to have speed go down by 2 when, for example, her cat runs to avoid a dog. Is there a better way to do this while keeping a structure that let's her view her stats easily?
Why not use classes?
class Animal:
def __init__(self):
self.Abilities = {}
def PrintStats(self):
print self.Abilities
class Cat(Animal):
def __init__(self):
self.Abilities = {'speed': 5, 'claw': 3}
def ChasedByDog(self):
self.Abilities['speed'] -= 2
def main():
Kitty = Cat()
Kitty.PrintStats()
Kitty.ChasedByDog()
Kitty.PrintStats()
if(__name__ == '__main__'):
main()

Total newbie... Need help understanding python classes [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
My understanding of python is zero to none... Been exhausting myself reading what seems like a hundred different ways to approach this. Below I put the assignment description and my code... As of now I am having trouble using the 'getAnimal()' command. I'm not sure what it does or how it works. Thanks in advance :D
Assignment description: "Write a class definition for a class 'Zoo.' It should have instance variables for animal type, herbivore/carnivore/omnivore, and inside/outside. It should also have a getAnimal() method to show the animals information. Write a separate "ZooDriver" class to a) create three instances of zoo animals, b) get user input on which animal to view (1,2,3)c) show the animal info suing getAnimal()."
~~~~~~~~ My code:
class Zoo:
def __init__(self, animal, animal_type, habitat):
self.animal = animal
self.animal_type = animal_type
self.habitat = habitat
user_input=raw_input("Enter the number 1, 2, or 3 for information on an animal.")
if user_input == "1":
my_animal = Zoo("Giraffe", "herbivore", "outside")
elif user_input == "2":
my_animal = Zoo("Lion", "carnivore", "inside")
elif user_input == "3":
my_animal = Zoo("Bear", "omnivore", "inside")
print "Your animal is a %s, it is a %s, and has an %s habitat." % (my_animal.animal, my_animal.animal_type, my_animal.habitat)
A class defines a type of thing. An instance is one thing of that type. For example, you could say Building is a type of thing.
Let's start with a class named Building:
class Building():
pass
Now, to create an actual building -- an instance -- we call it almost like it was a function:
b1 = Building()
b2 = Building()
b3 = Building()
There, we just created three buildings. They are identical, which isn't very useful. Maybe we can give it a name when we create it, and store it in an instance variable. That requires creating a constructor that takes an argument. All class methods must also take self as its first argument, so our constructor will take two arguments:
class Building():
def __init__(self, name):
self.name = name
Now we can create three different buildings:
b1 = Building("firehouse")
b2 = Building("hospital")
b3 = Building("church")
If we want to create a "driver" class to create three buildings, we can do that pretty easily. If you want to set an instance variable or do some work when you create an instance, you can do that in a constructor. For example, this creates such a class that creates three buildings and stores them in an array:
class Town():
def __init__(self):
self.buildings = [
Building("firehouse"),
Building("hospital"),
Building("church")
]
We now can create a single object that creates three other objects. Hopefully that's enough to get you over the initial hurdle of understanding classes.
OK I will try to answer the main question here: What a class is.
From Google: class /klas/
noun: class; plural noun: classes
1.
a set or category of things having some property or attribute in common and
differentiated from others by kind, type, or quality.
In programming, a class is just that. say, for example you have class Dog. Dogs bark "ruf ruff".
We can define the dog class in python using just that information.
class Dog:
def bark(self):
print "ruf ruff"
To use the class, it is instantiated by calling () its constructor:
Spot = Dog()
We then want Spot to bark, so we call a method of the class:
Spot.bark()
To further explain the details of the code here would be to go outside of the scope of this question.
Further reading:
http://en.wikipedia.org/wiki/Instance_%28computer_science%29
http://en.wikipedia.org/wiki/Method_%28computer_programming%29
As a direct answer:
class Zoo(object):
def __init__(self, name="Python Zoo"):
self.name = name
self.animals = list()
def getAnimal(self,index):
index = index - 1 # account for zero-indexing
try:
print("Animal is {0.name}\n Diet: {0.diet}\n Habitat: {0.habitat}".format(
self.animals[index]))
except IndexError:
print("No animal at index {}".format(index+1))
def listAnimals(self,index):
for i,animal in enumerate(self.animals, start=1):
print("{i:>3}: {animal.name}".format(i=i,animal=animal))
class Animal(object):
def __init__(self, name=None, diet=None, habitat=None):
if any([attr is None for attr in [name,diet,habitat]]):
raise ValueError("Must supply values for all attributes")
self.name = name
self.diet = diet
self.habitat = habitat
class ZooDriver(object):
def __init__(self):
self.zoo = Zoo()
self.zoo.animals = [Animal(name="Giraffe", diet="Herbivore", habitat="Outside"),
Animal(name="Lion", diet="Carnivore", habitat="Inside"),
Animal(name="Bear", diet="Herbivore", habitat="Inside")]
def run(self):
while True:
print("1. List Animals")
print("2. Get information about an animal (by index)"
print("3. Quit")
input_correct = False
while not input_correct:
in_ = input(">> ")
if in_ in ['1','2','3']:
input_correct = True
{'1':self.zoo.listAnimals,
'2':lambda x: self.zoo.getAnimal(input("index #: ")),
'3': self.exit}[in_]()
else:
print("Incorrect input")
def exit(self):
return
if __name__ == "__main__":
ZooDriver().run()
I haven't actually run this code so some silly typos may have occurred and the usual "off-by-one" errors (oops), but I'm fairly confident in it. It displays a lot of concepts your instructor won't expect you to have mastered yet (such as string formatting, most likely, and almost certainly hash tables with lambdas). For that reason, I STRONGLY recommend you not copy this code to turn in.

Categories

Resources