Skip to main content

Python @classmethod vs @staticmethod: When to Use Each

Intermediate20 min5 exercises70 XP
0/5 exercises

Imagine a company with three kinds of employees. Regular employees work on their own tasks (they need to know about themselves). Managers can hire new employees for the whole department (they need to know about the department). Utility workers do general tasks like cleaning that don't depend on any specific person or department.

Python methods work the same way. Regular methods operate on a specific object (self). Class methods operate on the class itself (cls). Static methods don't need access to either — they're just helper functions that logically belong to the class.

In this tutorial, you'll learn exactly when and how to use @classmethod and @staticmethod, and the powerful factory method pattern that makes @classmethod so useful.

How Do Regular Instance Methods Work?

Before we learn the new method types, let's quickly recap regular methods. A regular method takes self as its first parameter and can access and modify the object's data.

Regular method recap
Loading editor...

The describe() method needs self to access self.size and self.toppings. This is the most common type of method, and it's what you've been writing so far.

What Is @classmethod and How Does It Work?

A class method receives the class itself as its first argument instead of an instance. You mark it with the @classmethod decorator, and by convention the first parameter is called cls (not self).

@classmethod receives the class as cls
Loading editor...

Notice that change_default_size uses cls.default_size instead of self.default_size. It modifies the class attribute, which affects all instances — not just one specific pizza.

What Are Factory Methods?

The most powerful use of @classmethod is the factory method pattern. A factory method is an alternative constructor — it gives you a different way to create objects besides calling ClassName() directly.

For example, what if you want to create a Date object from a string like '2024-01-15'? You can't change __init__ to handle both formats cleanly. Instead, you create a factory method.

Factory methods — alternative constructors
Loading editor...

The key line is return cls(int(year), int(month), int(day)). The factory method parses the string, then uses cls(...) to create a new instance. Using cls instead of Date means this will work correctly even with subclasses.

Temperature factory method
Loading editor...

What Is @staticmethod and How Does It Work?

A static method doesn't receive self or cls. It's just a regular function that lives inside a class because it's logically related. It can't access or modify the instance or the class.

@staticmethod — no self, no cls
Loading editor...

These methods don't need any data from an instance or the class. They're just utility functions grouped under MathHelper for organization. You call them on the class directly.

Static method as a validator
Loading editor...

The validate_topping method doesn't need self or cls — it just checks a topping against a list. Making it a static method signals to other developers that this function is independent of any specific instance.

When Should You Use Each Method Type?

Here's the simple decision guide:

  • Regular method — needs access to the instance (self). This is the default. Use it for reading or changing an object's data.
  • @classmethod — needs access to the class (cls). Use it for factory methods and modifying class-level state.
  • @staticmethod — doesn't need self or cls. Use it for utility functions that logically belong to the class.
  • Ask yourself: does it need self or cls?
    # Needs self? -> Regular method
    def get_area(self):
        return self.width * self.height
    
    # Needs cls? -> @classmethod
    @classmethod
    def from_square(cls, side):
        return cls(side, side)
    Needs neither? -> @staticmethod
    # No self, no cls -> @staticmethod
    @staticmethod
    def is_valid_dimension(value):
        return value > 0
    All three method types in one class
    Loading editor...

    What Are Common Real-World Patterns?

    Let's look at a realistic example. Imagine you're building a configuration system where settings can come from different sources — a dictionary, a JSON string, or default values.

    Config class with multiple factory methods
    Loading editor...

    Notice how from_json calls cls.from_dict internally. Factory methods can chain together, keeping each one simple and focused on a single conversion step.

    Here is a quick summary of the three method types side by side:

    Quick reference cheat sheet
    # Regular method: operates on ONE specific object
    # -> def method(self, ...)
    # -> Access self.attribute, self.other_method()
    
    # @classmethod: operates on the CLASS itself
    # -> def method(cls, ...)
    # -> Access cls.class_attribute, cls(...) to create instances
    
    # @staticmethod: independent helper
    # -> def method(...)
    # -> No access to self or cls, just a utility function

    Practice Exercises

    Create a Factory Method
    Write Code

    Create a Person class with name and age attributes. Add a @classmethod called from_birth_year that takes name and birth_year, calculates the age (use 2024 as the current year), and returns a new Person.

    Create a person using Person.from_birth_year('Alice', 2000) and print their name and age on separate lines.

    Loading editor...
    Add a Static Validator
    Write Code

    Create a Password class with a value attribute. Add a @staticmethod called is_strong that takes a password string and returns True if it has 8 or more characters, False otherwise.

    Print the result of Password.is_strong('abc') and Password.is_strong('securepass123').

    Loading editor...
    Predict the Method Output
    Predict Output

    Read the code and predict the output. Pay attention to which method type each is.

    class Counter:
        count = 0
    
        def __init__(self):
            Counter.count += 1
    
        @classmethod
        def get_count(cls):
            return cls.count
    
        @staticmethod
        def info():
            return 'I count things'
    
    a = Counter()
    b = Counter()
    c = Counter()
    print(Counter.get_count())
    print(Counter.info())
    Loading editor...
    Fix the Factory Method
    Fix the Bug

    This Rectangle class has a factory method with two bugs. Fix them so the code prints:

    5 x 5
    Loading editor...
    Build a Temperature Converter
    Write Code

    Create a Temperature class that stores temperature in Celsius:

    1. __init__(self, celsius) stores the value

    2. @classmethod from_fahrenheit(cls, f) — creates from Fahrenheit: (f - 32) * 5 / 9

    3. @staticmethod is_freezing(celsius) — returns True if celsius <= 0

    4. to_fahrenheit(self) — returns the Fahrenheit value: self.celsius * 9 / 5 + 32

    Create a temperature using Temperature.from_fahrenheit(32). Print:

  • The celsius value (should be 0.0)
  • The result of to_fahrenheit() (should be 32.0)
  • The result of Temperature.is_freezing(0.0) (should be True)
  • Loading editor...