Python - Printing and Classes - python

I've read more posts and documentation on __str__ and __repr__ than is healthy, consulted various texts, and still cannot resolve this printing issue, so I'm putting this out there.
Below is a function I'd like to test. The code isn't mine, but I would like to know how exactly it works. I need to see a human-friendly printout of the results (i.e., no hex), so that I can play with it and generally, well, learn something because I truly have no clue how it's doing what it's doing.
def get_ordered_adoption_center_list(adopter, list_of_adoption_centers):
"""
The method returns a list of an organized adoption_center such that the scores for each AdoptionCenter to the Adopter will be ordered from highest score to lowest score.
"""
list_of_adoption_centers.sort(key=lambda center:center.name)
list_of_adoption_centers.sort(key=lambda center:adopter.get_score(center), reverse=True)
return list_of_adoption_centers
Below is a relevant cross-section of code that it draws upon, which I did write.
import random
import string
class AdoptionCenter:
"""
The AdoptionCenter class stores the important information that a
client would need to know about, such as the different numbers of
species stored, the location, and the name. It also has a method to adopt a pet.
"""
def __init__(self, name, species_types, location):
self.name = name
self.species_types = species_types
self.location = (float(location[0]), float(location[1]))
def get_number_of_species(self, species):
return self.species_types.get(species, 0)
def get_location(self):
return self.location
def get_species_count(self):
return self.species_types.copy()
def get_name(self):
return self.name
def adopt_pet(self, species):
self.species_types[species] = self.species_types[species] - 1
if self.species_types[species] <= 0:
del self.species_types[species]
def __str__(self):
return "%s" % (self.name)
class Adopter:
"""
Adopters represent people interested in adopting a species.
They have a desired species type that they want, and their score is
simply the number of species that the shelter has of that species.
"""
def __init__(self, name, desired_species):
self.name = name
self.desired_species = desired_species
def get_name(self):
return self.name
def get_desired_species(self):
return self.desired_species
def get_score(self, adoption_center):
num_desired = adoption_center.get_number_of_species(self.desired_species)
score = float(1 * num_desired)
return score
def __str__(self):
return "%s and score is %d" % (self.name, self.get_score)
class FlexibleAdopter(Adopter):
"""
A FlexibleAdopter still has one type of species that they desire,
but they are also alright with considering other types of species.
considered_species is a list containing the other species the adopter will consider
Their score should be 1x their desired species + .3x all of their desired species
"""
def __init__(self, name, desired_species, considered_species):
Adopter.__init__(self, name, desired_species)
self.considered_species = considered_species
def get_score(self, adoption_center):
num_Other = 0
for animal in self.considered_species:
if adoption_center.get_number_of_species(animal) > 0:
num_Other += adoption_center.get_number_of_species(animal)
adopter_score = Adopter.get_score(self, adoption_center)
score = adopter_score + 0.3 * num_Other
return score
def __str__(self):
return "%s and score is %d" % (self.name, self.get_score)
class FearfulAdopter(Adopter):
"""
A FearfulAdopter is afraid of a particular species of animal.
If the adoption center has one or more of those animals in it, they will
be a bit more reluctant to go there due to the presence of the feared species.
Their score should be 1x number of desired species - .3x the number of feared species
"""
def __init__(self, name, desired_species, feared_species):
Adopter.__init__(self, name, desired_species)
self.feared_species = feared_species
def get_score(self, adoption_center):
num_feared = adoption_center.get_number_of_species(self.feared_species)
adopter_score = Adopter.get_score(self, adoption_center)
score = adopter_score - (0.3 * num_feared)
return max(0.0, score)
def __str__(self):
return "%s and score is %d" % (self.name, self.get_score)
class AllergicAdopter(Adopter):
"""
An AllergicAdopter is extremely allergic to a one or more species and cannot
even be around it a little bit! If the adoption center contains one or more of
these animals, they will not go there.
Score should be 0 if the center contains any of the animals, or 1x number of desired animals if not
"""
def __init__(self, name, desired_species, allergic_species):
Adopter.__init__(self, name, desired_species)
self.allergic_species = allergic_species
def get_score(self, adoption_center):
for animal in self.allergic_species:
if animal in adoption_center.get_species_count().keys():
return 0.0
return 1.0 * adoption_center.get_number_of_species(self.desired_species)
def __str__(self):
return "%s and score is %d" % (self.name, self.get_score)
I've tried placing __str__ and __repr__ methods in the various classes. What I've read suggests __str__ is what I'm after. I've also tried placing a simple "for loop" with a print statement in the function body itself. This latter approach lead to a screen full of errors. I've printed using __str__ before with success, but everything seems to be failing me on this one. Any insight would be appreciated.

As per your comment, you are printing these within a list. The print function retrieves the string representation of each argument to be printed, defined by __str__. The string representation of a list consists of square brackets enclosing a comma+space-separated sequence of the repr representation of each item, defined by __repr__.
>>> class A:
... def __str__(self):
... return 's'
... def __repr__(self):
... return 'r'
...
>>> l = [A(), A()]
>>> print(l)
[r, r]
>>> print(*l)
s s
If you want to see the human-readable string representation of each item, either pass them directly to the print function with the * unpacking operator, or loop over that list and call print on each item. If you want to see a human-readable string representation of each item when you directly print the list that holds them, you can define a __repr__ that returns the same thing as the __str__ method does, but it's not recommended, as that's supposed to be a way of reproducing the object with eval.

Related

AttributeError: 'str' object has no attribute 'get_grade'

hello was following alone in a tutorial and the code that he has is the exact same that i have here but mine doesn't seem to work. when he ran the code his worked completely fine and i am running into errors. code it be that i need to add the parent to the subject such as subject(person)? or is something just wrong. The number get_average function should just return the number but it is having problems with that. appreciate the help
class Person():
def __init__(self, first, last, grade):
self.first = first
self.last = last
self.grade = grade
def get_grade(self):
return self.grade
class Subject():
def __init__(self, subject, number_students):
self.subject = subject
self.number_students = number_students
self.students = []
def add(self, name):
if len(self.students) < self.number_students:
self.students.append(name)
return True
return False
def average(self):
number = 0
for i in self.students:
number += i.get_grade()
return number
p1 = Person("dustin", "white", 83)
subs = Subject("science", 10)
subs.add(p1.first)
print(subs.students)
print(subs.average())
The error arises because you run subs.add(p1.first), which is a type str. p1.first does not have the method get_grade. What you want to run is: subs.add(p1) (the object which will have get_grade). Also, you can remove the redundant parentheses when defining the classes. You can write class Subject(): as class Subject:.
You can then change you add code to:
def add(self, student):
if len(self.students) < self.number_students:
self.students.append(student.first) # changed here
return True
return False

How do I return objects from a class without using the __str__ method?

I am working on a program that takes in a CSV file of people and returns a population of the people. The file contains a table with the first name, last name, and 5 different traits (Work, Socialize, Hobbies, Eat, Sleep) of the people. Currently, I have a Person class that accepts the first and last name as strings and the traits as dictionaries. I am also defining if the person has an unbalanced or balanced life based on their distribution of the traits. The str method of this class is being used to return whether the person is unbalanced or balanced, but I also need to create a list called the population of the person objects, but it is returning the unbalanced or balance status of each person.
class Person:
def __init__(self, first_name, last_name, traits):
self.first_name = str(first_name)
self.last_name = str(last_name)
self.traits = traits
def determine_lifestyle(self):
check = 0
check2 = 0
if(self.traits["Sleep"] != 0 and self.traits["Eat"] != 0):
for val in self.traits.values():
if(val == 3):
check += 1
if(val == 2):
check2 += 1
if(check >= 3 or check2 >= 3):
return True
else:
return False
def __str__(self):
if(self.determine_lifestyle()):
return self.first_name + " " + self.last_name + " has a balanced lifestyle"
else:
return self.first_name + " " + self.last_name + " has an unbalanced lifestyle"
import csv
def read_file(filename):
f = open(filename, 'r')
population = []
reader = csv.reader(f)
next(reader)
for row in reader:
population.append((row[0],row[1],{"Work":row[2],"Hobbies":row[3],"Socialize":row[4],"Eat":row[5],
"Sleep":row[6]}))
return population
Is there any way to return the person object without using the str method?
Your code seems incomplete, as your population variable is holding tuples, instead of Person object instances.
If you have a list of Person instances, and then you print those instances, their unbalanced or balanced status would show up, as print would print the string representation of the instance, and the representation is overwritten as you define a __str__() method for the class.
Is there any way to return the person object without using the str method?
If you instantiate a Person object, like aperson = Person(fname, lname, traits), then you could get attributes of it though aperson.first_name, aperson.last_name, and get its balance status using `aperson.determine_style

How to decrypted [<__main__.Food object at 0x1097ba828>

I am a python newbie. I want display actual names,values and calories instead of [<__main__.Food object at 0x1097ba828>, <__main__.Food object at 0x1097ba860>, <__main__.Food object at 0x1097ba898>] I know this question is very simple,but it would be a great help if you could let me know the answer!
class Food(object):
def __init__(self,n,v,w):
self.name = n
self.value = v
self.calories = w
def getValue(self):
return self.value
def getCal(self):
return self.calories
def density(self):
return self.getValue()/self.getCal()
def __str__(self):
return '<__main__.Food: '+self.name +' '+ self.value+' ' + self.calories
def buildMenu(self):
menu = []
for i in range(len(values)):
menu.append(Food(self.name[i], self.value[i], self.calories[i]))
return menu
names=['burger','fries','coke']
values=[1,2,3]
calories=[100,200,300]
if __name__ == '__main__':
new = Food(names, values, calories)
print(new.buildMenu())
Thank you!
I made two code changes to get what I think you're looking for. The first is to convert values to strings in your str function. The second is to use that.
def __str__(self):
return '<__main__.Food: '+ str(self.name) +' '+ str(self.value)+' ' + str(self.calories)
and
print (str(new)) #instead of print(new.buildMenu())
Now the output is:
<main.Food: ['burger', 'fries', 'coke'] [1, 2, 3] [100, 200, 300]
This is how I would do it, noting that we've created two classes: a separate Food and Menu class. The Menu class has an add method that appends to its foodItems property, though I don't feel like that's really necessary since we can just do direct property assignment:
m.foodItems = < some list of Food objects >
I've removed the confusing buildMenu method from the Food class, and defined __str__ methods for both classes:
class Food(object):
def __init__(self,n,v,w):
self.name = n
self.value = v
self.calories = w
def getValue(self):
return self.value
def getCal(self):
return self.calories
def density(self):
return self.getValue()/self.getCal()
def __str__(self):
return '\t'.join([self.name, str(self.value), str(self.calories)])
class Menu(object):
def __init__(self):
self.foodItems = []
def add(self, foodItem):
self.foodItems.append(foodItem)
def __str__(self):
"""
prints the food items
"""
s = 'Item\tValue\tCalories\n'
s += '\n'.join(str(f) for f in self.foodItems)
return s
names=['burger','fries','coke']
values=[1,2,3]
calories=[100,200,300]
m = Menu()
items = list(Food(n,v,c) for n,v,c in zip(names,values,calories))
m.foodItems = items
print(m)
And outputs like:
The issue you have is that you're printing a list of Food instances, not a single instance at a time. The list type's __str__ operator calls repr on the items the list contains, not str, so your __str__ method does not get run.
A simple fix is to just rename your __str__ method to __repr__.
I'd note that it's a bit strange that you're building a Food instance with lists of values for name, value and calories, just so that you can call a method on it to make a list of Food instances with the individual values. A more Pythoic approach would be to pass the lists to a classmethod that returns the list of instances, without the intermediate instance needing to exist:
#classmethod
def buildMenu(cls, names, values, calories):
menu = []
for i in range(len(values)): # consider using zip instead of looping over indexes
menu.append(cls(names[i], values[i], calories[i]))
return menu
You'd call it on the class:
if __name__ == '__main__':
print(Food.buildMenu(names, values, calories))

Aligning Python Class & Super-Class

Consider the following Python code snippet where we define a Portfolio, Company and Deposit class. A Portfolio object simply acts as a union of companies & deposits. And we can run metrics on the portfolio like Profit. Questions:
For every new metric I include in the Company or Deposit class I need to manually add a corresponding function in the Portfolio class; despite the fact that their behaviour is always the same: sum across all investments. Is there a way to improve this logic/construction of classes? What if we need to add 100 other metrics...
The Deposit class only has a Profit function, but not Loss (interest in a bank account is assumed to be guaranteed). Is there a way to treat "undefined" metrics as always returning 0? Or is there a cleaner/more correct to define these metrics? What if we need to cover 100 different investment types that may or may not have different metrics...
class Company():
def __init__(self, ItemsSold, ItemPrice, Expenses, Fines):
self.ItemsSold = ItemsSold
self.ItemPrice = ItemPrice
self.Expenses = Expenses
self.Fines = Fines
def Profit(self):
return self.ItemsSold * self.ItemPrice
def Loss(self):
return self.Expenses + self.Fines
def ProfitAndLoss(self):
return self.Profit() - self.Loss()
class Portfolio():
def __init__(self, Investments):
self.Investments = Investments
def Profit(self):
return sum([inv.Profit() for inv in self.Investments])
def Loss(self):
return sum([inv.Loss() for inv in self.Investments])
def ProfitAndLoss(self):
return sum([inv.ProfitAndLoss() for inv in self.Investments])
class Deposit():
def __init__(self, Notional, InterestRate, TimeHorizon):
self.Notional = Notional
self.InterestRate = InterestRate
self.TimeHorizon = TimeHorizon
def Profit(self):
return self.Notional * self.InterestRate * self.TimeHorizon
myComp1 = Company(100,2,50,20)
myComp2 = Company(200,2,100,80)
myDepos = Deposit(100,0.02,3)
myPortf = Portfolio([myComp1,myComp2,myDepos])
print(myPortf.Profit()) # Works fine
print(myPortf.ProfitAndLoss()) # Throws an error
The second question is easy: all you have to do is to create a Base class where each metrics is defined as a method returning 0. Then derive all your Invest classes (Company, Deposit, etc) from the Base class, so as all undefined metrics will call the corresponding method in the Base class.
The first question is a bit tougher as it requires some meta-programming. Your Portfolio class can also be derived from the Base class, then it looks in the method dictionary of the Base class (Base.__dict__) to retrieve all metrics names. Afterwards, for all these metrics, it creates a specific lambda method that calls this metrics for each item in your Investments list and sums up the results. Here is a skeleton code for this:
class Base(object):
def f1(self):
return 0
def f2(self):
return 0
class InvestA(Base):
def f2(self):
return 2
class InvestB(Base):
def f1(self):
return 1
class Portfolio(Base):
def __init__(self, invest):
self.invest = invest
for name in [n for n in Base.__dict__ if n[:2] != '__']:
self.__dict__[name] = lambda name=name: self.sum(name)
def sum(self, name):
return sum([i.__class__.__dict__[name](i) for i in self.invest
if name in i.__class__.__dict__])
A = InvestA()
print("A.f1 = %s, A.f2 = %s" % (A.f1(), A.f2()))
B = InvestB()
print("B.f1 = %s, B.f2 = %s" % (B.f1(), B.f2()))
P = Portfolio([A,A,B])
print('P.f1 = A.f1 + A.f1 + B.f1 =', P.f1())
print('P.f2 = A.f2 + A.f2 + B.f2 =', P.f2())
which produces the following output:
A.f1 = 0, A.f2 = 2
B.f1 = 1, B.f2 = 0
P.f1 = A.f1 + A.f1 + B.f1 = 1
P.f2 = A.f2 + A.f2 + B.f2 = 4
As you can see, A.f1, B.f2, P.f1 and P.f2 are not explicitely defined as methods, but they can be called thanks to inheritance and meta-programming

Python - Classes- Definition

I programmed a class aswell as a definition with the backthought the when you,
set everytime a value using class variable you can always recall the total amount by using the definition
class Hand():
def __init__(self, Hand=0):
self.Hand = Hand
def getHand(self, neue_Hand):
self.Hand = neue_Hand
def set_hand(self):
return self.Hand
c = Hand()
def Aufruf():
Total = 0
Total += c.getHand(0)
return Total
c.getHand(12)
Aufruf()
It changes the value each time, but doesn't accumulate it as it supposed to be.
You've mixed up the functionality in your getters and setters. The getter should return the variable and the setter should set the value.
class Hand():
def __init__(self,Hand=0):
self.Hand = Hand
def getHand(self):
return self.Hand
def set_hand(self, neue_Hand):
self.Hand = neue_Hand
def increment_hand(self, neue_Hand_incremenet):
self.Hand += neue_Hand_incremenet
c = Hand(10)
c.getHand()
>> 10
c.set_hand(20)
c.getHand()
>> 20
def Aufruf():
Total = 0
Total += c.getHand()
return Total
Aufruf()
>> 20
c.increment_hand(10)
Aufruf()
>> 30
Also as a side note:
If you look closely, you will realise your method Aufruf is actually an exact duplicate (logically) of the getHand() method. When you instantiate the variable total = 0 inside the method block of code, this value will ALWAYS be set to 0 when the method is called, meaning the value from c.getHand() will ALWAYS just be the value that's returned
Use method addition for changing value, and don't use capital letters or camelCase inside your class.
class Hand:
def __init__(self, 0):
self.hand = hand
def get_hand_bigger(self, addition):
self.hand += addition
Your class method getHand does not return any value, yet it is being called as such. Try this:
def getHand(self, neue_Hand):
self.Hand = neue_Hand
return self.Hand

Categories

Resources