Python OOP for Beginners — Class Method & Static Method

Most programmers stick only to instance methods, while there are two other types of methods — class methods and static methods — that can greatly improve your code quality. If the reason you don’t use them is simply a lack of understanding of when and why to use them, this guide is a must-read.

Introduction

As mentioned earlier, Python classes have three types of methods:

  • Instance Methods

  • Class Methods

  • Static Methods In the previous article, we covered instance methods. In this one, you’ll gain a clear understanding of class methods and static methods.

Now, to be honest, you can write entire software without ever using a single class method or static method. But if you truly care about the quality of your code — readability, maintainability, and flexibility — then understanding these two concepts is essential.

Static Method

The concept of static method is actually easier to understand than the concept of instance method. It's just a function inside a class. Unlike instance methods, you don’t add a self parameter because static methods don’t depend on any instance.

That means you don’t need to create an object to call them — they can be called directly on the class.

The syntax might look a bit intimidating at first since it involves decorators, but don’t worry — you don’t need to fully understand decorators to use static methods and class methods.

Dog Years to Human Years

Here's our current Dog class.

class Dog:

    def __init__(self,name,breed,color,age):
        self.name = name
        self.breed = breed
        self.color = color
        self.age = age

    def run(self):
        print(self.name, 'is running.')

    def sleep(self):
        print(self.name,'is sleeping.')

    def eat(self):
        print(self.name, 'is eating.')

Dogs don't live as long as humans. So let's create a function to covert dog years into human years using the following formula.

$$Human\;Years = Dog\;Years \times 7$$

Let's first create it as an instance method.

    def dog_years_2_human_years(self):
        return self.age*7

Usage:

tommy = Dog('Tommy','Labrador Retriever','Black',2)
print(tommy.dog_years2human_years())

It works fine. But what if you just want to convert any dog’s age into human years — not just the age of a Dog object you’ve created?

This won't work:

human_years = Dog.dog_years_2_human_years(4)

Why?

  1. dog_years_2_human_years is an instance method. You must create an instance before call it.

  2. It doesn't accept dog_years as a parameter — it expects self (an instance).

So here, you’re basically trying to pass an integer to self, which makes no sense.

In a scenario, you need to define a method inside a function but you need it to be able to use it not only inside the class but also outside the class without creating an instance, you can use static methods.

Static methods still belong to the class. The only difference is that they don’t need an instance (self) or the class (cls) to work. They’re just independent functions that are namespaced inside the class.

Let's modify dog_years_2_human_years as a static method.

    @staticmethod
    def dog_years_2_human_years(dog_years):
        return dog_years*7

What has have changed here?

  1. We have used the @staticmethod decorator to mark the dog_years_2_human_years as a static method.

  2. We have replaced self with a parameter dog_years because static methods don’t depend on instances.

  3. We replaced self.age with dog_years.

Now we can call it in different ways:

# Without creating an instance
human_years = Dog.dog_years_2_human_years(3)
print(human_years)

# With creating an instance
tommy = Dog('Tommy','Labrador Retriever','Black',2)
human_years = tommy.dog_years_2_human_years(2)
print(human_years)

And even use it inside the class.

    def age_in_human_years(self):
        return self.dog_years_2_human_years(self.age)

dora = Dog('Dora','German Shepherd','Black & Tan',6)
print(dora.age_in_human_years())

Class Method

Class methods are mostly used for:

  1. Factory methods (alternative ways to create objects)

  2. Accessing or modifying class-level data

Let’s start with factory methods.

Factory Methods

Here's how we normally create a instance.

dora = Dog('Dora','German Shepherd','Black & Tan',6)

But what if we want to create an instance from a string or a list like below?

dora_data_str = 'Dora,German Shepherd,Black & Tan,6'
dora_data_list = ['Dora','German Shepherd','Black & Tan',6]

With class methods, we can do this:

dog_1 = Dog.from_str(dora_data_str)
dog_2 = Dog.from_list(dora_data_list)

These are called factory methods.

Creating Factory Methods

We use the @classmethod decorator and define cls as the first parameter.

    @classmethod
    def from_str(cls,the_str):
        name, breed, color, age = the_str.split(',')
        return cls(name,breed,color,int(age))

cls refers to the class itself, just like self refers to the instance.

In the above method, we get a string and split it to a list by , and assign each one of them to variables.

Puppy Factory

Suppose we want to create puppies. Since every puppy’s age is always 0, we shouldn’t have to write that every time.

    @classmethod
    def puppy(cls,name,breed,color):
        return cls(name,breed,color,0)

Usage:

rex = Dog.puppy('Rex','German Shepherd','Black & Tan')

Class Level Data

Class-level data (also called class attributes) belong to the class itself, not to any single object. All instances share them.

class Dog:
    species = "Canis familiaris"  # <-- class-level data

    def __init__(self,name,breed,color,age):
        self.name = name    #     All of these
        self.breed = breed  #         are
        self.color = color  # <-- instance-level data
        self.age = age      #

We can access them just like instance attributes:

    def print_species(self):
        print(f'Species: {self.species}')

print(Dog.species)
dora = Dog('Dora','German Shepherd','Black & Tan',6)
print(dora.species)
dora.print_species()
Dog.print_species()
Class-wide operations

Here’s a great example of class-level data: tracking how many dogs we create.

class Dog:
    dog_count = 0

    def __init__(self,name,breed,age):
        self.name = name    
        self.breed = breed  
        self.age = age
        Dog.dog_count += 1      # class wide operation


tommy = Dog('tommy','Labrador Retriever',3)
print(Dog.dog_count)

dora = Dog('dora','German Shepherd',6)
print(Dog.dog_count)

luca = Dog('luca','Doberman',4)
print(Dog.dog_count)

Each time we create a new dog, dog_count increases because it belongs to the class, not to any single object.

Class Methods for Class Data

Let's reset our class to following one.

class Dog:
    species = ""

    def __init__(self,name,breed,age):
        self.name = name    
        self.breed = breed  
        self.age = age

Yet, the species class data is an empty string. So, Let’s add a class method to modify the species:

    @classmethod
    def set_species(cls,species):
        cls.species = species

Usage:

print(Dog.species)
Dog.set_species('Canis familiaris')
print(Dog.species)

Final Thoughts

By now, you should feel confident about when to use instance methods, class methods, and static methods. These techniques improve flexibility, readability, and maintainability of your code.
In the upcoming article, we’ll move to another key concept of OOP: encapsulation. You’ll learn how Python lets you control access to attributes, enforce restrictions, and design cleaner, safer class structures.

You can subscribe to the newsletter to get new articles delivered straight to your inbox the moment I post them. Follow me on GitHub for more contents— and maybe Instagram too.

You can support my effort by reacting to and commenting on this article. Share it with someone who would find it valuable.

6
Subscribe to my newsletter

Read articles from Beenuka Hettiarachchi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Beenuka Hettiarachchi
Beenuka Hettiarachchi