CS 111 f21 — Objects part 1

1 Objects

  • pull back the veil, show what's been going on this whole time
  • we've seen functions, and we've seen data
  • objects are just the combination of these two things into a single entity
  • you've been working with objects this whole time
  • We've made extensive use of methods—functions associated with a particular object
    • line.split() is an example of a string method we used in Lab 3
    • PGL graphics involves a lot of applying a particular operation to a particular graphical object
      • ball.setFilled(True), ball.setColor("Black")
      • gw.add(ball)
  • We've also seen an example of using an objects attribute (often called a field)
    • numpy arrays have a shape attribute—a particular piece of data associated with a particular array
    • We access it using a dot (.), but without parentheses, since we're not calling like a function
    • image.shape
  • Python let's us define new kinds of objects
    • The definition is called a class
    • We can have many instances of a class (many specific objects of the same kind)
    • For example, GRect is a class, and to make a grid of bricks, we created many GRect instances

Thought exercise: how could we represent a deck of cards?

  • Some useful parts of a class definition:
    • define a function for creating a new instance of the class
      • called the constructor
      • in Python, this method's name is __init__
    • define a function for displaying instances of the class as a string
      • the __repr__ method, short for representation
      • __repr__ is called automatically whenever we print an object
  • Inside a class definition, self refers to the current object
    • So in our Breakout! code, we call setColor on various objects (e.g., paddle.setColor("Black"), ball.setColor("Black"))
      def setColor(self, color):
          """
          Sets the color used to display this object.  The color parameter is
          usually one of the CSS color names.  The color can also be specified
          as a string in the form <code>"#rrggbb"</code> where <code>rr</code>,
          <code>gg</code>, and <code>bb</code> are pairs of hexadecimal digits
          indicating the red, green, and blue components of the color.
          """
          rgb = convertColorToRGB(color)
          self.color = convertRGBToColor(rgb)
          self.updateColor()
      
    • Earlier, I waved my hands and said Python takes care of providing the self parameter for us
    • What's happening is that Python takes the variable before the . and puts that in for self
      • Meaning, paddle.setColor("Black") is essentially setColor(paddle, "Black")
      • That way, paddle.setColor("Black") results in setColor being called where self is paddle, and ball.setColor("Black") results in setColor being called where self is ball
    • Inside a method definition, we use self to access the attributes and methods of whatever specific object the method is being called with
  • By convention, class names are capitalized
  • To create an instance of a class, we call the class name like a function
    • This causes __init__ to be called, and so we provide the non-self parameters
      c = Card(5, "♥")
      

1.1 Playing Card Class

import random
class Card:
    def __init__(self, value, suit):
        self.value = value
        self.suit = suit

    def __repr__(self):
        if self.value <= 10:
            return str(self.value) + " of " + self.suit
        face = ["Jack", "Queen", "King", "Ace"][self.value - 11]
        return face + " of " + self.suit

deck = []
for value in range(2, 15):
    for suit in ["♣", "♦", "♠", "♥"]:
        deck.append(Card(value, suit))
print("deck of", len(deck), "cards")
random.shuffle(deck)
print(deck[:5])
print(deck[0], ">", deck[1], deck[0] > deck[1])

1.2 Practice

1.2.1 Polls

1.2.2 Define a 2D Point class with fields x and y

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

1.2.3 What is printed by this code?

  • "getter" and "setter" methods to access or modify fields
class Student:
    def __init__(self, name):
        print("Created new student object.")
        self.name = name
        self.major = "CS"

    def getMajor(self):
        print("Fetching major from the database.")
        return self.major

    def setMajor(self, new_major):
        print("Changing major to", new_major)
        self.major = new_major

austin = Student("Austin")
bao = Student("Bao")
print("We made a class!")
mA = austin.getMajor()
mB = bao.getMajor()
print(mA)
print(austin.name)

1.3 History Class

# Prisoner's dilemma history objects support CS 111 Lab 1
# Aaron Bauer, Carleton College
# 2019, 2020, 2021

class History:
    """
    A representation of the action history for a prisoner's dilemma actioner.
    The history contains a "c" for cooperation and a "d" for defection.
    """
    def __init__(self):
        self.history = []

    def get_length(self):
        """Returns the number of actions in the history"""
        return len(self.history)

    def get_most_recent(self):
        """Returns the most recent action (equivalent to `get_past_action(1)`)"""
        if len(self.history) == 0:
            raise RuntimeError("cannot get the most recent action from an empty history")
        return self.history[-1]

    def get_past_action(self, n):
        """Returns the action taken `n` rounds ago"""
        if len(self.history) < n:
            raise RuntimeError("No past action {} rounds ago for a history of length {}".format(n, len(self.history)))
        return self.history[-n]

    def get_num_defects(self):
        """Returns the number of defections in the history"""
        return self.history.count('d')

    def get_num_coops(self):
        """Returns the number of cooperations in the history"""
        return self.history.count('c')

    def has_recent_defect(self, n):
        """Returns `True` if there was a defection in the last `n` actions, otherwise returns `False`"""
        return 'd' in self.history[-n:]

    def has_recent_coop(self, n):
        """Returns `True` if there was a cooperation in the last `n` actions, otherwise returns `False`"""
        return 'c' in self.history[-n:]

    def add_action(self, action):
        """Adds `action` to the end of the history"""
        if action != 'c' and action != 'd':
            raise ValueError("action must be either 'c' or 'd', {} given".format(action))
        self.history.append(action)

    def __iter__(self):
        return iter(self.history)

    def __repr__(self):
        return repr(self.history)
  • the History objects from lab 1 were just a class I wrote to provide list operations
  • classes can provide an interface for an internal data representation
  • note the documentation strings (doc strings) in triple quotes, format strings, and raised exceptions
    • doc strings allow documentation web page to be automatically generated
    • raising exceptions allows more informative error messages