Please use a larger screen

This presentation is designed for desktop or projector displays.

← Back to curriculum

PYTHON OOP — COURSE 1

Classes & Objects

Blueprints, instantiation, self & magic methods

Class obj_1 obj_2 obj_3 One blueprint, many objects

to navigate

Course Curriculum

Where we are in the journey

Agenda

A Simple Task: Display Book Info

Let's store and display information about a book:

  • Store the title, author, and year
  • Print a formatted summary
title = "Clean Code"
author = "Robert Martin"
year = 2008

print(f"{title} by {author} ({year})")
This works fine for a single book.

What About 10 Books?

Parallel lists quickly become unwieldy:

  • Data is scattered across separate lists
  • Easy to mix up indices
  • No clear connection between related data
titles = ["Clean Code", "Refactoring", ...]
authors = ["Robert Martin", "Martin Fowler", ...]
years = [2008, 1999, ...]

for i in range(len(titles)):
    print(f"{titles[i]} by {authors[i]}")

What If We Need to Add Functionality?

Adding features means more parallel structures:

  • New list for each new attribute
  • Functions need more and more parameters
  • Hard to maintain and debug
pages = [464, 431, ...]
ratings = [4.7, 4.5, ...]
is_available = [True, False, ...]

def display_book(title, author,
                 year, pages, rating):
    print(f"{title} by {author}")
    print(f"{pages} pages, rated {rating}")

What if code could be this simple?

book1 = Book("Clean Code", "Robert Martin", 2008)
book1.display_info()

Data and behavior — bundled together.

Procedural Programming Has Its Place

Good for

  • Simple scripts
  • Quick prototypes
  • Data pipelines
  • Small automation tasks

Struggles with

  • Large codebases
  • Complex data relationships
  • Team collaboration
  • Long-term maintenance
OOP helps us organize code around real-world concepts.

Dictionaries: A Middle Ground?

We could group data with dicts:

  • Data is grouped, but no behavior
  • No validation or structure
  • Typo in key name? No error, just a bug
book = {
    "title": "Clean Code",
    "author": "Robert Martin",
    "year": 2008
}

print(book["titel"])  # KeyError!

Section

Real-World Objects

Everything around us is an object

Real-World Objects

Look around you. Everything is an object with attributes (what it has) and actions (what it does).

Car

Has: color, speed, fuel

Does: accelerate, brake, turn

Student

Has: name, grade, courses

Does: study, take exam, enroll

Coffee Machine

Has: water level, beans

Does: brew, grind, dispense

Object Example: Book

Attributes (what it has)

  • Title
  • Author
  • Number of pages
  • ISBN

Actions (what it does)

  • Open
  • Read a page
  • Bookmark
📖 Book Attributes title author pages isbn Actions open() read_page() bookmark()

Object Example: Smartphone

Attributes

  • Brand
  • Color
  • Battery percentage

Actions

  • Make a call
  • Send a text
  • Browse the web
📱 Smartphone Attributes brand color battery_pct Actions make_call() send_text() browse()

Think in Terms of Attributes & Actions

Every object in the real world has properties and behaviors.

Attributes

What the object has

→ variables

Actions

What the object does

→ methods (functions)

Now let's translate this into Python code.

Section

Classes: The Blueprint

A class describes what something IS and what it CAN DO

What Does a Class Define?

Attributes

The data each object will hold

self.title = title
self.author = author
self.year = year

Methods

The behavior each object can perform

def display_info(self):
    print(f"{self.title}")
A class is a template. It doesn't represent a tangible item until we create an Object.

From Blueprint to Objects

🛠 Blueprint creates book_1 book_2 book_3 Each object has its own data, but shares the same structure

Creating Classes in Python

  • class keyword defines a new class
  • __init__ is the constructor
  • self refers to the current instance
  • Methods define object behavior
class Book:
    def __init__(self, title, author, year):
        self.title = title
        self.author = author
        self.year = year

    def display_info(self):
        print(f"{self.title} by {self.author} ({self.year})")

Smartphone Class

class Smartphone:
    def __init__(self, brand, color, battery_pct):
        self.brand = brand
        self.color = color
        self.battery_pct = battery_pct

    def make_call(self, number):
        print(f"{self.brand} calling {number}...")

    def check_battery(self):
        print(f"Battery: {self.battery_pct}%")

Bicycle Class

class Bicycle:
    def __init__(self, brand, gear_count):
        self.brand = brand
        self.gear_count = gear_count
        self.current_gear = 1

    def shift_up(self):
        if self.current_gear < self.gear_count:
            self.current_gear += 1
            print(f"Shifted to gear {self.current_gear}")

    def shift_down(self):
        if self.current_gear > 1:
            self.current_gear -= 1
            print(f"Shifted to gear {self.current_gear}")

Creating Objects (Instantiation)

Syntax:

object_name = ClassName(parameters)

Example:

book1 = Book("Clean Code", "Robert Martin", 2008)
book2 = Book("Refactoring", "Martin Fowler", 1999)
Book book1 "Clean Code" book2 "Refactoring"

Dot Notation

Calling Methods

object_name.method_name(params)

Asks the object to do something

Accessing Attributes

object_name.attribute

Reads the object's data

The dot is the bridge between you and the object's internals.

Interacting with a Smartphone Object

phone = Smartphone("iPhone", "black", 85)

phone.make_call("555-1234")      # iPhone calling 555-1234...
phone.check_battery()            # Battery: 85%

print(phone.brand)                # iPhone
print(phone.color)                # black

Direct Attribute Modification

Python allows direct access to attributes:

phone.battery_pct = -50  # No error!

This can lead to invalid states.

  • No validation
  • No control over who changes what
  • Bugs are hard to trace

We'll learn how to protect attributes in the next lecture: Encapsulation & Access Control

Section

Understanding self

How objects refer to themselves

Think of self like a person talking about themselves.
When you say "my name is...", self is the "my".

self in Action: Person Class

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def introduce(self):
        print(f"Hi, I'm {self.name}, age {self.age}")
person1 = Person("Alice", 25)
person2 = Person("Bob", 30)

person1.introduce()
# Hi, I'm Alice, age 25

person2.introduce()
# Hi, I'm Bob, age 30

self vs Other: Comparing Objects

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def compare_age(self, other_person):
        if self.age > other_person.age:
            print(f"{self.name} is older than {other_person.name}")
        else:
            print(f"{self.name} is not older than {other_person.name}")

alice = Person("Alice", 25)
bob = Person("Bob", 30)
alice.compare_age(bob)   # Alice is not older than Bob

Key Takeaway: self

self = the specific instance

  • Always the first parameter in methods
  • Refers to the object that called the method
  • Python passes it automatically

When you write:

person1.introduce()

Python sees:

Person.introduce(person1)

Section

Special Methods

Magic methods with double underscores

They let you define how your objects behave with built-in operations.

You Already Know One: __init__

The constructor — called automatically when you create an object.

  • Sets up initial attributes
  • Runs once per object creation
  • Always takes self as first parameter
class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author

book = Book("Clean Code", "Martin")
# __init__ called automatically

Common Magic Methods

__len__

Called by len(obj)

Returns length/size of object

__add__

Called by obj1 + obj2

Defines addition behavior

__str__ / __repr__

Called by print(obj)

String representation

These methods let your classes work with Python's built-in operators and functions.

__len__: MusicPlaylist

class MusicPlaylist:
    def __init__(self, name):
        self.name = name
        self.tracks = []

    def add_track(self, track):
        self.tracks.append(track)

    def __len__(self):
        return len(self.tracks)

playlist = MusicPlaylist("Road Trip")
playlist.add_track("Bohemian Rhapsody")
playlist.add_track("Hotel California")
print(len(playlist))  # 2

__add__: Combining Playlists

class MusicPlaylist:
    def __init__(self, name, tracks=None):
        self.name = name
        self.tracks = tracks or []

    def __add__(self, other):
        combined_name = f"{self.name} + {other.name}"
        combined_tracks = self.tracks + other.tracks
        return MusicPlaylist(combined_name, combined_tracks)

rock = MusicPlaylist("Rock", ["Stairway", "Smoke"])
pop = MusicPlaylist("Pop", ["Thriller"])
combined = rock + pop  # MusicPlaylist("Rock + Pop", [...])

__add__: Vector Math

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

    def __add__(self, other):
        return Vector(
            self.x + other.x,
            self.y + other.y
        )
v1 = Vector(3, 4)
v2 = Vector(4, 6)
v3 = v1 + v2

print(v3.x, v3.y)
# 7 10

__str__ vs __repr__

__str__

For end users

Called by print() and str()

Readable, friendly output

__repr__

For developers

Called in REPL and repr()

Unambiguous, often recreatable

__str__ vs __repr__: Date Example

class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    def __str__(self):
        return f"{self.day}/{self.month}/{self.year}"

    def __repr__(self):
        return f"Date({self.year}, {self.month}, {self.day})"

d = Date(2026, 3, 12)
print(d)       # 12/3/2026        (calls __str__)
print(repr(d)) # Date(2026, 3, 12) (calls __repr__)

isinstance(): Check Object Type

Check if an object is an instance of a specific class:

book = Book("Clean Code", "Martin")

print(isinstance(book, Book))  # True
print(isinstance(book, str))   # False
def process_data(item):
    if isinstance(item, Book):
        item.display_info()
    elif isinstance(item, str):
        print(item)
    else:
        print("Unknown type")

Section

Why Choose OOP?

Seven reasons to think in objects

1

Modularity

Break your program into self-contained units.

Each class is a module

Has its own data and logic. Can be developed and tested independently.

Easy to locate bugs

If the payment fails, check the Payment class — not the entire codebase.

2

Reusability

Create once, use many times.

class Customer:
    def __init__(self, name, email):
        self.name = name
        self.email = email

c1 = Customer("Alice", "alice@example.com")
c2 = Customer("Bob", "bob@example.com")
c3 = Customer("Charlie", "charlie@example.com")

One class, unlimited objects.

3

Maintainability

Change one class, all objects reflect the update.

Before

def display_info(self):
    print(self.title)

After

def display_info(self):
    print(f"{self.title} ({self.year})")

Every Book object now shows the year — no other code changed.

4

Abstraction

Hide complexity, expose a simple interface.

  • You don't need to know how an engine works to drive a car
  • Users of your class only see public methods
  • Implementation can change without breaking callers
car.start_engine()
# Simple interface
# Complex internals hidden
5

Encapsulation

Protect data, controlled access.

Without

account.balance = -1000
# Anyone can break it

With

account.withdraw(100)
# Validated, controlled

We'll dive deep into this in Lecture 2.

6

Polymorphism

Same method name, different behavior.

for shape in [circle, rectangle, triangle]:
    shape.area()  # Each calculates differently

We'll explore this in Lecture 4.

7

Real-World Modeling

Objects in code correspond to real entities.

Customer

name, email, orders

Product

name, price, stock

Order

items, total, status

Your code structure mirrors the problem domain.

Quiz Time! (Classes)

What is a class?

A) An object

B) A blueprint for creating objects

C) A variable

D) A function

A class is a blueprint (template) that defines the attributes and methods objects of that type will have

Quiz Time! (Instantiation)

What is instantiation?

A) Deleting an object

B) Creating a class

C) Creating an object from a class

D) Calling a method

Instantiation is the process of creating a concrete object from a class blueprint

Quiz Time! (Magic Methods)

What does the __len__ method do?

A) Returns string representation

B) Returns the length of the object

C) Adds two objects together

D) Initializes an object

__len__ is called by the built-in len() function and should return the length of the object

Quiz Time! (Syntax)

Which is correct syntax to create an object?

A) Book.create("title")

B) new Book("title")

C) Book("title")

D) create Book("title")

In Python, you create objects by calling the class name directly with parentheses — no new keyword needed

Quiz Time! (self)

What does self refer to?

A) The class itself

B) The current instance of the class

C) The parent class

D) A global variable

self refers to the specific object instance that called the method — each object has its own self

Quiz Time! (__str__ vs __repr__)

What is the difference between __str__ and __repr__?

A) They are identical

B) __str__ is for users, __repr__ is for developers

C) __repr__ is for users, __str__ is for developers

D) __str__ returns int, __repr__ returns string

__str__ provides a human-readable representation, while __repr__ provides an unambiguous developer-oriented representation

Key Takeaways

Course Curriculum

What's next

Next up: Encapsulation & Access Control

Thank You!

Questions?

See you next lecture for Encapsulation & Access Control