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::]
Related
I have a spread sheet containing student names and test scores in this format:
first name, last, score
Each student can take the test up to three times, however they are given another row if they attempt the test more than once, example:
John, Smith, 80
Sally, Williams, 90
John, Smith, 100
I am trying to create Student objects for each student and add these to a ClassOfStudents object. But I cannot figure out how to avoid creating 'John Smith' twice. Here are the two classes:
class Student:
def __init__(self, first_name, last_name ):
self.first_name = first_name
self.last_name = last_name
self.score_first_attempt = 0
self.score_second_attempt = 0
self.score_third_attempt = 0
class ClassOfStudents:
"""Represents one class"""
def __init__(self, cohort, assignment_name):
""" intitalize empty list, will hold Student objects """
self.students = []
def add_student(self, student_obj):
self.students.append(student_obj)
and here is my main.py where I read the excel data and create objects from said data:
from student import Student, ClassOfStudents
from openpyxl import load_workbook
# intentionally left out openpxyl code but am reading excel data via the 'sheet_obj' variable
# initialize object that will hold all Student objects
class_of_students= ClassOfStudents()
# Create Student objects and add them to class_of_students
for i in range(1, 3):
first_name = sheet_obj.cell(row = i, column = 2).value
last_name = sheet_obj.cell(row =i, column = 1).value
score = sheet_obj.cell(row = i, column= 3).value
student_obj = Student(first_name, last_name) # create student object
# if there are no Student objects in class_of_students object, add the first one
if not class_of_students_obj.students:
class_of_students.add_student(student_obj)
# loop through class_of_students, if student is already included in class_of_students do not add this iterations student_obj, just discard it
for student in class_of_students_obj.students:
if student.first_name == first_name and student.last_name == last_name:
# logic for retrieving existing object and adding score_second_attempt value would go here
else:
class_of_students_obj.add_student(student_obj)
My code creates 3 Student objects and adds them all to class_of_students (John Smith is created twice). I believe this is because 'Sally Williams' is eventually being compared to 'John Smith', thus creating the third object. I think my attempt is approaching this in the completely wrong way. Can anyone offer a better approach to avoid creating duplicate Student objects that represent the same physical student? Thanks for any help. (I also left out adding the score_first_attempt value intentionally since I need to avoid duplicates before focusing on that)
Just add a loop variable:
exists = False
for student in class_of_students_obj.students:
if student.first_name == first_name and student.last_name == last_name:
exists = student
break
if exists:
# logic for retrieving existing object and adding score_second_attempt value would go here
# the student object of the particular name is in exists object
else:
class_of_students_obj.add_student(student_obj)
In main.py:
from student import Student, ClassOfStudents
from openpyxl import load_workbook
# intentionally left out openpxyl code but am reading excel data via the 'sheet_obj' variable
# initialize object that will hold all Student objects
class_of_students= ClassOfStudents()
# Create Student objects and add them to class_of_students
for i in range(1, 3):
first_name = sheet_obj.cell(row = i, column = 2).value
last_name = sheet_obj.cell(row =i, column = 1).value
score = sheet_obj.cell(row = i, column= 3).value
student_obj = Student(first_name, last_name) # create student object
# if there are no Student objects in class_of_students object, add the first one
if not class_of_students_obj.students:
class_of_students.add_student(student_obj)
# loop through class_of_students, if student is already included in class_of_students do not add this iterations student_obj, just discard it
exists = False
for student in class_of_students_obj.students:
if student.first_name == first_name and student.last_name == last_name:
exists = student
break
if exists:
# logic for retrieving existing object and adding score_second_attempt value would go here
# the student object of the particular name is in exists object
else:
class_of_students_obj.add_student(student_obj)
You can make your class student a dataclass
import dataclasses
#dataclasses.dataclass
class Student:
first_name: str
last_name: str
score_first_attempt: float = 0
score_second_attempt: float = 0
score_third_attempt: float = 0
Now you do not have to loop through your class_of_students_obj.students, just make a check if your dataclass is in your list
# Create Student objects and add them to class_of_students
for i in range(1, 3):
first_name = sheet_obj.cell(row = i, column = 2).value
last_name = sheet_obj.cell(row =i, column = 1).value
score = sheet_obj.cell(row = i, column= 3).value
student_obj = Student(first_name, last_name) # create student object
# if student is already included in class_of_students do not add this iterations student_obj, just discard it
if student_obj in class_of_students_obj.students:
class_of_students_obj.add_student(student_obj)
Than we remove this part in your loop, since its useless now
# if there are no Student objects in class_of_students object, add the first one
if not class_of_students_obj.students:
class_of_students.add_student(student_obj)
If you wan't to make better, you can make the if not class_of_students_obj.students check part inside the add_student method
I have created a class that takes name,id number and salary for each object. inside the class there are functions for adding or deduction of the salary and showing the status for each employee:
class emp():
def __init__(self,name,id_num,salary):
self.name=name
self.id=id_num
self.s=salary
def bounus(self,bon):
self.s+=bon
print(" the empolyee %s got a raise of %s"%(self.name,bon))
def ded(self,d):
self.s-=d
print(" the empolyee %s got a deduction of %s"%(self.name,d))
def show(self):
s="the employee {} with id number {} has a salary of {}".format(self.name,self.id,self.s)
print(s)
so I wanted to create a number of objects of my chioce using "range" function in the "for" loop as the following:
for i in range(1,3) :
o=str(input("Enter the employees number %s name\n"%i))
p=input("Enter his\her id number\n")
q=input("Enter his\her salary\n")
ai=emp(o,p,q)
ai.show()
in that way, it loops through 1 and 2 creating objects a1 and a2 and it worked but when I try to show them outside the loop as the following:
a1.show()
it says,a1 is undefined although I could show them inside the loop , how can I store the objects so I can show or apply functions on them after looping .thanks
The i in ai does not get processed as a seperated variable, it just becomes one whole ai.
Instead, you should make a list a, which you can access with a[i].
a = []
for i in range(2) : # Slight change to start at i=0
o=str(input("Enter the employees number %s name\n"%i))
p=input("Enter his\her id number\n")
q=input("Enter his\her salary\n")
a.append(emp(o,p,q))
a[i].show()
Selcuk identified your issue, but here is a code snippet based on your code that may help you conceptualize his advice:
new_employees = []
for i in range(1,3):
name = input("Enter the employees number %s name\n" %i)
id_num = input("Enter his\her id number\n")
salary = input("Enter his\her salary\n")
employee = emp(name, id_num, salary)
employee.show()
new_employees.append(employee)
At the end of the loop you will now have a list of new employees that you can do other things with. So, per your comment assume you want to deduct $25 from the salary of on the employee with the employee id of 5. You could something like this if you didn't want to get fancy:
target_employee = None
for employee in new_employees:
if employee.id == 5:
target_employee = employee
break
if target_employee:
target_employee.ded(25)
Here is another way that auto-creates a name for each employee the way you intended and stores that name and the employee object in a dictionary. Each employee can then be called by his name from outside the loop with full access to all the class methods. Also class names should always be capitalized. Object names are in lower case:
class Emp():
def __init__(self, name, id_num, salary):
self.name = name
self.id = id_num
self.s = salary
def bonus(self, bon):
self.s += bon
print("The empolyee %s got a raise of %s" % (self.name, bon))
def ded(self, d):
self.s -= d
print("The empolyee %s got a deduction of %s" % (self.name, d))
def show(self):
s = "The employee {} with id number {} has a salary of {}".format(self.name, self.id, self.s)
print(s)
employees = {}
for i in range(1, 3):
o = input("Enter the employees number %s name\n" % i)
p = input("Enter his\her id number\n")
q = int(input("Enter his\her salary\n"))
emp = Emp(o, p, q)
name = "a" + str(i)
employees[name] = emp
employees["a1"].show()
employees["a2"].bonus(500)
employees["a2"].ded(200)
employees["a2"].show()
The first mistake you have done is declaring the class inside the for loop. The scope of the object is limited to the for loop and will be destroyed after the loop and moreover you cannot store all the values wrt to the loop as every time a loop is run the new object will be invoked destroying all the previous one hence us the list to append them and try
I am trying to add a new value to an existing class instance, but I fail. I will directly explain what I am trying to do and will show my code:
Let's say I have a number of people with different names and they have subjects with different value. The input should look like this:
Joe -> car -> 5000
Mike -> house -> 100000
John -> phone -> 1000
Joe -> house -> 80000
etc.
When I get an input with the same name, I should append the new subject to the existing one and make a list from all the subjects and adding the two values.
At the end, when I get an input:
Joe vs John
I should compare them only if they have at least one subject from the same category (in this example it will be "house") and this continues till I get the command "Stop".
class Person:
def __init__(self, name, subject, price):
self.name = name
self.subject = subject
self.price = price
people_list = []
while True:
data_input = input().split(" -> ")
if data_input[0] == "Stop":
break
elif len(data_input) == 3:
name, subject, price = data_input[0], data_input[1], data_input[2]
same_name = [x for x in people_list if x.name == name]
if len(same_name)>0:
(......)
else:
person = Person(name, subject, price)
people_list.append(person)
else:
info = data_input[0].split()
name_1, name_2 = info[0], info[2]
....
Therefore I have 3 questions:
Can I add the new subject and its value somehow in the class directly? This would create one list with all the subjects and one list with all the prices for each name in the class, right?
If this is not possible, can I somehow append the new subjects in the list (people_list in this example)? I think this is not possible or if possible, then much more complicated.
How can I search in the class without using list comprehension?
Thank you very much in advance!
I wrote a simple Python script to determine if all students grades are reported. The script first loops through and adds students to arrays regarding grade status. Then I loop through the file again, to determine if each students grades are in. I end up with three arrays that include students with "all grades reported", "some grades reported", "no grades reported". However, I want tackle this problem with more of a object oriented approach. I have attempted to create a class that works. I am stuck at how to loop through and create one Object for each student, then use addcourse to push each course into the Object. Any help I can get to become a better programmer would be great!
Data:
**id,fname,lname,course,grade,mode**
10001,Freddy,Freshman,Art-101,A,online
10001,Freddy,Freshman,Art-101,A,online
10002,Suize,Sophmore,Mat-102,C,inperson
10002,Suize,Sophmore,Bio-101, ,inperson
10002,Suize,Sophmore,Soc-201,D,online
10003,Jilly,Junior,mth-102, ,inperson
10003,Jilly,Junior,Bus-101, ,inperson
10003,Jilly,Junior,Che-204, ,inperson
Working Code:
fh = open('students.txt').readlines()
header = fh.pop(0)
gradereported = []
nogradereported = []
for line in fh:
students = line.split(',')
ids = students[0]
grade = students[4]
if grade != "":
gradereported.append(ids)
else:
nogradereported.append(ids)
allgradesin =[]
nogradesin = []
somegradesin = []
for i in fh:
students = line.split(',')
ids = students[0]
if ids in gradereported and ids not in nogradereported:
if ids not in allgradesin:
allgradesin.append(ids)
elif ids not in gradereported and ids in nogradereported:
if ids not in nogradesin:
nogradesin.append(ids)
elif ids in gradereportedand and ids in nogradereported:
if ids not in somegradesin:
somegradesin.append(ids)
Attempt at class:
class Student(object):
def __init__(self, lname, fname, term, courses = []):
self.studid = studid
self.lname = lname
self.fname = fname
self.term = term
self.courses = []
def addcourse(self, course, grade, mode):
self.course = course
self.grade = grade
self.mode = mode
self.courses.append((self.course, self.grade, self.mode))
You could do this, as #blade suggests, by creating a dictionary indexed by student id and then for each row of your input file either get the existing student from the dictionary if it exists or create a new one. In code, this would look like:
class Student(object):
def __init__(self, student_id, lname, fname):
self.studid = student_id
self.lname = lname
self.fname = fname
self.courses = []
def addcourse(self, course, grade, mode):
self.courses.append((course, grade, mode))
students = {}
fh = open('students.txt').readlines()
header = fh.pop(0)
for line in fh:
row = line.split(',')
if len(row) < 6:
continue
student_id, fname, lname, course, grade, mode = [i.strip() for i in row]
student = students.get(student_id, Student(student_id, lname, fname))
student.addcourse(course, grade, mode)
students[student_id] = student
A couple of things to note. First, I modified the constructor of your Student class, dropping the term argument since it wasn't clear where the term was specified in your input file. Furthermore, since you don't use the courses argument I dropped that as well. (Note that you probably don't want to use [] as a default argument. Read about mutable default arguments here.) You also don't need to create instance variables for the course, grade, and mode in your addcourse function, you can just append them directly to the array.
I also added a call to strip for each of the items pulled from the input file to clean up the newlines at the end of each row.
How about this:
Add a dict that id is the key, and the Student object is the value
Loop the file and if the key is in the dict, get the Student object from the dict. Otherwise create a new Student object. Then add the course to the Student object.
In addition to the answer of #JCVanHanne you could define another function in your class to collect the info, whether a student has none, some or all of his/her grades.
One possible way (assuming a missing grade is represented by an empty string while also grades like A+ or other none-empty values are possible) could be:
def gradeStatus(self):
miss = [course[1] for course in self.courses].count("") # count empty grades
if len(self.courses) == miss:
print('No grades at all')
elif miss in range(1, len(self.courses)):
print('Some, but not all grades')
elif miss == 0:
print('All grades provided')
else:
print('Invalid Data')
You probably would use status codes or other ways (like a return value to further process) to work with the information than just printing them. As an example with the print commands:
students['10003'].gradeStatus() # leads to: No grades at all
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.