Python for JS Devs · Fundamentals

Classes & dataclasses

Same OOP concepts, three surprises: explicit self, __init__, and dunder methods. Plus dataclasses, the "just give me a struct" shortcut.

Lesson 07Time ~13 min Win Write a class and a dataclassPrereq Lesson 06

1 · A class, mapped from JS

JavaScript
class User {
  constructor(name) {
    this.name = name;
  }
  greet() {
    return `hi ${this.name}`;
  }
}
new User("kd").greet();
Python
class User:
    def __init__(self, name: str):
        self.name = name        # no `this`; explicit self

    def greet(self) -> str:
        return f"hi {self.name}"

User("kd").greet()          # no `new` keyword

Three deltas:

2 · Dunder methods = protocols

Special __name__ methods hook your object into language features — the equivalent of implementing an interface. The two you'll use most:

class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __repr__(self):          # dev-friendly string (like toString)
        return f"Point({self.x}, {self.y})"
    def __eq__(self, other):       # makes == compare by value
        return (self.x, self.y) == (other.x, other.y)
Inheritance, briefly class Admin(User): subclasses; call the parent with super().__init__(...). Same concept as JS extends / super.

3 · dataclasses — skip the boilerplate

Most classes are just data holders. A @dataclass auto-generates __init__, __repr__, and __eq__ from typed fields. Think of it as a typed struct.

TypeScript-ish
interface User {
  name: string;
  age: number;
  active: boolean;
}
Python
from dataclasses import dataclass

@dataclass
class User:
    name: str
    age: int
    active: bool = True   # default

User("kd", 30)          # __init__ generated for you
Preview: why this matters A Pydantic model (Lesson 12) looks almost identical to a dataclass but adds runtime validation. When you reach FastAPI, remember: dataclass = plain typed struct; Pydantic BaseModel = typed struct that validates and (de)serializes. Same shape, different superpower. And the mutable-default rule from Lesson 05 applies — dataclasses make you use field(default_factory=list) for that reason.

🎯 Your win

In the REPL: write the User class the long way (with __init__ and __repr__), create one, and print it. Then rewrite it as a @dataclass and confirm print(User("kd", 30)) gives a nice representation for free. Feeling why dataclasses exist is the win — it's the gateway to Pydantic.

Check yourself

Q1. What is the constructor method called?
__init__ initializes the instance; you rarely touch __new__.
Q2. How is instance state accessed inside a method?
Every method takes self first, and state lives on self.x. No implicit this.
Q3. A @dataclass mainly saves you from writing:
It generates __init__/__repr__/__eq__. Validation is Pydantic's job, not a dataclass's.
Primary source Python docs — dataclasses. Skim the intro and field().
💬 Ask your teacher: what's a @classmethod vs @staticmethod, and when do I need them? Good pre-FastAPI question.