Construction Zone

Jed Rembold & Fred Agbo

March 18, 2024

Announcements

  • Problem sets 5 is due tomorrow at 10pm.
  • Midterm 2 is on Friday, and it will last for an hour (class time)
    • No assignment this week. Just practice chapter 6 exercises to prepare for the exams
    • It is totally open exam and could be taken from anywhere
  • Week 12 is Imageshop project and Week 13 is your personal project
    • You might want to use part of the break to engage with your personal project
  • Polling: https://www.polleverywhere.com/agbofred203

Recall!

  • We could accomplish a more more general class attributes by passing arguments to our function:

    def create_employee(name, title, salary):
      emp = Employee()
      emp.name = name
      emp.title = title
      emp.salary = salary
      return emp
  • We could then use that as:

    clerk = create_employee('Bob Cratchit', 'clerk', 15)
    boss = create_employee(
              'Ebeneezer Scrooge', 'founder', 1000
              )

Constructors

  • While the previous method works, it is not ideal
    • Forces the client to tinker with the internal workings of the Employee
    • Details of the data structure are the property of the implementation, not the client
  • Better to add a method to the Employee class called a constructor, which is responsible for initializing attributes to a newly created object
    • In Python, a constructor is created by defining a special function named __init__
    • The constructor function is called automatically whenever a new object of that type is created

Know Thy self

  • Moving the function into the Employee class has a problem:
    • When we set attributes, they are specific to a given object
    • The class itself though is just a template, and not linked to a specific object
  • We need a general way within the class to refer to whatever object is being created
    • The overwhelming convention in Python is to call this variable self
    • Whenever a new object is created, you could imagine that, for that object, Python replaces all of the selfs in the class with that object’s name
      • This isn’t quite the order of what is happening, but it can help envision what self is doing
  • self is always the first parameter to the __init__ constructor
    • Any other arguments provided are passed in as additional parameters afterwards

An Employee Constructor

class Employee:
    def __init__(self, name, title, salary):
        self.name = name
        self.title = title
        self.salary = salary


clerk = Employee('Bob Cratchit', 'clerk', 15)
  • Note that you do not need to provide an argument for self when creating the object, Python supplies this reference automatically
  • Viewing in PythonTutor can be helpful, as is shown here

Understanding Check

What is printed out on the final line of code to the right?

  1. Honda red 2006
  2. Honda blue 2006
  3. Toyota blue 2008
  4. Honda red 2008
class Car:
    def __init__(self, color, year):
        self.color = color
        self.year = year
        self.make = 'Toyota'

A = Car('blue', 2008)
B = Car('red', 2006)
A.make = 'Honda'
A.year = B.year
print(A.make, A.color, A.year)

What’s your Method?

  • Most classes define additional functions called methods to allow clients to read or update attributes or manipulate the object

  • Methods look like a normal function definition but will always declare the parameter self at the beginning of the parameter list

    • This is true even if the method has no other parameters
  • Methods are defined in the body of the class and would thus look something like:

    def method_name (self, other_parameters):
      ...body of the method...
  • For example

    def give_raise(self, amount):
      self.salary += amount

Accessing and Using Methods

  • After definition, there are two mains ways you can access and use the method:
    • Dot Notation (Conventional)
      • Python sets self to be a reference to the receiver, which is the object to which the method is applied

        clerk = Employee('Bob', 'clerk', 15)
        clerk.give_raise(15)
    • Function Notation
      • You retrieve the method from the class itself, and then provide self manually

        clerk = Employee('Bob', 'clerk', 15)
        Employee.give_raise(clerk, 15)

Visualization Summary

  • To summarize in a visual manner, we can look at everything together on PythonTutor

Getters and Setters

  • In the object-oriented model, the client is not supposed to muck-about with the object internals

  • The implementation should therefore provide methods to retrieve desired attributes (called getters) or to make changes to desired attributes (called setters)

  • Setting up getters and setters for the attribute salary might look like:

    def get_salary(self):
      return self.salary
    
    def set_salary(self, new_salary):
      self.salary = new_salary
  • Getters are far more common than setters, as you don’t always want the client to have the freedom to change attributes on a whim

Representation

  • Printing out an object that you just created as an instance of a custom class will look ugly:

    >>> C = Employee('Bob', 'clerk', 15)
    >>> print(C)
    <__main__.Employee object at 0x7f942ba13550>
  • You can define special methods for a class that specify how your object should be converted to a string (or anything else really)

    • All these special methods have double underscores before and after, and hence are frequently termed “dunder” (double underscore) methods
    • You can the __str__ or __repr__ method to specify how your object

A Good Employee

class Employee:
    def __init__(self, name, title, salary):
        self.name = name
        self.title = title
        self.salary = salary

    def __str__(self):
        return f"{self.name} ({self.title}): {self.salary}"

    def get_salary(self):
        return self.salary

    def set_salary(self, new_salary):
        self.salary = new_salary
// reveal.js plugins