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:
# 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:
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: TrueIn 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:
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: $1300Notice 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 type | First parameter | Decorator | Access |
|---|---|---|---|
| Instance method | self | None | Instance attributes and methods |
| Class method | cls | @classmethod | Class attributes and methods |
| Static method | None | @staticmethod | No direct access to class or instance |
Here is an example that illustrates these three types:
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.0self 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:
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():
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 doorsHere, 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:
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: 20Remember 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:
# 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!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
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 givenPython 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
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 definedWithout the self. prefix, Python looks for a local or global variable named name, which does not exist.
Confusing class attributes and instance attributes
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) # 2Best 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:
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))) # FalseIn 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
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.
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.
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.
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.