Skip to content

Week 13 - Introduction to Classes & Attributes

Introduction to Programmer-Defined Types AKA Classes

Objective:

By the end of this lesson, students should be able to:

  1. Create a simple class in Python
  2. Understand attributes and instance variables
  3. Manipulate class instances using functions

What is a Class?

  1. Explanation
    • A class as a blueprint for creating objects in all OOP languages, including Python.
    • A class organizes both code and data using programmer-defined types.
      • Data is associated with each object instance via instance variables or attributes.
      • Code and actions are encapsulated in class methods.
  2. Code Activity
    • Run the following code and observe the output.
      1
      2
      3
      4
      5
      6
      class Book:  # User defined type
          """Represents a book in a library."""
      
      # Creating an instance of the Book class
      my_book = Book()  # Calling the constructor
      print(type(my_book))  # Output: <class '__main__.Book'>
      

Creating and Using Attributes

  1. Explanation
    • One way of adding and accessing attributes/instance variables in a class is using dot notation.
    • Attributes are like variables, holding data, that belong to an object or an instance of a class.
      • Hence why they are sometimes called instance variables.
  2. Example
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    class Book:
        """Represents a book in a library."""
    
    my_book = Book()
    my_book.title = "1984"
    my_book.author = "George Orwell"
    my_book.year = 1949
    
    # Accessing attributes
    print(f"{my_book.title} by {my_book.author}, published in {my_book.year}.")
    
  3. Activity
    • How could we add genre, pages, publisher, and isbn attributes to this class?

Functions and Object Instances

  1. Explanation
    • We can create functions to display or manipulate object/instance data.
    • In fact, objects can be passed to function parameters just like any other data-type.
      • It is critical to know, that objects passed as parameters is really just passing by reference.
        • The object’s memory address is copied to the parameter, hence they point at the same object.
        • The object, nor its attributes, nor its attribute data is copied. Only the address is passed.
        • Thus, the object can be mutated.
        • This is different than passing primitive data types or immutable data types.
  2. Code Example
    1
    2
    3
    4
    5
    6
    7
    8
    9
    def display_book(book):
        print(f"{book.title} by {book.author}, published in {book.year}.")
    
    my_book = Book()
    my_book.title = "The Great Gatsby"
    my_book.author = "F. Scott Fitzgerald"
    my_book.year = 1925
    
    display_book(my_book)
    
  3. Activity
    • How would you modify the display_book function to include additional information, like genre or isbn?

Objects as Return Values

  1. Explanation
    • Below I create a function that constructs and returns an object.
    • Note that I only use parameters to set initial attribute values after I instantiate the object book.
    • Note, that I return the object book with return just like all other types we’ve dealt with in Python.
      • This returns the memory address of the object, effectively copying the reference or pointer, so whatever variable receives this reference will now point at the object. new_book in this case.
  2. Code Example
    1
    2
    3
    4
    5
    6
    7
    8
    9
    def create_book(title, author, year):
        book = Book()  # Creating an instance of Book (i.e. an object)
        book.title = title  # Setting the title instance variable / attribute
        book.author = author
        book.year = year
        return book
    
    new_book = create_book("To Kill a Mockingbird", "Harper Lee", 1960)  # Returned the address of the object to new_book, so new_book now points at the object.
    display_book(new_book)
    
  3. Create Activity
    • Can you write a similar function for a Movie class?

Mutability and Modifying Objects

  1. Explanation
    • We’ve learned the data types of strings and tuples are immutable objects, meaning once assigned their attributes can’t be changed. To change these, we must copy the original object.
    • Most objects are mutable, however. Mutability refers to being able to change the attributes of an object either directly or via its methods.
    • The implication of being able to modify an object is that methods can change the objects state or attributes without us being aware of the change.
    • For mutable data types, we have to be careful to control the attribute change in our code to prevent unexpected behavior.
    • Mutable types can make algorithms faster, as copying objects is NOT necessary to make updates.
    • Mutability however does preclude objects from being keys in data-structurs like dictionaries, as mutable types are not hashable.
  2. Code Example
    1
    2
    3
    4
    5
    def update_year(book, new_year):
        book.year = new_year
    
    update_year(new_book, 1961)
    display_book(new_book)  # Year is now updated to 1961
    
  3. Activity
    • Can you write a function to update your Book object’s genre attribute?

Copying Objects and Pure Functions

  1. Explanation
    • Since user defined types create mutable objects, we sometimes need a way to preserve the objects data or its state (i.e. the state/value of the objects attributes).
      • We can preserve an objects state utilizing the copy module, which does a deep copy of an object and its attributes, and returns a new equivalent object.
      • A programmer cann now modify the object copy without concern about modifying the original object (At the expense of a full copy, of course).
    • Introduce the copy module to duplicate objects.
    • Pure Functions are functions that don’t modify the original object passed as a parameter, and the copy module is one way to gurantee a function can be a pure function.
  2. Code Example
    1
    2
    3
    4
    5
    6
    from copy import copy
    
    book_copy = copy(new_book)
    update_year(book_copy, 2020)
    display_book(new_book)   # Original book remains unchanged
    display_book(book_copy)  # Copy has the updated year
    
  3. Let’s practice
    • Create a method for your Movie class that can modify a movie object by adding a producer without changing the original movie object.

Wrap-Up

  • Why use classes?
    • To organize and structure code and data.
    • To isolate human concepts into classes.
    • To encapsulate code that modifies an object of the user defined class type.
  • Remember pure functions are functions that DON’T modify the original object passed to it via parameters. Impure functions do alter the objects passed to them.
    • The implications of pure functions is that we can use them to test object state without ever changing the original object. This comes at decreased performance, however as copies take memory and time.
    • Impure functions can alter object state and make many algorithms faster due to not needing to copy objects, however altering the object can introduce bugs if we’re not careful about understanding what is being modified and when.
    • For concurrency purposes, pure functions are often better as they can often be treated as thread-safe functions..
  • QUESTIONS?