@staticmethod and When a Plain Function Is Better
18 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)Some logic belongs next to a class but needs neither an object nor the class itself. Python gives two places for it: a static method, or a plain function in the module. Choosing well keeps classes small and code easy to find.
A helper that needs no object
Static method is a method marked with @staticmethod. Python passes it neither self nor cls, so it works like a plain function that is stored in the class for organisation.
Checking whether an order code is well formed needs only the code text. It reads no instance data and no class data.
class Order: @staticmethod def is_valid_code(code): return code.startswith("ORD-") and code[4:].isdigit()A static method says that in the code.
Example 1 · Class or object, same result
class Order: @staticmethod def is_valid_code(code): return code.startswith("ORD-") and code[4:].isdigit()
print(Order.is_valid_code("ORD-2041"))print(Order.is_valid_code("2041"))print(Order().is_valid_code("ORD-1"))True False True
What this code does
"ORD-2041"starts withORD-and the rest is digits, so the result isTrue."2041"does not start withORD-, so the result isFalse.- The call through an object,
Order().is_valid_code(...), gives the same kind of answer.
Python passes no automatic first argument. The method behaves like a normal function that happens to live in Order.
Calling it from the class or an object
A static method is called as Order.is_valid_code(...). It also works through an instance, and the instance is not passed in.
class Order: def __init__(self, code): if not Order.is_valid_code(code): raise ValueError("bad order code") self.code = code
@staticmethod def is_valid_code(code): return code.startswith("ORD-") and code[4:].isdigit()Inside the class, other methods can use it too.
Example 2 · One rule used by the constructor
class Order: def __init__(self, code): if not Order.is_valid_code(code): raise ValueError("bad order code") self.code = code
@staticmethod def is_valid_code(code): return code.startswith("ORD-") and code[4:].isdigit()
print(Order("ORD-77").code)try: Order("77")except ValueError as error: print("Rejected:", error)ORD-77 Rejected: bad order code
What this code does
__init__calls the static method through the class name.- The bad code is stopped before the order exists.
The constructor and any outside caller use the same rule, defined once.
When a plain function is better
A module-level function needs no class name and no decorator. If the helper has nothing to do with the idea of the class, or many classes use it, keep it outside.
def format_money(cents): return f"{cents / 100:.2f}"
class Invoice: def __init__(self, cents): self.cents = cents
def label(self): return "Total: " + format_money(self.cents)Example 3 · The same helper as a function
def format_money(cents): return f"{cents / 100:.2f}"
class Invoice: def __init__(self, cents): self.cents = cents
def label(self): return "Total: " + format_money(self.cents)
print(Invoice(259900).label())print(format_money(500))Total: 2599.00 5.00
What this code does
format_moneyuses no class data, so it stays a plain function.- Other classes, such as a receipt, can call it without importing
Invoice.
Example 4 · Forgetting @staticmethod
class Pricing: def apply_discount(amount, percent): return amount - amount * percent / 100
print(Pricing.apply_discount(200, 10))try: Pricing().apply_discount(200, 10)except TypeError as error: print(error)180.0 Pricing.apply_discount() takes 2 positional arguments but 3 were given
What this code does
- Called on the class, the function is a plain function and works.
- Called on an object, Python inserts the object as the first argument, so three values go to two parameters.
Python is not Java: there is no rule that every function lives in a class. A utils class full of static methods is usually just a module.
Choosing the method type
Ask what the code needs, in this order, and stop at the first yes.
- It reads or changes data of one object: instance method (
self). - It builds objects or uses class-level data: class method (
cls). - It needs neither, and readers expect to find it with the class: static method.
- It needs neither and other code uses it as well: plain function in the module.
Next step
Check your understanding
10 quick questions