AppDividend
Latest Code Tutorials

The Definitive Guide on Python Self

0

If you started a Python programming a long time ago, then you came across the word “self.” The self comes across you whenever you are working on Object-oriented programming in Python. You have seen in method definitions and variable initialization. But before dive into self, let’s understand what class and instance variables are and what is class and instance methods.

Class variables in Python

Class variables are the type of variables that are being shared with all the instances of that class. That means if you are accessing the class variables from the instances, then the value will be the same. Class variables are defined just after the class definition and outside of any methods or functions.

class MyClass:
    var_1 = "COVID_19"
    var_2 = 100
    var_3 = False

In this code, var_1, var_2, and var_3 are class variables.

Instance variables in Python

Instance variables are types of variables in which all instances keep for themselves (i.e., a particular object owns its instance variables). So the values of instance variables differ from instance to instance.

class MyClass:
    var_1 = "COVID_19"
    var_2 = 100
    var_3 = False


def __init__(self, param1, param2):
    self.instance_var1 = param1
    # instance_var1 is a instance variable
    
    self.instance_var2 = param2
    # instance_var2 is a instance variable

In this code, the instance variables are instace_var1 and instance_var2.

Unlike the class variables, instance variables should be defined within methods.

class MyClass:
    var_1 = "COVID_19"
    var_2 = 100
    var_3 = False

    def __init__(self, param1, param2, param3):
        self.instance_var1 = param1
        # instance_var1 is a instance variable

        self.instance_var2 = param2
        # instance_var2 is a instance variable

        self.instance_var3 = param3
        # instance_var3 is a instance variable


obj1 = MyClass('Disease', 150, True)
print('The class variable is: ', obj1.var_1)
print('The instance variable is: ', obj1.instance_var1)

obj2 = MyClass('Rasparatory', 200, False)
print('The class variable is: ', obj2.var_1)
print('The instance variable is: ', obj2.instance_var2)

In this example, we have defined three class variables and three instance variables.

Then we have created two instances called obj1 and obj2.

Now, if you access the class variables with obj1 and obj2, then the values will be the same. In our example, we have access to var_1 and var_2 with different instances, and values are the same as you can see in the output.

But the when we access the instance variables with different instances, the values are different. This is because, at the time of instantiation, we are providing the values via the __init__() method, and thus, the values differ from instance to instance.

Class methods in Python

Class methods are useful to set or get the details (status) of the class. The class method has a particular parameter that should be placed as a first parameter. It is a cls parameter, which represents the class.

class MyClass:
    var_1 = "COVID_19"
    var_2 = 100
    var_3 = False

    @classmethod
    def class_method(cls):
        print("the class method was called")
        return 1


print(MyClass.class_method())
print(MyClass.var_1)
print(MyClass.var_2)
print(MyClass.var_3)

In this example, we have defined the class method using @classmethod decorator. The classmethod here is class_method, which takes cls as a parameter. It is the same as self when creating an instance method.

So, when we define a class method, we pass cls as the first parameter, and when we define an instance method, then we pass the self as the first parameter.

Then we have accessed the classmethod using just class and not object. We have also accessed the class variables using class. So, without even instantiating an object, we can access class methods as follows.

MyClass.class_method()

So, this is how you can define and access class methods in Python.

Instance methods in Python

Okay, so we have seen instance variables, class variables, and class methods. Now, we will see the instance methods.

When defining the instance method, the first parameter of the method should always be self. However, one can name it whatever you want and not necessarily self, but what that argument represents will always be the same. And it’s a better idea for sticking with self as it’s the global convention.

class MyClass:

    def add(self, a, b):
        return a + b


obj = MyClass()
print(obj.add(11, 21))

In this example, we have defined an instance method called add(), and it takes two parameters.

Then we have instantiated an object and call the add() method.

So as you can notice from in the above code, although when defining the instance method, the first parameter is self; when we are calling that method, we do not pass anything for self as arguments.

What is self in Python

The self keyword is used to represent the instance (object) of a given class. In the above case, the obj object has its own a and b attributes. If there was no self argument then that same class couldn’t contain the information for both this object.

However, since the class is a blueprint, the self allows access to the attributes and methods of each object in Python. This enables each object to have its attributes and methods. That is why, even long before creating these objects, we reference the objects as self while defining a class.

Defining self explicitly

Even when we know the use of self, it may seem odd, especially to programmers coming from other programming languages, that self is passed as an argument explicitly every single time we define the instance method.

class MyClass:

    def __init__(self, a, b):
        self.a = a
        self.b = b

    def multi(self):
        return self.a * self.b


obj = MyClass(3, 7)
print('The multiplication of a and b is: ', obj.multi())

If you run the above code, you will get the following output.

The multiplication of a and b is:  21

In the above example, the __init__() defines three parameters, but we just passed two (3 and 7). Similarly, the multi() function requires one, but zero arguments were passed when we are calling obj.multi() function. How come this does not give errors?

What happens under the hood

When we call the instance method with some parameters, the correlative class function is called by placing the method’s object before the first parameter. So, anything like obj.multi(args) becomes Class.multi(obj, args). The calling process is automatic, while the receiving process is not (its explicit).

This is the main reason the first argument of the function in class must be the object itself. Writing this parameter as a self is purely a convention. It is not the keyword and has no particular meaning in Python. We could use other names, but it is highly discouraged. Using names other than self is frowned upon by the most developers and degrades the readability of the code.

By now, you are clear that an object (instance) itself is passed along as the first argument, automatically. The implicit behavior can be avoided while making a static method. We have seen the classmethod earlier in this post.

The explicit self is not unique to Python. That idea was borrowed from Modula-3.

Python __init__()

In Python, the __init__() method is not a constructor. Many candid programmers get confused with it since __init__() gets called when we create the object.

If you inspect closely, then you can see that the first parameter in __init__() is the object itself (object already exists). The function __init__() is called directly after the object is created and is used to initialize it. Technically speaking, a constructor is a method that makes the object itself. In Python, this method is __new__(). A typical signature of this method is the following.

__new__(cls, *args, **kwargs)

When the __new__() method is called, the class itself is passed as a first argument automatically(cls).

Again, like self, cls is just a naming convention. Furthermore, the *args and **kwargs are used to take the arbitrary number of parameters during function calls in Python.
Some meaningful things to remember when implementing the __new__() are:

  1. The __new__() is always called before __init__().
  2. The first argument is a class itself, which is passed implicitly.
  3. Always return the valid object from __new__(). Not mandatory, but its main use is to create and return the object.

See the following code.

class MyClass:

    def __new__(cls, *args, **kwargs):
        print("__NEW__")
        print(cls)
        print(args)
        print(kwargs)

        # create our object and return it
        obj = super().__new__(cls)
        return obj

    def __init__(self, a, b):
        self.a = a
        self.b = b

    def multi(self):
        return self.a * self.b


obj = MyClass(3, 7)
print('The multiplication of a and b is: ', obj.multi())

Output

__NEW__
<class '__main__.MyClass'>
(3, 7)
{}
The multiplication of a and b is:  21

This example illustrates that __new__() is called before __init__(). We can also see that the parameter cls in __new__() is the class itself (Point).

Finally, the object is created by calling the __new__() method on object base class.

In Python, an object is the base class from which all the other classes are derived. In the above code, we have done this using super().

Conclusion

Now you see why you should always use the self as the first parameter of instance methods in Python and what happens behind the scene when we call an instance method. We have seen class and instance variables, class and instance methods, self, why we use self, self name convention.

That is it for the Python self.

Leave A Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.