Technology  /  Python

🐍 Python 78 guides · updated 2026

From first variable to OOP, generators, and real projects — the language that runs everything from data pipelines to AI agents, taught the practical way.

Python @staticmethod: Utility Functions That Belong in a Class Without Needing It

Python has three kinds of methods in a class: instance methods, class methods, and static methods. The first two are straightforward — instance methods get self, class methods get cls. Static methods get neither. They are regular functions that happen to live inside a class.

Why Put a Function Inside a Class at All?

If a static method is just a regular function, why not define it at module level? Sometimes that is the right choice. But there are cases where a function logically belongs with a class even though it does not touch any class or instance state:

Placing it as a @staticmethod signals to readers: “this function is related to this class, but it does not need an object or the class itself to work.”

Syntax

Apply @staticmethod above the method definition. The method takes no mandatory first parameter.

class TemperatureConverter:
@staticmethod
def celsius_to_fahrenheit(c):
return (c * 9 / 5) + 32
@staticmethod
def fahrenheit_to_celsius(f):
return (f - 32) * 5 / 9
print(TemperatureConverter.celsius_to_fahrenheit(100)) # 212.0
print(TemperatureConverter.fahrenheit_to_celsius(32)) # 0.0

You can also call a static method on an instance, though calling it on the class is clearer:

conv = TemperatureConverter()
print(conv.celsius_to_fahrenheit(37)) # 98.6 — works, but unusual style

A Realistic Example: Input Validation

import re
class User:
def __init__(self, username, email):
if not User.is_valid_username(username):
raise ValueError(f"Invalid username: {username!r}")
if not User.is_valid_email(email):
raise ValueError(f"Invalid email: {email!r}")
self.username = username
self.email = email
@staticmethod
def is_valid_username(username):
"""Username: 3-20 characters, letters, numbers, underscores only."""
return bool(re.match(r"^\w{3,20}$", username))
@staticmethod
def is_valid_email(email):
"""Very basic email format check."""
return bool(re.match(r"[^@]+@[^@]+\.[^@]+", email))
# Can validate without creating a User object
print(User.is_valid_username("alice_99")) # True
print(User.is_valid_username("a")) # False — too short
print(User.is_valid_email("bad-email")) # False
user = User("alice_99", "alice@example.com")
print(user.username) # alice_99

The validation methods do not need to know anything about an existing user. They just check a string against a rule. Making them @staticmethod means you can call them before creating an object — useful when you want to validate user input before you even try to build the object.

Comparing the Three Method Types

class PaymentProcessor:
fee_rate = 0.025 # 2.5% platform fee
def __init__(self, merchant_id):
self.merchant_id = merchant_id
self.processed = []
def process(self, amount):
"""Instance method — needs self to record the transaction."""
net = amount * (1 - self.fee_rate)
self.processed.append(net)
return net
@classmethod
def from_env(cls):
"""Class method — builds an instance using class-level knowledge."""
import os
return cls(os.environ.get("MERCHANT_ID", "default-id"))
@staticmethod
def format_currency(amount, symbol="£"):
"""Static method — pure utility, no class or instance needed."""
return f"{symbol}{amount:,.2f}"
p = PaymentProcessor("merchant-001")
net = p.process(1000)
print(PaymentProcessor.format_currency(net)) # £975.00
print(PaymentProcessor.format_currency(net, "$")) # $975.00

@staticmethod vs @classmethod

@staticmethod@classmethod
ReceivesNothing extracls (the class)
Access class stateNoYes
Inheritance-awareNoYes — cls is the calling subclass
Typical usePure utilities, validators, convertersFactory methods, registry patterns

The inheritance difference is significant. If you have a subclass:

class EuroConverter(TemperatureConverter):
pass
# @staticmethod — subclass call works, but cls is irrelevant
print(EuroConverter.celsius_to_fahrenheit(0)) # 32.0
class Animal:
@classmethod
def create(cls):
return cls() # returns a Dog or Cat depending on which calls it
class Dog(Animal):
pass
d = Dog.create()
print(type(d)) # <class '__main__.Dog'>

A @classmethod’s cls correctly points to the calling subclass. A @staticmethod has no such awareness.

When Not to Use @staticmethod

Do not reach for @staticmethod just to avoid writing self. If a function genuinely needs to read or modify instance or class state, it should be an instance method or class method respectively. A static method that constantly accesses global state through module-level variables is a code smell — the class is not actually helping.

Also, if a function would be equally clear as a standalone module-level function, there is no reason to nest it in a class. Putting unrelated functions inside a class “for organisation” creates a class that has no coherent identity.

Summary