Calculating the average of a list in Python 3 - python

I am currently trying to calculate the average of a list created by a method in a class. Firstly all information is passed to a Class that records/returns the data passed through from the main function. The issue is what do I pass in from the main function to firstly retrieve the self._marks list and then manipulate it in order for the average to be returned. Also am I using the correct code for the calculateAverageMark section? Thanks in advance
Here is the code:
class Student :
def __init__(self, id):
self._studentId = id
self._marks = []
##
# Converts the student to a string .
# #return The string representation .
#
# Sets the student's ID.
# #param newId is the student's new ID.
#
def setStudentId(self, id):
self._studentId = id
##
# Gets the student's ID.
# #return the student's ID
#
def getStudentId(self, newId):
return self._newId
##
# Appends a mark to the marks list
#
def addMark(self, mark):
self._marks.append(mark)
def __repr__(self) :
# Build a string starting with the student ID
# followed by the details of each mark .
s = "Student ID :" + self._studentId + " "
if len(self._marks) == 0 :
s += " <none \n>"
else :
for mark in self._marks :
s += " Course Module: " + mark.getModule() + " Raw Mark: " + str(mark.getRawMark())
return s
##
# Method to calculate the students average mark
#
def calculateAverageMark(self):
totalMarks = 0
count = 0
for mark in self._marks :
if mark == 0 :
count = count
else :
count = count + 1
totalMarks = totalMarks + mark
average = totalMarks / count
return average

Your current code is incorrect because you divide by count in every iteration (and before count is actually the number of marks). Calculating the average is very easy with a list of values:
def calculateAverageMark(self):
if self._marks: # avoid error on empty list
return sum(self._marks) / float(len(self._marks))
You don't need to pass anything in; all instance attributes are available via self. Unless you have specifically been told to exclude zero scores from the average, you should count them.

Related

Missing items when I loop over a dictionary

I tried to create a student class object, and collect some data and show it:
class Student:
subjects = []
grades = []
d = {}
def __init__(self, name, id):
self.name = name
self.id = id
def addGrade(self, subject, grade):
self.subjects.append(subject)
self.grades.append(grade)
def showGrades(self):
self.d = dict(zip(self.subjects, self.grades))
for subject in self.d:
return subject + ' : ' + str(self.d[subject])
when I try add to values:
stu =Student('Zaki', 23)
stu.addGrade('Math',90)
stu.addGrade('Physicis',95)
stu.addGrade('English',100)
print(stu.showGrades())
the output turns to be
Math : 90
and it ignores the other values!
When you call return subject + ' : ' + str(self.d[subject]) in showGrades you exit the function, you can't use return in a loop. You should instead parse and format the output to your liking and then use return outside of the loop.
The specific problem that you are having is that you are putting an (unconditional) return inside the for loop (so it returns on the first iteration of the loop), rather than for example, building a list and returning that list after the loop.
However, separate from this, your code is also modifying the class variables, and you will get a surprise if you create another instance stu2 = Student(...) and find that this second student already has subjects and grades.
What you need to do to fix this second problem is to make subjects and grades into instance variables instead - and initialise them in __init__. You could do similarly with your class variable d, but it looks like it is probably only for use inside showGrades and would probably be best just replaced with a local variable in that method (function).
Putting these together, you could have something like this:
class Student:
def __init__(self, name, id):
self.name = name
self.id = id
self.subjects = []
self.grades = []
def addGrade(self, subject, grade):
self.subjects.append(subject)
self.grades.append(grade)
def showGrades(self):
d = dict(zip(self.subjects, self.grades))
grades = []
for subject in d:
grades.append(subject + ' : ' + str(d[subject]))
return grades
stu =Student('Zaki', 23)
stu.addGrade('Math',90)
stu.addGrade('Physics',95)
stu.addGrade('English',100)
print(stu.showGrades()) # ['Math : 90', 'Physics : 95', 'English : 100']
stu2 = Student("Juan", 24)
print(stu2.showGrades()) # []
You expect that return subject + ' : ' + str(self.d[subject]) in showGrades run three times, but when it reaches the first return, it leaves the loop. So, change your showGrades code to:
def showGrades(self):
self.d = dict(zip(self.subjects, self.grades))
ret = []
for subject in self.d:
ret.append(subject + ' : ' + str(self.d[subject]))
return ret # this will return list containing [subject : grade]
Output:
['Math : 90', 'Physicis : 95', 'English : 100']

Items and dictionaries in a for loop with an array

I am working on a python program with for loops, dictionary, and items. I have an array from a class that supposedly has the items for the dictionaries however each time I run the code I get an error and I have change my code several time and I cant get it to output the right result or to even run properly. In contrast I can't change anything above my for loops and was wondering if i should input a dictionary. However it is stated that the dictionary should be in the DepartmentManager in the departments{} array. Therefore, i would like some help in trying to make my for loop run correctly without an error.
Here are the instructions for the first for loop:
Create a for in loop to loop over the items in the dictionary stored in the departments attribute of the DepartmentManager object
Here is the 2nd for loop instructions:
Create another for in loop inside the first loop. This loop will be used to print out the assets that belong to the Department if the asset value is less than $2,000
class Department:
def __init__(self, departmentSupervisor):
self.departmentSupervisor = departmentSupervisor
self.departmentAssets =[]
class DepartmentAsset:
def __init__(self, assetName, quantity, expectedLifeSpan, AssetValue):
self.assetName = assetName
self.quantity = quantity
self.expectedLifeSpan = expectedLifeSpan
self.AssetValue = AssetValue
class DepartmentManager:
def __init__(self):
self.departments = {}
deptManager = DepartmentManager()
mktDept = Department("Jamie Doe")
maktDeptAsset1 = DepartmentAsset ("desk", 5000, 2, 10)
maktDeptAsset2 = DepartmentAsset ("computer", 1500, 5, 5)
mktDept.departmentAssets.append(maktDeptAsset1)
mktDept.departmentAssets.append(maktDeptAsset2)
financeDept = Department("Ryan Doe")
financemaktDeptAsset1 = DepartmentAsset ("chair", 500, 2, 10)
financemaktDeptAsset2 = DepartmentAsset ("calculator", 500, 3, 5)
financeDept.departmentAssets.append(financemaktDeptAsset1)
financeDept.departmentAssets.append(financemaktDeptAsset2)
deptManager.departments["marketing"] = mktDept
deptManager.departments["financing"] = financeDept
for key, value in deptManager.departments():
print(mktDept.departmentSupervisor + "is in charge of the fowwling " +
departmentAsset.getkey() + " department and the following assets less than $2000." )
for mktDept in deptManager.departments["Marketing"]:
print (mktDept.departmentAssets.quantity + " " +
mktDept.departmentAssets.assetName + "at $" +
mktDept.departmentAssets.AssetValue + " each. This asset is expected to last for " +
mktDept.departmentAssets.expectedLifeSpan + "to the values stored in the DepartmentAsset belonging to the Department")
TypeError: 'dict' object is not callable
**This is the error I keep getting at the end of my code or something similar to this
Instead of
for key, value in deptManager.departments():
you want
for key, value in deptManager.departments.items():

Having a constantly changing variable in a python loop

I'm trying to write a program that would ask for a students name, a couple other numerical values, and assign them to groups, via their numerical value, to have all groups as close to equal as possible (by taking the the highest next value in the list, and assigning it to the next group and so on).
However, I'd need to save their number to some variable, as well as their name, to then print out the group's list.
For this I'd need a variable that changes everytime the loop goes through to add another student. I'd also need to sort these number, and then somehow call back the name they corrispond to after they've been sorted into groups, and I'm not sure how to do any of these. Is there any way for this to be done, would I have to use another language?
This is the code I have so far:
from easygui import *
times = 0
name = 0
s_yn = ynbox("Would you like to enter a student?")
while s_yn == 1:
msg = "Student's Information"
title = "House Sorting Program"
fieldNames = ["Name", "Grade","Athleticism (1-10)","Intellect (1-10)","Adherance to school rules (1-10)"]
fieldValues = []
fieldValues = multenterbox(msg,title, fieldNames)
times = times + 1
ath = fieldValues[2]
int_ = fieldValues[3]
adh = fieldValues[4]
ath = int(ath)
int_ = int(int_)
adh = int(adh)
total = ath+int_+adh
s_yn = ynbox("Would you like to enter a student?")
I believe it would be nice to create a Student class that holds all variables associated with a student. Then you could add each student to a list which you could sort by the values you want and divide to how many groups you want.
from easygui import *
from operator import attrgetter
class Student(object):
def __init__(self, name, grade, athleticism, intellect, adherance):
self.name = name
self.grade = int(grade)
self.athleticism = int(athleticism)
self.intellect = int(intellect)
self.adherance = int(adherance)
self.total = self.athleticism + self.intellect + self.adherance
def __str__(self): # When converting an instance of this class to a string it'll return the string below.
return "Name: %s, Grade: %s, Athleticism (1-10): %s, Intellect (1-10): %s, Adherance to school rules (1-10): %s"\
% (self.name, self.grade, self.athleticism, self.intellect, self.adherance)
student_group = []
while ynbox("Would you like to enter a student?"): # Returns 'True' or 'False' so it'll loop every time the user press 'yes'.
message = "Student's Information"
title = "House Sorting Program"
field_names = ["Name", "Grade", "Athleticism (1-10)", "Intellect (1-10)", "Adherance to school rules (1-10)"]
field_values = multenterbox(message, title, field_names)
student = Student(*field_values) # Unpack all elements in the list 'field_values' to the initializer.
student_group.append(student) # Add the student to the group 'student_group'.
# When the user has put in all the students we sort our group by 'total' (or any other value you want to sort by).
sorted_group = sorted(student_group, key=attrgetter("total"), reverse=True)
# Just as an example I divided the students into 3 groups based on their total.
best_students = sorted_group[:len(sorted_group) // 3]
average_students = sorted_group[len(sorted_group) // 3:2 * len(sorted_group) // 3]
worst_students = sorted_group[2 * len(sorted_group) // 3::]

Python list of object are sharing variables

class Journey has a list, self.leg_miles
class ManyJourneys has a list of journeys created by
self.journeys = []
for i in range(2):
self.journeys.append(Journey())
from some input I add to the list leg_miles for each Journey kind of like this:
self.journeys[self.count].addLeg(temp)
temp is a number i read from a list. it is always changed to what was inputted right before the above line.
for some reason instead of creating a new list for journey[1] it just adds to the created list.
for example:
if for journey[0] the leg_miles had [4,5,6]
and I moved on to journey[1] to add 4 and 6
it would have :
leg_miles = [4,5,6,4,6]
I do not understand why it is adding on. I have self in it.
I do not understand how to upload code or pictures.
I hope this is enough information to solve the problem
EDIT: Here is the code I have.
class Journey:
def __init__(self,odometer=0,leg_miles=[],leg_gas=[]): #constructor
self.odometer = odometer
self.leg_miles = leg_miles
self.leg_gas = leg_gas
def addLeg(self,miles,gas):
#adds to a list of miles and gas
self.leg_miles.append(miles)
self.leg_gas.append(gas)
def getLists(self):
#returns the list of miles and the list of gas
return self.leg_miles,self.leg_gas
def calculate(self):
#calculate the miles per litre for the journey and return
miles_per_litre = 0
for i in range(len(self.leg_miles)): #calcs miles per litre for each leg and adds to total
miles_per_litre += int(self.leg_miles[i]/self.leg_gas[i])
return miles_per_litre
class ManyJourneys:
def __init__(self,name):
self.myfile = open(name,"r")
self.content = self.myfile.read().split("\n")
self.journeys = []
for i in range(self.content.count("")+1):
self.journeys.append(Journey())
self.count = 0
for i in self.content:
if i == "":
self.count+=1
else:
temp = i.split(" ")
self.journeys[self.count].addLeg(int(temp[0]),int(temp[1]))
def statistics(self):
sum = 0
for i in self.journeys:
sum += i.calculate()
return sum/len(self.journeys)
def main():
filename = input("Please enter a file name. (Eg: test.txt): ")
manyJourneys1 = ManyJourneys(filename)
print("You got {0} miles per litre.".format(manyJourneys1.statistics()))
main()
and a sample text file would be
100 54
340 109
23 4
333 33
4500 678
There is an unobvious feature in Python regarding default values - they represent the same object each time, which causes surprising behavior when using mutable values as defaults:
def add(value, l=[]):
l.append(value)
return l
add(1) # [1]
add(2) # [1, 2]
To not fall into this trap do not use list or any other mutable value this way. If you need to initialize something to an empty list do it in a function/method body:
def __init__(self, odometer=0, leg_miles=None, leg_gas=None):
self.odometer = odometer
self.leg_miles = leg_miles or []
self.leg_gas = leg_gas or []
You screwed up your definition of Journey. Mutable types must be assigned in the initializer or in methods, not in the class or default arguments.

Python : is this local variable static?

My First Attempt :
def generate_id():
""" Create unique id of alphanumeric characters """
i = 0
id = ''
while i!=10:
id = id + random.choice(string.ascii_letters + string.digits)
i+=1
if check_unique(id):
return id
id = generate_id()
return id
def check_unique(id):
"""Check if id is unique"""
try:
instances = SomeModel.objects.get(id=id)
except ObjectDoesNotExist:
return True
return False
Second Way :
def generate_id():
""" Create unique id of alphanumeric characters """
i = 0
id = ''
while i!=10:
id = id + random.choice(string.ascii_letters + string.digits)
i+=1
if check_unique(id):
return id
generate_id()
def check_unique(id):
"""Check if id is unique"""
try:
instances = SomeModel.objects.get(id=id)
except ObjectDoesNotExist:
return True
return False
If I do it the second way , won't my logic of generating unique id's be wrong ? Because I might loose the id from the last call .
I am new to python and I don't know but I think my recursion concept looks messed up
Follow your code:
if check_unique(id): # If this is `false`, you keep going
return id
generate_id() # Now what? You call the function. Nothing gets returned.
If you want to create a unique ID, don't use recursion. Just use a while loop and generate new IDs as long as they're not unique:
characters = string.ascii_letters + string.digits
def generate_id(length=10):
return ''.join(random.choice(characters) for i in range(length))
def generate_unique_id(length=10):
id = generate_id(length)
while not check_unique(id):
id = generate_id(length)
return id
In the second way you should return a the end of the generate_id function:
return generate_id()
I would also suggest to make an iteration instead of a recursive call... it's seems cleaner in this situation.

Categories