class Course:
'''
A class representing a course offering that includes the following
information about the course: subject, course number, section,
enrolment cap, lecture days, lecture start time, lecture duration
in minutes, and unique student numbers of the students enroled.
If lectures occur on multiple days, they will all start at the same time.
'''
def __init__(self, subject, number, section, cap, days, start_time, dur):
'''
returns a new Course object with no students enroled, given
the subject, number, section, cap, days, start_time, and dur
__init__: Str Nat Nat Nat Str Time Nat -> Course
requires: number is a 3-digit number, section > 0, cap > 0,
days is string containing substrings representing the days
of the week 'M', 'T', 'W', 'Th', 'F', where if the course is
offered on more than one day, the days appear in order and
are separated by a single dash. For example, 'M-T-Th'
indicates the course is offered on Monday, Tuesday, and Th.
'''
self.subject = subject
self.number = number
self.section = section
self.cap = cap
self.days = days
self.start_time = start_time
self.dur = dur
def add_student(self, student_id):
'''
adds a student to the course enrolment if there is room in the course
if the number of students enroled already matches the
enrolment cap, then print the message "Course full"
if the student is already enroled in the course, the there is no
change to the Course object, and the message "Previously enroled"
is printed.
add_student: Course Nat -> None
Effects: Mutates self. May print feedback message.
'''
pass
For the method add_student, how would i implement a list if it not in the init method, (CANT ADD IT TO THE INIT METHOD)? The list need to be connected with the object so later on i can remove students from that list.
You can add it in the __new__ method instead:
class Course:
def __new__(cls, *args, **kwargs):
course = super(Course, cls).__new__(cls)
course.students = []
return course
naive method: try if the member exists, catch the attribute error, and create it if doesn't exist.
def add_student(self, student_id):
try:
self.__list
except AttributeError:
self.__list = []
self.__list.append(student_id)
better use a "getter" instead to make sure the list is created when you access it from whatever method:
def get_list(self):
try:
self.__list
except AttributeError:
self.__list = []
return self.__list
then add_student becomes:
def add_student(self, student_id):
self.get_list().append(student_id)
of course adding it to __init__ is better if you don't have some strange constraints...
You can initialize the list with a property getter, so that it would be initialized whenever it is first accessed:
class Course:
#property
def students(self):
try:
return self._students
except AttributeError:
self._students = []
return self._students
#students.setter
def students(self, value):
self._students = value
Related
I'm facing problem with my code. In fact, I need to create a list of instances of my class( Patent). The name of the list is patent_ints. But when I'm trying to verify if any element in that list is a Patent one, I'm always getting a False response. And when iterating the first element is like "<__main__.Patent at 0x7f107820b710>".
Here is my code, I need help !
import json
import datetime
patent_data = json.loads(open('NASA_data.json', "r").read())
unique_center = []
for thing in patent_data["Patent_Information"]["Results"]:
for val in thing:
if(val == 'NASA Center'):
unique_center.append(thing[val])
total_num_centers = len(set(unique_center))
class Patent:
def __init__(self, abbreviated_organization_name, dict_data):
self.org_name = abbreviated_organization_name
self.title = dict_data["Title"]
# initialize instance variable called year. The value can be extracted from dict_data.
# This should be a four digit string.
self.year = str(datetime.datetime.strptime(dict_data['Date'], '%m/%d/%Y').year) #dict_data['Date'].split('/')[2]
# initialize an instance variable called month. The value can be extracted from dict_data.
# This should be a two digit string.
self.month = str(datetime.datetime.strptime(dict_data['Date'], '%m/%d/%Y').month) #dict_data['Date'].split('/')[0]
# initialize an instance variable called day. The value can be extracted from dict_data.
# This should be a two digit string.
self.day = str(datetime.datetime.strptime(dict_data['Date'], '%m/%d/%Y').day) #dict_data['Date'].split('/')[1]
self.id = dict_data['Case Number']
self.access_limit = dict_data['SRA Final']
patent_ints = [Patent(i, data) for i in unique_center for data in patent_data["Patent_Information"]["Results"]]
patent_ints[0]
Thank you in advance!
<__main__.Patent at 0x7f107820b710> is the default representation of the class when you try to print it. Add an __str__ or __repr__ method to the class and define some custom logic to return your desired details as a string:
class Patent:
def __init__(self, abbreviated_organization_name, dict_data):
...
def __repr__(self):
# return a dictionary of items in the class but you can return whatever you want
# you could do f'{self.title} {self.id} {self.year}-{self.month}-{self.day}' but str(self.__dict__) is quick to test
return str(self.__dict__)
I am working on a project for school, simulating a payroll program, and I am getting an error. The error I am getting is
'Expected type 'Classification', got 'Employee' instead'. The relevant code is (I put *** around the code generating the error, it is the 5th function under the Employee Class).
class Employee:
def __init__(self, emp_id, first_name, last_name, address, city, state, zipcode, clas = None):
self.emp_id = emp_id
self.first_name = first_name
self.last_name = last_name
self.address = address
self.city = city
self.state = state
self.zipcode = zipcode
self.classification = clas
def make_hourly(self, hourly_rate):
self.clas = Hourly(hourly_rate)
self.classification = self.clas
def make_salaried(self, salary):
self.clas = Salaried(salary)
self.classification = self.clas
def make_commissioned(self, salary, rate):
self.clas = Commissioned(rate, salary)
self.classification = self.clas
def issue_payment(self):
***pay = Classification.compute_pay(self)***
print('Mailing', pay, 'to', self.first_name, self.last_name, 'at', self.address, self.city, self.state, self.zipcode)
class Classification(ABC):
''' Interface for employee classifications '''
#abstractmethod
def compute_pay(self):
pass
class Hourly(Classification):
''' Manages timecard info. Computes pay '''
def __init__(self, hourly_rate):
self.hourly_rate = hourly_rate
self.timecards = [] # A list of floats representing hours worked
def compute_pay(self):
for i in list_of_timecards:
if i[0] == self.emp_id:
self.timecards.extend(i[1:])
total = list(map(float, self.timecards))
total = sum(total)
self.timecards.clear()
return total * self.hourly_rate
def add_timecard(self, hours):
self.timecards.append(hours)
class Salaried(Classification):
def __init__(self, salary):
self.salary = salary
def compute_pay(self):
return self.salary / 24
class Commissioned(Salaried):
def __init__(self, salary, commission_rate):
self.commission_rate = commission_rate
self.salary = salary
self.receipts = []
def add_receipt(self, amount):
self.receipts.append(amount)
def compute_pay(self):
for i in list_of_receipts:
if i[0] == self.emp_id:
self.receipts.extend(i[1:])
total = list(map(float, self.receipts))
total = sum(total)
self.receipts.clear()
return (self.salary / 24) + ((self.commission_rate / 100) * total)
My understanding of the problem is that I need to pass my 'employee' object to the 'compute_pay' function, which then passes it to the relevant child class (hourly etc...) to run and return the result. I have tried changing
pay = Classification.compute_pay(self)
to
pay = Classification.compute_pay(self.clas)
however that returns error 'AttributeError: 'Employee' object has no attribute 'clas'
which makes no sense. Maybe it is that I am not assigning the employees to the class correctly?
The code for that is (it pulls from a CSV file, and it is pulling the data correctly and generating the class objects, I have checked)
def load_employees():
f = open("employees.csv")
f.readline() # skip header line
for line in f:
fields = line.strip().split(',')
emp = Employee(*fields[:7])
if fields[7] == '3':
clas = Hourly(fields[10]) # Need to define Hourly
emp.classification = clas
elif fields[7] == '2':
clas = Commissioned(fields[8], fields[9])
emp.classification = clas
elif fields[7] == '1':
clas = Salaried(fields[8])
emp.classification = clas
employees.append(emp)
I will figure out your line Classification.compute_pay(self):
Classification => the class Classification
compute_pay => class
method self => this = an Employee instance
pass means do nothing and is used to avoid unneccessary code.
Every class method has self as an argument to allow refering to this instance of the class.
To pass an argument (here your employee) use a parameter. Also implementing a method of the parent class overrides this method.
Every function compute_pay should have a second argument
def compute_pay(self, employee):
# do your stuff
And then you can use this line in issue_payment
pay = self.clas.compute_pay(self)
Two issues here,
Firstly, your Employee instance has two attributes: clas and classification. However, in your constructor, only classification is set.
def __init__(...
...
self.classification = clas
But self.clas is not set to anything. That's why you are getting that error 'Employee' object has no attribute 'clas'. It is only set when one of the make_hourly, make_salaried, or make_commissioned methods are invoked. So when you load the employees CSV, instead of manually creating the instance like you are doing here
clas = Hourly(fields[10])
you should be calling the method make_hourly on your emp instance, like so
emp.make_hourly(fields[10])
It's worth noting that fields[10] is terrible naming. Instead of unpacking all the fields at once, try to unpack them during the for loop:
for a, b, c, d in csv:
...
Secondly, this line of code is wrong in multiple ways
pay = Classification.compute_pay(self)
compute_pay is not a static function or a classmethod. So it shouldn't be called on the Classification class itself, but the Classification instance. This is what you stored in your self.clas attribute. So, compute_pay should be called on self.clas:
def issue_payment(self):
pay = self.clas.compute_pay()
...
In addition to that, when you call a method of a class from inside of another method in the same class, you don't ever need to pass the self argument. It is implied. So even if compute_pay was static or a class method, which it isn't, it would be called like so,
Classification.compute_pay()
Notice there is no self inside the parentheses. Similarly, when you call another method that is not static, self is never passed as an argument:
def my_method(self):
self.another_method()
I'm making a very simple to-do list application and I'm trying to create a list for each instance of my class. I was looking to have seven lists, one for each day, which would be used to store tasks however I can't get my head around it. Example of what I am trying to do:
class Day:
def __init__(self, name, todo_list):
self.name = name
self.todo_list = todo_list
day_Mon = Day('Monday', (MonList=[]))
day_Tue = Day('Tuesday', (TueList=[]))
...
I am very new to OOP and I'm just doing this to try try improve my understanding so I'm not sure if this is even possible/sensible (I've only seen questions about creating lists of instances). Is anyone able to help?
How about something like this?
class Day:
def __init__(self, name, todo_list=None):
self.name = name
if todo_list:
self.todo_list = todo_list
else:
self.todo_list = []
day_Mon = Day('Monday', ['errands', 'study'])
day_Tue = Day('Tuesday',['laundry', 'cook'])
day_Wed = Day('Wednesday')
The constructor accepts two arguments name and todo_list. Notice that todo_list has a default value of an empty list. So the only mandatory argument to create this object is a name - day_Wed is an object where the todo_list attribute is initialized an empty list. It is common when writing classes to have mandatory and optional arguments.
Your initializer can require a name parameter and allow an optional todo_list that defaults to an empty list.
For reasons you should learn about but I won't go in to here, you should never use an empty list as a default value of a function parameter, so we'll use None and then set the desired value in __init__.
>>> class Day:
... def __init__(self, name, todo_list=None):
... self.name = name
... self.todo_list = todo_list if todo_list else []
>>> day_Mon = Day('Monday')
>>> day_Mon.todo_list
[]
>>> day_Tue = Day('Tuesday', ['wash clothes', 'pack'])
>>> day_Tue.todo_list
['wash clothes', 'pack']
You could store the 7 days in a list or dictionary, but its common in OOP to define a container class that provides useful accessors. Here is one that lets you address days of the week by case-insensitive name or number (assuming Sunday is 0).
import datetime
class Day:
def __init__(self, name, todo_list):
self.name = name
self.todo_list = todo_list
class Week:
weekday_names = ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
'Friday')
weekday_index = {name.lower():idx
for idx,name in enumerate(weekday_names)}
weekday_index.update({idx:idx for idx in range(7)})
def __init__(self):
self.days = [Day(name, []) for name in self.weekday_names]
def __getitem__(self, name_or_ord):
if isinstance(name_or_ord, str):
name_or_ord = name_or_ord.lower()
return self.days[self.weekday_index[name_or_ord]]
def __setitem__(self, name, day):
raise ValueError("Cannot set days in week")
week = Week()
week['Sunday'].todo_list.append('dinner with parents')
week['Monday'].todo_list.append('laundry')
print("Monday's tasks:",
', '.join(week['monday'].todo_list))
print("Today's tasks:",
', '.join(week[datetime.date.today().weekday()].todo_list))
I have a class for rooms. I want that every time I create an object using that class the object would be added to a list of all rooms.
Rooms class:
class Rooms:
"""Room class, takes type,days, occupied or not and when it frees up"""
def __init__(self, room_type, days, occupied, when_free):
self.room_type = room_type
self.days = days
self.occupied = occupied
self.when_free = arrow.get(when_free,'YYYY-MM-DD')
Any other feedback is appreciated as well!
also not sure if I should create new topic on this but is it possible that when the object is created and True on occupied is passed to the object you wouldn't need to pass 4th variable and it would take it as the current date instead? in short if there is no 4th variable it passes arrow.get(str(arrow.utcnow()),'YYYY-MM-DD') instead
figured out my second issue. I did change the init to:
def __init__(self, room_type, days, occupied, when_free=str(arrow.get(str(arrow.utcnow()),'YYYY-MM-DD'))):
self.room_type = room_type
self.days = days
self.occupied = occupied
self.when_free = arrow.get(when_free,'YYYY-MM-DD')
I would suggest a slightly more elegant and logical way than the above:
class Building(object):
def __init__(self):
self.rooms = []
class Room(object):
def __init__(self, building=None)
if building:
building.rooms.append(self)
self.building = building
b = Building()
r = Room(b)
That way, you don't need every time call b.rooms.append and now it more agreese with OOP.
Ideally, you would want the scope of your room list to be where you plan to use it. Not as part of a room itself. So, if you have a building with rooms:
class Building():
def __init__(self):
self.rooms = []
b = Building()
b.rooms.append(Room(room_type, days, occupied, when_free))
The building is just for an example. The important part is rooms.append(). That should be declared and used wherever you actually need to use the list of rooms.
Might be better just to make the list a class variable:
class Room(object):
rooms = []
def __init__(self, room_type, days, occupied, when_free):
self.room_type = room_type
self.days = days
self.occupied = occupied
self.when_free = arrow.get(when_free,'YYYY-MM-DD')
Room.rooms.append(self)
r = Room('x', 1,2, True)
Room.rooms
[<Room object at 0x00000000C6325550>]
r.rooms
[<Room object at 0x00000000C6325550>]
Since it's a class variable, you can get to it through any class instance, or the class type itself.
edited to go through 'Room' instead of 'self', which is safer...
I was thinking you could decorate the __init__ method with a decorator that appends the instance to a list, to avoid cluttering the __init__ method with the instance registering. Now you only have to add one decorator to each class' init method if you want to keep track of the instances. Something like:
#!/usr/bin/env python3
import sys
class InstanceRegister:
def __call__(self, init):
def register(instance, *args, **kwargs):
init(instance, *args, **kwargs)
try :
instance.__class__.__instances__
except:
instance.__class__.__instances__ = []
instance.__class__.__instances__.append(instance)
return register
class Room:
"""Room class, takes type,days, occupied or not and when it frees up"""
#InstanceRegister()
def __init__(self, room_type, days, occupied, when_free):
self.room_type = room_type
self.days = days
self.occupied = occupied
self.when_free = arrow.get(when_free,'YYYY-MM-DD')
def __str__(self):
return "Room of type {:s}".format(self.room_type)
def main():
r1 = Room('type_a', 1, True, '1999-12-30')
r2 = Room('type_b', 2, True, '2000-12-30')
r3 = Room('type_c', 3, True, '2001-01-30')
for room in Room.__instances__:
print("{:s}".format(room))
return 0
if __name__ == '__main__':
sys.exit(main())
More on decorators at Understanding Python Decorators in 12 Easy Steps!
I have a text file that looks something like this where the first column is a student's name, the second column is the number of credits, and the third is the number of points (grade times hours).
john 5 15
bill 9 30
ted 7 22
I want to create a class that extracts the relevant information and calculates gpa.
class Student:
def __init__(self, name, hours, qpoints):
self.name = name
self.hours = float(hours)
self.qpoints = float(qpoints)
def getName(self):
return self.name
def getHours(self):
return self.hours
def getQPoints(self):
return self.qpoints
def gps(self):
return self.qpoints/self.hours
used to make extract the data (based on the fact that there is a tab between each piece of information)
def makeStudent(info):
name, hours, qpoints = info.split("\t")
return Student(name, hours, qpoints)
here I use a for loop to create a list based on the data in the text file by appending the relevant information from each line to the list
def readStudents(filename):
infile = open(filename, 'r')
students = []
for line in infile:
students.append(makeStudent(line))
infile.close()
return students
the problem is that I get this error:
[<__main__.Student object at 0x01FA4AD0>, <__main__.Student object at 0x01FA4AF0>,
<__main__.Student object at 0x01FA4B10>, <__main__.Student object at 0x01FA4B50>,
<__main__.Student object at 0x01FA4B30>]
Any ideas on why this is happening?
This is not an error. It is regular output. You should override the __str__ and __repr__ methods of the Student class, to tell python how to print Student objects.
Some help on your code, this is much better:
def readStudents(filename):
with open(filename) as student_file:
return [Student(*line.split()) for line in student_file]
Like everyone says, not an error. Just implement __str__, __repr__ or __unicode__ on your Student class.
However, I have one minor suggestion. You should use the csv module to read your file.
Your readStudents function can also be re-written like this:
def readStudents(filename):
students = []
with open(filename) as infile:
for line in csv.reader(infile, delimiter='\t'): # or excel-tab if you want.
students.append(Student(**line))
return students
Isn't that pretty?
Don't forget to put a import csv at the beginning of your python file!
I strongly suggest using the csv module (import csv): it will do most of the work for you, be more flexible, more readable and less prone to bugs.
Also, to be a little more strict: what is your actual question? I just see a blurb of code and some output you do not understand. That is not an error message, by the way, it is a list of five instances of the Student class. The code seems to work as intended: you parse the file, create students, and then..? What do you want to do with them? The parsing is done, you want to move on to the handling of the data.
You don't get an error, but a list of student objects. What you need to do is implement a __str__ or a __repr__ method: Special method names
Getter and setter methods are usually frowned on unless they are doing something active (over and above just retrieving or setting a value).
import csv
class Student(object):
def __init__(self, name, hours, qpoints):
super(Student,self).__init__()
self.name = str(name)
self.hours = float(hours)
self.qpoints = float(qpoints)
#property
def gpa(self):
return self.qpoints/self.hours
#gpa.setter
def gpa(self, value):
raise SyntaxError('gpa property is read-only; update .qpoints or .hours instead')
def __str__(self):
return "{name:20} {hours:>6.2f} {qpoints:>6.2f} {gpa:>6.2f}".format(name=self.name, hours=self.hours, qpoints=self.qpoints, gpa=self.gpa)
def loadStudents(fname, *args, **kwargs):
with open(fname) as inf:
return [Student(*line) for line in csv.reader(inf, *args, **kwargs)]
def main():
students = loadStudents('students.csv', delimiter='\t')
for s in students:
print s
if __name__=="__main__":
main()
results in
john 5.00 15.00 3.00
bill 9.00 30.00 3.33
ted 7.00 22.00 3.14