devnotes.

Classes and Objects

A class bundles data and behavior together, exactly as in other object-oriented languages, though Python's syntax has a few of its own conventions.

class Car:
    def __init__(self, brand, speed=0):    # constructor — 'self' is required explicitly
        self.brand = brand                    # instance attribute
        self.speed = speed

    def accelerate(self, amount):              # 'self' must be the first parameter
        self.speed += amount

    def show_status(self):
        print(f"{self.brand} at {self.speed} km/h")

my_car = Car("Toyota")
my_car.accelerate(50)
my_car.show_status()      # "Toyota at 50 km/h"

__init__ is Python's constructor — called automatically when Car(...) creates a new instance, used to set up initial attributes. Unlike most languages, Python requires self to be explicitly listed as the first parameter of every instance method — it's not hidden or implicit — and self refers to the specific instance the method was called on, exactly like this in C++/Java, just spelled differently and passed visibly.

Attributes assigned to self (like self.brand) are instance attributes — each object gets its own independent copy. Attributes defined directly in the class body (outside any method) are class attributes, shared by every instance unless individually overridden — a distinction that matters especially for mutable defaults, similar to the mutable default argument trap covered earlier.

Python has no real private/public/protected keywords like C++ or Java — by convention, a single leading underscore (_speed) signals "internal, please don't touch from outside" without technically enforcing it, and this convention-over-enforcement approach is a recurring theme throughout Python's design philosophy.

Classes and Objects in Python | DevNotes