Python: Classes that use other classes - python

So I have 2 files that work together using each other's classes.
I have
class Student:
"""A class to model a student with name, id and list of test grades"""
def __init__(self, name, id):
"""initializes the name and id number; sets list of grades to []"""
self.s_name = name
self.ident = id
self.tests=[]
def getID(self):
return self.ident
def get_name(self):
""" returns the student name"""
return self.s_name
def addtest(self,t):
"""adds a grade to the list of test grades """
self.tests.append(t)
def __str__(self):
"""returns the student name and the current list of grades"""
return self.s_name + " " + str(self.tests) + " "
def comp_av(self):
"""returns the average of the current set of grades or 'no grades'
if appropriate"""
if len(self.tests) > 0:
sum = 0.0
for item in self.tests:
sum = sum + item
average = float(sum)/len(self.tests)
return average
else:
return "no grades"
Which is completely done. I also have code that is from the teacher's point of view. The students are not just represented by their names but by an object of class Student. Each Student object has their name and ID number, but also a list of test scores. Right now Course has only the constructor and the __str__ method.
from LabStudentClass import *
class Course:
""" A class to model a course which contains a list of students"""
def __init__(self,teacher):
"""Sets up a class to hold and update students"""
self.students = []
self.teacher = teacher
def __str__(self):
""" prints the course by listing each student in the class"""
result = self.teacher+"'s Class\n"
for s in self.students:
name = s.get_name()
result = result + name + '\n'
return result
c = Course("Dr. Bradshaw")
#print c
def AddStudent(name, id):
student1 = Student('Mary Comtpon', '3456')
student2 = Student('Billy Jo', '2345')
student3 = Student( 'Anne lou', '1090')
print student1
print student2
print student3
My goal is to create a method AddStudent: This method gets two parameters, a student name and an ID. A new Student object is created and added to the course.
Add 3 students to your class and print out the class to test it.
However, the students aren't printing and I'm not really sure what the problem is.

Add this method to your Course class:
def addStudent(self, name, id):
student = new Student(name, id)
self.students.append(student)
Then, replace the function you wrote at the bottom with the following:
c.addStudent('Mary Comtpon', '3456')
c.addStudent('Billy Jo', '2345')
c.addStudent('Anne lou', '1090')
print c

Related

Create a method that lists instances that were set to True in a previous class

I have the following code that records job candidates' personal details in a class Employee:
class Employee:
def __init__(self, name, role, id):
self.name = name
self.role = role
self.id = id
self.interviewed = False
def __str__(self):
text = f'Candidate {self.name}; {self.id}. '
if self.interviewed == False:
return text + 'Not interviewed yet.'
else:
return text + 'Interviewed.'
def interview(self):
self.interviewed = True
I also have another class Database that lists all the candidates in a database for a particular employer:
class Database:
def __init__(self, company, employer):
self.company = company
self.employer = employer
self.candidates = []
def __str__(self):
text = f'{The hiring company is {self.company} and the employers name is {self.employer}'
return text
def add_candidate(self, candidate):
self.candidates.append(candidate)
Now, if we record personal details of two candidates in the class Employee and add them to the class Database using the method add_candidate, how do I create a new method called list_interviewed_candidates(self) in the class Database that will print all candidates that have self.interviewed set to True?
This is what I tried:
class Database:
def __init__(self, company, employer):
self.company = company
self.employer = employer
self.candidates = []
def __str__(self):
text = f'{The hiring company is {self.company} and the employers name is {self.employer}'
return text
def add_candidate(self, candidate):
self.candidates.append(candidate)
def list_interviewed_candidates(self):
for employee in self.candidates:
if employee.interviewed == True:
return employee
But that doesn't work. I have also tried list comprehension but it seems I just cannot access the boolean value that was set in the first class. Ideally, the output should look something like this:
database1 = Database('Google', 'Jack H')
print(database1)
'The hiring company is Google and the employers name is Jack H'
candidate1 = Employee('Anna S', 'web-designer', 12)
database1.add_candidate(candidate1)
print(database1.list_interviewed_candidates())
[]
candidate1.interview()
print(database1.list_interviewed_candidates())
['Candidate Ana S; 12 - Interviewed']
In your Database.list_interviewed_candidates method you are returning the first employee that was interviewed. Keep in mind that return exits from the current function (method in this case) as soon as is hit.
So it starts looking at your candidates and as soon as one interviewed is found, it returns that one.
You probably want to gather them all in a list and return that:
def list_interviewed_candidates(self):
retval = []
for employee in self.candidates:
if employee.interviewed == True:
retval.append(employee)
return retval
Something pretty interesting you could also use is yield:
def list_interviewed_candidates(self):
for employee in self.candidates:
if employee.interviewed == True:
yield employee
Which... you can try by doing:
print(list(database1.list_interviewed_candidates()))
Pretty cool. This opens the really fun world of iterators!
A list comprehension works, but realize __str__ is used by print but __repr__ is used for items displayed in a list. __repr__ is also used if __str__ isn't defined.
Try the following, but change __str__ to __repr__ in Employee:
def list_interviewed_candidates(self):
return [employee for employee in self.candidates if employee.interviewed]
Then:
database1 = Database('Google', 'Jack H')
print(database1)
candidate1 = Employee('Anna S', 'web-designer', 12)
candidate2 = Employee('Mark T', 'pythonista', 13)
database1.add_candidate(candidate1)
database1.add_candidate(candidate2)
print(database1.list_interviewed_candidates())
candidate1.interview()
candidate2.interview()
print(database1.list_interviewed_candidates())
Outputs:
The hiring company is Google and the employers name is Jack H
[]
[Candidate Anna S; 12. Interviewed., Candidate Mark T; 13. Interviewed.]
Customize __str__ and __repr__ individually if you want differing output between direct print of Employee and how it is displayed in a list.

How to return all objects from a class?

I created the following class from which I can "create" students:
import itertools
class Student (object):
id_iter = itertools.count()
def __init__(self, studentName):
self.studentName = [studentName, next(self.id_iter)]
def __str__(self):
return str(self.studentName)
This way, each student gets a unique ID starting from 0. My example list:
stud1 = Student("Justa Student")
stud2 = Student("Another One")
stud3 = Student("One Morestudent")
I'd like to create a function which would return a similar looking list with both the student names and student IDs (I created this list with another, long code):
Desired output list
The output would return all students and their respective IDs. Later on, I'd like to have another function with removing students as well, I'm thinking of making the function with a for loop going until the highest ID, but I can't properly return stuff with the class.
First, I suggest NOT storing both the name and id in a variable named studentName. This is just confusing.
Instead, you can have a separate studentId field:
import itertools
class Student (object):
id_iter = itertools.count()
def __init__(self, studentName):
self.studentName = studentName
self.studentId = next(self.id_iter)]
def __str__(self):
return str(self.studentName)
Now to create a list, you can make another method that does it:
def createStudents(names):
return [Student(name) for name in names]
To use this, you have to pass in a list of names:
students = createStudents(["Alice", "Bob", "Charles"])
Now you can print out the list of students with a for loop. I will leave this as an exercise for the reader.
You could create two class methods to store and remove registered students at the class level, meaning you have access to all students that were instantiated by the Student class during your program's process.
import itertools
class Student (object):
id_iter = itertools.count()
registeredStudents = {}
def __init__(self, studentName):
self.studentName = [studentName, next(self.id_iter)]
self.registeredStudents[self.studentName[0]] = self.studentName[1]
def __str__(self):
return str(self.studentName)
#classmethod
def registered_students(cls):
return [[k, v] for k, v in cls.registeredStudents.items()]
#classmethod
def remove_student(cls, studentName):
cls.registeredStudents.pop(studentName)
stud1 = Student("Justa Student")
stud2 = Student("Another One")
stud3 = Student("One Morestudent")
print(Student.registered_students()) # [['Justa Student', 0], ['Another One', 1], ['One Morestudent', 2]]
# or
print(stud1.registered_students()) # [['Justa Student', 0], ['Another One', 1], ['One Morestudent', 2]]
# remove student
stud1.remove_student('Justa Student')
# or --> Student.remove_student('Justa Student')
# see remaining students
print(Student.registered_students()) # [['Another One', 1], ['One Morestudent', 2]]

How to call object of class by input?

I am generating a class of persons and want to get information about a certain person by input. I would like to use the str funtction because I am trying to understand it better. My Idea goes as follows:
class Person:
__init__(self, f_name, l_name):
self.f_name = f_name
self.l_name = l_name
__str__(self):
return "The persons full name is:" + f_name + l_name
person1 = Person(Peter, Punk)
person2 = Person(Mia, Munch)
person = input("What persons full name would you like to know?")
print(person) #I am aware that this just fills in the string saved in person, but how do I connect it to the variable?
another idea was to do it as follows:
#class stays the same except:
__init__(self, f_name, l_name):
self.f_name = f_name
self.l_name = l_name
list.append(self)
#and then for the main:
list = []
person1 = Person(Peter, Punk)
person2 = Person(Mia, Munch)
person = input("What persons full name would you like to know?")
index = list(person)
print(list[index])
Thankful for any edvice since I am obviously new to Python :D
I think OP has some concept problems here which this answer may go some way to help with.
Start by building a robust class definition. Simple in this case as there are just 2 attributes. Note the use of setters, getters and str, repr and eq dunder overrides.
A small function that checks if a given Person can be found in a list of Persons and reports accordingly.
Create a list with 2 different Person instances
Create another Person that is known not to match anything already in the list.
Run check()
Modify the 'standalone' Person to make it equivalent to something previously constructed.
Run check()
class Person:
def __init__(self, forename, surname):
self._forename = forename
self._surname = surname
#property
def forename(self):
return self._forename
#forename.setter
def forename(self, forename):
self._forename = forename
#property
def surname(self):
return self._surname
#surname.setter
def surname(self, surname):
self._surname = surname
def __str__(self):
return f'{self.forename} {self.surname}'
def __repr__(self):
return f'{self.forename=} {self.surname=}'
def __eq__(self, other):
if isinstance(other, type(self)):
return self.forename == other.forename and self.surname == other.surname
return False
def check(list_, p):
if p in list_:
print(f'Found {p}')
else:
print(f'Could not find {p}')
plist = [Person('Pete', 'Piper'), Person('Joe', 'Jones')]
person = Person('Pete', 'Jones')
check(plist, person)
person.surname = 'Piper'
check(plist, person)
Output:
Could not find Pete Jones
Found Pete Piper
You probably want a mapping between a name and an object. This is what Python's dict dictionary structure is for:
people = {} # an empty dictionary
people[f'{person1.f_name} {person1.l_name}'] = person1
people[f'{person2.f_name} {person2.l_name}'] = person2
This is creating a string of the first and last name.
You can then lookup the Person object using the full name:
print(people['Peter Punk'])
You could do this with list comprehension like so (also allowing multiple people to have the same first name)
class Person:
__init__(self, f_name, l_name):
self.f_name = f_name
self.l_name = l_name
__str__(self):
return "The persons full name is:" + f_name + l_name
personList= []
personList.append(Person(Peter, Punk))
personList.append(Person(Mia, Munch))
personName = input("What persons full name would you like to know?")
print([str(person) for person in personList if person.f_name == personName])

Python beginner OOP functions

Can you help me to solve the problem, why am I not able to call my functions? I try to calculate the average mark of all student objects created but it somehow doesnt work.
class Student:
anz = 0
def __init__(self, vorname, nachname, studetnId, averageMark):
self.vorname = vorname
self.nachname = nachname
self.studentId = studetnId
self.averageMark = averageMark
Student.anz += 1
def totalAverageMark(self, input):
summe = 0
for s in input:
summe += self.averageMark
return (summe / Student.anz)
def getName(self, arr):
for s in arr:
return self.nachname
students = [Student("Maxine", "Muster", 2, 1.0), Student("Bert", "Beispiel", 1, 2.0)]
print(students.getName())
print(students.totalAverageMark())
It says: AttributeError: 'list' object has no attribute 'totalAverageMark'
students is a list of your students. List has not functions getName and totalAverageMark. You can use them only with object of Student class.
Simplest way to get total average mark of students:
class Student:
anz = 0
def __init__(self, vorname, nachname, studetnId, averageMark):
self.vorname = vorname
self.nachname = nachname
self.studentId = studetnId
self.averageMark = averageMark
Student.anz += 1
students = [Student("Maxine", "Muster", 2, 1.0), Student("Bert", "Beispiel", 1, 2.0)]
sum = 0
for s in students:
sum += s.averageMark
print(f"Total average mark is {sum / len(students)}")
Student define attributes of a single student only single. to get the collective result for example avg mark of all student, you need to create a class (that bind all students say) that defines the attribute of the class ie there students.
below is a simple implementation what you are trying to do using OOPs
class Student:
def __init__(self, vorname, nachname, studetnId, averageMark):
self.vorname = vorname
self.nachname = nachname
self.studentId = studetnId
self.averageMark = averageMark
class Students:
def __init__(self, students_list: list):
self.students = students_list
def avg_marks(self):
total_students = len(self.students)
marks = sum(student.averageMark for student in self.students)
return marks/total_students
students = [Student("Maxine", "Muster", 2, 1.0), Student("Bert", "Beispiel", 1, 2.0)]
student_data = Students(students)
print(student_data.avg_marks())
To iterate through the list and print out the result for each student
for student in students:
print(student.getName())
print(student.totalAverageMark())
Or to access an individual student
students[0].getName()

How to print as string a parameter which is a list from class in python

I want to print all grades as a csv string for the particular Student. How can i achieve this as it prints out memory location instead:
class Student:
def __init__(self,name,year):
self.name = name
self.year = year
self.grades = []
def add_grade(self,grade):
if type(grade) == Grade:
self.grades.append(grade)
else:
pass
def __repr__(self):
return "Student name is {name} and attends year {year}".format(name=self.name, year=self.year)
pieter = Student("Pieter Bruegel the Elder",8)
class Grade:
minimum_passing = 65
def __init__(self,score):
self.score = score
pieter.add_grade(Grade(100))
pieter.add_grade(Grade(95))
pieter.add_grade(Grade(85))
print(str(pieter.grades)) #this one prints [<__main__.Grade object at 0x7fc3874d76d8>]
print(pieter)
I also added it in the __repr__ but still the same.
Give Grade a __str__ method that returns the score as a string.
class Grade:
minimum_passing = 65
def __init__(self,score):
self.score = score
def __str__(self):
return str(self.score)
Then print(pieter.grades) should print:
[100,95,85]
You can't get rid of the [] surrounding it, because that's the way lists print. You can't customize the container's printing from the elements within it. If you need to do that, you should define your own GradeList class.

Categories