OOPLesson 1 of 3
Python Classes
Python classes look similar to JS classes with a few key twists. The constructor is called __init__ (not constructor), and every method takes self as its first parameter (like this, but explicit). You do not use the new keyword either: just call the class like a function.
Here is the biggest conceptual difference from JS: self is not this. In JavaScript, this is dynamically bound based on HOW a function is called. That is why you get the infamous 'lost this' bug when passing methods as callbacks: btn.addEventListener('click', user.greet) loses the this context, and you need .bind(this) or arrow functions to fix it. In Python, accessing user.greet gives you a bound method that remembers its instance. You can pass it around as a callback and self is still correct. No .bind(), no arrow functions, no surprises.
Properties work differently too. Instead of the get/set syntax JS uses, Python uses the @property decorator. This gives you a method that behaves like an attribute: you access it without parentheses, but code runs behind the scenes.
Static-style methods come in two flavours. @classmethod receives the class itself as cls, exactly like this inside a JS static method, which makes it perfect for alternative constructors (User.from_json(data)). @staticmethod receives neither the class nor an instance; it is just a function namespaced inside the class.
There is no real private in Python, unlike JS's #private fields. A leading underscore (_password) is a convention meaning 'internal, please do not touch'. A double underscore (__secret) triggers name mangling: Python renames it to _ClassName__secret, which prevents accidental clashes in subclasses but does not lock anything down. And __name__ style names (dunders) are reserved for Python's own special methods like __init__ and __str__; never invent your own.
Naming conventions: classes use PascalCase (MyClass), methods and attributes use snake_case (my_method), and constants use SCREAMING_CASE (MAX_RETRIES). self is also just a convention; you could technically call it anything, but every Python developer expects self.
Quick reference
| JavaScript | Python | Note |
|---|---|---|
| constructor() | __init__(self) | self is explicit |
| this.name | self.name | self always first param |
| new User() | User() | No new keyword |
| get prop() {} | @property | Decorator-based |
| #private | _convention | No true private |
| static create() { new this() } | @classmethod def create(cls) | cls = the class, like this in a static |
| static helper() (no this) | @staticmethod | No access to class or instance |
Side by side
// JavaScript Classes
class User {
#password;
constructor(name, email) {
this.name = name;
this.email = email;
this.#password = "";
}
greet() { return "Hi, I'm " + this.name; }
get info() { return this.name + " (" + this.email + ")"; }
set pass(v) {
if (v.length < 8) throw new Error("Too short");
this.#password = v;
}
static create(n, e) { return new this(n, e); }
}# Python Classes
class User:
def __init__(self, name, email):
self.name = name
self.email = email
self._password = ""
def greet(self):
return f"Hi, I'm {self.name}"
@property
def info(self):
return f"{self.name} ({self.email})"
@property
def password(self):
return "***hidden***"
@password.setter
def password(self, value):
if len(value) < 8:
raise ValueError("Too short")
self._password = value
@classmethod
def create(cls, name, email):
return cls(name, email)
@staticmethod
def is_valid_email(email):
return "@" in email
u = User("Alice", "a@b.com") # no new!
callback = u.greet # bound: no .bind() neededExercise
Real Python, running in your browserConvert this JS class to Python:
class Product {
constructor(name, price, category) {
this.name = name;
this.price = price;
this.category = category;
this._discount = 0;
}
get finalPrice() {
return this.price * (1 - this._discount);
}
set discount(percent) {
if (percent < 0 || percent > 100)
throw new Error("Invalid");
this._discount = percent / 100;
}
toString() {
return this.name + ": quot; + this.finalPrice.toFixed(2);
}
static compare(p1, p2) {
return p1.finalPrice - p2.finalPrice;
}
}Ctrl + Enter runCtrl + Shift + Enter checkTab indentEsc then Tab to leave the editorCtrl + / comment