AlgoMaster Logo

Static Methods (@staticmethod)

High Priority16 min readUpdated June 6, 2026
Listen to this chapter
Unlock Audio

A static method is a function that lives inside a class but takes neither self nor cls. It's a plain function that happens to be defined under a class for organizational reasons. This makes static methods the simplest of the three method types, and also the one that's easiest to overuse. This lesson covers what @staticmethod does, when to use it, when a module-level function is better, and the small set of cases where it fits over either alternative.

What @staticmethod Does

The @staticmethod decorator tells Python not to pass anything as the first argument. No instance, no class. The function inside is treated exactly like a top-level function; the class is purely a namespace.

First, the method definition doesn't have self or cls in the parameter list. It takes price directly. Second, the call works whether you go through the class (Product.is_valid_price(29.99)) or through an instance (some_product.is_valid_price(29.99)); both forms run the same plain function with the same single argument. Third, the body doesn't touch any class state or instance state, because it has no way to. The decorator made sure of that.

Removing @staticmethod and writing the same method body shows what the decorator does:

Without the decorator, calling through an instance still passes the instance as the first argument, so Python is now trying to pass both the instance and 29.99 into a function that only accepts one parameter. The error count is off by one for the same reason "missing self" errors are off by one. The decorator fixes this by telling Python "don't pass any extra arguments here". After that, the method behaves like a normal function regardless of how you call it.

Naming: Static methods don't get a self or cls parameter. The first parameter is whatever the function actually needs. Don't add a stub self or cls "just in case", that defeats the point of the decorator.

Static vs Class vs Instance: Side By Side

To pin down what makes static methods different, the same class with one of each kind of method.

The three methods are doing three different jobs. total reads per-instance state (self.items) and returns a value derived from it. default_tax reads class-level state (cls.tax_rate) and doesn't care about any single instance. is_valid_price doesn't read any state at all; it's a pure helper that lives on the class for organizational reasons.

A diagram makes the difference in argument binding clearer:

The cyan path passes the instance as self. The green path passes the class as cls. The teal path passes nothing extra; the function receives only what the caller wrote.

A decision table for picking the right one:

What the function needsMethod type
Per-instance data (reads or writes self.x)Instance method (default)
Class-level data, or builds a new instance@classmethod
Neither instance nor class data@staticmethod (or a module-level function)

The last row is the interesting one. If a method needs neither self nor cls, you have two choices: keep it on the class as a static method, or move it out to a plain function in the module. The next section is about how to pick between those two.

When to Use @staticmethod (and When Not To)

@staticmethod is rarely the best tool in Python, because Python already has a great place for plain functions: the module. Other languages don't have free-standing functions, so they force you to attach helpers to a class even when there's no real reason to. Python doesn't have that constraint, and the Pythonic preference is usually to write a module-level function unless there's a clear reason to put the helper on the class.

That said, there are real cases where @staticmethod fits. Three common ones follow.

Case 1: A Helper That Logically Belongs to the Class

If a helper function is conceptually about the class but doesn't need any of its state, putting it on the class can improve readability. Callers who already know about the class find the helper through dot completion. Readers see at a glance that the function relates to the class.

is_valid_status doesn't read any instance attributes or class attributes; it just checks whether a string is in a fixed set. Putting it on the class makes the relationship obvious. Callers who want to validate a status without first creating an order can use Order.is_valid_status(...). The instance method update_status calls it via Order.is_valid_status(...) to enforce the rule.

The judgment call is whether the function is "about the class" enough to belong here. A function called is_valid_status for Order clearly is. A function called format_currency probably isn't, even if every Order uses it; that one belongs in a formatting module that any class can import.

Case 2: A Function That Subclasses Should Be Able to Override

If you have a helper that a subclass might reasonably want to customize, a static method on the class lets the subclass override it the same way it would override any other method. A module-level function wouldn't support that.

That's not quite what we wanted. The EUProduct label still uses the US formatter. The reason is the explicit Product.format_price(...) call in label. To let subclasses override, we'd need to dispatch through the actual instance's class, which means using type(self) or, more commonly, calling the method through self:

Now self.format_price(...) looks up format_price on the actual instance's class, finds the overridden version on EUProduct, and uses it. The static method participates in the normal lookup chain, which is what gives subclasses room to override. If format_price had been a module-level function instead, this kind of override wouldn't be possible without changing the call site.

This is the strongest reason to choose @staticmethod over a module-level function. If a subclass might want to change the helper's behavior, keep it on the class.

When a class has multiple small helpers that work together, grouping them as static methods on the class can read better than scattering them across a module. The class acts as a small namespace.

Money here isn't a real class in the OOP sense; nothing is instantiated. It's a bag of related helpers. Some Python codebases lean on this pattern for organization; others prefer a module called money.py with three top-level functions. Both are valid; the choice depends on the codebase's conventions.

Calling a static method through an instance (cart.is_valid_price(29.99)) has the same speed as calling it through the class (Cart.is_valid_price(29.99)). The decorator skips the bind step, so there's no per-call overhead beyond the dotted lookup.

Static Method vs Module-Level Function

When considering @staticmethod, ask whether a module-level function would be cleaner. For helpers that don't really need to live near a class, the function is usually the better choice.

Both produce the same result. The differences:

Aspect@staticmethodModule-level function
DiscoverabilityFound via dot completion on the classImported from the module
Override in subclassYes, subclasses can replace itNo, the function is fixed
Implies relationship to classYes, the location signals "this is about Currency"Not necessarily
BoilerplateNeeds class definition and decoratorJust def
Pythonic preferenceLess common in idiomatic codeMore common

A rule of thumb: write the helper as a module-level function by default. Promote it to a static method on a class when (a) it's strongly tied to one class conceptually, or (b) subclasses might want to override it. If neither applies, leave it as a function.

The Python standard library follows this rule. The math module has math.sqrt, not math.Number.sqrt. The string module has string.ascii_letters, not string.String.letters. Functions live at module level by default; classes are reserved for things that genuinely need instances. Static methods show up in a few places (str.maketrans, dict.fromkeys), but the pattern is rare relative to module-level functions.

Static Method vs Class Method

Static methods and class methods look similar (both can be called through class or instance) but they're not interchangeable. The difference comes down to whether the function needs the class itself.

from_dict builds an instance. It uses cls(...) to do that, which is what lets the method work correctly in subclasses (the subclass gets cls = SubClass, and cls(...) builds the subclass). A static method couldn't do this; it wouldn't have access to cls.

is_valid_price only checks a value. It doesn't care which class it was called on, because there's no class state involved in the check. A class method here would work but would be misleading; it would imply that the class matters when it doesn't.

The test: if the body of the method needs to reference the class (to read class state or to build an instance), use @classmethod. If the body doesn't, use @staticmethod (or a module-level function).

Common Pitfalls

A few mistakes show up with @staticmethod. Recognizing them saves time during code review.

Using @staticmethod for Methods That Should Be Regular Functions

A common pitfall is putting a function on a class as a static method when nothing about it is tied to the class. The function would be just as useful in a utils.py module, and putting it on the class adds unnecessary coupling.

The static-method version forces every caller to import Customer even when they don't care about customers. The module-level version is reachable from anywhere without that dependency. Use the static method only when the helper is conceptually tied to the class.

Adding a Stub self or cls "Just to Be Safe"

If a method has no use for self or cls, don't add one anyway. The point of @staticmethod is to declare "this function doesn't need an instance or a class". Adding a stub parameter contradicts that and signals confusion to readers.

The decorator already prevents self from being passed. Listing self in the parameter list means the function will try to receive an extra argument that the caller didn't provide.

Using @staticmethod to Skip self Inside an Instance Method

Some developers use @staticmethod when they have an instance method that doesn't happen to use self in its body, but is otherwise clearly part of the instance interface. This is usually a mistake. The convention in Python is that methods stay instance methods (with self) by default unless there's a clear reason to mark them otherwise.

The method welcome doesn't reach into self, but it's still part of the Cart interface. Making it a static method would force callers who already have a cart instance to write Cart.welcome() or accept that calling cart.welcome() does the same thing internally. The cost of keeping it as an instance method is essentially zero, and the consistency wins. If the method belongs on the class as a static helper (validation, formatting), then @staticmethod is fine. If it's an instance method that doesn't happen to need state today, leave it as is.

Treating the Class as a Namespace When a Module Would Work

Using a class purely as a namespace for static methods is sometimes correct, but more often it's a sign that what you really want is a module. If the class has no instances, no class state, and no inheritance, ask whether the same code in a separate file would be cleaner.

Both work. The class-as-namespace approach can be useful when you have many related helpers and a module-per-helper-group feels heavy. It's also useful when you want a single import (from utils import StringUtils) instead of importing each function. For most codebases, the module is the Pythonic default.

A Worked Example: An Order Class With All Three Method Types

Putting it all together, an Order class with instance methods, class methods, and a static method, each chosen for the right reason.

Three method types, three different jobs. total and update_status work on a single order's state. from_dict and count_in_status either build new instances or take a class-level view. is_valid_status is a pure check that doesn't depend on any instance or class data; it lives on Order because it's logically about order statuses.

is_valid_status could also be a class method, so subclasses could override the valid set. That argument has merit. The choice depends on whether you expect subclasses to redefine the rules. If yes, use @classmethod and read cls.valid_statuses. If the rules are fixed at the parent-class level, @staticmethod works. Either way, putting the helper on the class instead of in a separate validation.py module makes sense because it's tightly tied to the Order concept.

Quiz

Static Methods (@staticmethod) Quiz

10 quizzes