CMU 15-112: Fundamentals of Programming and Computer Science
Class Notes: Object-Oriented Programming (OOP), Part 1
Using Objects and Methods


  1. Methods vs Functions
  2. Classes and Instances
  3. Objects and Object-Oriented Programming (OOP)
  4. Namespaces
  5. Dataclasses
  6. Updated Example: Adding and Deleting Shapes
  7. Objects and Aliases

  1. Methods vs Functions
    We call methods using s.f() rather than f(s):
    s = 'This could be any string!' print(len(s)) # len is a function print(s.upper()) # upper is a string method, called using the . notation # we say that we "call the method upper on the string s" print(s.replace('could', 'may')) # some methods take additional arguments

    See how we get different errors for improperly calling methods vs functions:
    n = 123 print(len(n)) # TypeError: object of type 'int' has no len() # This means that len() cannot work properly with int's n = 123 print(n.upper()) # AttributeError: 'int' object has no attribute 'upper' # This means that there is no method upper() for int's

  2. Classes and Instances
    • Classes are also called "Types" in Python.
      • For example, these are classes: int, float, str, bool
    • Instances are values of a given class or type.
      • For example: 'abc' is a str instance (also called a string)

  3. Objects and Object-Oriented Programming (OOP)
    • Every value in Python is an Object.
      • Every instance is an object, and its type is some class.
      • Every class is an object, too (its type is type!).
    • That is why we call this Object-Oriented Programming
      • We are using objects only a little bit now.
      • Soon we will write our own classes.
      • Then we will add some sophistication to how we write and use classes and objects.
      • Even so, because we are using objects now, we are already using Object-Oriented Programming (OOP).

  4. Namespaces
    We can use namespaces to create mutable objects. This is a very handy way to store a collection of properties (or "fields") in a single object. (Note: at this time, brython does not support namespaces, but standard python of course does.)
    # Don't forget this import: from types import SimpleNamespace # Now we can create new object representing dogs: dog1 = SimpleNamespace(name='Dino', age=10, breed='shepherd') print(dog1) # prints: namespace(age=10, breed='shepherd', name='Dino') print(dog1.name) # prints: Dino # Next, let's show that this is in fact mutable: dog1.name = 'Fred' print(dog1) # prints: namespace(age=10, breed='shepherd', name='Fred') print(dog1.name) # prints: Fred # Now let's show that == works properly: dog2 = SimpleNamespace(name='Spot', age=12, breed='poodle') dog3 = SimpleNamespace(name='Fred', age=10, breed='shepherd') print(dog1 == dog2) # prints: False print(dog1 == dog3) # prints: True # Finally, let's see what the type of a dog object is: print(type(dog1)) # prints <class 'types.SimpleNamespace'&rt; (yuck)

  5. Dataclasses
    A Dataclass is like a SimpleNamespace, with these improvements:
    • It has required fields.
    • It has a custom type.
    Let's revisit the example above, this time using a Dataclass:
    # Don't forget this import: from dataclasses import make_dataclass # Now we can create a new class named Dog where # instances (individual dogs) have 3 properties # (fields): name, age, and breed Dog = make_dataclass('Dog', ['name', 'age', 'breed']) # Now we can create an instances of the Dog class: dog1 = Dog(name='Dino', age=10, breed='shepherd') print(dog1) # prints: Dog(name='Dino', age=10, breed='shepherd') print(dog1.name) # prints: Dino # Next, let's show that this is in fact mutable: dog1.name = 'Fred' print(dog1) # prints: Dog(name='Fred', age=10, breed='shepherd') print(dog1.name) # prints: Fred # Now let's show that the fields are in fact required: try: dog2 = Dog(name='Dino', age=10) except Exception as e: print(e) # prints: missing 1 required positional argument: 'breed' # Now let's show that == works properly: dog2 = Dog(name='Spot', age=12, breed='poodle') dog3 = Dog(name='Fred', age=10, breed='shepherd') print(dog1 == dog2) # prints: False print(dog1 == dog3) # prints: True # Finally, let's see what the type of a dog object is: print(type(dog1)) # prints <class 'types.Dog'&rt; (better) print(isinstance(dog1, Dog)) # prints True (great!)

  6. Updated Example: Adding and Deleting Shapes
    Here is an improved version of the adding-and-deleting-shapes example from here. Instead of representing dots as (cx, cy) pairs, here we will create a Dot class using make make_dataclass. Now, each instance will use dot.cx, dot.cy, dot.r, dot.counter, and dot.color.
    from cmu_112_graphics import * import random from dataclasses import make_dataclass Dot = make_dataclass('Dot', ['cx', 'cy', 'r', 'counter', 'color']) def appStarted(app): app.dots = [ ] def pointIsInDot(x, y, dot): return (((dot.cx - x)**2 + (dot.cy - y)**2)**0.5 <= dot.r) def getRandomColor(): colors = ['red', 'orange', 'yellow', 'green', 'blue', 'pink', 'lightGreen', 'gold', 'magenta', 'maroon', 'salmon', 'cyan', 'brown', 'orchid', 'purple'] return random.choice(colors) def mousePressed(app, event): # go through dots in reverse order so that # we find the topmost dot that intersects for dot in reversed(app.dots): if pointIsInDot(event.x, event.y, dot): dot.counter += 1 dot.color = getRandomColor() return # mouse click was not in any dot, so create a new dot newDot = Dot(cx=event.x, cy=event.y, r=20, counter=0, color='cyan') app.dots.append(newDot) def keyPressed(app, event): if (event.key == 'd'): if (len(app.dots) > 0): app.dots.pop(0) else: print('No more dots to delete!') def redrawAll(app, canvas): # draw the dots and their counters for dot in app.dots: canvas.create_oval(dot.cx-dot.r, dot.cy-dot.r, dot.cx+dot.r, dot.cy+dot.r, fill='white', outline=dot.color, width=15) canvas.create_text(dot.cx, dot.cy, text=str(dot.counter)) # draw the text canvas.create_text(app.width/2, 20, text='Example: Adding and Deleting Shapes') canvas.create_text(app.width/2, 40, text='Mouse clicks outside dots create new dots') canvas.create_text(app.width/2, 60, text='Mouse clicks inside dots increase their counter') canvas.create_text(app.width/2, 70, text='and randomize their color.') canvas.create_text(app.width/2, 90, text='Pressing "d" deletes circles') runApp(width=400, height=400)

  7. Objects and Aliases
    # Objects are mutable so aliases change! # Run this with the visualizer to make it clear! from types import SimpleNamespace import copy dog1 = SimpleNamespace(name='Dino', age=10, breed='shepherd') dog2 = dog1 # this is an alias dog3 = copy.copy(dog1) # this is a copy, not an alias dog1.name = 'Spot' print(dog2.name) # Spot (the alias changed, since it is the same object) print(dog3.name) # Dino (the copy did not change, since it is a different object)