█
LastWrite
  • > Curriculum
  • > Pricing
  • > For Educators
  • > About
  • > Contact
Log InGet Started

Questions, concerns, bug reports, or suggestions? We read every message, write to us at [email protected].

More ways to reach us →
LastWrite

Structured computer science lessons for aspiring developers and security professionals.

[email protected]

(201) 785-7951

Mon–Fri, 9 AM–5 PM EST

Learn

  • Curriculum
  • Pricing

Company

  • About
  • For Educators & Schools
  • Contact Us

Legal

  • Terms of Service
  • Privacy Policy
© 2026 LastWrite. All rights reserved.
Curriculum/Programming Languages/Python (Professional)/Object-Oriented Python
50 minBeginner

Object-Oriented Python

After this lesson, you will be able to: Write Python classes with inheritance, dunder methods, properties, and dataclasses.

Python is multi-paradigm; you don't need OOP for everything. But when modeling real domains, classes + dataclasses are the cleanest path.

Prerequisites:Python Fundamentals

Classes the modern way

Dataclasses cut 80% of boilerplate.

python
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class User:
name: str
email: str
age: int = 0
tags: list[str] = field(default_factory=list) # mutable default trick
def adult(self) -> bool:
return self.age >= 18
u = User(name="Alex", email="[email protected]", age=30, tags=["admin"])
print(u) # auto __repr__ via @dataclass
print(u == User("Alex", "[email protected]", 30, ["admin"])) # auto __eq__
# Frozen (immutable)
@dataclass(frozen=True)
class Point:
x: float
y: float

Dunder methods (the magic methods)

__init__: constructor. __repr__: debug str. __str__: human str. __eq__: equality. __hash__: for set/dict keys. __len__: enables `len(obj)`. __iter__/__next__: iteration. __enter__/__exit__: context manager (`with obj:`). __call__: makes instance callable.

Properties + inheritance

Use properties for computed values; inheritance sparingly.

python
class Animal:
def __init__(self, name: str): self._name = name
@property
def name(self) -> str: return self._name
@name.setter
def name(self, value: str):
if not value.strip(): raise ValueError("empty")
self._name = value
def speak(self) -> str: return "..."
class Dog(Animal):
def speak(self) -> str: return "Woof"
class Cat(Animal):
def speak(self) -> str: return "Meow"
# Polymorphism
for pet in [Dog("Rex"), Cat("Mimi")]:
print(f"{pet.name}: {pet.speak()}")

Common mistakes only experienced Python devs avoid

Reaching for inheritance when composition would do. 'Has-a' beats 'is-a' more often than not. Skipping @dataclass. Hand-writing __init__ + __repr__ + __eq__ is yesterday's pattern. Storing _private attributes without good reason. Python has no real privacy; convention only. Forgetting `field(default_factory=list)` for mutable dataclass defaults.

Quick Check

Why use @dataclass over a hand-written class?

Pick the cleanest answer.

Sign in and purchase access to unlock this lesson.

Sign in to purchase
←Python Fundamentals (Modern Python 3.12+)
Back to Python (Professional)
Pythonic Code→