11 minutes read

As you already know, decorators can help change the behavior of a function without modifying its code. In this topic, we will discuss three main built-in decorators that can help us to work with classes: staticmethod, classmethod, and property.

@staticmethod

The @staticmethod decorator can be used to bind a function to a class as its method. In the following example, we have the CharType class and the method to get the type of character:

class CharType(object):

    @staticmethod
    def get_type(char):
        if char.isalpha():
            return 'letter'
        elif char.isdigit():
            return 'digit'
        else:
            return 'other'


print(CharType.get_type('a'))    # letter
print(CharType().get_type('1'))  # digit

As you can see, we may access the static method without creating an instance of the class first. Of course, we can extend the functionality by adding other methods which will interact with the static methods within the class.

Note that such static methods do not have any mandatory parameters; in contrast to instance methods we have dealt with before, the functions decorated with the @staticmethod decorator do not take self as the first argument; nor do they accept the cls argument, as opposed to class methods you will read about below. Even though a static method belongs to a class and all its instances, it does not get any access to instance internals. This method is used for a matter of convenience or to make a better design for the code.

@classmethod

Now, let's move on to the @classmethod decorator. As you know, a "regular" method takes an instance of the class as its first argument and then we use self to refer to it. Class methods, on the contrary, do not require particular class instances; they have access only to general class attributes and properties. Because of this, their first parameter should always be cls, which represents the class itself, and not the class instance, denoted by self.

In the following example, we have the class User and a single string, containing both a name and a surname. Now, we need to process the string to receive the name and the surname as two separate string variables. We are going to do it with the help of the from_string method.

class User(object):

    def __init__(self, name, surname):
        self.name = name
        self.surname = surname

    def get_info(self):
        return self.name + ' (' + self.surname + ')'

    @classmethod
    def from_string(cls, data):
        name, surname = data.split(' ')
        return cls(name, surname)  # passing the string values to the initialization call


user = User.from_string('Santa Claus')  # using the class name to call the method
print(user.get_info())  # Santa (Claus)

As you can see, class methods are used when we do not need attributes of specific instances but want to use the class information for some purpose. The most common example is alternative constructors. For one, if the required information comes from some outer source, a file, for example, in this case, we cannot pass the data directly as class instances. We may carry out some kind of preprocessing before actually creating a new object, you have seen this case in the example above. With the help of the decorator, we call a class method using the class itself rather than a class instance.

However, we can also call the from_string method on the User instance, even though it makes less sense.

user2 = user.from_string('Father Christmas')
user2.get_info()  # Father (Christmas)

@classmethod vs @staticmethod

To make it clearer, let's outline the main differences between static and class methods:

  1. Static methods have neither cls nor self as their parameters, so we cannot operate on the class or particular instances within the method. In general, they act similarly to functions outside the class and are often used as utility methods.
  2. Class methods always take the class as the first argument, usually called cls. They are frequently used as alternative constructors that create class objects for various use cases.

@property

Finally, let's create a basic example of the @property decorator usage. This one is designed to access a method as if it was an attribute of a class. Once again, we have an instance of the User class:

class User:
    def __init__(self, name, surname):
        self.name = name
        self.surname = surname
        self.full_name = self.name + ' ' + self.surname


user = User('Santa', 'Claus')
print(user.full_name)  # Santa Claus

When we change the name of the user to 'Father', we notice that the full_name attribute of our object remains the same. It happens because we have only set it during the initialization.

user.name = 'Father'

print(user.name)  # Father
print(user.full_name)  # Santa Claus

It is a perfect case to use the @property decorator! If we define full_name using the decorator, we will evaluate the method every time it is accessed as an attribute.

class User:
    def __init__(self, name, surname):
        self.name = name
        self.surname = surname

    @property
    def full_name(self):
        return self.name + ' ' + self.surname


user = User('Santa', 'Claus')
print(user.full_name)  # Santa Claus

user.name = 'Father'
print(user.name)  # Father
print(user.full_name)  # Father Claus

Summary

In this topic, we have learned that:

  • the @staticmethod decorator allows us to use a function without access to class instances as a class method;
  • the @classmethod decorator can be used to call a method of a class instead of a class instance;
  • the @property decorator is helpful when we want to access a method as an attribute.

Now let's practice!

162 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo