Missing items when I loop over a dictionary - python

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']

Related

Python object creating a group with 3 members of aggregation relationship

I had an assignment to create a python code using class to create a group with 3 members (aggregation relationship). This is my code so far:
class Member:
def __init__(self,name,age):
self.name = name
self.age = age
def getInfo(self):
memberInfo = "Name: " + str(self.name) + "." + "Age: " + str(self.age)
return memberInfo
class Group:
def __init__(self,name):
self.name = name
self.memlist = []
def addMember(self,member):
self.memlist.append(member)
def getInfo(self):
info = "Member List: \n"
for i in range(len(self.memlist)):
info += self.memlist[i].getInfo() + "\n"
print(info)
break
mem1 = Member("Chi",20)
mem2 = Member("Bach",7)
mem3 = Member("Gen", 22)
group1 = Group("Siblings")
group1.addMember(mem1)
group1.addMember(mem2)
print(group1.getInfo())
print(mem2.getInfo())
print(group1.memList)
But it has shown an error: AttributeError: 'Group' object has no attribute 'memList'. Is there anything I can do to fix this?
I wrote little function for listing members and their ages.
class member:
def __init__(self, name, age):
self.name = name
self.age = age
def member_Info(self):
memberInfo = f"Name: {str(self.name)}-->Age: {str(self.age)}"
return memberInfo
class Group:
def __init__(self, name):
self.name = name
self.memlist = []
def addMember(self, name):
self.memlist.append(name)
def getInfo(self):
for i in range(len(self.memlist)):
info = self.memlist[i].member_Info() + "\n"
print(info)
This all_members function is basically getting the information stored in the member class and return to list. I print using memlist in Group but it didn't work out so I made a new list using all_member function and get information from memlist in group1 with the code that you used for getting information in memlist at group1.getInfo .
def all_members():
all_mems = []
for i in range(len(group1.memlist)):
all_mems.append(group1.memlist[i].member_Info())
print(all_mems)
mem1 = member("Chi", "20")
mem2 = member("Bach", "7")
mem3 = member("Gen", "22")
group1 = Group("Siblings")
group1.addMember(mem1)
group1.addMember(mem2)
group1.addMember(mem3)
print(group1.getInfo())
print(mem2.member_Info() + "\n")
print(all_members())
I guess this isn't the best answer you can get but I think it will work and also I learn many things while trying to correct it so thank you for posting that.
change
print(group1.memList)
to
print(group1.memlist)

How to print as string a parameter which is a list from class in python

I want to print all grades as a csv string for the particular Student. How can i achieve this as it prints out memory location instead:
class Student:
def __init__(self,name,year):
self.name = name
self.year = year
self.grades = []
def add_grade(self,grade):
if type(grade) == Grade:
self.grades.append(grade)
else:
pass
def __repr__(self):
return "Student name is {name} and attends year {year}".format(name=self.name, year=self.year)
pieter = Student("Pieter Bruegel the Elder",8)
class Grade:
minimum_passing = 65
def __init__(self,score):
self.score = score
pieter.add_grade(Grade(100))
pieter.add_grade(Grade(95))
pieter.add_grade(Grade(85))
print(str(pieter.grades)) #this one prints [<__main__.Grade object at 0x7fc3874d76d8>]
print(pieter)
I also added it in the __repr__ but still the same.
Give Grade a __str__ method that returns the score as a string.
class Grade:
minimum_passing = 65
def __init__(self,score):
self.score = score
def __str__(self):
return str(self.score)
Then print(pieter.grades) should print:
[100,95,85]
You can't get rid of the [] surrounding it, because that's the way lists print. You can't customize the container's printing from the elements within it. If you need to do that, you should define your own GradeList class.

Python: Classes that use other classes

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

How do I assign a variable to an object name?

Tried the following, where "objectname" contains a string name, to be assigned on creation of an object.
for record in result:
objectname = 'Customer' + str(record[0])
print objectname
customername = str(record[1])
objectname = Customer(customername)
Where Customer is a class.
In my test, this loop runs twice printing "objectname" as Customer1 and Customer2, yet creates 2 objects, but the objects are called "objectname" (it overwrites each loop), opposed to the 2 unique objects Customer1 or Customer2.
Its simply not assigning strings(customer1,2) inside the variable, but purely the variables name.
I've tried assigning strings to the object name, but that gives a syntax error
Surely this must be done all the time, thanks for your help in advance.
Instead of using a new variable for each customer you could store your object in a Python dictionary:
d = dict()
for record in result:
objectname = 'Customer' + str(record[0])
customername = str(record[1])
d[objectname] = Customer(customername)
print d
An example of objects stored in dictionaries
I just could'nt help my self writting some code (more than I set out to do). It's like addictive. Anyway, I would'nt use objects for this kind of work. I probably would use a sqlite database (could be saved in memory if you want). But this piece of code show you (hopefully) how you can use dictionaries to save objects with customer data in:
# Initiate customer dictionary
customers = dict()
class Customer:
def __init__(self, fname, lname):
self.fname = fname
self.lname = lname
self.address = None
self.zip = None
self.state = None
self.city = None
self.phone = None
def add_address(self, address, zp, state, city):
self.address = address
self.zip = zp
self.state = state
self.city = city
def add_phone(self, number):
self.phone = number
# Observe that these functions are not belonging to the class.
def _print_layout(object):
print object.fname, object.lname
print '==========================='
print 'ADDRESS:'
print object.address
print object.zip
print object.state
print object.city
print '\nPHONE:'
print object.phone
print '\n'
def print_customer(customer_name):
_print_layout(customers[customer_name])
def print_customers():
for customer_name in customers.iterkeys():
_print_layout(customers[customer_name])
if __name__ == '__main__':
# Add some customers to dictionary:
customers['Steve'] = Customer('Steve', 'Jobs')
customers['Niclas'] = Customer('Niclas', 'Nilsson')
# Add some more data
customers['Niclas'].add_address('Some road', '12312', 'WeDon\'tHaveStates', 'Hultsfred')
customers['Steve'].add_phone('123-543 234')
# Search one customer and print him
print 'Here are one customer searched:'
print 'ooooooooooooooooooooooooooooooo'
print_customer('Niclas')
# Print all the customers nicely
print '\n\nHere are all customers'
print 'oooooooooooooooooooooo'
print_customers()
It is generally not that useful to have dynamically generated variable names. I would definitely suggest something like Niclas' answer instead, but if you know this is what you want here is how you can do it:
for record in result:
objectname = 'Customer' + str(record[0])
print objectname
customername = str(record[1])
exec '%s = Customer(%r)' % (customername, customername)
This will result in the variables Customer1 and Customer2 being added to the innermost scope, exactly like if you had executed the following lines:
Customer1 = Customer('Customer1')
Customer2 = Customer('Customer2')
When doing it this way you need to make sure that customername is a valid Python identifier.
What you need is a dictionary:
customers = {}
for record in result:
objectname = 'Customer' + str(record[0])
customers[customername] = Customer(str(record[1])) #assignment to dictionary

python unbound method again

This gets me into difficult time (sorry, i am still very new to python)
Thank you for any kind of help.
The error
print Student.MostFrequent() TypeError: unbound method
MostFrequent() must be called with
Student instance as first argument
(got nothing instead)
This Student.MostFrequent() is called all the way in the end (last line) and the def is last def in the class
EDITED - Naming convention
My long code
import csv
class Student:
sports = []
ftopics = []
stopics = []
choice_list = []
choice_dict = {}
def __init__(self, row):
self.lname, self.fname, self.ID, self.gender, self.sport, self.movie, self.movieyr, self.country, self.ftopic, self.stopic = row
self.sports.append(self.sport)
self.ftopics.append(self.ftopic)
self.stopics.append(self.stopic)
def print_information(self):
return (self.lname, self.fname, self.ID, self.gender)
def print_first(self):
return (self.lname, self.fname, self.sport)
def print_second(self):
return (self.lname, self.fname, self.movie, self.movieyr)
def print_third(self):
return (self.lname, self.fname, self.country)
def print_fourth(self):
return (self.lname, self.fname, self.ftopic, self.stopic)
def most_frequent(self):
for choice in self.choice_list:
self.choice_dict[choice] = self.choice_dict.get(choice, 0) + 1
self.mostFrequent = sorted([(v, k) for k, v in self.choice_dict.items()], reverse=True)
print self.mostFrequent
reader = csv.reader(open('new_mondy_csc_data_revise.csv'), delimiter=',', quotechar='"')
header = tuple(reader.next())
print "%-17s|%-10s|%-6s|%s" %header[:4]
print "-" * 45
students = list(map(Student, reader)) # read all remaining lines
for student in students:
print "%-17s|%-10s|%-6s|%3s" % student.print_information()
print "%-17s|%-10s|%s" %(header[0],header[1],header[4])
print "-" * 45
for student in students:
print "%-17s|%-10s|%s" %student.print_first()
print "%-17s|%-10s|%-16s|%s" %(header[0],header[1],header[5],header[6])
print "-" * 45
for student in students:
print "%-17s|%-10s|%-16s|%s" % student.print_second()
print "%-17s|%-10s|%s" %(header[0],header[1],header[7])
print "-" * 45
for student in students:
print "%-17s|%-10s|%s" %student.print_third()
print "%-17s|%-10s|%-15s|%s" %(header[0],header[1],header[8],header[9])
print "-" * 45
for student in students:
print "%-17s|%-10s|%-16s|%s" % student.print_fourth()
k = len(students)
# Printing all sports that are specified by students
for s in set(Student.sports): # class attribute
print s, Student.sports.count(s), round(((float(Student.sports.count(s)) / k) *100),1)
# Printing sports that are not picked
allsports = ['Basketball','Football','Other','Baseball','Handball','Soccer','Volleyball','I do not like sport']
allsports.sort()
for s in set(allsports) - set(Student.sports):
print s, 0, '0%'
Student.choice_list = Student.sports
X = Student()
X.most_frequent()
#class Search(Student):
# def __init__(self):
# Student.__init__
first read PEP-8 on naming conventions:
Method Names and Instance Variables
Use the function naming rules: lowercase with words separated by
underscores as necessary to improve readability.
second you are calling mostFrequest on the class Student, not an instance of it. Use the method on an instance instead:
student = Student(row)
student.MostFrequent()
use Student().MostFrequent()
edit:
beware that you use class attributes and this is dangerous. here an example:
>>> class Person:
... name = None
... hobbies = []
... def __init__(self, name):
... self.name = name
...
>>> a = Person('marco')
>>> b = Person('francesco')
>>> a.hobbies.append('football')
>>> b.hobbies
['football']
>>> a.name
'marco'
>>> b.name
'francesco'
>>> a.name = 'mario'
>>> b.name
'francesco'
>>> a.name
'mario'
>>>
as you can see i modify marco's hobbies and francesco's hobbies are modified consequentially.
What you probably want is to define most_frequent as a classmethod:
#classmethod
def most_frequent(cls):
for choice in cls.choice_list:
cls.choice_dict[choice] = cls.choice_dict.get(choice, 0) + 1
cls.mostFrequent = sorted([(v, k) for k, v in cls.choice_dict.items()], reverse=True)
return cls.mostFrequent
First, I recommend making function names lower case only.
The error you get results from the usage of MostFrequent as a static method. For this to work, you need to explicitly pass an instance of Student as first argument.
If called directly on an instance of Student, the instance will implicitly be passed as first argument.
Consider using the staticmethod decorator for static usage of functions.
You only rarely call methods on a class definition (Student)
Almost always, you create an instance of the class
someStudent = Student(someRow)
Then you call the method on the instance ("object"), someStudent.
someStudent.MostFrequent()
Student.MostFrequent means You're trying to use static method, not instance method. So You must first create instance by calling Student() and then call MostFrequent() on it.
P.S.: If this is not part of some arcane project, I urge you to follow PEP 8 and use most_frequent as method name.
in your class def, the method definition
def MostFrequent(self,mostFrequent):
has the extra variable mostFrequent that you probably don't want there. Try changing to :
def MostFrequent(self):

Categories