Python for JS Devs · Fundamentals
Same OOP concepts, three surprises: explicit self, __init__, and dunder methods. Plus dataclasses, the "just give me a struct" shortcut.
class User {
constructor(name) {
this.name = name;
}
greet() {
return `hi ${this.name}`;
}
}
new User("kd").greet();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` keywordThree deltas:
__init__ is the constructor (a "dunder" — double-underscore method).self is explicit — every method takes it as the first parameter, and you access instance state as self.x. There is no implicit this.new. You call the class like a function: User("kd").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)
class Admin(User): subclasses; call the parent with super().__init__(...). Same concept as JS extends / super.Most classes are just data holders. A @dataclass auto-generates __init__, __repr__, and __eq__ from typed fields. Think of it as a typed struct.
interface User {
name: string;
age: number;
active: boolean;
}from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
active: bool = True # default
User("kd", 30) # __init__ generated for youBaseModel = 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.
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.
__init__ initializes the instance; you rarely touch __new__.self first, and state lives on self.x. No implicit this.@dataclass mainly saves you from writing:__init__/__repr__/__eq__. Validation is Pydantic's job, not a dataclass's.@classmethod vs @staticmethod, and when do I need them? Good pre-FastAPI question.