Advanced Method Constructs in Python

Category Data Engineering

The idea of method or function definition is probably language and domain agnostic, i.e., more of a rational way of writing reusable codes and a clever way of implementing DRY (Don’t Repeat Yourself)

A method definition in Python is as easy as in any other language. Functional block starts with a def keyword, followed by a function name and parentheses.

def hello_world():
"""Print Hello World"""
print "Hello World"
>>>hello_world()
"Hello World"

So, how does a method work in Python? We will first look into the instance methods. Let’s start with a familiar example.

class Animal:
"""Animal class"""
def __init__(self, name):
"""Initialize animal"""
self.name = name
def animal_name(self):
"""Return animal name"""
return "This is a {}".format(self.name)

Here, we have a class called Animal with two methods __init__ and animal_name, where former is a special method and later is a plain pythonic instance method declaration. It can be seen that we have passed ‘self’ keyword in both these methods. What does this mean? Let us try to understand it with an example.

>>> Animal.animal_name
<unbound method Animal.animal_name>
>>>Animal.animal_name()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Animal.animal_name()
TypeError: unbound method animal_name() must be called with Animal instance as first argument (got nothing instead)

The keyword ‘self’ is just a naming convention (other words can also be used). By providing ‘self’ as the first argument we are specifying that the methods within the class Animal are bound to the class instance, i.e, the first argument passed is itself the instance of a class. The below example will make it clearer.

>>>animal = Animal('dog')
>>>animal.animal_name
<bound method Animal.animal_name of <__main__.Animal instance at 0x0216F9E0>>
>>>animal.animal_name()
'This is a dog'

In this case, we create an instance of class Animal which is passed as the first argument to the method ‘animal_name’ or more precisely speaking Bound method which is same as doing:

>>>Animal.animal_name(Animal('cat'))
'This is a cat'

Bound and Unbound methods

In simple terms, a bound method is the one which is dependent on the instance of the class as the first argument. That’s why they are called instance methods and in our example, animal_name is an instance methodStaticmethods are examples of unbound methods. Speaking in terms of our previous example we can find an analogy, any animal_name is first bound to Animal first and then supposed to display its characteristics.

Staticmethod

In simpler terms, staticmethods are those methods which are not bound to the class instance and deal only with the arguments it takes. To declare a staticmethod we just have to add ‘@staticmethod’ decorator over the method definition as shown in the next example.

class Animal:
"""Animal class"""
def __init__(self, name):
"""Initialize Animal"""
self.name = name
def animal_name(self):
"""Return animal name"""
return "This is a {}".format(self.name)
@staticmethod
def message():
"""Return message"""
return "Love animals!"

We can see in the example that we have not added ‘self’ keyword in the message which suggests that it is not related to the class or its instance. Since it is not related to class anyway, it is not mandatory to call staticmethods with instance of class. Following example will give a clear idea.

>>>Animal.message()
"Love animals!"
>>>a = Animal('dog')
"Love animals!"
>>>Animal.message
<function __main__.message>

Hence, we see that staticmethods are not bound to the class. Also, the analogy with the example is that the message (“Love animals”) is more of a generic type and not related to the class, so staticmethods are the ones which do not relate to class anyway. By using staticmethods, we are essentially trying to demonstrate that the method depends on arguments and does not touch the instance (i.e. ‘self’). We can see the implementation in datetime module of CPython repository.

Classmethod

Much like staticmethods, classmethods also are independent of the instance of class but are dependent on the class itself, i.e., bound to the class. Thus, quite obviously it takes class as the first parameter. Classmethods can be created with ‘@classmethod’ decorator as described in the next example.

class Animal:
"""Animal class"""
animal = 'dog'
@classmethod
def change_animal(cls, new_animal):
"""Change animal for class"""
return cls.animal = new_animal
def print_animal(self):
"""Return animal"""
return self.animal

In the above example, we are passing ‘cls’ as the class reference. Let’s see how this works.

>>>Animal.animal
'dog'
>>>Animal.change_animal
<bound method classobj.change_nimal of <class __main__.Animal at 0x7f63aefca9a8>>
>>>Animal.change_animal('bird')
>>>Animal.animal
'bird'

The example makes it clear that classmethods are bound to the class itself and thus, is powerful enough to change the class properties; in this case, the class is variable. Also, it is notable that much like staticmethods, these do not need to be called on class objects. Hence, classmethods are also called factory methods or alternate constructors. An example of alternate constructor is shown below:

class Animal:
"""Animal class"""
def __init__(self, legs):
"""Initialize animal"""
self.legs = legs
@classmethod
def change_legs(cls):
"""Change legs for animal."""
return cls(4)
>>>animal = Animal(2)
>>>animal.legs
2
>>>animal = Animal.change_legs()
>>>animal.legs
4

An implemented example of classmethod can be seen here. Apart from classmethods and staticmethods, abstract methods are of great usage. You can read about abstractmethods and abstract base classes here. Happy Python!

Ready to embark on a transformative journey? Connect with our experts and fuel your growth today!