1 of 35

Object-Oriented Programming

Prof. Seungtaek Choi

2 of 35

Recap

  • OOP Basics
    • Class
      • Class vs. Object (Instance)
      • Encapsulation
      • Method
      • Constructor (__init__)
      • Inheritance
      • Method Overriding
    • Module
    • Package
      • __init__.py
      • __all__

3 of 35

Recap: Why OOP?

  • Procedural code scales poorly when stateful components multiply.
  • OOP groups state and behavior together per object.

4 of 35

Recap: Class (Class? Object?)

  • A class is a blueprint; �an object (instance) is a concrete entity created from it.

5 of 35

Recap: FourCal API (4)

  • The problem of invalid state
    • If we forget .setdata() and call .add() first, we get an attribute error.

How can we prevent invalid state at creation time?

6 of 35

Recap: FourCal API (4)

  • Constructors with __init__ (double _ on both side)
    • Use __init__ to require necessary data and establish invariants.

It’s safer than .setdata() as instances are valid immediately.

7 of 35

Recap: Why __init__ Matters

  • A good initializer prevents “half-built” objects, centralizes defaulting/validation, and improves readability.

8 of 35

Recap: Inheritance (Extending Behavior)

  • Inheritance reuses and extends functionality without modifying the base.
    • syntax: class ClassName(ParentClassName):
  • It’s better than copy-paste (DRY and maintainable).

9 of 35

Recap: Method Overriding (Changing Behavior)

  • Override a method in a subclass to alter behavior (e.g., safe division).

10 of 35

Recap: Class Variables vs. Instance Variables (2)

  • Instance variables live on each object; class variables are shared across objects.

11 of 35

Recap: Class Variables vs. Instance Variables (3)

  • An instance attribute with the same name shadows the class variable on that object only.
    • Attribute lookup order: instance 🡪 class 🡪 bases.

12 of 35

Recap: Glossary

  • Terms you should be fluent with.
    • Class: blueprint for objects.
    • Object / Instance: a concrete value created from a class.
    • Attribute / Instance variable: per‑object data stored on self.
    • Class variable: shared data stored on the class.
    • Method: function defined inside a class (first arg self).
    • Constructor: __init__, initializes new objects.
    • Inheritance: reuse/extend a class.
    • Overriding: redefine a method in a subclass.
    • Encapsulation: keep data and methods together, hide internals.

13 of 35

Recap: Package

  • Package = directory tree of modules; __init__.py can configure and expose API.
  • Import submodules by dotted path; re-export via __init__.py when convenient.
  • Use __all__ to control from pkg import *; prefer explicit imports in real projects.
  • Use relative imports (. / ..) for clean intra-package references.

14 of 35

Today

  • More OOP Basics
  • Announcement: 2nd Assignment!
    • Practice session :)

15 of 35

OOP Basics

16 of 35

Type Hints

  • Type hints annotate what types you intend for variables and functions. They don’t enforce types at runtime; tools (mypy/IDE) use them to catch bugs early and improve autocomplete.

17 of 35

Type Hints - Collections, Optional, and Unions

  • Use built-in generics like list[int], dict[str, float]. Represent “maybe” values with T | None. Unions are written | (e.g., int | str).

18 of 35

Duck Typing: Behavior over Types

  • In Python you often rely on behavior, not declared types: “if it has the method I need, I can call it.” This keeps code short and flexible for small projects and prototypes.

19 of 35

Safer Duck Typing (EAFP: try/except)

  • Duck typing doesn’t check types ahead of time. Add a runtime guard (EAFP) so your function degrades gracefully when the method is missing.

20 of 35

When to Prefer ABC (Explicit Interface)

  • For team code and long-term maintenance, define an explicit contrast with Abstract Base Classes. Missing methods fail early at instantiation time.

21 of 35

EAFP vs. LBYL

  • EAFP: Easier to Ask Forgiveness than Permission
    • try/except
  • LBYL: Look Before You Leap

22 of 35

Access Control (Python Way)

  • Python uses conventions: public (name), “protected” (_name), and name-mangled private (__name). use them to signal intent.

23 of 35

Practical Access Control

  • Prefer properties and clear APIs over rigid hiding.
  • Trust callers; document invariants.
  • Pythonic style > heavy ceremony.

24 of 35

When Private Makes Sense

  • Use name-mangling (e.g., __pin) sparingly for safety against accidental access, not for true security.

25 of 35

Imports that Matter to OOP

  • import loads modules so your classes can be organized across files/packages.
  • Keep public APIs explicit.

26 of 35

Imports that Matter to OOP (Practical)

  • import lets you split classes/functions into separate files and keep code organized.
  • Make public APIs explicit at the top, push less-used internals (protected/private) down.

27 of 35

__new__ vs. __init__

  • __new__ creates and returns the instance (allocation); �__init__ initializes it.
  • In Python, __init__ is commonly called the constructor, but technically creation happens in __new__.

28 of 35

__del__ (Destructor): Use with Care

  • __del__ may run when an object is about to be destroyed; timing is not guaranteed (GC/refcount/cycles). Prefer context managers for resource cleanup.

29 of 35

Object Lifecycle (Creation 🡪 Init 🡪 (Optional) Destruct)

  • Lifecycle: __new__ allocates 🡪 __init__ initializes 🡪 later GC may reclaim; __del__ can fire but is fragile around cycles/exceptions.

30 of 35

super(): Why & What

  • super() returns a proxy to the next class in the MRO (Method Resolution Order). Use it to reuse and extend parent behavior without copy-pasting or naming the base directly.

31 of 35

super(): in __init__ (Chaining Init)

  • When a subclass adds state, chain to the parent initializer so inherited attributes are set properly. Avoid duplicating parent setup.

32 of 35

Override + super(): Extend, Don’t Replace

  • Override a method, call super() to keep the base guarantee, then add your extra behavior. That’s safer than re-implementing everything.

33 of 35

Common Pitfall: Forgetting super() in __init__

  • If you don’t call super().__init__, parent fields may never initialize. You’ll get errors later when code assumes they exist.

34 of 35

super() Chains (Mental Model)

  • Each override can do pre/post work and call super() once to move along the chain. The order follows the class MRO.

35 of 35

Next

  • Advanced Python
    • Built-in Functions
    • Standard Libraries
    • External Libraries (with pip)