University of Geneva · CUI BSc · Algorithmics & Data Structures

Python Basics II: OOP & Data Structures

Seminar 3

Today's Agenda

  • Data structures — choosing list / tuple / dict / set
  • Functions — required vs optional parameters (core first)
  • OOP from the ground up — class, constructor (__init__), self
  • Class variables + the four pillars of OOP

Built-in Collections at a Glance

TypeOrderedMutableUniqueUse for
listSequences, stacks
tupleFixed records, keys
dict✓ (3.7+)Keys onlyKey→value mapping
setMembership tests, dedup

Choosing the Right Structure

  • Need an ordered sequence you'll modify? → list
  • Fixed record or dictionary key? → tuple (immutable = hashable)
  • Fast lookup by key, counting, caching? → dict — O(1) average
  • Membership test, deduplication? → set — O(1) average
  • Rule of thumb: start with the simplest structure that fits
  • Wrong choice ≠ wrong answer, but it can mean O(n) vs O(1)

Functions — Core Concepts

  • A function is a reusable, named block of computation
  • Core parameter types for today: positional + default
  • Bonus for later: *args / **kwargs when APIs need flexibility
  • Return values: every function returns something (None if implicit)
  • Scope (first pass): local variables live inside the function
  • Type hints document intent and enable static analysis
  • def add(a: int, b: int) -> int

What is OOP?

  • Procedural: functions operate on separate data
  • Object-oriented: bundle data + behaviour into objects
  • Core idea — model the world:
  • Class = blueprint (what attributes & methods exist)
  • Object = instance (a concrete thing built from the blueprint)
  • Why OOP in this course?
  • Data structures (Stack, Queue, Tree) are naturally objects
  • Encapsulation keeps invariants safe (e.g. balance ≥ 0)

Class vs Object

Class vs Object
  • Class = template defining attributes + methods
  • Object = concrete instance with its own state
  • Multiple objects from one class, each independent
  • alice.grade = 87, bob.grade = 42

Classes in Python — Anatomy

A class defines data + behaviour. Focus on constructor and self:
class Student:
    def __init__(self, name, grade):   # constructor
        # __init__ runs automatically on Student(...)
        self.name = name               # saved on THIS object
        self.grade = grade

    def is_passing(self):             # instance method
        return self.grade >= 50

Constructor (__init__) and self

When you call Student('Alice', 87), Python creates a new object.

Then Python calls __init__(self, ...) to initialise that object.

self means: the current instance being initialised or used.

alice = Student('Alice', 87)
bob = Student('Bob', 42)

print(alice.name, alice.grade)   # Alice 87
print(bob.name, bob.grade)       # Bob 42

Class Variables vs Instance Attributes

Class variable = shared by all objects from the class.

Instance attribute = stored separately in each object.

class Student:
    school = 'UNIGE'              # class variable (shared)

    def __init__(self, name):
        self.name = name          # instance attribute (per object)

    def label(self):
        return f"{self.name} @ {self.__class__.school}"

print(Student.school)             # access via class
print(Student('Alice').label())   # access via method/self

OOP — The Four Pillars

  • Encapsulation — bundle data + methods; hide internals
  • Convention: _prefix for private attrs (self._balance)
  • Abstraction — expose a clean interface, hide complexity
  • Users call account.deposit(100), not account._update_ledger()
  • Inheritance — class Dog(Animal): — reuse & specialise
  • Subclass inherits all attributes/methods, can override
  • Polymorphism — same interface, different class-specific behaviour
  • Example: dog.speak() -> 'Woof', cat.speak() -> 'Meow'

Encapsulation Example — Bank Account

Goal: the object protects its own state with controlled access.

class BankAccount:
    def __init__(self, balance=0):
        self._balance = balance

    @property
    def balance(self):
        return self._balance

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError('amount must be > 0')
        self._balance += amount

account = BankAccount(100)
account.deposit(50)
account.balance = 50              # this is not allowed and will rais an error
print(account.balance)            # 150

Users can read balance, but updates must pass validation in deposit().

Abstraction Example — Coffee Machine

Goal: expose a simple interface and hide internal steps.

class CoffeeMachine:
    def make_coffee(self):
        self._heat_water()
        self._grind_beans()
        self._brew()
        return 'Coffee is ready!'

    def _heat_water(self): pass
    def _grind_beans(self): pass
    def _brew(self): pass

machine = CoffeeMachine()
print(machine.make_coffee())

Caller cares about make_coffee(), not hidden mechanics. He is not aware (or has to care!) about i.e. brew(). Here, you see, internal functions are using a starting '_', which is a pythonic way of saying, this is an internal function call only (it does, however, not enfirce it not being callable)

Inheritance Example — University Members

Goal: reuse shared fields/behaviour, then specialise.

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

class Student(UniversityMember):
    def __init__(self, name, email, student_id):
        super().__init__(name, email)
        self.student_id = student_id

s = Student('Alice', 'alice@unige.ch', 'S12345')
print(s.name, s.student_id)

Student is-a UniversityMember with extra data.

Polymorphism Example — Payment Methods

Goal: same method call, class-specific implementation.

from abc import ABC, abstractmethod

class PaymentMethod(ABC):
    @abstractmethod
    def pay(self, amount):
        pass

class CreditCard(PaymentMethod):
    def pay(self, amount):
        return f'Paid {amount} with card'

class PayPal(PaymentMethod):
    def pay(self, amount):
        return f'Paid {amount} with PayPal'

def checkout(payment_method: PaymentMethod, amount):
    print(payment_method.pay(amount))

checkout(CreditCard(), 42)
checkout(PayPal(), 42)

All concrete classes share the same abstract pay() contract.

Today's Exercises

  • Core Ex 1 — Comprehension warm-up: sorted uniques, dict by length, repeated words
  • Core Ex 2 — Character frequency counter using a dict
  • Core Ex 3 — Remove duplicates without using set() — preserve order
  • Core Ex 4 — Merge two dicts (loop and .update())
  • Core Ex 5 — Student class: __init__, add_grade, average, is_passing
  • Bonus — best_subject, report(), and additional polish

Key Takeaways

  • Choose your data structure deliberately — it shapes complexity
  • Start with function basics first; advanced signatures are bonus today
  • OOP = data + behaviour in one place; class = blueprint, object = instance
  • __init__ is the constructor; self refers to the current object
  • Class variables are shared; instance attributes belong to each object
  • Four pillars: encapsulation, abstraction, inheritance, polymorphism
  • Next week: applying these structures to real algorithms
University of Geneva · CUI BSc · Algorithmics & Data Structures

Thank You & Have Fun!