Python Class Setter Method Works Only Once - python

class courseInfo(object):
def __init__(self, courseName):
self.courseName = courseName
self.grade = "No Grade"
def setGrade(self, grade):
if self.grade == "No Grade":
self.grade = grade
def getGrade(self):
return self.grade
class edx(object):
def __init__(self, courses):
self.myCourses = []
for course in courses:
self.myCourses.append(courseInfo(course))
def setGrade(self, grade, course="6.01x"):
"""
grade:integer greater than or equal to 0 and less than or equal to 100
course: string
This method sets the grade in the courseInfo object named by `course`.
If `course` was not part of the initialization, then no grade is set, and no
error is thrown.
The method does not return a value.
"""
for crs in self.myCourses:
if crs.courseName == course:
crs.setGrade(grade)
def getGrade(self, course="6.02x"):
"""
course: string
This method gets the grade in the the courseInfo object named by `course`.
returns: the integer grade for `course`.
If `course` was not part of the initialization, returns -1.
"""
for crs in self.myCourses:
if crs.courseName == course:
return crs.getGrade()
else:
return -1
My test cases for the code above are:
> edX = edx( ["6.00x","6.01x","6.02x"] )
> edX.setGrade(100, "6.00x")
> edX.getGrade("6.00x")
> 100
So far so good, but running edX.setGrade(50, "6.00x") doesn't take and getGrade still returns 100. Also, setting grades for 6.01 and 6.02 doesn't seem to work and getGrade returns -1 all the time.
Any help pointers would be much appreciated!
(Full disclosure: this is for an on-line course. But I don't think there are spoilers here for anyone and I really want to understand what's going on. Tks)

Of course it only works once, you coded it that way:
def setGrade(self, grade):
if self.grade == "No Grade":
self.grade = grade
After setting the grade, the test self.grade == "No Grade" is no longer true.
You do have a problem in the getGrade() method:
for crs in self.myCourses:
if crs.courseName == course:
return crs.getGrade()
else:
return -1
You return -1 if the first course doesn't match the name; you return from the for loop right there. Perhaps you want to return -1 only after testing all courses:
for crs in self.myCourses:
if crs.courseName == course:
return crs.getGrade()
return -1
Now the for loop is only interrupted if the matching course is found, and you no longer return -1 for anything other than "6.00x".

Related

bottom up method development parameter passing

I develop bottom up, starting with small simple methods to go to the big full fledged implementation
class Pop(object):
def welcome(self, name, new_member = False):
response = ""
if new_member:
response = " NOT"
return str("hello there "+name+", you seem"+response+" to be a member\n")
def ageVerification(self, name, age, new_member = False):
the_welcome_string = self.welcome(name, new_member)
minimum = ""
excuse = ""
if age < 16:
minimum = " NOT"
excuse = ", sorry"
return str(the_welcome_string+str(age)+" is"+minimum+" the minimum required age to buy beer in Belgium"+excuse+"\n")
def theWholething(self, name, age, address, new_member = False):
if age < 16:
appology = str("you cannot order any beer\n")
else:
appology = str("your beer will be shipped to "+address+"\n")
return str(self.ageVerification(name, age, new_member)+appology)
# EOF
My question is if it is normal that when i reach theWholeThingMethod, I carry along all the parameters of the previously defined methods? Is this pythonic?
My population class has almost 20 "helper" methods called in theWholeThing, and it seems I am just fiddling with parameters to get them in the right order ...
theWholeThing(self,\
name,\
age,\
address,\
registered = True,\
first_date_entered,\
last_date_entered,\
purchased_amount,\
favorite_beer,\
promotional_code,\
and_so_on0,\
and_so_on1,\
and_so_on2,\
and_so_on3,\
and_so_on4,\
and_so_on5,\
and_so_on6,\
and_so_on7,\
and_so_on8,\
and_so_on9,\
and_so_on10):
My question is if it is normal that when i reach theWholeThingMethod, I carry along all the parameters of the previously defined methods? Is this pythonic?
Neither.
There is really no point in having a class if all the methods take all the arguments anyway. These might as well just be functions.
There are many ways this could be done, depending on whether the various parameters are mandatory, or what happens when one is not provided, but here is one possibility:
from dataclasses import dataclass
#dataclass
class Pop(object):
name: str
age: int
address: str
new_member : bool = False
def welcome(self):
response = ""
if self.new_member:
response = " NOT"
return str("hello there "+self.name+", you seem"+response+" to be a member\n")
def ageVerification(self):
the_welcome_string = self.welcome()
minimum = ""
excuse = ""
if self.age < 16:
minimum = " NOT"
excuse = ", sorry"
return str(the_welcome_string+str(self.age)+" is"+minimum+" the minimum required age to buy beer in Belgium"+excuse+"\n")
def theWholething(self):
if self.age < 16:
appology = str("you cannot order any beer\n")
else:
appology = str("your beer will be shipped to "+self.address+"\n")
return str(self.ageVerification()+appology)
# EOF
Note: #nneonneo had a great suggestion of using a dataclasses, so answer tweaked to incorporate that

Can't run through a list in print function

I'm a beginner to python. I've written this code but I can't execute the for loop inside the print function to iterate through the list of marks:
class student:
def __init__(self, name, age, iden, lMark):
self.name=name
self.age=age
self.iden=iden
self.lMark=lMark
def printAve(self, obj):
for i in obj.lMark:
res+=i
av=res/len(lMark)
return av
def disInfo(self, obj):
print("the name is: ",obj.name)
print("the age is: ",obj.age)
print("the identity is: ",obj.iden)
print("the marks are: ", for i in lMark:
print(i))
def addStudent(self, n, a, i, l):
ob=student(n,a,i,l)
ls.append(ob)
return true
def searchStudent(self, _val):
for i in len(ls):
if ls[i]==_val:
return i
ls=[]
s=student("Paul", 23,14, list(15,16,17,20,12))
bool added = s.add("Van", 20, 12, list(12,18,14,16))
if added==True:
print("Student added successfully")
for i in range(ls.__len__())
s.disInfo(ls[i])
Can someone help me to solve this problem and explain me how to do?
You can't put a bare for loop in the argument list to print, and of course the embedded print doesn't return what it prints. What you can do is
print("the marks are:", ", ".join(lMark))
or perhaps
print("the marks are:", *lMark)
(Also, I believe you mean obj.lMark.)
To reiterate, if you call some code in the argument to print, that code should evaluate to the text you want print to produce; not do printing on its own. For example,
def pi()
return 3.141592653589793 # not print(3.141592653589793)
def adjective(arg):
if arg > 100:
return "big" # not print("big")
else:
return "small" # not print("small")
print(str(pi()), "is", adjective(pi()))
Notice how each function call returns something, and the caller does something with the returned value.
from statistics import mean
# classes are named LikeThis
# constants named LIKE_THIS
# other variables should be named like_this
# hungarian notation is not used in Python
class Student:
# instead of a global variable named ls,
# I'm using a class variable with a clear name instead
all_students = []
def __init__(self, name, age, iden, marks):
self.name = name
self.age = age
self.iden = iden
self.marks = marks
def printAve(self):
# you don't need to pass two arguments when you're only operating on a single object
# you don't need to reinvent the wheel, see the import statement above
return mean(self.marks)
def disInfo(self):
print("the name is:", self.name)
print("the age is:", self.age)
print("the identity is:", self.iden)
# you can't put a statement inside of an expression
# I'm guessing you want the marks all on the same line?
# the *args notation can pass any iterable as multiple arguments
print("the marks are:", *self.marks)
# this makes more sense as a classmethod
# clear variable names are important!
#classmethod
def addStudent(cls, name, age, iden, marks):
# I'm using cls instead of Student here, so you can subclass Student if you so desire
# (for example HonorStudent), and then HonorStudent.addStudent would create an HonerStudent
# instead of a plain Student object
cls.all_students.append(cls(name, age, iden, marks))
# note the capital letter!
return True
# again, this should be a classmethod
#classmethod
def searchStudent(cls, student):
# use standard methods!
try:
return cls.all_students.index(student)
except ValueError:
return None
# the literal syntax for lists in Python is `[1, 2, 3]`, _not_ `list(1, 2, 3)`.
# it also makes more sense to use Student.addStudent here, because just calling Student() doesn't add it
# to the list (although you could do that in __init__ if you always want to add them to the list)
Student.addStudent("Paul", 23, 14, [15, 16, 17, 20, 12])
# in Python, type annotations are optional, and don't look like they do in C or Java
# you also used `add` instead of `addStudent` here!
added: bool = Student.addStudent("Van", 20, 12, [12,18,14,16])
# don't need == True, that's redundant for a boolean value
if added:
print("Student added successfully")
# never call `x.__len__()` instead of `len(x)`
# (almost) never use `for i in range(len(x))` instead of `for item in x`
for student in Student.all_students:
student.disInfo()
First I answer your initial question, you can print a list in different ways, here are some of them.
You can iterate through it with a for loop and print every element on a different line:
for elem in self.lMark:
print(elem)
You can also append all values to a string and separate them by a character or something.
(Note: There will be a trailing space at the end.)
myString = ""
for elem in self.lMark:
myString = myString + str(elem) + " "
print(myString)
Better is this by doing it with strings-join method and a short version of the container iteration:
", ".join([str(i) for i in self.lMark])
There were some more issues in the code example.
Here is a running version of the script:
class Student:
def __init__(self, name, age, iden, lMark):
self.name=name
self.age=age
self.iden=iden
self.lMark=lMark
def printAve(self, obj):
for i in obj.lMark:
res+=i
av=res/len(self.lMark)
return av
def disInfo(self):
print("the name is: ",self.name)
print("the age is: ",self.age)
print("the identity is: ",self.iden)
print("the marks are: ", ", ".join([str(i) for i in self.lMark]))
class StudentList:
def __init__(self):
self.__list_of_students = []
def add(self, s):
self.__list_of_students.append(s)
return True
def get(self):
return self.__list_of_students
def search(self, name):
for s in self.__list_of_students:
if s.name == name:
return s
return None
ls = StudentList()
s=Student("Paul", 23,14, [15,16,17,20,12])
ls.add(s)
added = ls.add(Student("Van", 20, 12, [12,18,14,16]))
if added:
print("Student added successfully")
search_name1 = "Paula"
search_name2 = "Van"
if ls.search(search_name1):
print(search_name1, "is part of the list!")
else:
print("There is no", search_name1)
if ls.search(search_name2):
print(search_name2, "is part of the list!")
else:
print("There is no", search_name2)
for student in ls.get():
student.disInfo()
print("-"*10)
I would suggest to separate the list of students and the students to two different classes as shown in the code above.
You can unpack your list with the * operator:
print("the marks are: ", *lMark)
Try setting this as a function. i.e
def quantity():
for i in lMark:
print(i)
Then,
def disInfo(self, obj):
print("the name is: ",obj.name)
print("the age is: ",obj.age)
print("the identity is: ",obj.iden)
print("the marks are: ", + quantity)

__repr__ returned non-string type error

Below is a dummy example of my class method:
class A:
def __init__(self, name):
self.name = name
def __repr__(self):
for i in range(0,5):
if i == 0:
print(self.name)
else:
print("-")
i += 1
m1 = A("x")
m1
It prints out the result for me. However, in the meantime, it gives an error saying that __repr__ returned non-string. I understand that I need to use return instead of print for __repr__, however return would stop my program when the first condition is met. I also tried yield but had no luck.
I know this dummy program looks ridiculous and I could have used some other methods instead of __repr__ to print out the results. But this is just a simplified version of my actual program and I have to use __repr__ or __str__ for some reasons.
You have two basic problems. The first is that you altered the loop index within the loop -- bad form, but not at all fatal. The second is that you fail to return a value.
IMMEDIATE REPAIR
def __repr__(self):
for i in range(0,5):
if i == 0:
val = self.name
else:
val += "-"
return val
However, even this is ineffective.
EVEN BETTER
It appears that you want the name with four dashes appended. Why not just do that?
def __repr__(self):
return self.name + "----"
I am assuming that you want your __repr__ output exactly as you are printing it. For that you will just need to change the logic a bit. Something like this should work:
def __repr__(self):
string = ""
for i in range(0,5):
if i == 0:
string += self.name
else:
string += "-"
i += 1
return string

Using classes and objects to compute a students GPA

Here's my code:
class student:
def __init__(self):
self.totalSumOGrades = 0
self.numberOGrades = 0
def getAverageScore(self):
return (self.totalSumOGrades / self.numberOGrades)
def addGrade(self,grade):
self.totalSumOGrades = str(grade)
self.numberOGrades = self.numberOGrades + 1
return (self.totalSumOGrades)
class GPA:
def __init__(self,grade):
self.grade = grade
self.score = 0
def gradesScore(self):
gradeLetter = self.grade[0]
gradeSign = ' '
if (len(self.grade)) == 2:
gradeSign = self.grade[1]
if (gradeLetter == 'A'):
self.score = 4
elif (gradeLetter == 'B'):
self.score = 3
elif (gradeLetter == 'C'):
self.score = 2
elif (gradeLetter == 'D'):
self.score = 1
elif (gradeLetter == 'F'):
self.score = 0
if (gradeSign == '+'):
self.score += 0.3
elif (gradeSign == '-'):
self.score -= 0.3
def getScore(self):
self.gradesScore()
return self.score
I need both classes on one sheet. The problem I'm having is the argument that is being taken for class GPA is what "getScore" is calculating. I need it so the addGrade from class student adds a grade and I can have "getScore" calculate those grades instead. How do i fix this?
So as I promised an answer / feedback version to help the OP walk through and pick some new questions - here it is (offered in the hope that train hacking is sufficiently high quality to show case some concepts and offer agood start to create a real solution based on this):
#! /usr/bin/env python
from __future__ import division, print_function
class GradePointAverage(object):
"""Implements a "Grade Point Average" (GPA) calculation.
Note: It keeps all grades to offer an incremental update path.
"""
FILL_CHAR_ZERO_ADJUST = '.'
VALID_LETTERS = ('A', 'B', 'C', 'D', 'E', 'F')
VALID_ADJUSTMENTS = ('+', FILL_CHAR_ZERO_ADJUST, '-')
BASE_SCORE_MAP = dict(zip(reversed(VALID_LETTERS), (0., 1., 2., 3., 4.)))
ADJ_SCORE_MAP = dict(zip(VALID_ADJUSTMENTS, (0.3, 0., -0.3)))
def __init__(self, grades=None):
"""Inintializes the _grades, a sequence of canonical grade
values e.g. ['A+', 'B-'] where any grade recieved is
mapped to uppercase letter plus either ''|'+'|'-' or
and exception ValueError is thrown.
"""
if grades is None:
self._grades = list()
else:
self._grades = [self.canonicalize_grade(g) for g in grades]
def __repr__(self):
"""Helper to (de)serialize and put more in print et al."""
return ('GradePointAverage(%s)' % (str(self._grades)))
def add_grades(self, grades):
"""Add a new result / grade to data."""
for g in grades:
self.add_grade(g)
def add_grade(self, grade):
"""Add a new result / grade to data."""
self._grades.append(self.canonicalize_grade(grade))
def count_grades(self):
"""Return the count of grades, the scoring is based upon."""
return len(self._grades)
def grades(self):
"""Return the grades as list, the scoring is based upon."""
return self._grades
def canonicalize_grade(self, grade):
"""Ensure grade is valid, ensure uppercase letter plus either
''|'+'|'-' on output. If invalid, let raise or throw ValueError. """
c_grade = grade.strip().upper() # May raise
if 1 <= len(c_grade) <= 2:
if len(c_grade) < 2:
c_grade += self.FILL_CHAR_ZERO_ADJUST
else:
raise ValueError("Invalid grade length")
if c_grade[0] not in self.VALID_LETTERS:
raise ValueError("Invalid main grade")
if c_grade[1] not in self.VALID_ADJUSTMENTS:
raise ValueError("Invalid grade adjustment")
return c_grade
def _score(self, canonical_grade):
"""Calculate score from canonical grade."""
base, adj = canonical_grade[0], canonical_grade[1]
return self.BASE_SCORE_MAP[base] + self.ADJ_SCORE_MAP[adj]
def average_score(self):
"""Calculate average score."""
if not self.count_grades():
return None
# implicit else:
score_sum = sum(self._score(c_g) for c_g in self._grades)
return score_sum / float(self.count_grades())
def median_score(self):
"""Calculate median score."""
if not self.count_grades():
return None
# implicit else:
middle_index = self.count_grades() // 2
return sorted([self._score(c_g) for c_g in self._grades])[middle_index]
def best_score(self):
"""retrieve highest score."""
return NotImplemented
class Student:
"""Models student with updateable Grade Point Average."""
def __init__(self, grades):
self._gPA = GradePointAverage(grades)
self.number_of_grades = self._gPA.count_grades()
def __repr__(self):
"""Helper to (de)serialize and put more in print et al."""
return ('Student(%s)' % (str(self._gPA.grades())))
# Delegated / proxy methods
def average_score(self):
return self._gPA.average_score()
def count_grades(self):
return self._gPA.count_grades()
def grades(self):
return self._gPA.grades()
def median_score(self):
return self._gPA.median_score()
def best_score(self):
return self._gPA.best_score()
def add_grade(self, grade):
return self._gPA.add_grade(grade)
def add_grades(self, grades):
return self._gPA.add_grades(grades)
def main():
"""Drive some tests on "scored" Students."""
print('Positive test cases:')
print('... service class under test:')
gPA = GradePointAverage(['a+', 'c-'])
print(gPA)
print('... main class under test:')
student = Student(['F+'])
print(student)
print(student.count_grades())
print(student.average_score())
print(student.median_score())
a_grade = 'E-'
print("Added %s" % (a_grade,))
student.add_grade('E-')
print(student.count_grades())
print(student.average_score())
print(student.median_score())
some_grades = ['E', 'b+', 'b-', 'c+', 'D', 'D']
print("Added %s" % (str(some_grades),))
student.add_grades(some_grades)
print(student.count_grades())
print(student.average_score())
print(student.median_score())
print(student.grades())
print('Negative test cases:')
print(student.best_score())
print('... too long:')
try:
_ = GradePointAverage(['aa+', 'no_impact'])
except ValueError as e:
print(e)
print('... wrong grade letter:')
try:
_ = GradePointAverage(['z', 'no_impact'])
except ValueError as e:
print(e)
print('... wrong adjustment:')
try:
_ = GradePointAverage(['A*', 'no_impact'])
except ValueError as e:
print(e)
print('... wrong grade "type" we did let it just bubble:')
try:
_ = GradePointAverage([42, 'no_impact'])
except AttributeError as e:
print(e)
if __name__ == '__main__':
main()
So a student instance always delegates grade and score related tasks to the member instance of the GradePointsAverage class. This shifts the Student class close to ebing a superfluous layer (as is) but in reality you would now stuff personal info identifying the student being modelled into the Student instance.
When I run above code on my machine (with a python v2 interpreter):
Positive test cases:
... service class under test:
GradePointAverage(['A+', 'C-'])
... main class under test:
Student(['F+'])
1
0.3
0.3
Added E-
2
0.5
0.7
Added ['E', 'b+', 'b-', 'c+', 'D', 'D']
8
2.1625
2.0
['F+', 'E-', 'E.', 'B+', 'B-', 'C+', 'D.', 'D.']
Negative test cases:
NotImplemented
... too long:
Invalid grade length
... wrong grade letter:
Invalid main grade
... wrong adjustment:
Invalid grade adjustment
... wrong grade "type" we did let it just bubble:
'int' object has no attribute 'strip'
[Finished in 0.0s]
One should IMO not over engineer toy problems, but this one might offer interesting extension tasks, like storing a hash/anonymized id with the student instances. That would match more real life, where the storage of data that might allow identification of a person or might have the potential to disclose private details of a person is often split into spreading salted hashes to attach to all class instances that need a back reference, but keep the mapping back to ther real names, dates and places etc. in one especially secure place only.
Also maybe introduce (besides the added median) also min/max i.e. worst/best score or grade "info" from any student instance, maybe even try a simple linear regression on the scores to find some "trend".
Another class of extensions would be trying to "cover" all paths in tests.
A further way to "blow things up" might be, a more elgant mini language internal mapping, where the "collection stages" (those mapping from grades to numeric values) are fully transformed to integers eg. by scaling all by a factor of ten and so have lossless arithmetics, with the price to think about "reporting" the expected back transformed real scores (i.e. as sample 4.3 and not 43) but also have the benefit of reporintg easily the grade representation from any score and remebering to only perform one final "rounding" step.
Note also that the helpful pep8 tool or e.g. python3 -m pep8 so_get_gpa_for_student_edited.py gives neither errors nor warnings.
Another hint, often during development and when extending objects / adding fetaures, the names slowly drift out of bounds. Until now (to me) GradePointAverage is a matching class / type name, as I often accept a median in comparison to an arithmetic average as a usefull twin information. But if I already had entered say. a trend method, then it would have been a good time to further separate the functionality or rename the class.
Also deciding on a consisten error strategy helps a lot. In above code we mostly state the problem class, but eg. do not report back what exactly caused the problem. In many cases this is ok or even wanted, but in other cases one might add some detail to the response.
One last detail: Just so you know how to first define all interface methods, and second implement these one by one while incrementally testing I also added one of the tricks to signal when somethig is planned, but not (yet) implemented. Here I simply return NotImplementedin the best_score method. One might also raise NotImplementedError("Method_best_score") instead, but as this leads to:
File "/Users/sthagen/tmp/so_get_gpa_for_student_edited.py", line 184, in <module>
main()
File "/Users/sthagen/tmp/so_get_gpa_for_student_edited.py", line 157, in main
print(student.best_score())
File "/Users/sthagen/tmp/so_get_gpa_for_student_edited.py", line 117, in best_score
return self._gPA.best_score()
File "/Users/sthagen/tmp/so_get_gpa_for_student_edited.py", line 90, in best_score
raise NotImplementedError("Method best_score")
NotImplementedError: Method best_score
I often during active creation from "zero" the more silent return NotImplemented option and in (pre-)production when a call to a not yet implemented method or function is more probable an error in usage, I switch to the Exception, your mileage may vary ...
Please feel free to comment (in case I misread the task) or if I forgot to comment on a change you notice when comparing to your code smaple.

AttributeError: 'list' object has no attribute 'assignmentScores'

I don't understand the meaning of this problem or how to fix it!
I keep getting the problem AttributeError: 'list' object has no attribute 'assignmentScores'
What does this mean? and how do I fix this issue?
My code is:
class Student:
studentName = ""
studentCourse = ""
averageMark = 0
grade = "none"
assignmentScores = [1, 2, 3, 4]
def __init__(self, n, c, a, g,m):
self.studentName = n
self.studentCourse = c
self.averageMark = a
self.grade = g
self.assignmentScores = m
def getName(self):
return self.studentName
def getCourse(self):
return self.studentCourse
def getAverage(self):
return self.averageMark
def getGrade(self):
return self.grade
def getMarks(self):
return self.assignmentScores
def setAverage(self):
mark = self.averageMark
return mark
def setGrade(self):
grade = self.grade
return grade
def setMarks(self):
marks = self.setMarks()
return marks
def addMark(self):
score = list.append(self, self.assignmentScores)
def calculateAverage(self):
if len(self.assignmentScores) > 0:
average = sum(self) / float(len(self.assignmentScores))
return average
else:
return 0
def determineGrade(self):
return 0
print(calculateAverage(assignmentScores))
First, please use 4 spaces for all indentation, it helps a lot. PEP 8 is your friend and will keep everyone friendly and helpful.
As for your problem, after running the code myself and looking at the traceback, it looks like you assigned the self.assignmentScores list to self itself, so when you type self.assignmentScores you are looking up an attribute of self, which is now a list instead of an instance of the class.
This mistake comes from the way you called the method:
calculateAverage(assignmentScores)
This method only requires one argument, which is supposed to be an instance of the class Student, but not only are you calling the method directly from the class instead of from an instance, you are using the assignmentScores list as an argument for the method. This makes it so that the method calculateAverage() replaces self with self.assignmentScores so when you try to check if the list is empty the code is reading it as self.assignmentScore.assignmentScore instead of the intended way.
The way you have the class defined at the moment strongly encourages you to call the method like this.
billy = Student("","",0,"none",[1,2,3,4])
print(billy.calculateAverage())
There is another error standing in your way after you solve this problem, but a good look at the traceback and a careful reading of the relevant code will lead you to the solution. Right now all you need is a better understanding of classes and calling methods work.

Categories

Resources