Instance, Class, and Static Methods

RoadmapsPython

Overview

Python classes support three distinct types of methods based on what state they need to access. The key mental model is to look at the first parameter to determine the method's scope. - Instance methods: The default method type. They operate on specific object instances and take `self` as their first argument. - Class methods: Decorated with `@classmethod`. They operate on the class itself by taking `cls` as their first argument. They return a new instance when used as alternative constructors. - Static methods: Decorated with `@staticmethod`. They act like regular functions that take neither `self` nor `cls`. They live inside a class's namespace purely for organizational purposes. A common edge case involves modifying class state through an instance method. class Counter: count = 0 def increment(self): # Trap: Creates an instance attribute, shadowing the class attribute! self.count += 1 Writing `self.count += 1` reads the class attribute `count`, adds `1`, and creates a new instance attribute `count` instead of updating `Counter.count` (covered fully in class-attributes). To modify class state safely, use a `@classmethod` and update `cls.count`. Think of `self` as a single employee, `cls` as the department manager, and `@staticmethod` as the office calculator.

They allow you to logically group related behaviors. Instead of having a bunch of disconnected functions, you can bundle utility functions (`@staticmethod`) and alternative constructors (`@classmethod`) directly onto the class they relate to.

Where used: Alternative constructors like `dict.fromkeys()`, Utility functions attached to models in `Pydantic`

Why learn this

Code walkthrough

class Config:
    prefix = 'app_'

    @classmethod
    def get_key(cls, name):
        return cls.prefix + name

print(Config.get_key('port'))

Focus: return cls.prefix + name

Aha moment

class Parent:
    @classmethod
    def create(cls):
        return cls()

class Child(Parent):
    pass

print(type(Child.create()).__name__)

Prediction: What does this print? (`Parent` or `Child`?)

Common guess: `Parent`

Because it's a `@classmethod`, `cls` becomes the class it was called on (`Child`), not the class where it was defined (`Parent`). This makes class methods perfect for inheritable constructors.

Common mistakes

Glossary

constructors
Special methods used to create and initialize a new instance of a class, e.g. `def __init__(self, name): self.name = name`.
namespace
A conceptual space or container that holds a set of names, ensuring that all names are unique and don't conflict with each other, e.g. `Config.clean_key` lives in the `Config` namespace.

Recall questions

Understanding checks

What does this code output?

2

The `@classmethod` takes `cls` (the class itself) as the first argument. Modifying `cls.count` updates the class-level attribute `count` directly. Calling it twice increments the class attribute to `2`.

A developer writes a utility function inside a class that doesn't use `self` or `cls`, but they forget to add the `@staticmethod` decorator. Why will this fail when called from an instance?

Without `@staticmethod`, Python assumes it is an instance method and automatically passes the instance (`self`) as the first argument, causing a `TypeError` for receiving too many arguments.

Python's default behavior for methods inside a class is to pass the instance as the first argument. `@staticmethod` explicitly disables this behavior.

Practice tasks

Alternative Constructor

Modify the `User` class to include a class method named `from_csv_string`. It should take a `csv_string` (e.g., 'alice,alice@example.com'), split it by the comma, and return a new instance of the class.

Challenge

Config Parser

Write a `ConfigParser` class. It should have a static method `clean_key(key)` that strips whitespace and lowercases a string. It should also have a class method `from_dict(data)` that takes a dictionary, cleans all its keys using `clean_key`, and returns a new `ConfigParser` instance where the cleaned data is stored in `self.config`. Its `__init__` should just take and store the config dictionary.

Continue learning

Previous: Defining functions and return values

Previous: Function Decorators

Return to Python Roadmap