How a class definition and __init__ build an instance
A class definition introduces a new type. The class body lists the attributes and methods every instance gets, and the dunder method **__init__** runs once per instantiation to set the initial state. The first parameter of every instance method is **self**, the reference to the instance being acted on. Python passes it automatically when you call `obj.method()`.
The pattern below is the one shown in 90% of CS50P submissions. Notice three things: the constructor takes the data needed to make a meaningful object, every attribute is assigned to `self`, and a regular method reads those attributes through `self.` again. No `self.` prefix means a local variable that disappears when the method returns.
Instantiation looks like a function call on the class name. `Student("Alice", 21)` builds a fresh object with its own `name` and `age`. Two students built this way share no state, which is the whole point.
class Student:
def __init__(self, name, age):
# self.name and self.age are instance attributes
self.name = name
self.age = age
def introduce(self):
# methods read state through self
return f"I am {self.name}, age {self.age}."
alice = Student("Alice", 21)
bob = Student("Bob", 19)
print(alice.introduce()) # I am Alice, age 21.
print(bob.introduce()) # I am Bob, age 19.