🐍 Python Course · Stage 4 · Lesson 66 of 89 · Concept: Classes
Course Overview →

🟩 These concept pages connect Python to programming in general. Read Beginner tab first.

← Concept: Scope Concept: Objects →
thecodex.expert · The Codex Family of Knowledge
CS Concepts

Classes

The blueprint that defines what objects of a type contain and can do — written once, instantiated many times.

CS Concepts Language-independent ~18 min read Last verified:
Canonical Definition

A class is a named template that defines the attributes and methods shared by all objects of that type, serving as a blueprint from which individual instances are created, each with its own copy of the class's instance attributes.

🟩 Beginner

Blueprints for objects

What you'll learn: What a class is (a blueprint), what an instance is (an object built from that blueprint), constructors, and the difference between instance and class attributes.

How to read this tab: This concept page explains the why. The Python-specific syntax is at Object-Oriented Python.

⏱ 20 min 📄 4 sections 🔶 Prerequisite: Functions + Scope
One sentence that captures it

A class is a blueprint — it describes what data an object will hold and what actions it can perform, and from it you create as many individual objects as you need.

The blueprint/building analogy: a class is the architectural drawing; objects are the actual buildings. The drawing says "buildings have 3 bedrooms and a kitchen." Each building is built from the same drawing but can have different colours, owners, and furnishings. In code: the class defines the structure; each instance has its own data.

Class as blueprint, object as instance

The class BankAccount defines what every account has (owner, balance) and what it can do (deposit, withdraw). Each actual account — Priya's, Rahul's — is a separate instance with its own data but sharing the class's methods.

Pythonclasses.py
class BankAccount:
    def __init__(self, owner, balance=0):  # constructor
        self.owner = owner       # instance attribute
        self.balance = balance

    def deposit(self, amount):   # instance method
        self.balance += amount
        return self.balance

    def __str__(self):           # string representation
        return f"Account({self.owner}: Rs{self.balance:,.2f})"

priya = BankAccount("Priya", 10000)
rahul = BankAccount("Rahul")
priya.deposit(500)
print(priya)   # Account(Priya: Rs10,500.00)
💡

The constructor runs once, when the object is created. In Python it's __init__(self, ...). In Java it's a method with the same name as the class. Its job: set up the object's initial state by assigning instance attributes. Keep constructors simple — do assignment, not complex computation. Move complex setup into separate methods.

The constructor

The constructor runs automatically when an instance is created. Python: __init__. Java/C++: method with the class name. Rust: associated function new(). Its job is to initialise the instance's attributes to valid starting values.

💡

Three types of method in Python: Instance methods (take self — access instance data). Class methods (@classmethod, take cls — access class data, useful for alternative constructors). Static methods (@staticmethod — take neither — just a regular function namespaced inside the class). Use instance methods by default; the others only when there's a clear reason.

Instance, class, and static methods

An instance method operates on one specific object (self in Python is that object). A class method (@classmethod) operates on the class itself — cls is the class. A static method (@staticmethod) is just a function grouped inside a class for organisation — it receives no implicit first argument.

📎

Python's access control is convention, not enforcement. One underscore: _method — "private by convention, don't use this from outside." Two underscores: __method — name mangling makes it harder to access from outside (becomes _ClassName__method) but not impossible. No underscore: fully public. Java enforces private with the compiler; Python trusts the developer.

Access control

Java: private, protected, public — enforced by compiler. Python: _name = convention ("don't use externally"), __name = name mangling (harder to access but not impossible). Rust: private by default; pub opts in to visibility.

✅ Beginner tab complete — check your understanding

  • I can explain class vs instance in plain English without code
  • I know what a constructor is and what it does (initialises the object's attributes)
  • I know the difference between an instance attribute (belongs to one object) and a class attribute (shared by all instances)

Continue to Objects →

🔵 Intermediate

Access control, dunder methods, and dataclasses

What you'll learn: Access control (public/private/protected) and how Python's convention-based approach differs from Java's enforced access. Dunder methods for operator overloading. Dataclasses as the modern shorthand.

How to read this tab: Python has no enforced private — _ is a convention, __ triggers name mangling but isn't truly private. Understanding this makes you read Python code correctly.

⏱ 20 min 📄 3 sections 🔶 Prerequisite: Beginner tab

Class attributes are shared — this surprises everyone once. If a class attribute is mutable (a list or dict), all instances share the same object. Mutating it in one instance affects all instances. Best practice: only use class attributes for constants and counters that should truly be shared. Put data that belongs to each instance in __init__ as instance attributes.

Class attributes vs. instance attributes

Class attributes are shared across all instances. Instance attributes are private to one object. A class attribute can be read through an instance, but assigning to it through an instance creates a new instance attribute that shadows the class attribute.

Pythonclass_vs_instance.py
class Dog:
    species = "Canis familiaris"   # class attribute — shared

    def __init__(self, name):
        self.name = name           # instance attribute — unique

rex = Dog("Rex")
print(rex.species)         # "Canis familiaris" — reads class attr
rex.species = "Wolf"       # creates INSTANCE attribute on rex
print(rex.species)         # "Wolf" — instance shadows class
print(Dog.species)         # "Canis familiaris" — class unchanged

Dunder methods hook your class into Python's built-in operations. __str__ is called by print() and str(). __len__ is called by len(). __eq__ is called by ==. __lt__ is called by <. __iter__ and __next__ make your object iterable. Implementing these makes custom objects feel native — they work with all Python's built-in functions and idioms.

Dunder (magic) methods

Python classes support operator overloading and protocol integration via double-underscore methods: __str__ (str()), __len__ (len()), __add__ (+), __eq__ (==), __iter__/__next__ (for loops), __enter__/__exit__ (with statements).

💡

@dataclass is the modern way to write data-holding classes. It auto-generates __init__, __repr__, and __eq__ from your field annotations. @dataclass class Point: x: float; y: float gives you a fully functional class in 3 lines. With frozen=True it's immutable and hashable. With order=True it automatically generates comparison operators.

Dataclasses

Python 3.7 @dataclass (PEP 557) auto-generates __init__, __repr__, __eq__ from annotated fields. Kotlin data class. Java 16 record. All eliminate boilerplate for data-holding classes.

Commonly confused
Class attributes are shared; instance attributes are not. Assigning to a class attribute via an instance creates a new instance attribute — the class attribute is untouched. This surprises many Python newcomers.
self is a naming convention, not special syntax. The first parameter of an instance method receives the instance automatically. You can name it anything — self is the Python-wide convention, not a keyword.
A class is not a type in all languages. Python unifies types and classes. Java has primitive types (int, boolean) that are not classes. Go has no classes — only struct types with methods defined separately.
How this connects
Enables
Confused with

✅ Intermediate tab complete — check your understanding

  • I know Python doesn't enforce private access — _ is a convention meaning "please don't use this directly"
  • I know __ (double underscore) triggers name mangling, not true private access
  • I can write a @dataclass to avoid boilerplate __init__, __repr__, __eq__

Continue to Objects →

🔴 Expert

Metaclasses, MRO, and class creation

What you'll learn: Metaclasses — the classes whose instances are classes. The Method Resolution Order (MRO) — how Python resolves method calls in multiple inheritance. The full class creation process.

How to read this tab: Read after Stage 2 when you encounter frameworks like Django's ORM or Python's ABCs and want to understand how they work internally.

⏱ 20 min 📄 2 sections 🔶 Prerequisite: After Stage 2

Metaclasses and class creation

In Python, a class is itself an object — an instance of its metaclass, type by default. Executing a class statement calls type(name, bases, namespace). Custom metaclasses (subclasses of type) intercept class creation — used by Django's ORM to translate class-level field definitions into database schema metadata.

Method resolution order (MRO)

Python uses C3 linearisation (Barrett et al., 1996) to compute the MRO — the order in which base classes are searched during method lookup. Accessible via C.__mro__. Guarantees: a class precedes its parents; left-to-right order among parents is preserved; local precedence rule holds.

Specification reference

Python Language Reference §3.3 — Special method names. PEP 557 — Data Classes. Barrett et al. (1996) C3 linearisation. OOPSLA '96. Java Language Specification §8 — Class Declarations.

✅ Expert tab complete

  • I know that type is the default metaclass — type is what creates classes
  • I can use type.mro() to inspect the method resolution order of any class

Continue to Objects →

Sources

1
Python Software Foundation. Language Reference §3.3. docs.python.org/3/reference/datamodel.html.
2
PEP 557 — Data Classes. peps.python.org/pep-0557/.
3
Barrett, D. et al. (1996). "A Monotonic Superclass Linearisation for Dylan." OOPSLA '96. ACM.
4
Java Language Specification §8 — Class Declarations. Oracle.
Source confidence: High Last verified: Primary source: Python Language Reference §3.3 — Special method names