What is self in Python? Complete explanation

Discover the role of self in Python: how it references the current instance of a class, why it is essential, and how to use it correctly.
10 min read
Believemy logo

Definition of self in Python

In Python, self is a naming convention used as the first parameter of instance methods within a class. It refers to the current instance of the object on which the method is called. Thanks to self, each object can access its own attributes and methods, which is at the very core of object-oriented programming.

Unlike other languages such as Java or C++ where the this keyword is implicit, Python requires that self be declared explicitly as the first parameter of every instance method. This is one of Python's peculiarities that often surprises beginners, but it makes the code more readable and more explicit — true to the "Explicit is better than implicit" philosophy of the Zen of Python.

If you want to fully master object-oriented programming in Python, including the use of self, we recommend following our complete Python course which covers these concepts in depth.


Why is self necessary?

To understand why self is essential, you first need to understand how Python handles instance methods internally. When you call a method on an object, Python performs an automatic transformation:

PYTHON
# When you write:
my_object.my_method(arg1, arg2)

# Python internally transforms this call into:
MyClass.my_method(my_object, arg1, arg2)

In other words, Python automatically passes the instance (my_object) as the first argument of the method. That is why this first parameter — which we call self by convention — is mandatory: it allows the method to know which object it is working on.

Without self, a method would have no way of distinguishing the attributes of one instance from another. Imagine two objects created from the same class: each has its own data, and it is self that allows Python to differentiate them.


Basic usage of self

Declaring and accessing instance attributes

The most common use of self is in the __init__ method (the constructor) to initialize the object's attributes, and then in other methods to access them:

PYTHON
class User:
    def __init__(self, name, email):
        self.name = name          # Instance attribute
        self.email = email        # Instance attribute
        self.active = True        # Attribute with default value

    def display_profile(self):
        print(f"Name: {self.name}")
        print(f"Email: {self.email}")
        print(f"Active: {self.active}")

# Creating two distinct instances
alice = User("Alice", "alice@example.com")
bob = User("Bob", "bob@example.com")

alice.display_profile()
# Name: Alice
# Email: alice@example.com
# Active: True

bob.display_profile()
# Name: Bob
# Email: bob@example.com
# Active: True

In this example, self.name in the display_profile method refers to the name attribute of the instance on which the method is called. When you call alice.display_profile(), self equals alice. When you call bob.display_profile(), self equals bob.


Calling other methods via self

self is not only used to access attributes. It also allows you to call other methods of the same class from within a method:

PYTHON
class BankAccount:
    def __init__(self, holder, balance=0):
        self.holder = holder
        self.balance = balance
        self.history = []

    def deposit(self, amount):
        self.balance += amount
        self._record_operation("deposit", amount)

    def withdraw(self, amount):
        if amount > self.balance:
            print("Insufficient balance!")
            return
        self.balance -= amount
        self._record_operation("withdrawal", amount)

    def _record_operation(self, op_type, amount):
        self.history.append(f"{op_type}: ${amount}")

    def display_history(self):
        print(f"History for {self.holder}:")
        for operation in self.history:
            print(f"  - {operation}")
        print(f"Current balance: ${self.balance}")

account = BankAccount("Alice", 1000)
account.deposit(500)
account.withdraw(200)
account.display_history()
# History for Alice:
#   - deposit: $500
#   - withdrawal: $200
# Current balance: $1300

Notice how self._record_operation() is called from deposit() and withdraw(). Thanks to self, each method can access the full functionality of the instance.


self and the different types of methods

It is essential to understand that self is only used in instance methods. Python provides three types of methods in a class, and each handles the reference to the object differently:

Method typeFirst parameterDecoratorAccess
Instance methodselfNoneInstance attributes and methods
Class methodcls@classmethodClass attributes and methods
Static methodNone@staticmethodNo direct access to class or instance

Here is an example that illustrates these three types:

PYTHON
class Product:
    vat = 0.20  # Class attribute

    def __init__(self, name, price_excl):
        self.name = name              # Instance attribute
        self.price_excl = price_excl  # Instance attribute

    # Instance method: uses self
    def price_incl(self):
        return self.price_excl * (1 + self.vat)

    # Class method: uses cls, not self
    @classmethod
    def set_vat(cls, new_vat):
        cls.vat = new_vat

    # Static method: neither self nor cls
    @staticmethod
    def is_valid_price(price):
        return price > 0

product = Product("Computer", 1000)
print(product.price_incl())           # 1200.0
print(Product.is_valid_price(-5))     # False

Product.set_vat(0.055)
print(product.price_incl())           # 1055.0


self and inheritance

One of the most powerful aspects of self manifests in the context of inheritance. When a child class inherits from a parent class, self always refers to the actual instance, even when a method from the parent class is being executed:

PYTHON
class Animal:
    def __init__(self, name):
        self.name = name

    def introduce(self):
        print(f"I am {self.name} and I say: {self.sound()}")

    def sound(self):
        return "..."

class Dog(Animal):
    def sound(self):
        return "Woof!"

class Cat(Animal):
    def sound(self):
        return "Meow!"

rex = Dog("Rex")
rex.introduce()
# I am Rex and I say: Woof!

felix = Cat("Felix")
felix.introduce()
# I am Felix and I say: Meow!

In this example, the introduce() method is defined in Animal, but when it calls self.sound(), it is the subclass version that gets called. This is the principle of polymorphism, made possible thanks to self.


Using self with super()

When you override the __init__ method in a child class, it is common to call the parent class constructor with super():

PYTHON
class Vehicle:
    def __init__(self, brand, year):
        self.brand = brand
        self.year = year

    def description(self):
        return f"{self.brand} ({self.year})"

class Car(Vehicle):
    def __init__(self, brand, year, num_doors):
        super().__init__(brand, year)  # Call to parent constructor
        self.num_doors = num_doors     # Specific attribute

    def description(self):
        base = super().description()
        return f"{base} - {self.num_doors} doors"

my_car = Car("Toyota", 2023, 5)
print(my_car.description())
# Toyota (2023) - 5 doors

Here, self remains consistent throughout the inheritance chain. When super().__init__(brand, year) is called, self in the Vehicle constructor still refers to the Car instance.


Difference between self and local variables

A frequent confusion among beginners is the difference between self.variable and a simple local variable. Understanding this distinction is fundamental:

PYTHON
class Example:
    def __init__(self, value):
        self.value = value  # Instance attribute: persists

    def calculate(self):
        result = self.value * 2        # Local variable: temporary
        self.last_calculation = result  # Instance attribute: persists
        return result

    def display(self):
        print(f"Value: {self.value}")
        print(f"Last calculation: {self.last_calculation}")
        # print(result)  # ERROR: result does not exist here!

obj = Example(10)
obj.calculate()
obj.display()
# Value: 10
# Last calculation: 20
Warning

Remember this rule: everything prefixed with self. is stored in the instance and accessible from any method of that object. Variables without self. are local to the method and disappear when the method finishes executing.


self is just a convention

An important point to understand: self is not a reserved keyword in Python. It is a naming convention universally adopted by the community. Technically, you could use any other name:

PYTHON
# This works... but NEVER do it!
class BadIdea:
    def __init__(this, name):
        this.name = name

    def greet(this):
        print(f"Hello, {this.name}!")

obj = BadIdea("Alice")
obj.greet()  # Hello, Alice!
Warning

Although technically valid, using a name other than self is considered very bad practice. PEP 8 (Python's official style guide) explicitly recommends using self. Not following this convention makes your code confusing for other developers.


Common mistakes with self

Forgetting self in the method signature

PYTHON
class Error1:
    def __init__(self, name):
        self.name = name

    def greet():  # Forgot self!
        print(f"Hello, {self.name}")

obj = Error1("Alice")
obj.greet()
# TypeError: greet() takes 0 positional arguments but 1 was given

Python tries to pass the instance as the first argument, but the method does not accept any parameters, hence the error.


Forgetting self. when accessing an attribute

PYTHON
class Error2:
    def __init__(self, name):
        self.name = name

    def greet(self):
        print(f"Hello, {name}")  # Forgot self.!

obj = Error2("Alice")
obj.greet()
# NameError: name 'name' is not defined

Without the self. prefix, Python looks for a local or global variable named name, which does not exist.


Confusing class attributes and instance attributes

PYTHON
class Error3:
    counter = 0  # Class attribute

    def __init__(self, name):
        self.name = name
        self.counter += 1  # Creates an instance attribute, does NOT modify the class attribute!

a = Error3("Alice")
b = Error3("Bob")
print(a.counter)          # 1 (instance attribute of a)
print(b.counter)          # 1 (instance attribute of b)
print(Error3.counter)     # 0 (class attribute unchanged!)

# Correct solution:
class Correct:
    counter = 0

    def __init__(self, name):
        self.name = name
        Correct.counter += 1  # Modifies the class attribute directly

c = Correct("Alice")
d = Correct("Bob")
print(Correct.counter)  # 2


Best practices with self

Here are the main best practices to follow when working with self in Python:

  • Always use the name self: never use another name, it is Python's universal convention.
  • Initialize all attributes in __init__: even if you assign a default value like None, declare all attributes in the constructor for better readability.
  • Do not overload self: avoid storing too much data in the instance. If your class has dozens of attributes, it is a sign it should be split into several classes.
  • Use dataclass when appropriate: for classes that primarily serve to store data, dataclasses reduce the repetitive code related to self.
  • Prefix private methods with an underscore: use self._private_method() to signal that a method is intended for internal use.
  • Document attributes with docstring: add clear documentation to describe the role of each instance attribute.


Advanced usage: self with magic methods

Python makes extensive use of self in special methods (also called "dunder methods" for double underscore). These methods allow you to customize the behavior of your objects:

PYTHON
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

    def __len__(self):
        return int((self.x ** 2 + self.y ** 2) ** 0.5)

    def __bool__(self):
        return self.x != 0 or self.y != 0

v1 = Vector(3, 4)
v2 = Vector(1, 2)
v3 = v1 + v2

print(v3)            # Vector(4, 6)
print(v1 == v2)      # False
print(len(v1))       # 5
print(bool(Vector(0, 0)))  # False

In each of these methods, self refers to the instance on which the operation is performed. For example, in v1 + v2, self equals v1 and other equals v2.


Frequently asked questions

Question

Can you name the first parameter something other than self?

Yes, technically it is possible because self is not a reserved keyword in Python. However, it is an extremely strong convention respected by the entire community and recommended by PEP 8. Using a different name would make your code difficult to read and maintain. Never do it in professional or shared code.


Question

What is the difference between self and cls?

self refers to a specific instance of a class and is used in instance methods. cls refers to the class itself and is used in class methods decorated with @classmethod. With self, you access instance attributes; with cls, you access class attributes.


Question

Why doesn't Python make self implicit like this in other languages?

This is a Python design choice that follows the philosophy "Explicit is better than implicit." Making self explicit makes the code more readable and reduces ambiguities. You immediately know whether you are accessing an instance attribute or a local variable. It is also more consistent with Python's internal workings where methods are just functions in a class namespace.


Question

How can I learn to use self and OOP in Python properly?

The best way to learn to use self correctly is to practice object-oriented programming with real projects. We recommend following our dedicated Python course which will guide you step by step through mastering classes, self, inheritance, and all advanced OOP concepts.

Related terms

Discover our python glossary

Browse the terms and definitions most commonly used in development with Python.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.