Python `self` Parameter Explained

Learn how the self parameter works in Python classes, why every instance method needs it, and how Python uses self to connect method calls to the right object.

7 min read

The Python self parameter is the single most important name in object-oriented Python, and it is also one of the most frequently misunderstood. Every instance method you write inside a class must include self as its first parameter, and every call to an instance method through dot notation on an object causes Python to pass that object as the self argument automatically. Despite appearing in nearly every line of object-oriented Python code, self is not a keyword in the language, not a reserved word, and not enforced by the interpreter in any special way. It is a convention, but it is the strongest convention in the entire Python ecosystem, followed by every developer, every library, every framework, every linter, and every code editor. Understanding what self actually is, how Python passes it, and what you can and cannot do with it is essential for writing correct and idiomatic object-oriented Python code.

If you have read the article on instance methods in Python, you already know that instance methods receive self as their first parameter and use it to access the object's attributes and call other methods on the same object. This article goes deeper into the mechanics of self itself: what happens under the hood when Python processes a method call, why the language designers chose to make self explicit rather than implicit, and how self behaves in edge cases like nested function calls, method references, and dynamic attribute access. By the end, the self parameter will no longer feel like boilerplate you type by rote; it will feel like a tool you understand and control.

The design decision to make self explicit is one of the philosophical differences between Python and languages like Java or C++, where the equivalent concept, often called this, is implicit and automatically available inside every method body without being declared in the parameter list. Python's creator has written extensively about this choice, arguing that explicit self makes code easier to read because you can see at a glance whether a name refers to an instance attribute, a local variable, or a global, and because it reinforces the idea that methods are just functions that happen to receive their object as the first argument.

What self actually is: a regular function parameter

The most important thing to understand about self is that it is an ordinary function parameter. There is nothing in the Python interpreter that treats the name self differently from any other parameter name. The interpreter does not check that the first parameter of a method is named self. It does not enforce that self refers to the current instance in any special way. It simply passes the object as the first positional argument, and whatever name you have given to the first parameter in the method definition receives that object. The following two class definitions are functionally identical as far as the Python interpreter is concerned:

pythonpython
class Demo:
    def __init__(self, value):
        self.value = value
 
class AlsoDemo:
    def __init__(the_object, value):
        the_object.value = value

Both classes work correctly. In the second version, the first parameter is named the_object instead of self, and Python passes the new instance as that argument just as it would for any other name. The assignment inside the method body uses the_object.value instead of self.value, and the attribute is stored on the instance exactly as expected. No error occurs, no warning is emitted, and the program behaves identically.

The reason nobody writes code like the second version is that the convention of using self is universal, and violating it makes your code incomprehensible to every other Python developer. Code editors and IDEs recognize self and provide autocomplete suggestions based on the class's attributes when you type self followed by a dot. Linters check that self is used consistently and flag methods that do not use self as potentially misclassified. Documentation generators and type checkers all expect self. The convention has become so deeply embedded in the Python tooling ecosystem that using any other name is not just unconventional; it is actively harmful to the development experience of anyone who works with your code, including your future self.

How Python passes self during a method call

The mechanism that connects a method call on an object to the method definition on the class relies on Python's descriptor protocol and the way functions defined inside a class body are transformed into bound methods. When Python executes a class definition, it encounters the functions defined inside the class body as ordinary function objects. But when those functions are accessed through dot notation on an instance, Python wraps them in a bound method object that stores the instance and the function together. When the bound method is called, it calls the underlying function with the stored instance inserted as the first argument.

Consider a method call broken into its constituent steps. When you write account.deposit(100), Python first looks up the name deposit on the account object. The lookup finds the deposit function in the Account class, but because the access happened through an instance, Python creates a bound method that pairs the account object with the deposit function. When you then call that bound method with the argument 100, Python calls the underlying deposit function with account as the first argument and 100 as the second, matching the method signature where self receives the object and amount receives the numeric value.

This step-by-step breakdown reveals that you can capture a bound method and call it later, and it will still remember which object it belongs to. The following code demonstrates this:

pythonpython
method_ref = account.deposit
method_ref(50)
print(account.balance)

The variable method_ref holds a bound method that is permanently associated with the account object. Calling it with an argument of 50 has exactly the same effect as calling account.deposit(50) directly. The object reference is stored inside the bound method at the moment of access and used every time the method is called, regardless of how much time passes or where the bound method travels through the program.

Accessing attributes and calling methods through self

Inside a method body, self serves as the channel through which the method reads and writes the object's attributes and calls other methods on the same object. Every attribute access that begins with self followed by a dot is reading from or writing to the specific object that the method was called on. Every method call that begins with self followed by a dot and parentheses calls another method on that same object. This consistent use of self for all intra-object communication is what keeps object-oriented code organized: you never need to guess whether a name refers to a local variable, a global, or an attribute, because attributes are always accessed through self.

The use of self for internal method calls enables a design pattern where a high-level method describes a process in terms of lower-level methods, each of which handles one step. A process_order method might call self.validate_items, self.calculate_total, self.apply_discounts, and self.finalize, each defined as a separate method on the same class. The high-level method reads like an outline, and each low-level method is short enough to test and understand in isolation. Without self, this kind of internal delegation would require passing the object explicitly through every function call, which would clutter the code and obscure the relationship between the functions and the data they operate on.

The self parameter also enables methods to modify the object's state over time. A deposit method reads self.balance, adds the deposit amount, and assigns the result back to self.balance. A mark_complete method sets self.status to a new value. These mutations are the reason objects are useful: they maintain state across multiple method calls, and each method call can change that state in a controlled, predictable way. The article on creating and using Python objects covers the patterns for reading and writing attributes in more detail.

Common misconceptions about self

One persistent misconception is that self must be named self because it is somehow special. As demonstrated earlier, the name is purely conventional. Another misconception is that self is automatically available inside nested functions defined within a method. A nested function defined inside a method body does not receive self automatically, because Python only performs the automatic binding for functions defined directly in the class body. If a nested function needs access to the object, it must either receive self as an explicit argument or capture it from the enclosing method's scope through a closure.

A third misconception concerns classes that do not define an initialization method. Python allows classes without an explicit constructor, and you can create instances of such classes. The instances simply have no attributes set up automatically. Any methods defined on the class still receive self when called, but self will reference an object with no attributes unless they were added after creation. The article on Python constructors covers the initialization method in depth and explains the patterns for ensuring every object starts with a predictable set of attributes.

Understanding self at this level of detail prepares you for every subsequent OOP concept in this learning path. Inheritance, where methods defined on a parent class receive instances of a child class as self. Decorators, where functions that modify other functions need to preserve the self parameter correctly. Properties and descriptors, where attribute access on self triggers method calls behind the scenes. Each of these advanced topics builds on the foundation that self is a regular parameter that Python passes automatically, and that understanding how that passing works gives you control over it.

Rune AI

Rune AI

Key Insights

  • The self parameter is the first parameter of every instance method and receives the object the method was called on.
  • self is a convention, not a keyword; you could rename it, but doing so breaks every expectation in the Python ecosystem.
  • Python passes the object as the first argument automatically when you call a method using dot notation on an instance.
  • Inside a method, self provides access to the object's attributes and to other methods on the same object.
  • Explicitness is a Python design value; writing self explicitly distinguishes instance methods from other method types and makes code easier to read.
RunePowered by Rune AI

Frequently Asked Questions

Is self a keyword in Python?

No, self is not a Python keyword. It is a universal convention that every Python developer follows. You could name the first parameter of an instance method anything you want, and Python would still pass the object as that argument. But deviating from self will confuse every Python developer, linter, and IDE that reads your code, so it is treated as a de facto requirement.

Why does Python require me to write self explicitly instead of making it implicit?

Python's design philosophy favors explicitness over implicitness. Requiring self in the method signature makes it clear that the method operates on an instance, distinguishes instance methods from static methods at a glance, and is consistent with Python's treatment of all variable access as explicit. Languages like Java and C++ use an implicit this, but Python's explicit self is a deliberate design choice that the language's creator has defended as making code easier to read and reason about.

What happens if I forget to include self in an instance method definition?

If you define a method without self as the first parameter and then call it on an instance, Python will still pass the instance as the first argument, but your method will not have a parameter to receive it. This causes a TypeError saying the method takes a certain number of arguments but was given one more. The error message will show that an extra argument was passed, which is Python's way of telling you that the instance was passed automatically but your method signature did not account for it.

Conclusion

The self parameter is the mechanism that makes object-oriented programming in Python work. It is not magic, not a keyword, and not an implementation detail to be memorized and ignored. It is a regular function parameter that Python fills in automatically when you call a method on an object, and it gives every method a reference to the specific object it should operate on. Understanding self deeply removes the mystery from method calls and prepares you for every advanced OOP topic that builds on it.