Static Method and Class Method in Python. What is The Difference?

Are you trying to understand the concept of static method and class method in Python? In this tutorial you will learn how to use both.

A static method cannot access class attributes or instance attributes. It is equivalent to a normal function but it’s part of a class because it has in common the same logical context. A class method cannot access instance attributes and its often used as factory method.

In this tutorial it will become clear to you how you can use static and class methods compared to standard instance methods.

Let’s get coding!

What is a Static Method in Python?

A static method is a method that you can call without creating an instance of a class. It’s like a standard function inside a class. Static methods act as utility methods for a class.

To understand how a static method looks like let’s start from a Python class that represents movies.

This class has a constructor and a basic instance method that prints a brief description of a movie.

class Movie:
    def __init__(self, name, release_year):
        self.name = name
        self.release_year = release_year

    def get_description(self):
        print(f"The value of self is {self}.")
        print(f"The movie {self.name} was released in {self.release_year}.")

When you define an instance method in a class, that method receives self as first argument where self is the instance of the class on which that method is called.

Notice that in the instance method we are also printing the value of self so I can show you what’s inside it.

Now create an instance of this class and call the method get_description() on that instance.

movie = Movie("Rambo", "1982")
movie.get_description()

[output]
The value of self is <__main__.Movie object at 0x104f08490>.
The movie Rambo was released in 1982.

From the output you can see that self is a Movie object, or in other words, an instance of the class Movie.

And now create another instance method called get_years_from_release() that will tell us the number of years passed from the release date of a movie.

def get_years_from_release(self):
    return datetime.datetime.now().year - int(self.release_year)

Note: remember to import the datetime module otherwise you will see a “name not defined” exception.

Let’s use this method to print a message that tells how old is our movie…

movie = Movie("Rambo", "1982")
print(f"The movie {movie.name} is {movie.get_years_from_release()} years old.")

[output]
The movie Rambo is 40 years old.

That’s cool!

Now, let’s do a little experiment…

We will add the decorator @staticmethod to this method and try to execute it again.

We get back the following error:

Traceback (most recent call last):
  File "static_class_methods.py", line 17, in <module>
    print(f"The movie {movie.name} is {movie.get_years_from_release()} years old.")
TypeError: get_years_from_release() missing 1 required positional argument: 'self'

The cause of this error is that an instance method, when called, implicitly passes the instance as self parameter.

But the same doesn’t happen when you decorate a method with @staticmethod.

That’s because…

A static method is not specific to an instance of a class and that’s why the self parameter doesn’t apply to it.

A static method doesn’t receive self as first argument, it behaves like a simple function with the only difference that it’s part of a class.

Based on this we will:

  • Remove the self argument from the static method.
  • Pass the movie release year as an argument to the static method considering that we cannot refer to self.release_year in the method (the static method doesn’t have access to self).
@staticmethod
def get_years_from_release(release_year):
    return datetime.datetime.now().year - int(release_year)

This time the output of the method is correct:

movie = Movie("Rambo", "1982")
print(f"The movie {movie.name} is {movie.get_years_from_release(movie.release_year)} years old.")

[output]
The movie Rambo is 40 years old.

Note: here we had to pass movie.release_year to the static method while before, in the instance method, the release year was automatically read from the instance using self.release_year.

And below you can see that we can also call the static method using the class instead of the instance.

The output doesn’t change.

print(f"The movie {movie.name} is {Movie.get_years_from_release(movie.release_year)} years old.")

[output]
The movie Rambo is 40 years old.

The reason why you would define a static method in a class is that the method is logically related to the class itself.

Here’s how our code is so far:

import datetime

class Movie:
    def __init__(self, name, release_year):
        self.name = name
        self.release_year = release_year

    def get_description(self):
        print(f"The value of self is {self}.")
        print(f"The movie {self.name} was released in {self.release_year}.")

    @staticmethod
    def get_years_from_release(release_year):
        return datetime.datetime.now().year - int(release_year)

Can a Static Method Access Instance Attributes?

This is something we have already seen in the previous section…

But repetition is good to make sure this concept is clear.

Static methods in Python cannot access instance attributes because, differently from instance methods, they don’t receive self as argument.

In the next section I will show you another type of method you can create in your Python class, a method that is different from an instance method or a static method.

What is a Class Method in Python?

Another type of method you can create in a Python class is a class method.

Let me tell you more about it…

A class method is a method that implicitly receives the class as first argument. Class methods are commonly used as factory methods. In other words they are used to create instances of a class similarly to what a class constructor does.

A class method receives cls (a class) as first argument pretty much like an instance method receives self (a class instance) as first argument.

Here is how a class method looks like:

class MyClass:
    @classmethod
    def my_class_method(cls, arg1, arg2, ..., argN):
        <method_body>

Let’s try to create a class method similarly to what we have done in the previous section with a static method.

But before doing that we will add another instance attribute to our class: the genre of the movie.

This is the updated code…

import datetime

class Movie:
    def __init__(self, name, release_year, genre):
        self.name = name
        self.release_year = release_year
        self.genre = genre

    def get_description(self):
        print(f"The value of self is {self}.")
        print(f"The {self.genre.lower()} movie {self.name} was released in {self.release_year}.")

    @staticmethod
    def get_years_from_release(release_year):
        return datetime.datetime.now().year - int(release_year)

Try to create an instance and call both methods to make sure this code works.

movie = Movie("Rambo", "1982", "Action")
movie.get_description()
print(f"The {movie.genre.lower()} movie {movie.name} is {movie.get_years_from_release(movie.release_year)} years old.")

[output]
The value of self is <__main__.Movie object at 0x1028e7ac0>.
The action movie Rambo was released in 1982.
The action movie Rambo is 40 years old.

Let’s add a class method that we can use to create action movies…

@classmethod
def action_movie(cls, name, release_year):
    print(f"The value of cls is {cls}.")
    return cls(name, release_year, "Action")

Notice that the method we have created is using the @classmethod decorator.

We are also printing the value of cls to compare it with the value of self in our output.

Call the class method using the class name Movie:

movie = Movie.action_movie("Rambo", "1982")
movie.get_description()
print(f"The {movie.genre.lower()} movie {movie.name} is {movie.get_years_from_release(movie.release_year)} years old.")

[output]
The value of cls is <class '__main__.Movie'>.
The value of self is <__main__.Movie object at 0x104c58910>.
The action movie Rambo was released in 1982.
The action movie Rambo is 40 years old.

The code works fine.

As you can see we are able to create a movie instance using the new class method instead of the constructor.

Also, can you see the difference between the values of cls and self?

The argument cls is the class that calls the class method. The argument self is an instance of that class (or an object). Using cls and self is a standard naming convention across Python developers but it’s not something the Python interpreter enforces. You could call them differently but this would make your code harder to read for other developers.

When to Use a Static Method vs a Function in Python

You might be wondering…

Why would you define a static method in a class when in fact it could be a basic Python function?

After all it doesn’t receive any class or instance specific arguments like cls or self.

Can we replace a static method with a function?

A function can replace a static method, and there are scenarios in which it makes more sense to use a function. For example, if the static method is so generic that it can be relevant to the context of multiple classes. In that case it could be a separate function called by each class when needed.

Also…

You would define a static method in a class instead of using a function if that method is so specific to what your class does that it makes more sense to keep it in the class.

For example…

Let’s take our latest code and convert the static method info a function to see how it works.

import datetime

class Movie:
    def __init__(self, name, release_year, genre):
        self.name = name
        self.release_year = release_year
        self.genre = genre

    def get_description(self):
        print(f"The value of self is {self}.")
        print(f"The {self.genre.lower()} movie {self.name} was released in {self.release_year}.")

    @staticmethod
    def get_years_from_release(release_year):
        return datetime.datetime.now().year - int(release_year)

    @classmethod
    def action_movie(cls, name, release_year):
        print(f"The value of cls is {cls}.")
        return cls(name, release_year, "Action")

Move the static method outside of the class and remove the @staticmethod decorator.

The code becomes…

import datetime

def get_years_from_release(release_year):
    return datetime.datetime.now().year - int(release_year)

class Movie:
    def __init__(self, name, release_year, genre):
        self.name = name
        self.release_year = release_year
        self.genre = genre

    def get_description(self):
        print(f"The value of self is {self}.")
        print(f"The {self.genre.lower()} movie {self.name} was released in {self.release_year}.")

    @classmethod
    def action_movie(cls, name, release_year):
        print(f"The value of cls is {cls}.")
        return cls(name, release_year, "Action")

And finally let’s call this function after creating an instance of the class Movie.

movie = Movie.action_movie("Rambo", "1982")
movie.get_description()
print(f"The {movie.genre.lower()} movie {movie.name} is {get_years_from_release(movie.release_year)} years old.")

[output]
The value of cls is <class '__main__.Movie'>.
The value of self is <__main__.Movie object at 0x1012cc640>.
The action movie Rambo was released in 1982.
The action movie Rambo is 40 years old.

Works well!

Can Static Methods Be Inherited in Python?

I wonder if we can inherit a static method inside a child class.

Let’s verify it…

Start from a simplified version of our previous class.

import datetime

class Movie:
    def __init__(self, name, release_year, genre):
        self.name = name
        self.release_year = release_year
        self.genre = genre

    @staticmethod
    def get_years_from_release(release_year):
        return datetime.datetime.now().year - int(release_year)

We want to verify if a static method can be inherited.

So let’s define a new class without implementation called FantasyMovie that inherits the class Movie:

class FantasyMovie(Movie):
    pass

Then create an instance of this class.

Let’s see if we can call the static method of the parent class get_years_from_release() and if we get back the correct result.

movie = FantasyMovie("Ghostbusters", "1984", "Fantasy")
print(movie.get_years_from_release(movie.release_year))

[output]
38

We do!

This means that…

Static methods are inherited by a subclass in Python.

Can Class Methods Be Inherited in Python?

Let’s do a similar test to the one we have done in the previous section but with a class method this time.

Start with a class that only has the constructor and a class method.

class Movie:
    def __init__(self, name, release_year, genre):
        self.name = name
        self.release_year = release_year
        self.genre = genre

    @classmethod
    def action_movie(cls, name, release_year):
        print(f"The value of cls is {cls}.")
        return cls(name, release_year, "Action")

Then use the same child class we have created before…

class FantasyMovie(Movie):
    pass

And call the class method using this subclass.

movie = FantasyMovie.action_movie("Ghostbusters", "1984")
print(movie.__dict__)

[output]
The value of cls is <class '__main__.FantasyMovie'>.
{'name': 'Ghostbusters', 'release_year': '1984', 'genre': 'Action'}

As you can see we have created the instance successfully even if at this point we are mixing two movie genres: Fantasy and Action.

After all this is just an example to show you that you can call a class method from a child class.

Class methods are inherited by a subclass in Python.

Conclusion

Let’s recap how instance method, class method and static method differ from each other based on their first argument:

  • Instance method: receives the instance as first argument (self).
  • Class method: receives the class as first argument (cls).
  • Static method: doesn’t receive any special arguments.

And now it’s time for you to start using these methods in your code! 🙂

Keep writing code and keep learning!

Leave a Reply

Your email address will not be published.

How to Import a Python Function fro... x
How to Import a Python Function from Another File