Back to Blog
Python

Python self vs cls: Instance and Class Methods

python self vs cls: Understand the difference between self and cls in Python: when instance methods receive the instance and class methods receive the class, with prac...

Pythonclass methodsinstance methodsOOPselfcls
Diagram contrasting self and cls parameter binding in Python methods, showing instance versus class targets.

When you define a method inside a Python class, the first parameter name you choose signals how Python binds that method. self binds the method to an instance; cls binds it to the class itself. This distinction between python self vs cls determines what the method can access and how it behaves under inheritance.

What self and cls Actually Bind To

The first parameter of a method defined inside a class determines how the method is bound when accessed through an instance or the class itself. self receives the instance on which the method was called. cls receives the class object, not an instance.

class Example: def instance_method(self): return self @classmethod def class_method(cls): return cls

When you call Example().instance_method(), Python passes the instance as self. When you call Example.class_method(), Python passes the Example class itself as cls. The names self and cls are conventions, not keywords; Python only cares about position. But the convention is so universal that deviating from it makes code harder to read.

Instance Methods: Working with self

An instance method receives the instance as its first argument. Through self, the method can read and modify instance attributes, call other instance methods, and access the instance's __dict__.

class Account: def __init__(self, owner: str, balance: float): self.owner = owner self.balance = balance def deposit(self, amount: float) -> None: self.balance += amount def describe(self) -> str: return f"{self.owner}: {self.balance}"

The deposit method modifies self.balance, which is per-instance state. Two different Account objects hold independent balance values. If you called deposit without an instance, Python would require you to pass one explicitly: Account.deposit(account, 50.0).

Class Methods: Working with cls

A class method receives the class itself as its first argument. It cannot access instance attributes because no instance exists. It can read and modify class attributes, and it can call other class methods.

class Config: defaults = {"timeout": 30, "retries": 3} @classmethod def get_default(cls, key: str): return cls.defaults.get(key) @classmethod def with_timeout(cls, timeout: int): config = cls() config.timeout = timeout return config

The get_default method reads from cls.defaults, which is shared across all instances. The with_timeout method is an alternative constructor: it creates an instance through cls() and configures it. Using cls() instead of Config() matters when a subclass inherits this method, because cls will be the subclass, not Config.

How Python Performs the Binding

The binding behavior comes from the descriptor protocol. When you access instance.method, Python finds the function on the class and calls its __get__ method. For a plain function, the binding produces a bound method where the instance is passed as the first argument. For a class method, the binding passes the class instead.

account = Account("Ada", 100.0) bound = account.describe print(bound()) # "Ada: 100.0"

The function object describe itself never receives the instance automatically; the binding step does that work. This is why accessing the method on the class rather than the instance requires an explicit argument: Account.describe(account).

The Role of @staticmethod

A static method receives neither self nor cls. It behaves like a plain function that happens to live inside a class namespace. Use it when the method does not need class or instance state.

class MathUtils: @staticmethod def clamp(value: float, low: float, high: float) -> float: return max(low, min(value, high))

Static methods are still inherited, but the subclass does not replace the class argument. If you need polymorphic behavior—where the method should know which subclass called it—use a class method instead.

Choosing Between self and cls

The decision depends on what the method needs to access:

NeedParameterExample
Read or modify instance stateselfUpdate a field, call another instance method
Read or modify class stateclsChange a default, track instance count
Create an instance with custom setupclsAlternative constructor
Neither class nor instance statenoneUtility function inside a class

Use self when the method's behavior depends on the specific instance's data. Use cls when the behavior is the same for the whole class, especially for factory methods or operations on class-level attributes. If a method touches neither, consider whether it belongs in the class at all; a module-level function is often clearer.

Inheritance and cls in Practice

The most important difference between self and cls appears under inheritance. A class method defined on a base class receives the subclass when called through the subclass.

class Base: @classmethod def create(cls): return cls() class Child(Base): pass child = Child.create() print(type(child)) # <class '__main__.Child'>

Here cls is Child, not Base. If create had used Base() directly, it would return a Base instance even when called on Child. This is why alternative constructors should use cls rather than the hardcoded class name.

The same polymorphism applies to instance methods through self: self is always the actual instance, even when the method is defined on a parent class.

Common Mistakes and Edge Cases

One frequent error is defining a class method without the @classmethod decorator and expecting cls to be passed. Without the decorator, the method is an instance method, and the first argument receives the instance instead of the class.

Another mistake is using cls to access instance attributes. Since no instance exists, cls.attr looks up a class attribute, and AttributeError is raised if it does not exist.

Naming the first parameter something other than self or cls works syntactically but breaks the convention. Tools, linters, and other developers rely on those names to understand what the method expects.

Maintainability Implications

Class methods give you a clean way to expose alternative constructors and class-level operations without requiring an instance. They also make code easier to test in isolation because they do not depend on instance setup. Instance methods, on the other hand, are the right choice when the method's output depends on the instance's state.

A common design pattern is to use a class method as a factory and an instance method for behavior:

class Order: def __init__(self, items: list[str]): self.items = items @classmethod def from_line(cls, line: str) -> "Order": return cls(line.split(",")) def total(self) -> float: return sum(item.price for item in self.items)

The factory from_line uses cls so subclasses return instances of the subclass. The total method uses self because it needs the instance's items. Keeping this separation makes the class's contract explicit and avoids surprising behavior when the class is subclassed.

python self vs cls: Practical Usage and Code Examples | RYUSLOG DEV