Classes and objects — the basics

The starting point for everything else in this domain — what a class actually is, what an object is, and the two special methods every class you write will have.

Beginner

3 min read

An object bundles data and behavior together

name = "Ada"          # just a string
age = 25                # just a number

vs.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
 
    def greet(self):
        print(f"Hi, I'm {self.name}")
 
ada = Person("Ada", 25)
ada.greet()   # "Hi, I'm Ada"

A plain variable holds one piece of data. An object bundles related data (name, age) together with the functions that operate on that data (greet) into one single thing. A class is the blueprint that describes what data and behavior every object built from it will have; an instance (ada) is one actual object built from that blueprint. Person describes the shape "a person has a name, an age, and can greet"; ada is one specific person matching that shape. You can create as many independent instances from the same class as you want, each with its own separate data.

__init__: what runs when you create an object

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
 
ada = Person("Ada", 25)
grace = Person("Grace", 36)

__init__ (short for "initialize") runs automatically every time a new instance is created — Person("Ada", 25) calls it, passing "Ada" and 25 in as name and age. Its job is almost always the same shape: take in some starting values, and store them on the object via self.attribute = value so they can be used later by other methods. ada and grace are two completely independent objects — changing ada.name has no effect on grace.name whatsoever, because each instance has its own separate copy of the data __init__ set up for it.

self: how a method refers to "this particular object"

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
 
    def have_birthday(self):
        self.age += 1

Every method defined inside a class takes self as its first parameter — it's how the method refers to the specific instance it's being called on. ada.have_birthday() runs have_birthday with self automatically bound to ada, so self.age += 1 increments ada.age specifically, not some other person's age. You don't pass self explicitly when calling a method (ada.have_birthday(), not ada.have_birthday(ada)) — Python fills it in automatically because you called the method on ada.

Attributes and methods: the two things a class defines

class Person:
    species = "Homo sapiens"     # class attribute — shared by every instance
 
    def __init__(self, name, age):
        self.name = name          # instance attribute — unique per instance
        self.age = age
 
    def greet(self):              # method — behavior every instance can do
        print(f"Hi, I'm {self.name}")

An attribute is a piece of data stored on an object (self.name); a method is a function defined inside the class that operates on that data (greet). There's also a distinction worth knowing early: an attribute set inside __init__ via self.x = ... is an instance attribute — every object gets its own separate copy. An attribute defined directly in the class body (species above) is a class attribute — shared by every instance of the class unless a specific instance overrides it.

__str__: controlling how an object prints

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
 
    def __str__(self):
        return f"{self.name} ({self.age})"
 
ada = Person("Ada", 25)
print(ada)          # without __str__: <__main__.Person object at 0x...>
                     # with __str__:    Ada (25)

Without a __str__ method, printing an object shows Python's unhelpful default representation — the class name and a memory address, not useful information. Defining __str__ to return a readable string is one of the first "special methods" (also called dunder methods, for the double underscores) worth learning, since almost every class benefits from being printable in a way a human can actually read. There are many more special methods (covering equality, comparison, arithmetic, and more) that let a custom class integrate with Python's built-in behaviors — worth exploring once classes themselves feel comfortable.

Further reading

Check your understanding

A quick comprehension check — not tracked, not graded, just for you.

1. What's the actual difference between a class and an instance?

2. What does __init__ actually do?

3. Why does every method inside a class take self as its first parameter?

4. What's the difference between an instance attribute and a class attribute?