update class size quarters in ProfCourses() example
class
__init__
Given what we've discussed in this course so far, if you wanted to store information about a date, how would you do so?
# A date, stored as a string
date_string = '29/09/1988'
print(date_string)
# A date, stored as a list of number
date_list = [29, 09, 1988]
date_list
# A date, stored as a series of numbers
day = 29
month = 9
year = 1988
print(day)
# A date, stored as a dictionary
date_dictionary = {'day': 29, 'month': 9, 'year': 1988}
date_dictionary
Ways to organize data (variables) and functions together.
# Import a date object
from datetime import date
date?
# Set the data we want to store in our date object
day = 29
month = 9
year = 1988
# Create a date object
my_date = date(year, month, day)
print(my_date)
# Check what type of thing `my_date` is
type(my_date)
.
, followed by the attribute/method name on the object.
Attributes look up & return information about the object.
attributes maintain the object's state, simply returning information about the object to you
# Get the day attribute
my_date.day
# Get the month attribute
my_date.month
# Get the year attribute
my_date.year
These are functions that belong to and operate on the object directly.
methods modify the object's state
# Method to return what day of the week the date is
my_date.weekday()
# Reminder: check documentation with '?'
date.weekday?
It's also possible to carry out operations on multiple date objects.
# define a second date
my_date2 = date(1980, 7, 29)
print(my_date, my_date2)
# calculate the difference between times
time_diff = my_date - my_date2
print(time_diff.days, "days") #in days
print(time_diff.days/365,"years") #in years
dir
¶# tab complete to access
# methods and attributes
my_date.
# works to find attributes and methods
# for date type objects generally
date.
## dir ouputs all methods and attributes
## we'll talk about the double underscores next lecture
dir(my_date)
Given the code below:
my_date = date(year = 1050, month = 12, day = 12)
Which is the best description:
my_date
is an object, with methods that store data, and attributes that store proceduresmy_date
is variable, and can be used with functionsmy_date
is an attribute, with methods attached to itmy_date
is a method, and also has attributesmy_date
is an object, with attributes that store data, and methods that store proceduresFor an object lets
with a method do_something
, how would you execute that method?
do_something(lets)
lets.do_something
lets.do_something()
lets.do.something()
For an object lets
with an attribute name
, how would you return the information stored in name
for the object lets
?
name(lets)
lets.name
lets.name()
dir()
returns methods & attributes for an objectobj.method()
obj.attribute
date
and datetime
are two types of objects in Pythonclass
keyword opens a code block for instructions on how to create objects of a particular type.
Think of classes as the blueprint for creating and defining objects and their properties (methods, attributes, etc.). They keep related things together and organized.
# Define a class with `class`.
# By convention, class definitions use CapWords (Pascal)
class Dog():
# Class attributes for objects of type Dog
sound = 'Woof'
# Class methods for objects of type Dog
def speak(self, n_times=2):
return self.sound * n_times
# Initialize a dog object
george = Dog()
# george, has 'sound' attribute(s) from Dog()
george.sound
# george, has 'Dog' method(s)
# remember we used `self`
george.speak()
A reminder:
class
notes:
()
after Dog
indicate that this is callableclass
self
is a special parameter for use by an objectWhich of the following statements is true about the example we've been using?
class Dog():
sound = 'Woof'
def speak(self, n_times=2):
return self.sound * n_times
Dog
is a Class, sound
is an attribute, and speak
is a method.Dog
is a function, sound
is an attribute, and speak
is a method.Dog
is a Class, sound
is a method, and speak
is an attribute.Dog
is a function, sound
is an method, and speak
is an attribute.# Initialize a group of dogs
pack_of_dogs = [Dog(), Dog(), Dog(), Dog()]
# take a look at this
pack_of_dogs
# take a look at this
type(pack_of_dogs[0])
for dog in pack_of_dogs:
print(dog.speak())
self
refers to the current instance.
# Initialize a dog object
george = Dog()
From our example above:
george
was an instance of that classAn instance attribute specific to the instance we're on. This allows different instances of the same class to be unique (have different values stored in attributes and use those in methods).
# Initialize a group of dogs
pack_of_dogs = [Dog(), Dog(), Dog(), Dog()]
This creates four different Dog
type objects and stores them in a list. But, up until now...every Dog
was pretty much the same.
__init__
is a special method used to define instance attributes.
dunder
, or double underscore) is used to indicate something Python recognizes and knows what to do every time it sees it.__init__
to execute the code within it every time you initialize an object.class Dog():
# Class attributes for Dogs
sound = 'Woof'
# Initializer, allows us to specify instance-specific attributes
# leading and trailing double underscores indicates that this is special to Python
def __init__(self, name):
self.name = name
def speak(self, n_times=2):
return self.sound * n_times
# Initialize a dog
# what goes in the parentheses is defined in the __init__
gary = Dog(name = 'Gary')
# Check gary's attributes
print(gary.sound) # This is an class attribute
print(gary.name) # This is a instance attribute
# Check gary's methods
gary.speak()
Edit the code we've been using for the Class Dog
to include information about the breed of the Class Dog in NewDog
?
# EDIT CODE HERE
class NewDog():
sound = 'Woof'
def __init__(self, name):
self.name = name
def speak(self, n_times=2):
return self.sound * n_times
## We'll execute here
# Define a class 'Cat'
class Cat():
sound = "Meow"
def __init__(self, name):
self.name = name
def speak(self, n_times=2):
return self.sound * n_times
# Define some instances of our objects
pets = [Cat('Jaspurr'), Dog('Barkley'),
Cat('Picatso'), Dog('Ruffius')]
for pet in pets:
print(pet.name, ' says:')
print(pet.speak())
What will the following code snippet print out?
class MyClass():
def __init__(self, name, email, score):
self.name = name
self.email = email
self.score = score
def check_score(self):
if self.score <= 65:
return self.email
else:
return None
student = MyClass('Rob', 'rob@python.com', 62)
student.check_score()
Good Code Style
class MyClass():
def __init__(self, name, email, score):
self.name = name
self.email = email
self.score = score
def check_score(self):
if self.score <= 65:
return self.email
else:
return None
Code Style to Avoid
class my_class(): # uses snake case for name
def __init__(self, name, email, score):
self.name = name
self.email = email
self.score = score # no blank lines between methods
def check_score(self):
if self.score <= 65:
return self.email
else:
return None
ProfCourses()
¶Let's put a lot of these concepts together in a more complicated example...
What if we wanted some object type that would allow us to keep track of Professor Ellis' Courses? Well...we'd want this to work for any Professor, so we'll call it ProfCourses
.
We would likely want an object type and then helpful methods that allow us to add a class to the course inventory and to compare between courses.
class ProfCourses():
# create three instance attributes
def __init__(self, prof):
self.n_courses = 0
self.courses = []
self.prof = prof
ellis_courses = ProfCourses('Ellis')
print(ellis_courses.n_courses)
print(ellis_courses.prof)
0 Ellis
add_class()
method
class ProfCourses():
def __init__(self, prof):
self.n_courses = 0
self.courses = []
self.prof = prof
# add method that will add courses as a dictionary
# to our attribute (courses)...which is a list
def add_course(self, course_name, quarter, n_students):
self.courses.append({'course_name': course_name,
'quarter' : quarter,
'n_students': n_students})
# increase value store in n_courses
# by 1 any time a class is added
self.n_courses += 1
# create ellis_courses
ellis_courses = ProfCourses('Ellis')
# add a class
ellis_courses.add_course('COGS18', 'fa20', 363)
# see output
print(ellis_courses.courses)
ellis_courses.n_courses
[{'course_name': 'COGS18', 'quarter': 'fa20', 'n_students': 363}]
1
compare()
method
class ProfCourses():
def __init__(self, prof):
self.n_courses = 0
self.courses = []
self.prof = prof
def add_course(self, course_name, quarter, n_students):
self.courses.append({'course_name': course_name,
'quarter' : quarter,
'n_students': n_students})
self.n_courses += 1
# add method to compare values in courses
def compare(self, attribute, direction='most'):
fewest = self.courses[0]
most = self.courses[0]
for my_course in self.courses:
if my_course[attribute] <= fewest[attribute]:
fewest = my_course
elif my_course[attribute] >= most[attribute]:
most = my_course
if direction == 'most':
output = most
elif direction == 'fewest':
output = fewest
return output
# create ellis_courses
ellis_courses = ProfCourses('Ellis')
# add a bunch of classes
ellis_courses.add_course('COGS18', 'fa20', 363)
ellis_courses.add_course('COGS108', 'fa20', 447)
ellis_courses.add_course('COGS18', 'su20', 88)
ellis_courses.add_course('COGS108', 'sp20', 469)
# see the courses
print(ellis_courses.n_courses)
ellis_courses.courses
5
[{'course_name': 'COGS18', 'quarter': 'fa20', 'n_students': 363}, {'course_name': 'COGS108', 'quarter': 'fa20', 'n_students': 447}, {'course_name': 'COGS18', 'quarter': 'su20', 'n_students': 88}, {'course_name': 'COGS108', 'quarter': 'sp20', 'n_students': 469}, {'course_name': 'COGS108', 'quarter': 'sp19', 'n_students': 825}]
# make comparison among all courses
# returns the class with the most students
ellis_courses.compare('n_students')
{'course_name': 'COGS108', 'quarter': 'sp19', 'n_students': 825}
# return the class with the fewest students
ellis_courses.compare('n_students', 'fewest')
{'course_name': 'COGS18', 'quarter': 'su20', 'n_students': 88}
extending the functionality of the compare()
method
class ProfCourses():
def __init__(self, prof):
self.n_courses = 0
self.courses = []
self.prof = prof
def add_course(self, course_name, quarter,
n_students, n_exams, n_assignments):
# add in additional key-value pairs
self.courses.append({'course_name': course_name,
'quarter' : quarter,
'n_students': n_students,
'n_exams' : n_exams,
'n_assignments' : n_assignments})
self.n_courses += 1
def compare(self, attribute, direction='most'):
fewest = self.courses[0]
most = self.courses[0]
for my_course in self.courses:
if my_course[attribute] <= fewest[attribute]:
fewest = my_course
elif my_course[attribute] >= most[attribute]:
most = my_course
if direction == 'most':
output = most
elif direction == 'fewest':
output = fewest
return output
# create ellis_courses
ellis_courses = ProfCourses('Ellis')
# add a bunch of classes
ellis_courses.add_course('COGS18', 'fa20', 363, 2, 5)
ellis_courses.add_course('COGS108', 'fa20', 447, 0, 6)
ellis_courses.add_course('COGS18', 'su20', 88, 3, 5)
ellis_courses.add_course('COGS108', 'sp20', 469, 0, 6)
ellis_courses.add_course('COGS108', 'sp19', 825, 0, 5)
ellis_courses.add_course('COGS18', 'fa19', 301, 2, 4)
ellis_courses.add_course('COGS18', 'wi22', 355, 2, 4)
ellis_courses.add_course('COGS18', 'sp23', 355, 2, 4)
# see the courses
print(ellis_courses.n_courses)
6
# return the class with the most exams
ellis_courses.compare('n_exams', 'most')
{'course_name': 'COGS18', 'quarter': 'su20', 'n_students': 88, 'n_exams': 3, 'n_assignments': 5}
# return the class with the fewest assignments
ellis_courses.compare('n_assignments', 'fewest')
{'course_name': 'COGS18', 'quarter': 'fa19', 'n_students': 301, 'n_exams': 2, 'n_assignments': 4}
Improving & updating this code
compare()
compare()
to make the for
loop and following conditional more intuitiveclass
creates a new class typeobj.attribute
accesses data stored in attributeobj.method()
carries out code defined within method__init__
__init__
is a reserved method in Pythonself
refers to current instanceClassType
):object_name = ClassType(input1, input2)
self
is not given an input when creating an object of a specified classprint(isinstance(True, object))
print(isinstance(1, object))
print(isinstance('word', object))
print(isinstance(None, object))
a = 3
print(isinstance(a, object))
print(isinstance(sum, object))
print(isinstance(max, object))
# Custom function are also objects
def my_function():
print('yay Python!')
isinstance(my_function, object)
class MyClass():
def __init__(self):
self.data = 13
my_instance = MyClass()
print(isinstance(MyClass, object))
print(isinstance(my_instance, object))