Class function correct output but wrong method - python

My homework question asks me to write the following functions using a predetermined class 'Person', it runs 5 tests, one code analyser that ensures it was done the way they want and 4 practical examples, my code passes the practical examples but the analyser fails me.
This is the question:
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}'
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):
"""
(str) Return the name
"""
return self._name
def get_age(self):
"""
(int) Return the age
"""
return self._age
def get_gender(self):
"""
(str) Return the gender
"""
return self._gender
def set_friend(self, friend):
self._friend = friend
def get_friend(self):
"""
(Person) Return the friend
"""
return self._friend
def print_friend_info(person):
print(person._name)
print(person._age)
if person.get_friend() != None:
print("Friends with {}".format(person._friend._name))
All tests pass, including the print_friend_info tests, except for this test that outputs the following error:
----- Analysis Errors -----
You need to print the person's name in print_friend_info
You need to print the person's age in print_friend_info
You need to print the name of the person's friend in print_friend_info

I have tried your code and it works for me with Python3.6.6. I have some suggestions which could be problem. First of all your shouldn't use the protected attributes of your class (underscored variables). You have getter methods to get the data of object so use them. It seems to me that the output can fulfill the test what you mentioned in your question.
Changed part of code:
def print_friend_info(person):
print("Name: {}".format(person.get_name()))
print("Age: {}".format(person.get_age()))
if person.get_friend(): # You don't need to use "!= None". A simple if is enough.
print("Friends with {}".format(person.get_friend().get_name())
print("\n")
joe = Person("Joe", 42, "Male")
print_friend_info(joe)
mary = Person("Mary", 55, "Female")
print_friend_info(mary)
joe.set_friend(mary)
print_friend_info(joe)
Output:
>>> python3 test.py
Name: Joe
Age: 42
Name: Mary
Age: 55
Name: Joe
Age: 42
Friends with Mary

Related

How can I print each item in an unknown length list that is a class attribute as a string in python?

I have a class (Student) with different attributes, such as studentId, address, and courses. My str method for the class returns all the information that the user put in. However, for the attributes that are lists, such as courses, the location of the information is printed out instead of the actual information. Here is the code (sorry it's a little long, there's a bunch of classes):
class Person:
__name = None
__age = None
__address = None
def __init__(self, name, age=0, address=None):
self.set_name(name)
self.set_age(age)
self.set_address(address)
def __str__(self):
return 'Name: ' + self.__name + '\n' + \
'Age: ' + str(self.__age) + '\n' + \
'Address: ' + str(self.__address)
def set_name(self, name):
self.__name = name
def get_name(self):
return self.__name
def set_age(self, age):
self.__age = age
def get_age(self):
return self.__age
def set_address(self, address):
self.__address = address
def get_address(self):
return self.__address
class Student(Person):
def __init__(self, name, studentID= None, age= 0, address= None):
super(Student, self).__init__(name, age, address)
self.set_studentID(studentID)
self.__courses =[]
def __str__(self):
result = Person.__str__(self)
result += '\nStudent ID:' + self.get_studentID()
for item in self.__courses:
result += '\n ' + str(item)
return result
def set_studentID(self, studentID):
if isinstance(studentID, str) and len(studentID.strip()) > 0:
self.__studentID = studentID.strip()
else:
self.__studentID = 'NA'
def get_studentID(self):
return self.__studentID
def add_course(self, course):
print('in add_course')
self.__courses.append(course)
def get_courses(self):
for i in range(len(self.__courses)):
return self.__courses[i]
class Course:
__courseName = None
__dept = None
__credits = None
def __init__(self, courseName, dept= 'GE', credits= None):
self.set_courseName(courseName)
self.set_dept(dept)
self.set_credits(credits)
def __str__(self):
return self.get_courseName() + '/' + self.get_dept() + '/' + str(self.get_credits())
def set_courseName(self, courseName):
if isinstance(courseName, str) and len(courseName.strip()) > 0:
self.__courseName = courseName.strip()
else:
print('ERROR: Name must be a non-empty string')
raise TypeError('Name must be a non-empty string')
def get_courseName(self):
return self.__courseName
def set_dept(self, dept):
if isinstance(dept, str) and len(dept.strip()) > 0:
self.__dept = dept.strip()
else:
self.__dept = "GE"
def get_dept(self):
return self.__dept
def set_credits(self, credits):
if isinstance(credits, int) and credits > 0:
self.__credits = credits
else:
self.__credits = 3
def get_credits(self):
return self.__credits
students = []
def recordStudentEntry():
name = input('What is your name? ')
age = input('How old are you? ')
studentID= input('What is your student ID? ')
address = input('What is your address? ')
s1 = Student(name, studentID, int(age), address)
students.append(s1)
s1.add_course(recordCourseEntry())
print('\ndisplaying students...')
displayStudents()
print()
def recordCourseEntry():
courses = []
for i in range(2):
courseName = input('What is the name of one course you are taking? ')
dept = input('What department is your course in? ')
credits = input('How many credits is this course? ')
c1 = Course(courseName, dept, credits)
print(c1)
courses.append(c1)
displayCourses(courses)
return courses
def displayCourses(courses):
print('\ndisplaying courses of student... ')
for c in range(len(courses)):
print(courses[c])
def displayStudents():
for s in range(len(students)):
print()
print(students[s])
recordStudentEntry()
This is how the code above prints out the 'displaying students...' part:
displaying students...
Name: sam
Age: 33
Address: 123 st
Student ID:123abc
[<__main__.Course object at 0x000002BE36E0F7F0>, <__main__.Course object at
0x000002BE36E0F040>]
I know that it is printing out the location because I need to index into the list. However, the length of the list will be different every time. Normally if I wanted to index into a list, for example, to print a list of names, I would do:
listOfNames = ['sam', 'john', 'sara']
for i in range(len(listOfNames)):
print(listOfNames[i])
or
listOfNames = ['sam', 'john', 'sara']
for i in listOfNames:
print(i)
(not sure what if any difference there is between the 2 ways since they both print out the same way:)
sam
john
sara
How can I write something like the indexing into a list technique shown here in my str method for my class so that it prints the information and not the location?
It would be good to keep to the standard conventions for Python, such as naming
private attributes for objects with single underscores, not double underscores.
The latter are reserved for Python "internal" attributes and methods.
Also, it is convention to use object attributes for objects with get/set methods,
not class attributes. This will make it easier to inspect your objects, while
still maintaining data hiding. Example:
class Course:
def __init__(self, courseName, dept= 'GE', credits= None):
self._courseName = None
self._dept = None
self._credits = None
self.set_courseName(courseName)
...
Your question about why the courses don't print out the way you expected
is rooted in a programming error with the way you programmed the recording
of courses. In recordCourseEntry(), you record two courses and put them
in a list. However, you pass that to your Student object using a method
intended for one course at a time. My suggested fix would be:
...
# s1.add_course(recordCourseEntry())
courses = recordCourseEntry()
for course in courses:
s1.add_course(course)
...
This will probably be enough to get you going. An example output I got was:
Name: Virtual Scooter
Age: 33
Address: 101 University St.
Student ID:2021
ff/GE/3
gg/GE/3

Is it possible to create nested class attributes?

I'm pretty new to Python and have recently learned about classes. I've been experimenting around with them and have come up with a student/course grading system. Here's the code so far:
class course:
TOTAL_COURSES = 0
def __init__(self, name, room, max_students):
self.name = name
self.room = room
self.max_students = max_students
self.student_list = []
course.TOTAL_COURSES += 1
def addStudent(self, student):
# Checks if the class is below max capacity
if len(self.student_list) < self.max_students:
self.student_list.append(student)
# Adds the course to the student's course list
student.course_list.append(self)
return True
return False
So this creates a course class, which I can add students to and set their rooms and other stuff. I've got another class, which is intended to store information on students:
class student:
TOTAL_STUDENTS = 0
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
# Courses in course_list are stored as objects
self.course_list = []
student.TOTAL_STUDENTS += 1
Pretty simple; I've just been stuck on how to create the actual grading system. If I were to do:
s1 = student("Tom", 17, "Male")
c1 = course("English", "E123", 25)
Would it be possible to then use "nested attributes"? So I'd assign that student's grade of a course to a value like:
s1.c1.grade = "A+"
This however doesn't work, and throws an (expected) AttributeError. So would I have to use my previously created course_list?
s1.course_list[0].grade = "A+"
Even then I'm not sure how I'd assign grade to that course object.
Here's a solution that addresses some of the above issues by assigning a "course slot" to a student, rather than the course itself. As you might imagine, there is a limit to the number of course slots available which depends on the course max size. The code can be developed a lot further, but I thought this could be good to get you started:
class Student:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
self.courses = {}
def addCourse(self, course):
if course.status=='Enrolled':
self.courses[course.name] = course
else:
self.courses[course.name] = course.status
class Course:
def __init__(self, name, room, max_students):
self.name = name
self.room = room
self.max_students = max_students
self.student_list = []
self.course_slots_filled = 0
self.course_slots_available = max_students
def __str__(self):
return 'Course_object_{}'.format(self.name)
def check_slots_available(self):
if self.course_slots_filled < self.max_students:
return True
else:
return False
class CourseSlot:
def __init__(self, name, student_name, status):
self.name = name
self.student_name = student_name
self.status = status
self.grade = 'No grade assigned'
def __repr__(self):
return 'CourseSlot_object_{}'.format(self.name)
def set_grade(self, grade):
self.grade = grade
def assign_course_slot(self, student_name):
if self.check_slots_available():
self.course_slots_filled+=1
self.course_slots_available-=1
status = 'Enrolled'
self.student_list.append(student_name)
else:
print('Sorry, {} class is full! {} not enrolled.'.format(self.name, student_name))
status = 'Not enrolled'
return self.CourseSlot(self.name, student_name, status)
Example usage
Instantiate courses:
physics = Course('Physics','101',5)
chemistry = Course('Chemistry','102',1)
Instantiate student 1 and assign a course slot:
s1 = Student("Tom", 17, "Male")
s1.addCourse(physics.assign_course_slot(s1.name))
s1.addCourse(chemistry.assign_course_slot(s1.name))
s1.courses['Physics'].set_grade('A+')
[v for v in s1.courses.values()]
# >>> [CourseSlot_object_Physics, CourseSlot_object_Chemistry]
Instantiate student 2 and assign a course slot
s2 = Student("Susan", 18, "Female")
s2.addCourse(physics.assign_course_slot(s2.name))
s2.addCourse(chemistry.assign_course_slot(s2.name))
#>>> Sorry, Chemistry class is full! Susan not enrolled.
Get course info:
print('Physics course slots filled: ',physics.course_slots_filled)
print('Physics course slots available: ',physics.course_slots_available)
print('Chemistry course slots filled: ',chemistry.course_slots_filled)
print('Chemistry course slots available: ',chemistry.course_slots_available)
#>>> Physics course slots filled: 2
# Physics course slots available: 3
# Chemistry course slots filled: 1
# Chemistry course slots available: 0
print('Physics student list: ',physics.student_list)
print('Chemistry student list: ',chemistry.student_list)
# >>> Physics student list: ['Tom', 'Susan']
# Chemistry student list: ['Tom']
for s in [s1,s2]:
for c in s.courses.values():
try:
print('{} {} grade: {}'.format(s.name, c.name, c.grade))
except AttributeError:
pass
# >>> Tom Physics grade: A+
# Tom Chemistry grade: No grade assigned
# Susan Physics grade: No grade assigned
I guess the cheat is that the course student_list only gets the name of the student and not the Student object, which could probably work if you pass it a unique ID and then iterate through a list of Student objects to match on ID. Something to think about anyway.
Grade is assigned to a combination of a student and a course object, so it cannot be a single attribute of any of them.
I would consider grades closer related to students than courses, so I would add a dictionary to student with a unique course ID (for example its name) as key and the grade as value. Next, you will probably need a function to select the course with the given ID (name) from your list of courses.
To further improve your code you can make course a hashable class, which is a class with a __hash__ method (look this up in the Python docs). Then you can use course objects directly as dictionary keys instead of working with IDs.

Print output is correct but analysis says its wrong

Using the pre-loaded class Person
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}'
Write a function create_fry() which returns a Person instance representing Fry. Fry is 25 and his full name is 'Philip J. Fry'
Write a function make_friends(person_one, person_two) which sets each argument as the friend of the other.
I have gotten the right output with what needs to be printed but when I try run the code through our code Analyzer it says this error:
"You need to print the name of the person's friend in print_friend_info"
class Person(object):
def __init__(self, name, age, gender):
"""Construct a person object given their name, age and gender
Parameters:
name(str): The name of the person
age(int): The age of the person
gender(str): Either 'M' or 'F' for male or female
"""
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):
"""
(str) Return the name
"""
return self._name
def get_age(self):
"""
(int) Return the age
"""
return self._age
def get_gender(self):
"""
(str) Return the gender
"""
return self._gender
def set_friend(self, friend):
self._friend = friend
def get_friend(self):
"""
(Person) Return the friend
"""
return self._friend
def print_friend_info(person):
person_name = person.get_name()
person_age = person.get_age()
person_gender = person.get_gender()
person_friend = person.get_friend()
print (person_name)
print (person_age)
if person_friend == None:
return
else:
friendd = person_friend
nameoffriend = friendd.get_name()
print ('Friends with', nameoffriend)
return
def create_fry():
fry = Person("Philip J. Fry", 25, "M")
return fry
def make_friends(person_one, person_two):
made_friend1 = person_one.set_friend(person_two)
made_friend2 = person_two.set_friend(person_one)
return
--------- Test 1 ---------
Expected Output: Code Analyser
Test Result:
----- Analysis Errors -----
You need to print the name of the person's friend in print_friend_info
Input: bob = Person('Bob Smith', 40, 'M')
--------- Test 2 ---------
Expected Output: print_friend_info(bob)
Code Output: Bob Smith 40
Test Result: Correct!
Input: ed = Person('Ed', 8, 'M')
--------- Test 3 ---------
Expected Output: bob.set_friend(ed)
Code Output: Bob Smith 40 Friends with Ed
Test Result: Correct!
Input: fry = create_fry()
--------- Test 4 ---------
Expected Output: str(fry) -> 'Mr Philip J. Fry 25'
Test Result: Correct!
Input: leela = Person('T. Leela', 22, 'F')
--------- Test 5 ---------
Expected Output: make_friends(fry, leela)
Test Result: Correct!

"TypeError :super() takes at least 1 argument (0 given) " how to solve this i have used argument as a super class name but it didn't worked

students = []
class Student:
school_name = 'Maharshi Science school'
def __init__(self,name,student_id=336):
self.name = name
self.student_id= student_id
students.append(self)
def __str__(self):
return "student: " + self.name
def get_name_capitalize(self):
return self.name.capitalize()
def get_school_name(self):
return self.school_name
class HighschoolStudent(Student):
school_name = 'Maharshi High School'
def get_school_name(self):
return "This is a high school student"
def get_name_capitalize(self):
original_value = super().get_name_capitalize()
return original_value + "-HighschoolStudent"
chirag = HighschoolStudent('chirag')
print(chirag.get_name_capitalize())
This error will only occur if you are using Python 2. To fix this, replace
super().get_name_capitalize()
with
super(HighschoolStudent, self).get_name_capitalize()
If you upgrade to Python 3, your code should work fine.
You are getting the error due to Python 2. Please try the below code:
students = []
class Student(object):
school_name = 'Maharshi Science school'
def __init__(self,name,student_id=336):
self.name = name
self.student_id= student_id
students.append(self)
def __str__(self):
return "student: " + self.name
def get_name_capitalize(self):
return self.name.capitalize()
def get_school_name(self):
return self.school_name
class HighschoolStudent(Student):
school_name = 'Maharshi High School'
def get_school_name(self):
return "This is a high school student"
def get_name_capitalize(self):
original_value = super(HighschoolStudent, self).get_name_capitalize()
return original_value + "-HighschoolStudent"
chirag = HighschoolStudent('chirag')
print(chirag.get_name_capitalize())
Output:
Chirag-HighschoolStudent
There are two changes in this:
class Student --> class Student(object)
Passing your class name as input in super as mandated by Python 2

why this function addStuent() doesn't return what i expect in python

i have a python class that called student.py where it contains attributes and methods one of these method is called addStudent(self,name,age). it takes 2 arguments.
the problem is this function display the name and the age of the new student using the setter method.
but the console display this :
None is a new student and he/she has None years old
where None MUST BE THE NAME AND THE AGE
student.py
class student():
def __init__(self,name,age, grade):
self.stdName=name
self.stdAge = age
self.stdGrade = grade
def getName(self):
print("the student name {}".format(self.stdName))
def setName(self,Name):
self.stdName = Name
def getAge(self):
print("the student age :{}".format(self.stdAge))
def setAge(self, Age):
self.stdAge = Age
def getGrade(self):
print("the student {0}, have a grade {1}".format(self.stdName,self.stdGrade))
if self.stdGrade<2.5:
print("you failed")
elif self.stdGrade>2.5:
print("good job continue")
elif self.stdGrade == 5.0:
print("you are a honor student")
def setGrade(self,Grade):
self.stdGrade = Grade
def getTheAVG(self,initial,*grades):
value = initial
for item in grades:
value+=float(item)
return value/(len(grades)+1)
print("Average of the student is " + str((grades)))
def addStudent(self,name,age):
print("{0} is a new student and he/she has {1} years old"
.format(self.setName(name),self.setAge(age)))
std1 = student("georges", 17, 3.4)
std1.getName()
std1.getAge()
std1.getGrade()
std1.addStudent("jiji",12)
std1.getTheAVG(2.4,5.0,4.6,2.2,1.2)
You function is not returning a value hence it is getting None. You can do this.
def setAge(self, Age):
self.stdAge = Age
return self.stdAge
But, Having said it is a setter method from a design point of view you won't expect a setter function to return any value
Your print relies on the return values of two methods that don't return anything. Either have thesetName and setAge methods return the value they set, or just use the attribute values.
If you want your setter method to return something, do something like this
class Student:
def setName(self,Name):
self.stdName = Name
return self.stdName
def setAge(self, Age):
self.stdAge = Age
return self.stdAge
check this:
class student():
def __init__(self,name,age, grade):
self.stdName=name
self.stdAge = age
self.stdGrade = grade
def getName(self):
print("the student name {}".format(self.stdName))
def setName(self,Name):
self.stdName = Name
return self.stdName
def getAge(self):
print("the student age :{}".format(self.stdAge))
def setAge(self, Age):
self.stdAge = Age
return self.stdAge
def getGrade(self):
print("the student {0}, have a grade {1}".format(self.stdName,self.stdGrade))
if self.stdGrade<2.5:
print("you failed")
elif self.stdGrade>2.5:
print("good job continue")
elif self.stdGrade == 5.0:
print("you are a honor student")
def setGrade(self,Grade):
self.stdGrade = Grade
def getTheAVG(self,initial,*grades):
value = initial
for item in grades:
value+=float(item)
return value/(len(grades)+1)
print("Average of the student is " + str((grades)))
def addStudent(self, name, age):
print("{0} is a new student and he/she has {1} years old"
.format(self.setName(name), self.setAge(age)))
std1 = student("georges", 17, 3.4)
std1.getName()
std1.getAge()
std1.getGrade()
std1.addStudent("jiji",12)
std1.getTheAVG(2.4,5.0,4.6,2.2,1.2)
This will give you your desired output.
Note: Your function is not returning a value thats way it give you None but from a design point of view as your method is a setter method and setter methods don't return a value.

Categories

Resources