+ + + +
+ +
+
+ +
+
+ +
+ +
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+ + + + + + + + +
+ +
+

Classes#

+
    +
  • objects

  • +
  • class

    +
      +
    • attributes

    • +
    • methods

    • +
    +
  • +
  • instances

    +
      +
    • __init__

    • +
    +
  • +
+
+

Objects#

+
+Objects are an organization of data (called attributes), with associated code to operate on that data (functions defined on the objects, called methods). +
+

Class Question #1#

+

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) string

  • +
  • B) dictionary

  • +
  • C) list

  • +
  • D) integers stored in separate variables

  • +
+
+
+

Storing Dates (Motivation)#

+
+
+
# 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
+
+
+
+
+

Objects are ways to organize data (variables) and functions together.

+
+
+

Example Object: Date#

+
+
+
# Import a date object
+from datetime import date   
+
+
+
+
+
+
+
date? 
+
+
+
+
+
+
+
# Set the data we want to store in our date object
+my_day = 29
+my_month = 9
+my_year = 1988
+
+# Create a date object
+my_date = date(my_year, my_month, my_day)
+print(my_date)
+
+
+
+
+
+
+
# Check what type of thing `my_date` is
+type(my_date) 
+
+
+
+
+
+
+
+

Accessing Attributes & Methods#

+
+Attributes and methods are accessed with a ., followed by the attribute/method name on the object. +
+

Date - Attributes#

+

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
+
+
+
+
+
+
+

Date - Methods#

+

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
+
+
+
+
+
+
+

Listing Attributes & Methods : 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)
+
+
+
+
+
+

Class Question #2#

+

Given the code below:

+
+
+
my_date = date(year = 1050, month = 12, day = 12)
+
+
+
+
+

Which is the best description:

+
    +
  • A) my_date is an object, with methods that store data, and attributes that store procedures

  • +
  • B) my_date is variable, and can be used with functions

  • +
  • C) my_date is an attribute, with methods attached to it

  • +
  • D) my_date is a method, and also has attributes

  • +
  • E) my_date is an object, with attributes that store data, and methods that store procedures

  • +
+
+
+

Class Question #3#

+

For an object lets with a method do_something, how would you execute that method?

+
    +
  • A) do_something(lets)

  • +
  • B) lets.do_something

  • +
  • C) lets.do_something()

  • +
  • D) lets.do.something()

  • +
  • E) ¯\_(ツ)_/¯

  • +
+
+
+

Class Question #4#

+

For an object lets with an attribute name, how would you return the information stored in name for the object lets?

+
    +
  • A) name(lets)

  • +
  • B) lets.name

  • +
  • C) lets.name()

  • +
  • D) lets.get.name()

  • +
  • E) ¯\_(ツ)_/¯

  • +
+
+
+
+

Objects Summary#

+
    +
  • Objects allow for data (attributes) and functions (methods) to be organized together

    +
      +
    • methods operate on the object type (modify state)

    • +
    • attributes store and return information (data) about the object (maintain state)

    • +
    +
  • +
  • dir() returns methods & attributes for an object

  • +
  • Syntax:

    +
      +
    • obj.method()

    • +
    • obj.attribute

    • +
    +
  • +
  • date and datetime are two types of objects in Python

  • +
+
+
+
+

Classes#

+
+Classes define objects. The class 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.

+
+
+

Example Class: Dog#

+
+
+
# 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
+
+
+
+
+

A reminder:

+
    +
  • attributes maintain the object’s state; they lookup information about an object

  • +
  • methods alter the object’s state; they run a function on an object

  • +
+

class notes:

+
    +
  • classes tend to use CapWords convention (Pascal Case)

    +
      +
    • instead of snake_case (functions and variable names)

    • +
    +
  • +
  • () after Dog indicate that this is callable

    +
      +
    • like functions, Classes must be executed before they take effect

    • +
    +
  • +
  • can define attributes & methods within class

  • +
  • self is a special parameter for use by an object

    +
      +
    • refers to the thing (object) itself

    • +
    +
  • +
  • like functions, a new namespace is created within a Class

  • +
+
+
+
# 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()
+
+
+
+
+
+

Class Question #5#

+

Which 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
+
+
+
+
+
    +
  • A) Dog is a Class, sound is an attribute, and speak is a method.

  • +
  • B) Dog is a function, sound is an attribute, and speak is a method.

  • +
  • C) Dog is a Class, sound is a method, and speak is an attribute.

  • +
  • D) Dog is a function, sound is an method, and speak is an attribute.

  • +
+
+
+

Using our Dog Objects#

+
+
+
# 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(6))
+
+
+
+
+
+
+
+

Instances & self#

+
+An instance is particular instantiation of a class object. self refers to the current instance. +
+
+
# Initialize a dog object
+george = Dog()
+
+
+
+
+

From our example above:

+
    +
  • Dog is the Class we created

  • +
  • george was an instance of that class

  • +
  • self just refers to whatever the current instance is

  • +
+
+

Class Question #6#

+

How many instances of Dog() are created below and how many times does the speak() method execute?

+
+
+
pack_of_dogs = [Dog(), Dog(), Dog(), Dog()]
+
+counter = 1
+
+for doggie in pack_of_dogs:
+    if counter <= 2:
+        print(doggie.speak())
+        counter += 1
+    else:
+        break
+
+
+
+
+
    +
  • A) 2 instances, 2 method executions

  • +
  • B) 2 instances, 4 method executions

  • +
  • C) 4 instances, 2 method executions

  • +
  • D) 4 instances, 4 method executions

  • +
  • E) ¯\_(ツ)_/¯

  • +
+
+
+
+

Instance Attributes#

+

An 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()]
+
+print(pack_of_dogs[0].sound)
+print(pack_of_dogs[1].sound)
+pack_of_dogs[1].sound = 'meow'
+print(pack_of_dogs[0].sound)
+print(pack_of_dogs[1].sound)
+
+
+
+
+

This creates four different Dog type objects and stores them in a list. But, up until now…every Dog was pretty much the same.

+
+Instance attributes are attributes that we can make be different for each instance of a class. __init__ is a special method used to define instance attributes. +
+
+

Example Class: Dog Revisited#

+
    +
  • Two trailing underscores (a dunder, or double underscore) is used to indicate something Python recognizes and knows what to do every time it sees it.

  • +
  • Here, we use __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='Fido'):
+        self.name = name
+        print('yay! a puppy!')
+    
+    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') 
+another_dog = Dog()
+
+
+
+
+
+
+
# 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()
+
+
+
+
+
+

Class Question #7#

+

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'
+    breed = 'mutt'
+    
+    def __init__(self, name, breed):
+        self.name = name
+        self.breed = breed
+    
+    def speak(self, n_times=2):
+        return self.sound * n_times
+
+
+
+
+
+
+
polly = NewDog('Polly', 'Corgi')
+print(polly.name)
+print(polly.breed)
+
+polly.breed = 'Dalmatian'
+print(polly.breed)
+
+
+
+
+
    +
  • A) I did it!

  • +
  • B) I think I did it!

  • +
  • C) So lost. -_-

  • +
+
+
+
+

Class example: Cat#

+
+
+
# 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 
+
+
+
+
+
+
+

Instances Examples#

+
+
+
# 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())
+
+
+
+
+
+

Class Question #8#

+

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()
+
+
+
+
+
    +
  • A) True

  • +
  • B) ‘Rob’

  • +
  • C) False

  • +
  • D) ‘rob@python.com’

  • +
  • E) None

  • +
+
+
+
+

Code Style: Classes#

+
    +
  • CapWords for class names

  • +
  • one blank line between methods/functions

  • +
+

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
+
+
+
+
+
+

Example: 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 the professor’s 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
+
+
+
+
+
+
+
morgan_courses = ProfCourses('Morgan')
+print(morgan_courses.n_courses)
+print(morgan_courses.prof)
+
+
+
+
+

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 morgan_courses
+morgan_courses = ProfCourses('Morgan')
+
+# add a class
+morgan_courses.add_course('COGS18', 's123', 80)
+
+# see output
+print(morgan_courses.courses)
+morgan_courses.n_courses
+
+
+
+
+

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 morgan_courses
+morgan_courses = ProfCourses('Morgan')
+
+# add a bunch of classes
+morgan_courses.add_course('COGS18', 's123', 80)
+morgan_courses.add_course('COGS108', 's223', 80)
+morgan_courses.add_course('COGS101a', 'sp23', 160)
+morgan_courses.add_course('COGS18', 'fa23', 300)
+
+# see the courses
+print(morgan_courses.n_courses)
+morgan_courses.courses
+
+
+
+
+
+
+
# make comparison among all courses
+# returns the class with the most students
+morgan_courses.compare('n_students')
+
+
+
+
+
+
+
# return the class with the fewest students
+morgan_courses.compare('n_students', 'fewest')
+
+
+
+
+

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 morgan_courses
+morgan_courses = ProfCourses('Morgan')
+
+# add a bunch of classes
+morgan_courses.add_course('COGS18', 's123', 80, 2, 5)
+morgan_courses.add_course('COGS108', 's223', 80, 2, 5)
+morgan_courses.add_course('COGS101a', 'sp23', 160, 3, 0)
+morgan_courses.add_course('COGS18', 'fa23', 300, 3, 5)
+
+# see the courses
+print(morgan_courses.n_courses)
+
+
+
+
+
+
+
# return the class with the most exams
+morgan_courses.compare('n_exams', 'most')
+
+
+
+
+
+
+
# return the class with the fewest assignments
+morgan_courses.compare('n_assignments', 'fewest')
+
+
+
+
+

Improving & updating this code

+
    +
  • account for ties in compare()

  • +
  • edit code in compare() to make the for loop and following conditional more intuitive

  • +
  • add a method to put dictionary in time order

  • +
  • etc.

  • +
+
+
+

Classes Review#

+
    +
  • class creates a new class type

    +
      +
    • names tend to use CapWords case

    • +
    • can have attributes (including instance attributes) and methods

      +
        +
      • obj.attribute accesses data stored in attribute

      • +
      • obj.method() carries out code defined within method

      • +
      +
    • +
    +
  • +
+
    +
  • instance attributes defined with __init__

    +
      +
    • __init__ is a reserved method in Python

    • +
    • This “binds the attributes with the given arguments”

    • +
    • self refers to current instance

    • +
    +
  • +
+
    +
  • to create an object (instance) of a specified class type (ClassType):

    +
      +
    • object_name = ClassType(input1, input2)

    • +
    • self is not given an input when creating an object of a specified class

    • +
    +
  • +
+
+
+
+

Everything in Python is an Object!#

+
+

Data variables are objects#

+
+
+
print(isinstance(True, object))
+print(isinstance(1, object))
+print(isinstance('word', object))
+print(isinstance(None, object))
+
+a = 3
+print(isinstance(a, object))
+
+
+
+
+
+
+

Functions are objects#

+
+
+
print(isinstance(sum, object))
+print(isinstance(max, object))
+
+
+
+
+
+
+
# Custom function are also objects
+def my_function():
+    print('yay Python!')
+    
+isinstance(my_function, object)
+
+
+
+
+
+
+

Class definitions & instances are objects#

+
+
+
class MyClass():
+    def __init__(self):
+        self.data = 13
+
+my_instance = MyClass()
+
+print(isinstance(MyClass, object))
+print(isinstance(my_instance, object))
+
+
+
+
+
+
+
+

Object-Oriented Programming#

+
+Object-oriented programming (OOP) is a programming paradigm in which code is organized around objects. Python is an OOP programming langauge. +
+
+ + + + +
+ + + + + + + + +
+ + + + + + +
+
+ + +
+ + +