I have a class called Student, where they have two attributes: studentName and a unique number for each student, studentID.
In another class, called Course, I already have a function
add_student(self, student) that adds the given student to the given course. I want to create another function add_student_list(self, lst) which intakes a list of studentIDs. I have a for loop which looks at the IDs in lst to see which students to add, I just need to call the add_student(self, student) function for the ones in lst, however, I'm unable to do this, as I only have an id, and not the student object.
So my question is, how can I call the necessary student object based on the unique ID?
EDIT: here's the relevant part for my code:
import itertools
import pandas as pd
class Student (object):
#The following two objects are used to assign them unique IDs and to keep track of them.
id_iter = itertools.count()
all_students = pd.DataFrame(columns=["Student", "Student ID"])
#Adding a new student:
def __init__(self, studentName):
#The name of the student will be given, the ID will be generated:
self.studentName = [studentName, next(self.id_iter)]
#The new student will have a list of courses attended:
self.courses = pd.DataFrame(columns=["Course", "Course ID", "Passed/Failed", "Completed Assignments"])
#The new student will be added to the list of all students:
Student.all_students = pd.concat([Student.all_students, pd.DataFrame.from_dict({"Student": self.studentName[0], "Student ID": self.studentName[1]}, orient = "index")], ignore_index = True, axis = 1)
Student.all_students = Student.all_students.dropna(axis=1)
def __str__(self):
return str(self.studentName)
class Course (object):
#The two objects are similar to that of the student class:
id_iter = itertools.count()
all_courses = pd.DataFrame(columns=["Course", "Course ID"])
#the courses are similarly added as students above
def add_student(self,student,completed_assignments):
#this adds students with the number of assingments they completed (not relevant), the code is not relevant
def add_student_list(self, lst):
for i in range(0,len(lst)):
for j in range(0,len(Student.all_students.swapaxes("index", "columns"))):
if lst[i][0] == Student.all_students[j][1]:
self.add_student()
Two methods:
Store your students in a dictionary with a id as the key. Than can retrieve the students based on their ids.
Search through the students everytime you have a list.
ids_to_search = [2, 34, 3]
students = [student for student in students if student.id==id_to_search]
Related
My question is similar to this one posted a few years back, but I have one additional thing that I do not understand how to do. Say I have two lists, each holding a set of objects. These objects share a common attribute:
class Student:
def __init__(self, value) -> None:
self.value = value
class School:
def __init__(self, value) -> None:
self.value = value
self.students = []
Student_List = [Student(1), Student(1), Student(3), Student(3), Student(5)]
School_List = [School(5), School(2), School(1), School(3), School(4)]
My goal is to search for objects with matching values and then perform operations on the objects held within the lists. The current method I have is as follows:
for student in Student_List:
for school in School_List:
if student.value == school.value:
school.students.append(student)
break
This is obviously very inefficient, and finding an intersection would be much easier. However, notice how I am altering the attributes of School by appending the Student object to the School's list.
You can create a reverse mapping that maps each school value to the corresponding school object, so that you can iterate through the list of students and efficiently obtain the matching school object with the value mapping to append the student object to:
schools = {school.value: school for school in School_List}
for student in Student_List:
schools[student.value].students.append(student)
With this approach you can reduce the time complexity from O(n ^ 2) to O(n).
Demo: https://replit.com/#blhsing/ImpracticalSimultaneousLock
For this specific example, I believe you could do it this way:
for school in School_List:
school.students.extend([s for s in Student_List if s.value == school.value])
What we need is a a quick way to look up the school, given a value. For that, I create a dictionary called school_dict:
class Student:
def __init__(self, value) -> None:
self.value = value
def __repr__(self):
return f"Student({self.value})"
class School:
def __init__(self, value) -> None:
self.value = value
self.students = []
def __repr__(self):
return f"School(value={self.value}, students={self.students})"
Student_List = [Student(1), Student(1), Student(3), Student(3), Student(5)]
School_List = [School(5), School(2), School(1), School(3), School(4)]
school_dict = {s.value: s for s in School_List}
for student in Student_List:
value = student.value
try:
school_dict[value].students.append(student)
except KeyError:
pass
School_list now is:
[
School(value=5, students=[Student(5)]),
School(value=2, students=[]),
School(value=1, students=[Student(1), Student(1)]),
School(value=3, students=[Student(3), Student(3)]),
School(value=4, students=[])
]
Notes
school_dict allows quick look up to a school object, given a value
The try/except construct guards against those student values that do not have corresponding school.
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]]
I'm trying to make a dictionary which calls the name of a restaurant, and the type of cuisine it is known for.
I wanted to create a dictionary so I could call each restaurant type up later. The problem is every method I've tried so far overwrites my value pairs which pertain to each key.
I understand that I somehow need to alter each key so as not to overwrite my values, but so far every attempt I've tried has not been successful. To circumvent that, I tried to place a dictionary within a dictionary. The input/output of the code is also seen below.
Some of the things I attempted are below:
restaurant_dict[restaurant] = value
main:
from nine_two import Restaurant
def increment(min_num, max_num, name_of_variable):
#enter the name of the variable and how many times to increment it
list_1 = []
for x in range(min_num,max_num):
list_1.append(f"{name_of_variable}_{x}")
for variable in list_1:
#return a list of the incremented variables
return list_1
#Created dictionaries & variables
restaurant_dict = {}
restaurant_dict_2 = {}
list_1 = increment(1,4,"Restaurant")
for variable in list_1:
print(f"\n{variable}:")
user_restaurant = input("Enter name of restaurant: ")
user_cuisine = input("Enter cuisine of restaurant: ")
#FIXME attempt to store variables in a callable dictionary
restaurant_dict_2[variable] = restaurant_dict
restaurant_dict["Restaurant"] = user_restaurant
restaurant_dict["Cuisine type"] = user_cuisine
print(restaurant_dict_2)
#ignore this
variable = Restaurant(user_restaurant,user_cuisine)
variable.describe_restaurant()
variable.open_restaurant()
Imported code is:
class Restaurant:
"""Creates a restaurant"""
def __init__(self, restaurant_name, cuisine_type):
"""Intialize name and cuisine attributes."""
self.name = restaurant_name
self.cuisine = cuisine_type
def describe_restaurant(self):
"""describes the restaurant"""
print(f"The {self.name} is a restaurant that specializes in {self.cuisine}.")
def open_restaurant(self):
"""Opens the restaurant"""
print(f"The {self.name} is now open!")
Output of code above
This answer comes in two parts:
How to fix your dictionary problem.
You don't need to use a dictionary at all.
Part 1: How to fix the problem with your dictionaries:
Your problem is that you assign the same restaurant_dict to multiple keys. When you do dict2['key1'] = dict1, you didn't create a copy of dict1 to assign to dict2['key1']. So when later you do dict2['key2'] = dict1, the same dict gets assigned to both key1 and key2. Then, when you change dict1['name'], since the same dict1 is referenced by both key1 and key2, it changes the value in both places.
To fix this, move the line restaurant_dict = {} inside your for variable in list_1: loop, so that you create a new dictionary for each restaurant.
#FIXME attempt to store variables in a callable dictionary
restaurant_dict = {}
restaurant_dict_2[variable] = restaurant_dict
restaurant_dict["Restaurant"] = user_restaurant
restaurant_dict["Cuisine type"] = user_cuisine
Or, better yet, you can simply assign the Restaurant and Cuisine type keys while you're creating the dict
#FIXME attempt to store variables in a callable dictionary
restaurant_dict = {"Restaurant": user_restaurant, "Cuisine type": user_cuisine}
restaurant_dict_2[variable] = restaurant_dict
Part 2: You don't need to use a dictionary at all
Usually when you use a dictionary, you want to map a key to a value. For example, if you were creating a directory of restaurants for every cuisine, you could have a dictionary where the keys were the cuisine type, and the values were a list of restaurants serving that cuisine.
However, in this case, it seems like you are simply making a list of restaurants (because your keys are sequential), so a simple list would suffice. Also, since you have a class to represent a restaurant, you just need to create an object of this class and append it to your list. No need to create dictionaries and Restaurant objects.
restaurants = []
for _ in range(num_restaurants):
user_restaurant = input("Enter name of restaurant: ")
user_cuisine = input("Enter cuisine of restaurant: ")
restaurant = Restaurant(user_restaurant, user_cuisine)
restaurants.append(restaurant)
restaurant.describe_restaurant()
restaurant.open_restaurant()
If you wanted to do the thing I mentioned in the previous paragraph and create a map of cuisines with restaurants, you could then process this list into a dict:
cuisine_restaurants = {}
for rest in restaurants:
cuisine = rest.cuisine
if cuisine not in cuisine_restaurants:
cuisine_restaurants[cuisine] = []
cuisine_restaurants[cuisine].append(rest)
Then, you could ask the user for a cuisine, and show all restaurants that serve that cuisine without having to re-iterate over the entire restaurants list:
c = input("What would you like to eat today?")
suggested_rest = cuisine_restaurants.get(c, []) # .get() returns the second argument if the key given by c doesn't exist
print("You could eat at: ")
for r in suggested_rest:
r.describe_restaurant()
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
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