Command Palette

Search for a command to run...

Hectal
PHASE 8Intermediate ~25 min· topic 3 of 12

Topic 8.3

Instance, Class and Static Methods

In one line

Python has three kinds of method. An instance method works on one object (self), a class method works on the class itself (cls) and is the standard way to write alternative constructors, and a static method is a plain function that lives inside a class for organisation.

Think of it like this

A pizza shop. Slicing this particular pizza needs that pizza in your hands: an instance method. Printing the menu is about the shop's recipe book, not one pizza: a class method. A sign reading "our sizes are 20 to 40 cm" does not need any pizza or recipe at all, it is just useful information kept next to the pizzas: a static method.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Instance method
A method whose first parameter is self. It works on one object.
Class method
A method marked @classmethod whose first parameter is cls, the class. It works on the class.
Static method
A method marked @staticmethod that receives neither self nor cls. It is a plain function stored in the class.
Decorator
A line starting with @ above a function that wraps or changes it. Full details in Topic 9.6.
`cls`
By convention, the name of the first parameter of a class method. It is the class the method was called on.
Alternative constructor
A class method that builds an object from some other starting data, such as text or a dictionary.
Bound method
A method looked up on an object. It remembers the object and passes it as self when called.
Factory
A function or method whose job is to create and return objects.

Step by step

01The three kinds side by side

The class below has one of each. Read the first parameter of each: self, cls, and nothing. That first parameter tells you what the method can touch.

main.pywhole filepython
class Pizza:
    def describe(self):           # needs one pizza
        ...

    @classmethod
    def margherita(cls):          # needs the class
        ...

    @staticmethod
    def is_valid_size(size):      # needs neither
        ...

02Instance methods use self

describe reads self.size and self.toppings. Without an object there is nothing to describe, so calling Pizza.describe() with no argument is an error: Python complains that self is missing.

03Class methods build objects

from_text takes text like "25:corn,onion", splits it and calls cls(size, toppings). You get a finished Pizza without anyone remembering the order of __init__ arguments.

A class can have as many of these as it needs, each with a clear name: from_text, from_dict, margherita. This is far clearer than one __init__ with many optional arguments.

main.pywhole filepython
    @classmethod
    def from_text(cls, text):
        size, toppings = text.split(":")
        return cls(int(size), toppings.split(","))

04cls follows inheritance

When a subclass calls an inherited class method, cls is the subclass. That is why return cls(...) is right and return Message(...) is wrong. The first example in the code section shows LoudMessage.from_dict(raw) returning a LoudMessage with the louder behaviour built in.

cls follows inheritancediagram
Rendering diagram…

05Static methods are plain functions

is_valid_size uses neither the object nor the class, so it is a static method. It can be called on the class or an instance with the same result.

Ask yourself: if this were a plain function in the module, would anything be worse? Often not. Choose a static method when keeping the function next to the class helps readers find it.

06What binding looks like

p1.describe is a bound method. Its __self__ is p1. Pizza.from_text.__self__ is the class Pizza. Pizza.describe(p1) calls the plain function stored on the class and passes the object yourself. (Python 3 has no separate "unbound method" type: looking up a method on the class just returns the function.) It gives the same answer as p1.describe().

Seeing these small facts removes the mystery: nothing magic happens, Python just supplies the first argument for you.

07Choosing between them

Ask what the method needs. One object's data: instance method. Only the class (to build objects or read class attributes): class method. Nothing: static method or a plain function. When in doubt, start with an instance method.

Try it yourself

  1. 1

    Add a constructor

    In the first example add @classmethod def pepperoni(cls): return cls(35, ["pepperoni"]) and print Pizza.pepperoni().describe(). Predict the output first.

  2. 2

    Subclass it

    Add class BigPizza(Pizza): pass and call BigPizza.margherita(). Print type(...).__name__. It is BigPizza, because cls follows the subclass. Now change margherita to return Pizza(30, ...) and run again to see what the hard-coded name loses.

  3. 3

    Call it three ways

    Call is_valid_size(25) on the class and on an object. Same answer. Then try calling describe() on the class with no argument and read the error.

Code & diagrams

Three kinds of method on a Pizza New tab

Add a new alternative constructor, such as pepperoni(), that returns a 35 cm pizza with pepperoni.

Sign in to run this example in your browser.

Expected output

30cm pizza with ham
25cm pizza with corn, onion
30cm pizza with tomato, mozzarella
False True
method
True
True
True
Alternative constructors for chat messages New tab

AI code constantly turns dictionaries from JSON into objects. Note that LoudMessage.from_dict returns a LoudMessage because it uses cls. The line super().__init__(...) in LoudMessage runs the parent class's setup; Topic 8.5 teaches it.

Sign in to run this example in your browser.

Expected output

Message('user', 'Hello')
Message('assistant', 'Hi there')
LoudMessage('assistant', 'HI THERE')
LoudMessage
{"role": "user", "content": "Hello"}
Which first parameter, which receiverdiagram
Rendering diagram…

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Call an instance method on the class with no object

Write Pizza.describe() instead of Pizza.describe(p1) or p1.describe().

main.pywhole filepython
class Pizza:
    @classmethod
    def margherita(cls):
        return cls()

    def describe(self):
        return "pizza"


print(Pizza.describe())
terminal
$ python main.py
── what you'll see ──
Traceback (most recent call last):
File "main.py", line 10, in <module>
print(Pizza.describe())
~~~~~~~~~~~~~~^^
TypeError: Pizza.describe() missing 1 required positional argument: 'self'

Myth vs fact

Myth

Static methods are the same as plain functions, so they are always the right choice for helpers.

Fact

They behave like plain functions, but they travel with the class and can be overridden in subclasses. A module-level function is often simpler. Use a static method when the class is the natural home for it.

Myth

A class method is just a way to change class attributes.

Fact

That is one use. Its most common job is building objects (alternative constructors), because it receives the class and can call it.

Myth

cls and self are keywords.

Fact

Both are only naming conventions for the first parameter. The @classmethod decorator decides what is passed in, not the name.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    classmethod, staticmethod and plain functions are all descriptors: objects with a __get__ method that Python calls during attribute lookup. A function's __get__ returns a bound method, a classmethod's returns a method bound to the class, and staticmethod's returns the raw function. This is the whole mechanism.

  • ▸

    Alternative constructors are better than overloading __init__ with many optional arguments or type checks. Python has no method overloading, so from_text, from_dict and from_file communicate intent and keep each signature honest.

  • ▸

    Pydantic models use exactly this pattern: Model.model_validate(data) is a class method that parses raw input, often an LLM's JSON, into a checked object. The standard library has the same idea in datetime.fromisoformat, dict.fromkeys and int.from_bytes.

  • ▸

    Class methods should not be used to mutate global state through class attributes if a plain object would do. Shared mutable state on classes is hard to test and surprising in subclasses.

  • ▸

    Version note: Python 3.9 let you stack @classmethod on top of @property to make class-level properties. That was deprecated in 3.11 and removed in 3.13, so code that relies on it breaks on current Python. Use a plain class method, or a custom descriptor, instead.

Remember this

  1. 1

    An instance method is the normal kind. Its first parameter is self, the object it was called on (Topic 8.1), so it can read and change that object's attributes. Use it whenever the behaviour depends on one object's data.

  2. 2

    A class method is marked with the decorator @classmethod. A decorator is a line starting with @ placed above a function that changes how the function behaves (Topic 9.6 explains how they work). A class method receives the class as its first parameter, named cls by convention, instead of an object. It works the same whether you call it on the class or on an instance.

  3. 3

    The best use of class methods is alternative constructors: extra ways to build an object. Python has only one __init__, so Pizza.from_text("25:corn,onion") or Message.from_dict(data) give you named ways to build objects from other shapes of data. They end with return cls(...).

  4. 4

    Writing cls(...) instead of the class name matters. If a subclass inherits the class method, cls is the subclass, so LoudMessage.from_dict(raw) returns a LoudMessage. Hard-coding Message(...) would always return a Message and quietly break inheritance.

  5. 5

    A static method is marked @staticmethod and receives neither self nor cls. It is an ordinary function that happens to be stored inside the class because it belongs to the topic, such as Pizza.is_valid_size(size). If a function never uses the class or an object, a plain module-level function is often just as good.

  6. 6

    Under the hood, looking up an instance method on an object gives a bound method: a small object that remembers the instance and passes it as the first argument when called. A class method is bound to the class. A static method is returned unchanged. That is the entire difference.

Explain it without notes

01

What is the difference between an instance method, a class method and a static method?

02

Why write return cls(...) in a class method instead of return ClassName(...)?

03

What is an alternative constructor, and why does Python use class methods for them?

04

What is a bound method?

Practice

01

Write a class Temperature with attribute celsius, an instance method to_fahrenheit(), a class method from_fahrenheit(f) that builds one, and a static method is_freezing(c) that is true when c <= 0.

02

Write a class User with name and email, and a class method from_dict(data). Build a user from {"name": "Asha", "email": "a@x.com"} and print both attributes.

03

Make a subclass Admin(User) and show that Admin.from_dict(...) returns an Admin. Print the class name.

Trade-offs

  • ↔

    Class methods give clear, inheritable constructors, at the cost of one more concept. One __init__ plus named class methods is usually clearer than one __init__ that guesses what you passed.

  • ↔

    Static methods keep related helpers next to the class, but add little over a plain function. They help discoverability; they do not add power. Prefer a module-level function when the class is not a natural home.

Done when you can

  • Done when you can say what self and cls receive and when to use each.

  • Done when you can write an alternative constructor with @classmethod and return cls(...).

  • Done when you can explain why cls keeps subclasses working.

  • Done when you can decide whether a helper should be an instance, class or static method.

  • Done when you can explain what a bound method is.