How to access an object in a list by property? - python

Consider a list of objects and a method that works on one object:
beers = []
beers.append(Beer('Carlsberg', 'tasty'))
beers.append(Beer('Guinness', 'bitter'))
beers.append(Beer('Leffe', 'perfect'))
def howIsBeer (name):
taste = ???
print taste
Class Beer:
def __init__ (self, name, taste):
self.name = name
self.taste = taste
How would the howIsBeer() method go about getting the taste of a beer if it is provided only with the beer name? My first inclination is to iterate the beers list, but being Python I suspect that there is a more direct method. Is there?

Can't really think of any better way, simply use a for-loop.
def howIsBeer(name):
for beer in beers:
if beer.name == name:
return beer.taste
In [1]: howIsBeer("Carlsburg")
Out[1]: tasty
Also notice, that keyword class is written with small c. You will also have to define your class before you use it for creating instances.
Edit:
One way, as suggested in the commments, would be to define dictionaries. If you find it useful, you can use the code below. Notice, however, this is only recommended if you have HUGE amount of Beer objects and performance speed is really important for you. Else use the first code provided
class Beer:
names = {}
def __init__(self, name, taste):
Beer.names[name] = self
self.name = name
self.taste = taste
In [3]: Beer.names["Carlsburg"].taste
Out[3]: tasty

Just loop through the list and check for each one.
You need to move the code around a little though, as the class def needs to be above the use of the class, and also it uses a small c. You should also watch out for the case where it's not recognised.
You also spelt Carlsberg wrong :/
class Beer:
def __init__ (self, name, taste):
self.name = name
self.taste = taste
beers = []
beers.append(Beer('Carlsberg', 'tasty'))
beers.append(Beer('Guinness', 'bitter'))
beers.append(Beer('Lef', 'perfect'))
def howIsBeer (name):
taste = "I have no idea"
for beer in beers:
if beer.name == name:
taste = beer.taste
print taste
howIsBeer("Carlsberg") # tasty
I'd do it like this though (using the dictionaries here allows for the flexibility of having more than one property):
beers = {}
beers["Lef"] = {"taste": "tasty"}
beers["Staropramen"] = {"taste": "tasty"}
beers["Peroni"] = {"taste": "tasty"}
beers["Coors Light"] = {"taste": "What is this?!"}
def howIsBeer (name):
taste = "I have no idea"
if name in beers:
taste = beers[name]["taste"]
print taste
howIsBeer("Lef")
If you just want to store the tastes, then you could do this:
beers = {}
beers["Lef"] = "tasty"
beers["Staropramen"] = "tasty"
beers["Peroni"] = "tasty"
beers["Coors Light"] = "What is this?!"
def howIsBeer (name):
taste = "I have no idea"
if name in beers:
taste = beers[name]
print taste
howIsBeer("Lef")
If you are looking to store a series of objects - as you mention in the question - then you want a dictionary of objects - not a dictionary that is a variable of the class.
i.e.
beers = {}
def add_beer(beer):
beers[beer.name] = beer
then to get data on the beer you're looking at;
if beer in beers:
beers[beer].taste
This can be extended to any object type, and i believe is exactly what you're looking for;
e.g.
cheeses = {}
add_cheese(cheese):
cheeses[cheese.name] = cheese
where
class cheese:
def __init__(self, name, smelliness, hardness, colour, country):
self.name = name
self.smelliness = smelliness
self.hardness = hardness
self.colour = colour
self.country = country

For sake of completeness, I have found a way to use a Linq-like single-line statement which also has the advantage of being a bit easier to read:
taste = [b.taste for b in beers if b.name==name][0]
The [0] at the end is to return the first item, since the query returns a list of matching items. So to get the whole list:
[b.taste for b in beers if b.name==name]
This is why I love Python!

Related

How do I add a value to a parameter in an init function in python

Would it work if I added these values in this way or do I need to add double quotes to the values like "ENGR", "ARTS", ...etc"? Also am I supposed to use curly brackets or square brackets?
def __init__(self, d_code = {ENGR, ARTS, CHHS}, d_name = {Engineering, Art, College of Health and Human Services}
You would write it like this:
class Classes:
def __init__(self, d_code = ["ENGR", "ARTS", "CHHS"], d_name = ["Engineering", "Art", "College of Health and Human Services"]):
self.codes = d_code
self.names = d_name
However, Tom pointed out a very nasty gotcha that can occur with situations like this. If you modify self.codes, that modifies the list object, and will affect future instances of the object. Thus, code like that is sometimes written this way:
class Classes:
def __init__(self, d_code = None, d_name = None):
self.codes = d_code or ["ENGR", "ARTS", "CHHS"]
self.names = d_name or ["Engineering", "Art", "College of Health and Human Services"]

How to split received data into classes based on content

I am trying to write a chunk of code that will organize different types of data into classes. I can split them as of now, but I'm not sure how to get Python to look at the string and automatically sort them into either class based on the content of the string. For example, I have the following and would like to pass the string to either class depending on which type of data is being given to me:
#The data comes in by two different types continuously and is displayed as such below:
animal=dog, age=13, colour=brown, name=Jeff
animal=cat, age=9, colour=white, declawed=yes, friendly=yes, name=Jimmy
class Dogclass():
def __init__(self,age,colour,name):
self.age = age
self.colour = colour
self.name = name
class Catclass():
def __init__(self,age,colour,declawed,friendly,name):
self.age = age
self.colour = colour
self.declawed = declawed
self.friendly = friendly
self.name = name
def splitter():
m = re.split('[, =]', data),
if "dog" in m:
I would like my splitter function to not only have the ability to split the strings, but also go on to sort the split data into classes. This is what I had before (did not work) but would like to figure out a way to utilize OOP more and understand the use of classes.
dog = []
cat = []
def splitter(data):
m = re.split('[, =]', data)
if 'dog' in m:
dog['age'] = (m[7])
dog['colour'] = (m[11])
dog['name'] = (m[13])
elif 'cat' in m:
cat['age'] = (m[7])
cat['colour'] = (m[9])
cat['declawed'] = (m[11])
cat['friendly'] = (m[13])
cat['name'] = (m[15])
else:
return()
I have also tried to create dictionaries to store the data I want to call to, but everything I have tried does not successfully take the splitted data and assign it to a value within my dictionary. Any help would be appreciated.
Lets say you got a string that represent data like this :
"animal=dog, age=13, colour=brown, name=Jeff"
The fisrt thing you would have to do is to parse it to a dictionary like object with a simple function like this one :
def parser(stringToParse):
remove_space = stringToParse.replace(" ", "")
addQuotes = {i.split('=')[0]: i.split('=')[1]
for i in remove_space.split(',')}
return addQuotes
Then you would get an object and you could get its corresponding class by any of the corresponding attribute (lets say your class is based on the "animal" attribute, you could define a simple function to to that :
def getConstructor(classname):
return globals()[classname]
All in one :
import json
class dog():
def __init__(self, age, colour, name):
self.age = age
self.colour = colour
self.name = name
class cat():
def __init__(self, age, colour, declawed, friendly, name):
self.age = age
self.colour = colour
self.declawed = declawed
self.friendly = friendly
self.name = name
def parser(stringToParse):
remove_space = stringToParse.replace(" ", "")
addQuotes = {i.split('=')[0]: i.split('=')[1]
for i in remove_space.split(',')}
return addQuotes
def getConstructor(classname):
return globals()[classname]
def buildIt(any_animal):
my_animal = parser(any_animal)
my_animal_constructor = getConstructor(my_animal["animal"])
if my_animal_constructor.__name__ == "dog":
return dog(my_animal["age"], my_animal["colour"], my_animal["name"])
my_new_animal = buildIt("animal=dog, age=13, colour=brown, name=Jeff")
print(my_new_animal.colour)
In this example i build a dog from the input. If you try to print its coulour you get : "brown"
Of course you will have to implement the if statement for the other class in order to get the cat (and other) class work too...
EDIT
Also, if you want to improve your code you should implement it as an Object oriented one as suggested in Yaron Grushka's answer (create an Animal parent class and makes cat and dog inherit from it)
First of all, I would suggest that in general for cases like these that you use inheritance. You can have a parent class called Animal which has all the common attributes such as age, name and color. Then you can create the Cat and Dog classes that inherit from the parent class, each having unique attributes (such as declawed for cats). Like so:
class Animal():
def __init__(self, name, age, colour):
self.name = name
self.age = age
self.colour = colour
class Cat(Animal):
def __init__(self, name, age, colour, declawed, friendly):
super().__init__(name, age, colour)
self.declawed = declawed # Can make this a boolean with an if/else
self.friendly = friendly # Same
For splitting, you can actually use the split function that Python offers, and use commas as the separator. Then make it into a dictionary. e.g:
def create_animal(data):
details = data.split(",");
attributes = {}
for detail in details:
pair = detail.split("=")
attributes[pair[0]] = pair[1]
print(attributes)
if attributes["animal"] == "cat":
animal = Cat(attributes[" name"], attributes[" age"], attributes[" colour"], attributes[" declawed"], attributes[" friendly"])
else: # Dog creation, same idea...
return animal
a = create_animal("animal=cat, age=9, colour=white, declawed=yes, friendly=yes, name=Jimmy")
print(a.name)
# =>"Jimmy"

Can I use a list as an attribute when creating a new object?

I've been learning python, and want to get started on my first project, ive finished learning about classes today, and would like to work on my understanding of algorithms, and how everything I've learned can tie together. I'd like to do this because I feel like these online resources give you good Info, but don't teach much about applying these concepts to projects.
I want to make a simple program, where I can type in a recipe name, and print the ingredients, cook time, steps, and name. I'd like to use a list for ingredients, and steps, and I'd like to print those in list format (perhaps wrapped in a border). Is this possible?
Class Recipe:
def __init__(self, recipe_name, ingredients, cook_time, steps)
(self.recipe_name = recipe_name)
(self.ingredients = ingredients)
(self.cook_time = cook_time)
(self.steps = steps)
Chicken Noodle = Recipe(Chicken Noodle, [Broth, noodles], 7 minutes, [Bring water to boil, add broth, etc.]
Making a class to contain one recipe makes sense to me, but I would prefer a class to contain all my recipes:
class Recipes:
def __init__(self):
self.recipes = {}
def add_recipes(self, to_add):
for key in to_add:
self.recipes[key] = to_add[key]
def display_recipe(self, name):
recipe = self.recipes[name]
print("Name: ",name)
print("Ingredients: ", *recipe["ingredients"])
print("Cook time: ", recipe["cooktime"])
r = Recipes()
r.add_recipes({"Chicken Noodle": {"ingredients": ["Broth", "noodles"], "cooktime": "7 minutes"}})
r.display_recipe("Chicken Noodle")
You have some errors in your code:
Chicken Noodle = Recipe(Chicken Noodle, [Broth, noodles], 7 minutes, [Bring water to boil, add broth, etc.]
Needs to become:
ChickenNoodle = Recipe("Chicken Noodle", ["Broth", "noodles"], "7 minutes", ["Bring water to boil", "add broth, etc."])
The class definition also needs to change a bit, to conform to conventional style and some syntax rules:
class Recipe:
def __init__ (self, recipe_name, ingredients, cook_time, steps):
self.recipe_name = recipe_name
self.ingredients = ingredients
self.cook_time = cook_time
self.steps = steps
I think you were pretty close! You don't need those parens in your constructor method. I removed those. To print out the entire recipe, we can simple use the to string function. Change it as you desire:
class Recipe:
def __init__(self, recipe_name, ingredients, cook_time, steps):
self.recipe_name = recipe_name
self.ingredients = ingredients
self.cook_time = cook_time
self.steps = steps
def __str__(self):
output = ''
output += 'Here is the recipe for {}:\n'.format(self.recipe_name)
output += 'You will need: {}\n'.format(self.ingredients)
output += 'This recipe takes: {}\n'.format(self.cook_time)
output += 'Here are the steps involved:\n'
for i, step in enumerate(self.steps):
output += 'Step {}: {}\n'.format(i + 1, step)
return output
You can run this:
chicken_noodle = Recipe('Chicken Noodle', ['Broth', 'noodles'], '7 minutes', ['Bring water to boil', 'add broth'])
print (chicken_noodle)
output:
Here is the recipe for Chicken Noodle:
You will need: ['Broth', 'noodles']
This recipe takes: 7 minutes
Here are the steps involved:
Step 1: Bring water to boil
Step 2: add broth

Simple OOP query. Python: saving object names in __init__

As close to the title as possible. I am very new to OOP (and coding in general) and would like to create a program that plays Blackjack. I want to save the objects I create into a list automatically so once it's created I can use the list to cycle through them (I want to create player objects, but save the variable names (right word???) to a list so once it's created using user input I can automatically access them.
So far I've built this:
ROSTER = []
class Player():
"""player in the game"""
def __init__(self, name, score= 0):
self.name = name
self.score = score
ROSTER.append(self.name)
But of course this only gives me the names put into the variable self.name... how can I capture the variable names (right term once again?). self.name won't (afaik) let me access the individual objects via:
excuse the crap formatting plz. =/
Also, if I'm using the wrong terms plz correct me. Learning on your own is kinda hard as far as mastering all the terms.
EDIT: sorry, my post was confusing. The code I posted was meant to show a dead end, not what I am looking for, and my terminology is pretty bad (I feel like a foreigner most of the time). When I said variable names, I think I should have said 'object names' (?) so:
p1 = Player("bob")
p2 = Player("sue")
I want ["p1","p2"] (or if a string format will give me problems when I try to call them, whatever the appropriate way is.)
Once again, sorry for the super confusing first post. Hopefully this edit is a little clearer and more focused.
You could put self in the roster instead. I.e.:
ROSTER = []
class Player():
def __init__(self, name, score = 0):
self.name = name
self.score = score
ROSTER.append(self)
Then you would use the ROSTER list like this:
>>> p1 = Player("Jane")
>>> p2 = Player("John")
>>> ROSTER
[<__main__.Player instance at 0x10a937a70>, <__main__.Player instance at 0x10a937a28>]
>>> for p in ROSTER:
... print p.name, p.score
...
Jane 0
John 0
Or, perhaps better, you could make ROSTER a dictionary:
ROSTER = dict()
class Player():
def __init__(self, name, score = 0):
self.name = name
self.score = score
ROSTER[self.name] = self
That way you can access the player objects by name using ROSTER[name], and you can cycle through them with ROSTER.values(). For example:
>>> p1 = Player("Jane")
>>> p2 = Player("John")
>>> print ROSTER["Jane"].name, ROSTER["Jane"].score
Jane 0
>>> print ROSTER["John"].name, ROSTER["John"].score
John 0
>>> for p in ROSTER.values():
... print p.name, p.score
...
Jane 0
John 0
Are you talking about this?
ROSTER = []
class Player():
def __init__(self, name, score= 0):
self.name = name
self.score = score
ROSTER.append(self)
a=Player('Jack',100)
b=Player('Blackk',1000)
c=Player('Mike')
for x in ROSTER:
print(x.name,x.score)
output:
Jack 100
Blackk 1000
Mike 0

Python's equivalence?

Is there anyway to transform the following code in Java to Python's equivalence?
public class Animal{
public enum AnimalBreed{
Dog, Cat, Cow, Chicken, Elephant
}
private static final int Animals = AnimalBreed.Dog.ordinal();
private static final String[] myAnimal = new String[Animals];
private static Animal[] animal = new Animal[Animals];
public static final Animal DogAnimal = new Animal(AnimalBreed.Dog, "woff");
public static final Animal CatAnimal = new Animal(AnimalBreed.Cat, "meow");
private AnimalBreed breed;
public static Animal myDog (String name) {
return new Animal(AnimalBreed.Dog, name);
}
}
Translating this code directly would be a waste of time. The hardest thing when moving from Java to Python is giving up most of what you know. But the simple fact is that Python is not Java, and translating line by line won't work as you expect. It's better to translate algorithms rather than code, and let Python do what it's good at.
It's unclear to me what the desired semantics of your Java would be. I'm guessing you're sort of trying to model a collection of animals (species, not breeds, incidentally) and imbue a set of associated classes with the behavior that varies according to the type of animal (roughly speaking the sounds that each makes).
In Python the natural way to do this would be through meta programming. You create a class or a factory function which returns each of the classes by passing arguments into a template. Since functions and classes are first order object in Python they can be passed around like any other object. Since classes are themselves objects you can access their attributes using setattr (and its cousins: hasattr and getattr).
Here's a simple example:
#!/usr/bin/env python
def Animal(species, sound):
class meta: pass
def makeSound(meta, sound=sound):
print sound
setattr(meta, makeSound.__name__, makeSound)
def name(meta, myname=species):
return myname
setattr(meta, 'name', name)
return meta
if __name__ == '__main__':
animal_sounds = (('Dog', 'woof'),
('Cat', 'meow'),
('Cow', 'moo'),
('Chicken', 'cluck'),
('Elephant', 'eraunngh'))
menagerie = dict()
for animal, sound in animal_sounds:
menagerie[animal] = Animal(animal, sound)
for Beast in menagerie:
beast = Beast()
print beast.name(), ' says ',
beast.makeSound()
Dog = menagerie['Dog']
fido = Dog() # equivalent to fido = menagerie['Dog']()
fido.makeSound()
# prints "woof"
Cat = menagerie['Cat']
felix = Cat()
felix.makeSound()
Mouse = Animal('Mouse', 'squeak')
mickey = Mouse()
mouse.makeSound()
# prints "squeak"
This seems like a trite example but I hope it gets the point across. I can create a table (in this case a tuple of tuples) which provides the arguments which will be used to fill in the varying parameters/behavior of our classes. The classes returned by Animal are just like any other Python classes. I've tried to show that in the examples here.
This is not a line-for-line translation, but something in the ballpark:
class Animal(object):
animal_breeds = "Dog Cat Cow Chicken Elephant".split()
animals = {}
def __init__(self, breed, name):
self._breed = breed
self.name = name
Animal.animals[name] = self
#property
def breed(self):
return Animal.animal_breeds[self._breed]
#staticmethod
def myDog(name):
return Animal(Animal.AnimalBreed.Dog, name)
# add enumeration of Animal breeds to Animal class
class Constants(object): pass
Animal.AnimalBreed = Constants()
for i,b in enumerate(Animal.animal_breeds):
setattr(Animal.AnimalBreed, b, i)
# define some class-level constant animals
# (although "woff" and "meow" are not what I would expect
# for names of animals)
Animal.DogAnimal = Animal(Animal.AnimalBreed.Dog, "woff")
Animal.CatAnimal = Animal(Animal.AnimalBreed.Cat, "meow")
# this code would be in a separate module that would import this
# code using
# from animal import Animal
#
print Animal.myDog("Rex").breed
print Animal.animals.keys()
http://code.activestate.com/recipes/413486/ contains a lot of help on this topic. Be warned that deepcopy support probably doesn't work with it.

Categories