The python self keyword: How It Works
Learn how the python self keyword works in instance methods, why it's explicit, and how to use it correctly with practical code examples.
The python self keyword is not a keyword in the strict sense; it's a conventional name for the first parameter of an instance method. When you call obj.method(), Python automatically passes the instance as the first argument, which you name self by convention. This explicit design choice makes the language's object model more transparent than languages that hide the receiver, but it also trips up developers who expect self to behave like a hidden reference.
What self Actually Is in Python
In Python, an instance method is just a function defined inside a class. When you access it through an instance, Python binds the instance to the method's first parameter. The name self is not enforced by the interpreter; it is a strong convention that every Python developer follows. The following two classes are functionally identical:
class Person: def greet(self): return f"Hello, {self.name}" class PersonAlternative: def greet(instance): return f"Hello, {instance.name}"
The second class works, but it violates the community standard. Tools like linters and static analyzers expect self, and code reviews will reject instance in most projects. The real point is that the first parameter always receives the instance, regardless of its name.
How Instance Methods Use self
Instance methods need self to access other attributes and methods on the same object. Without it, a method cannot read or modify instance state. Consider a simple bank account:
class BankAccount: def __init__(self, initial_balance): self.balance = initial_balance def deposit(self, amount): self.balance += amount return self.balance def withdraw(self, amount): if amount > self.balance: raise ValueError("Insufficient funds") self.balance -= amount return self.balance
When you call account.deposit(100), Python translates it to BankAccount.deposit(account, 100). The self parameter is how deposit knows which account to modify. If you tried to define deposit without self, the call would fail with a TypeError because the first argument would be interpreted as amount.
self in __init__ and Attribute Initialization
The __init__ method is where instance attributes are typically created. self is the bridge between the constructor and the new object. Without self, you cannot attach data to the instance:
class Point: def __init__(self, x, y): self.x = x self.y = y def distance_from_origin(self): return (self.x ** 2 + self.y ** 2) ** 0.5
Here, self.x = x creates an attribute named x on the current instance. Every subsequent method can access it through self. This pattern is so common that you will rarely see an __init__ without self assignments. The same applies to any method that needs to initialize state lazily or update an existing attribute.
Class Methods and Static Methods: When self Is Not Used
Not every method defined inside a class needs self. The @classmethod decorator replaces self with cls, which points to the class itself, and @staticmethod receives no automatic first argument at all. This distinction matters when you are deciding how a method should be called.
class Config: defaults = {"timeout": 30} @classmethod def from_env(cls, env): config = cls() config.timeout = env.get("TIMEOUT", cls.defaults["timeout"]) return config @staticmethod def validate_timeout(value): return value > 0
A class method is useful for alternative constructors or operations that need the class object. A static method is for logic that belongs to the class conceptually but does not depend on instance or class state. In both cases, self is absent because there is no instance to bind.
The Naming Convention: Why self Is Not a Keyword
Python reserves keywords like def, class, and return, but self is not one of them. You can assign to a variable named self outside a class, and you can even use a different name for the first parameter. The language does not care. However, the convention is so entrenched that deviating from it harms readability and tooling. Most IDEs and linters will flag a method whose first parameter is not named self (or cls for class methods). The Python documentation and PEP 8 explicitly recommend self for instance methods and cls for class methods.
This convention also makes the explicit nature of Python's object model clear. When you see def method(self, arg), you immediately know that the method operates on an instance. There is no hidden this pointer to reason about.
Common Mistakes with self
One of the most frequent errors is forgetting to include self as the first parameter. The result is a confusing TypeError when the method is called:
class Broken: def add(self, a, b): # correct return a + b def subtract(a, b): # missing self return a - b
Calling obj.subtract(5, 3) raises TypeError: subtract() takes 2 positional arguments but 3 were given because Python passes the instance as the first argument. Another common mistake is calling an instance method on the class directly instead of on an instance. Broken.subtract(5, 3) works because no instance is passed, but it is not the intended usage. Always remember that the instance is implicitly prepended to the argument list for instance methods.
How self Affects Method Binding and Mutability
Because self gives you a reference to the instance, methods can mutate the object's state. This is the core of object-oriented programming in Python. The same self reference also allows methods to call other methods on the same instance:
class Stack: def __init__(self): self.items = [] def push(self, item): self.items.append(item) def pop(self): if not self.items: raise IndexError("pop from empty stack") return self.items.pop() def size(self): return len(self.items)
Here, push and pop modify the list stored in self.items. If you pass self to another method, you are passing the same object, so any changes are visible everywhere. This is different from passing a copy of the instance. Understanding that self is just a reference helps you reason about aliasing and shared state.
Using self in Inheritance and Super Calls
In inheritance, self always refers to the instance of the actual class that was created, even when a method is defined in a parent class. This is what enables polymorphic behavior. When you call super().method(), the parent method receives the same self instance, so it can access attributes defined in the child class.
class Animal: def __init__(self, name): self.name = name def speak(self): raise NotImplementedError class Dog(Animal): def __init__(self, name, breed): super().__init__(name) self.breed = breed def speak(self): return f"{self.name} says woof"
When you create a Dog instance, the __init__ method of Dog calls super().__init__(name), which executes Animal.__init__ with the same self. That method sets self.name, and then Dog.__init__ adds self.breed. The speak method in Dog overrides the parent's version, but it still uses self.name to access the attribute set by the parent. This chain works because self is the same object throughout the entire inheritance hierarchy.