Print method output as a string, not as a function - python

In my Student subclass below, I am calculating average GPA from manually inputted grades stored in the self.courses dict attribute as {course:grade}.
The user should be able to enter in the console >>>print(fred.gpa),given fred is a proper Student instance with grades in self.courses, and should get 3.8 (for example) printed to the console.
However, 3.8 does not print to console, rather <bound method Student.gpa of <college.Student object at 0x7ff55e122ad0>>
I understand that this is the result of printing a function, but I want to print just a number using just print(fred.gpa) and not fred.gpa()
Does this mean I have to convert the output of gpa.Student into a string?
Here is my code for ref:
def __init__(self, name, cid, email):
self.courses = {}
super().__init__(name, cid, email)
def add_course(self, course):
if course in self.courses:
# duplicate courses not allowed
raise ValueError
print("student is already registered for this course")
else:
self.courses.update({course:0})
return self
def update_grade(self, course, grade):
if course not in self.courses:
# student must be registered in class to receive grade
raise ValueError
print("student is not registered for this course")
else:
self.courses[course] = float(grade)
return self
def gpa(self):
grades = list(self.courses.values())
totGPA = sum(grades)/len(grades)
return str(totGPA)

What you need is something that will let you implement something as a method, but access it as a non-method attribute. That turns out to be quite easy - it's called a property, and it works like this:
class Student:
#property
def gpa(self):
# Rest of the implementation, unchanged
Ta-da!
Note that you can fix up your implementation of gpa a little: sum can take any iterable (it doesn't have to be a list), and dicts (and their keys, values and items views) have a length, so you can do:
#property
def gpa(self):
return sum(self.courses.values())/len(self.courses)
I've omitted your call to str, since it seems from your question that that was a first attempt to fix your problem. You could reinstate it if you need it there for some other reason.

Related

Validation input using descriptors

Hello I am messing around with descriptors and I am trying to understand how they work.
So this is the stupid code I am working on:
class InputValidator:
def __set__(self, instance, value):
if value != value.lower():
raise ValueError("Please enter lowercase letters")
self._name = value
def __get__(self, instance, owner):
return self._name
class InputForm:
def __init__(self):
self.name = ""
self.start()
def start(self):
input_name = input("Please enter your name: ")
self.name = input_name
name = InputValidator()
INPUT = InputForm()
print(INPUT.name)
So if I am not mistaken that’s what descriptors should be used for, to provide some validation when an attribute is set or deleted.This is how my code should run.So first in the InputForm class we initiliaze an empty attribute self.name and then we call a function self.start().This function is going to ask the user for input and this input is going to be the value of the key “name” in the dictionary of attributes.However we want to validate this input, so we define a descriptor InputValidator which is going to check if all the letters are lowercase, in case the condition is not met a value error is raised and self.name is not succesfully saved.Now I got two questions:
How can I ask the user again for input (I would like to put something like instance.start()after raise ValueError but this is not possible).
Is this a good way to approach this problem, or is it just trash?In the latter case, could someone provide a professional and advanced answer(how would you go about this problem in the best way possible)
Thank you all for your attention and help

Where does this (student) append come from

class Student:
def __init__(self,name,age,grade): #giving the attributes
self.name=name
self.age=age
self.grade=grade
def get_grade(self):
return self.grade
class Course:
def __init__(self,lesson,max_student):#giving attributes
self.lesson=lesson
self.max_student=max_student
self.students=[]
def add_student(self,student):
if len(self.students)<self.max_student:
self.students.append(student) #append(student) from where
#I don't get where the append(student) get the name from.
#As above code did't use student.
return True
return False
s1=Student('Tim',19,95) #Naming the student
s2=Student('bill',19,75)
s3=Student('jill',19,65)
course1=Course('Math',2)
course1.add_student(s1) #Adding the Student to a list by appending
course1.add_student(s2)
print(course1.students[0].name)
#Will give Tim but how do i print them all at once
#instead of multiple print maybe like a [0:1] but got error
The append method is part of Python. It's part of the list class, so all lists have this method (among others). Check the docs. You set self.students to an empty list in the line self.students = [].
The student variable comes from the argument to add_student, as you specified here: def add_student(self,student). So, when you call course1.add_student(s1), the s1 will be student inside the method (because for class methods, the first argument self is always the class instance itself and doesn't have to be specified in the call).

Having a list in a python class with a different name?

Here is the task: Write a class called LineUp.This class should contain
one (private) field (called acts) to store up to 30 acts. This field should be initialised in the constructor.
A method add_act that takes an Act (keep in mind I've written code above this with 'Act' and that's all fine) as an argument and adds it to acts if there are fewer than 30 acts already, otherwise a message “The festival is full!” should be printed,
add a method toString or str which produces a nice string with full line-up,
add a method print which prints a nice string with the full line-up.
I'm assuming that the first point is asking for a list. I think I've found a way to have a list in a class, but it has the same name (LineUp, as opposed to 'acts'). Here's what I have
class LineUp(list):
def __init__(self):
self.acts = []
def add_act():
if len(acts) >= 30:
print("The festival is full!")
else:
acts.append(Act)
def __str__(self):
string = "LineUp" + str(LineUp)
def println(self):
print(__str__(self))
Thanks in advance! Keep in mind this is my first draft.
EDIT: should I actually use a dictionary, not a list? Know that in another file I'm testing this code
You don't nee to inherit from list or get acts as parameter. To create a list for the class use self.acts in the constructor. You should also add __repr__
class LineUp:
def __init__(self):
self.acts = []
def add_act(self, act):
if len(self.acts) >= 30:
print("The festival is full!")
else:
self.acts.append(act)
def __repr__(self):
return f'LineUp{str(LineUp)}'
def __str__(self):
return f'LineUp{str(LineUp)}'
def println(self):
print(f'LineUp acts:{[act for act in self.acts]}')
Notice that str(LineUp) will return something like <class 'ExampleTest.LineUp'>, you might want to edit it.

How to call a class in a function Python

I'm a complete amateur, and trying to work out how to write a function that takes a list of objects, and returns a list of the names of said objects (based on whether they pass if statement). This is the class I've written from help of tutorials:
class Student:
passmark=50
def __init__(self,name,mark):
self.name=name
self.mark=mark
def passes(self):
return self.mark > Student.passmark
So from now I'm assuming I make a list of objects, say:
students = []
Though this list is just a brand new list, which was necessary sure but how would I link it to the class? From this point I want to find out which students have failed, and return them and also where I am confused:
def failed(list):
for student in Students:
if passmark > self.mark:
return list
Is all I can muster, sorry I've just gotten to classes and calling classes is quite confusing for me. The above code doesn't reference the class at all, and I really am confused on how to do so. I've no syntax errors or anything, I think my logic is fatally flawed.
You want to take all the student from the students list. So use that in the for loop. Also, you correctly encapsulated the logic of pass/fail criteria in a method, so use that.
Here is the code I think will do want you want:
def failed(list_of_students):
failed_students = []
for student in list_of_students:
if not student.passes():
failed_students.append(student.name)
return failed_students
A more advanced way of doing it is by using list comprehension:
def failed(list_of_students):
return [student for student in list_of_students if not student.passes()]
It is more pythonic, but my be harder to understand for a beginner with a C or Java background.
You can use a list comprehension like this:
def failed(list):
return [student.name for student in students if not student.passes()]
Try this code. Using list comprehension to return results. It's a very powerful python tool.
class Student:
passmark = 50
def __init__(self, name, mark):
self.name=name
self.mark=mark
def passes(self):
return self.mark > Student.passmark
def __repr__(self):
return '{} {}'.format(self.name, self.mark)
def failed(students_list):
return [student for student in students_list if student.mark < Student.passmark]
Given a Student class like you defined:
class Student:
passmark=50
def __init__(self,name,mark):
self.name=name
self.mark=mark
def passes(self):
return self.mark > Student.passmark
You could instantiate a list of students with:
students = [Student("John", 49), Student("Mary", 75)]
It looks like you are also trying to define a function that will return a list of all the failed students; you could do something like this:
def failed(student_list):
return [x for x in student_list if not x.passes()]
mark_to_pass = 50
#Approach one
class Student:
def __init__(self, student_name, student_mark):
self.name = student_name
self.mark = student_mark
self.pass_mark = self.calculate_passing_mark(mark_to_pass)
def calculate_passing_mark(self, mark_to_pass):
if self.mark >= mark_to_pass:
return True
return False
if __name__ == '__main__':
example_student = Student("Swanson", 75)
print(example_student.pass_mark)
With this approach every time a student object is created it will tell create a field telling you that student has passed. When working with lists such as a list of student objects you need to add the student to your list. Example
students = []
students.append(example_student)
Now you can look through your student list by doing
for student in students:
print(student.pass_mark) # or do some other logic passed on who passed or failed. Or even here you dont need to create pass_mark object you can just check if student.mark > pass_mark
I'm assuming that failed isn't a member function of the class Student. The below code should work for what you are trying to do.
class Student:
passmark=50
def __init__(self,name,mark):
self.name=name
self.mark=mark
def passes(self):
return self.mark > Student.passmark
students = [Student("tom",40),Student("joe",70)]
def failed(listofStudents):
listofStudentsThatFail = []
for student in listofStudents:
if not student.passes():
listofStudentsThatFail.append(student)
return listofStudentsThatFail
for s in failed(students):
print s.name
The ouput when you run the code is:
tom

Using created classes

I have this class Person created:
class Person(object):
def __init__(self, name, age, gender):
self._name = name
self._age = age
self._gender = gender
self._friend = None
def __eq__(self, person):
return str(self) == str(person)
def __str__(self):
if self._gender == 'M':
title = 'Mr'
elif self._gender == 'F':
title = 'Miss'
else:
title = 'Ms'
return title + ' ' + self._name + ' ' + str(self._age)
def __repr__(self):
return 'Person: ' + str(self)
def get_name(self):
return self._name
def get_age(self):
return self._age
def get_gender(self):
return self._gender
def set_friend(self, friend):
self._friend = friend
def get_friend(self):
return self._friend
I now need to have 3 functions:
Using the Person class, write a function print_friend_info(person) which accepts a single argument, of type Person, and:
prints out their name
prints out their age
if the person has any friends, prints 'Friends with {name}'
A function create_fry() which returns a Person instance representing Fry. Fry is 25 and his full name is 'Philip J. Fry'
A function make_friends(person_one, person_two) which sets each argument as the friend of the other.
And this is what I've got:
def print_friend_info(person):
person= Person
person_name=person.get_name(person)
person_age=person.get_age(person)
person_gender=person.get_gender(person)
person_friends=person.get_friend(person)
return person_name, person_age, person_gender, person_friends
"""print (person_name)
print (person_age)
print ('Friends with {'+person_friends+'}')"""
def create_fry():
fry=Person("Philip J. Fry", 23, "M")
return fry
def make_friends(person_one,person_two):
return person_one.set_friend(person_two)
And the error handler says "type object 'Person' has no attribute '_name'"
Don't understand what you try to put into print_friend_info, if the parameter "person" is an instance already
I think your problem is this line
person = Person
by doing that, you just override person which is an instance and replace it with just a definition of class Person
Try to remote that line, and see what happend
You want to remove this line
person= Person
from the print_friend_info(person): function.
That line doesn't create a new Person instance, and even if it did you wouldn't need to. You already get the person passed in as parameter, all you need to do there is retrieve his information.
Since classes are objects in python, by doing person= Person you are just saying that person is the same as Person. Then when you try to get_name on person, that doesn't work because person is not an instance of a class, it is a class object. Therefore it has no _name attribute. It will only have a _name attribute after you instantiate it with the proper parameters.
it still dosn't print the info needed
return person_name, person_age, person_gender, person_friends
"""print (person_name)
print (person_age)
print ('Friends with {'+person_friends+'}')"""
This code doesn't seem right. Instead of printing the data, it returns the data. The text between """ does nothing because you have already left the function with return
Should probably be replaced with
print (person_name)
print (person_age)
print ('Friends with {'+person_friends+'}')
return # this is not necessary, but it's better to be explicit.
Once you have a Person object, say with a label person, you can access its functions like so:
>>> person = Person('Pete', 43, 'Male')
>>> person.get_age()
43
You don't need to pass person to the member function get_age:
>>> person.get_age(person)
Traceback (most recent call last):
...
TypeError: get_age() takes exactly 1 argument (2 given)
But the definition of get_age does have a parameter, self:
def get_age(self):
return self._age
This is implicitly passed to the function when you call it on an object. So person is being passed as self. You can see in the error message above that we were passing two parameters, the implicit self person, and the (wrong, unneeded) explicit person.
self._age is the same as person._age, but is encapsulated, and hidden behind your 'getter' interface.
So, to fix your code, get rid of:
person = Person
and change calls like person.get_age(person), to person.get_age().

Categories