Report this

What is the reason for this report?

Python staticmethod: When and How to Use Static Methods

Updated on September 16, 2025
Python staticmethod: When and How to Use Static Methods

Python static method

In this tutorial, we will learn how to create and use a Python static method. We will also have a look at what advantages and disadvantages static methods offer as compared to the instance methods. Let’s get started. python static method

Key Takeaways

  • Staticmethods are for stateless helpers tightly coupled to a class. Use them when the logic belongs to the class’s domain language but doesn’t need self or cls. Prefer module-level functions for broadly reusable utilities to reduce coupling and simplify testing and mocking.

  • @staticmethod vs staticmethod() is about intent at definition time. The decorator is clearer and prevents drift; the functional form is useful when retrofitting existing class functions. Both produce a non‑data descriptor that returns the original function without binding.

  • Understand descriptors to reason about performance and behavior. Instance methods bind self; staticmethods don’t. That explains typical overhead differences and why staticmethods cannot implicitly access state. Always measure with timeit on your hardware before optimizing micro‑paths.

  • Factories belong to @classmethod; staticmethods support them. Keep constructors thin. Put parse/validate/normalize steps in static helpers, then call them from a classmethod like from_csv. This separation improves testability and keeps domain logic modular and composable.

  • Be explicit with types, docs, and decorator order. Add type hints and brief docstrings to static helpers. For 3.12 use typing_extensions.deprecated; for 3.13+ use typing.deprecated. Apply deprecation decorators inside @staticmethod so tooling and runtime behavior remain consistent.

  • Know when not to use staticmethods. If a helper crosses modules, needs dependency injection, or benefits from memoization, keep it at module scope. Over‑namespacing inside classes increases coupling and leads to awkward imports and harder testing.

  • AI/ML workflows benefit from static helpers. Use staticmethods to encapsulate preprocessing/validation, enforce conventions via linting/PR bots, simplify framework refactors (TensorFlow/PyTorch), and power CI steps that auto‑generate tests/docs—predictable, side‑effect‑free utilities that keep pipelines reproducible and decoupled from object state.

What is a static method?

Static methods in Python are extremely similar to python class level methods, the difference being that a static method is bound to a class rather than the objects for that class. This means that a static method can be called without an object for that class. This also means that static methods cannot modify the state of an object as they are not bound to it. Let’s see how we can create static methods in Python.

Why and when to use static methods

Static methods are useful when you want to:

  • Group utility functions together in a class that logically belong together
  • Create methods that don’t need access to instance attributes or methods
  • Implement functionality that belongs conceptually to a class but doesn’t require an instance
  • Write helper methods that operate on class attributes rather than instance attributes
  • Organize namespace and improve code readability by keeping related functions in one class
  • Create factory methods that can return different instances of the class based on parameters
  • Perform operations that are related to the class but don’t need to access or modify class state
  • Avoid creating unnecessary instances when you just need to call a function

Static methods are particularly common in utility classes, factory pattern implementations, and for operations that are conceptually related to a class but don’t depend on instance-specific data.

Creating python static methods

Python Static methods can be created in two ways. Let’s see each of the ways here:

Using staticmethod()

Let’s directly jump to sample code snippet on how to use the staticmethod() approach:

class Calculator:

    def add_numbers(x, y):
        return x + y

# create add_numbers static method
Calculator.add_numbers = staticmethod(Calculator.add_numbers)

print('Sum:', Calculator.add_numbers(15, 110))

Note that we called add_numbers without creating an object. When we run this program, here is the output we will get: python staticmethod function There were no surprises there. This approach lets you turn a function defined on the class into a static method wherever needed. Let’s see another approach with the same example here.

Using @staticmethod

This is a more subtle way of creating a Static method as we do not have to rely on a statement definition of a method being a class method and making it static at each place you make it static. Let’s use this annotation in a code snippet:

class Calculator:

    # create add_numbers static method
    @staticmethod
    def add_numbers(x, y):
        return x + y

print('Sum:', Calculator.add_numbers(15, 110))

When we run this program, here is the output we will get: python static method annotation This was actually a much better way to create a static method as the intention of keeping the method static is clear as soon as we create it and mark it with the @staticmethod annotation.

Advantages of Python static method

Static methods have a very clear use-case. When we need some functionality not w.r.t an Object but w.r.t the complete class, we make a method static. This is pretty much advantageous when we need to create Utility methods as they aren’t tied to an object lifecycle usually. Finally, note that in a static method, we don’t need the self to be passed as the first argument. API Reference: Python Documentation.

Practical Use Cases

Grouping utility functions inside a class namespace

Static methods are particularly useful for grouping utility functions that are related to a class but don’t require access to class or instance state. This approach helps to keep the namespace clean and organized, making it easier to understand and use the class. For example, consider a StringUtilities class that provides various string manipulation methods. You might define a static method strip_punctuation to remove punctuation from a string, as it’s a utility function that doesn’t depend on any class or instance state.

class StringUtilities:
    @staticmethod
    def strip_punctuation(input_string):
        # Implementation to remove punctuation from the input string
        pass

By using a static method, you can call StringUtilities.strip_punctuation without creating an instance of the class, making it a convenient utility function.

Creating reusable logic that’s logically tied to a class but doesn’t need class or instance state

Static methods are ideal for creating reusable logic that is conceptually tied to a class but doesn’t require access to class or instance state. This is particularly useful when you need to perform operations that are related to the class but don’t depend on instance-specific data. For instance, consider a MathUtilities class that provides mathematical operations. You might define a static method calculate_factorial to calculate the factorial of a number, as it’s a mathematical operation that doesn’t rely on any class or instance state.

class MathUtilities:
    @staticmethod
    def calculate_factorial(number):
        # Implementation to calculate the factorial of the input number
        pass

By using a static method, you can call MathUtilities.calculate_factorial without creating an instance of the class, making it a reusable piece of logic that’s tied to the class conceptually.

Common Errors and Debugging

Forgetting to use @staticmethod decorator

One common error is forgetting to use the @staticmethod decorator when defining a static method. This can lead to confusion and errors, as the method will not be recognized as static without the decorator. For example, consider the following incorrect implementation:

class Calculator:
    def add_numbers(x, y):
        return x + y

In this case, add_numbers is not a static method because it’s missing the @staticmethod decorator. To fix this, you would add the decorator as follows:

class Calculator:
    @staticmethod
    def add_numbers(x, y):
        return x + y

Confusing static methods with class methods

Another common mistake is confusing static methods with class methods. While both types of methods are bound to a class, they have different use cases and behaviors. Static methods do not require access to class or instance state, whereas class methods do. Understanding the differences between these two types of methods is crucial for effective use.

For example, consider a Person class with a static method get_average_age that calculates the average age of all persons, and a class method get_person_count that returns the total number of persons. The get_average_age method doesn’t require access to any instance state, making it a good candidate for a static method. On the other hand, get_person_count requires access to the class state to count the total number of persons, making it a good candidate for a class method.

class Person:
    person_count = 0

    @staticmethod
    def get_average_age():
        # Implementation to calculate the average age of all persons
        pass

    @classmethod
    def get_person_count(cls):
        return cls.person_count

By understanding the differences between static and class methods, you can effectively use them to organize your code and avoid common errors.

Comparison of Python Method Types

Understanding the differences between static methods, class methods, instance methods, and module-level functions is crucial for writing clear, maintainable, and efficient Python code. The following expanded chart summarizes their characteristics, typical use cases, and behaviors:

Method Type Typical Use Case(s) Access to Class State (cls) Access to Instance State (self) How to Call Receives Implicit First Argument? Can Be Overridden in Subclass? Common Pitfalls / Notes
Static Method Utility/helper functions logically grouped with a class, but not needing class/instance data No No ClassName.method() or instance.method() No Yes Use @staticmethod decorator. Cannot access or modify class or instance state unless explicitly passed.
Module-level Function General-purpose utilities, helpers reused across modules or projects No No module.function() No N/A Not tied to any class. Prefer for helpers not conceptually bound to a class.
Class Method Factory methods, alternative constructors, methods needing to access/modify class state Yes No ClassName.method() or instance.method() Yes (cls) Yes Use @classmethod decorator. Can access/modify class variables, but not instance variables.
Instance Method Regular object behavior, accessing/modifying instance and class state Yes Yes instance.method() Yes (self) Yes Default method type. Can access both instance and class variables.

Additional Details

  • Static Method:

    • Defined with @staticmethod decorator.
    • Does not receive an implicit first argument (self or cls).
    • Useful for utility functions that conceptually belong to the class’s domain but do not need to access or modify class or instance data.
    • Can be called on the class or an instance, but neither is passed to the method.
  • Module-level Function:

    • Defined outside of any class, directly in a module.
    • Fastest to call (single attribute lookup).
    • Best for helpers that are not conceptually tied to a class, or are reused across multiple classes or modules.
  • Class Method:

    • Defined with @classmethod decorator.
    • Receives the class (cls) as the first argument.
    • Can access and modify class variables, and is often used for alternative constructors or methods that need to know about the class (including subclasses).
  • Instance Method:

    • The default method type in Python classes.
    • Receives the instance (self) as the first argument.
    • Can access and modify both instance and class variables.
    • Used for behavior that depends on the state of a specific object.

Example Table with Expanded Use Cases

Method Type Example Use Case Can Access/Modify Class Vars Can Access/Modify Instance Vars Receives self/cls Decorator
Static Method String parsing, math utilities, validation No No None @staticmethod
Module-level Function File I/O helpers, general-purpose algorithms No No None None
Class Method Alternative constructors, class-wide counters Yes No cls @classmethod
Instance Method Object state manipulation, business logic Yes Yes self None (default)

Tip:

  • Use static methods for helpers that are conceptually part of a class’s domain, but do not need access to class or instance data.
  • Use module-level functions for helpers that are generic or reused across multiple classes or modules.
  • Use class methods when you need to operate on the class itself (e.g., to create new instances in different ways).
  • Use instance methods for behavior that depends on the state of a specific object.

Note: Prefer a module-level function when the helper is reused across domains, needs easy mocking/memoization, or should not couple to a class’s import path. Keep staticmethods for helpers tightly coupled to a class’s domain language.

Examples

Static Method Example

class MathUtilities:
    @staticmethod
    def calculate_factorial(number):
        # Implementation to calculate the factorial of the input number
        pass

In this example, calculate_factorial is a static method that can be called without creating an instance of the MathUtilities class. It does not have access to class or instance state.

Class Method Example

class Person:
    person_count = 0

    @classmethod
    def add_person(cls):
        cls.person_count += 1
        return cls()

In this example, add_person is a class method that has access to the class state (the person_count class variable). It does not have access to instance state.

Instance Method Example

class Person:
    def __init__(self, name):
        self.name = name

    def greet(self):
        print(f"Hello, my name is {self.name}!")

In this example, greet is an instance method that has access to instance state (the name instance variable). It also has access to class state, but it is not shown in this example.

Performance Considerations: @staticmethod vs instance method vs module-level function

When performance matters, remember the call path. A module-level function is typically the fastest: a single attribute lookup on the module, then a direct callable. An instance method is usually the slowest: Python must resolve the descriptor on the class, create a bound method (capturing self), and then call it. A staticmethod sits between the two: it’s a non-data descriptor that returns the original function unchanged when accessed on the class or instance, so you pay an extra attribute lookup on the class/instance but avoid bound-method creation. In microbenchmarks (use timeit), the ordering is commonly: module-level function ≤ staticmethod < instance method. In production code, prefer clarity first; only optimize after measuring realistic workloads, not synthetic loops.

How to Benchmark (Repeatable, User-Centric Methodology)

Performance benchmarking should reflect how users actually experience method calls, not just synthetic micro-optimizations. Here’s a reproducible benchmark that you can tweak for your real-world workloads—on your actual Python version and hardware. It compares a module-level function, a static method, and an instance method for the same simple operation.

import timeit

setup = """
class C:
    @staticmethod
    def s(x): return x + 1
    def i(self, x): return x + 1
def m(x): return x + 1
c = C()
"""

stmt_fn   = "m(1)"
stmt_stat = "C.s(1)"           # or "c.s(1)"
stmt_inst = "c.i(1)"

for label, stmt in [("module", stmt_fn), ("staticmethod", stmt_stat), ("instance", stmt_inst)]:
    t = timeit.timeit(stmt, setup=setup, number=1_000_000)
    print(label, t)

Use this harness on your target Python and hardware; don’t rely on third‑party numbers.

Descriptor Under the Hood (Why Calls Differ)

@staticmethod is a non‑data descriptor. When you access it on a class or an instance, its __get__ simply returns the original function unchanged—no binding to self or cls. By contrast, a plain function defined on a class is a descriptor that returns a bound method when accessed via an instance, capturing self (extra object allocation + indirect call). That is why call overhead typically orders as: module‑level function ≤ staticmethod < instance method.

class C:
    def inst(self, x): return x
    @staticmethod
    def stat(x): return x

c = C()
assert type(c.inst).__name__ == "method"      # bound method
assert callable(C.stat) and callable(c.stat)    # same function, no binding

Understanding this descriptor behavior explains both the performance results and why staticmethods cannot see self/cls unless you pass them explicitly.

Edge Cases and Gotchas

Using @staticmethod with inheritance

@staticmethod participates in normal attribute lookup and is inherited, but it is not polymorphic (no self/cls). Overriding works by name shadowing in subclasses. Because there is no super() context, “calling the parent implementation” requires spelling the base explicitly, e.g., Base.util(...). Be careful when refactoring an instance/class method into a staticmethod in a base class—downstream subclasses that relied on binding (e.g., accessing cls or self) will silently lose that capability. Prefer explicit module-level helpers if you anticipate heavy overriding; they compose better with dependency injection and testing.

Staticmethods in metaclasses

Metaclasses run at class creation time. A staticmethod on a metaclass is just a namespaced utility that you can call as Meta.helper(...) or via SomeClass.__class__.helper(...). Do not expect it to hook instance behavior; for that you need descriptors or __new__/__init_subclass__. Example:

class Meta(type):
    @staticmethod
    def validate_name(name: str) -> str:
        if not name.isidentifier():
            raise ValueError("Invalid class name")
        return name

    def __new__(mcls, name, bases, ns):
        name = mcls.validate_name(name)
        return super().__new__(mcls, name, bases, ns)

Here validate_name is a pure helper; using @classmethod instead would pass mcls, which you only need if the logic depends on the metaclass type.

Dataclasses and attrs

Staticmethods coexist cleanly with @dataclass and attrs models because they are methods, not fields—so they are ignored by field discovery. A frequent pattern is to use a staticmethod as a default_factory provider or as a validation helper. For default_factory, pass the callable itself (which access via the class yields as a plain function):

from dataclasses import dataclass, field
from itertools import count

class IdGen:
    _ctr = count(1)
    @staticmethod
    def new_id() -> int:
        """Deterministic, side‑effect‑free ID generator for examples/tests."""
        return next(IdGen._ctr)

@dataclass
class Order:
    id: int = field(default_factory=IdGen.new_id)

This works because accessing IdGen.new_id returns the underlying function object. The same applies to attrs (attr.s / attr.ib(factory=...)). Keep factories pure and side‑effect free for determinism and testability.

Real‑World Patterns

Using @staticmethod inside factory patterns

In factory or builder designs, use staticmethods for stateless helpers: parsing, validation, normalization—leaving object construction to classmethods (which receive cls). Example:

class User:
    def __init__(self, email, name): self.email, self.name = email, name

    @staticmethod
    def _parse(raw: str) -> tuple[str, str]:
        email, name = raw.split(",", 1)
        return email.strip().lower(), name.strip()

    @classmethod
    def from_csv(cls, raw: str) -> "User":
        email, name = cls._parse(raw)
        return cls(email, name)

_parse is naturally static—no access to cls/self—and can be unit‑tested in isolation. This separation keeps constructors thin and pushes reusable logic into small, composable helpers.

When not to use @staticmethod

Prefer module-level functions when the helper is broadly useful across modules, you want lighter imports, or you expect to inject/memoize/mock it in tests. Over‑namespacing utilities inside classes can increase coupling and lead to awkward imports (from foo import Bar; Bar.util(...)). If usage crosses class boundaries or you foresee reuse in unrelated code, keep it at module scope. As a rule of thumb: make it a staticmethod only if the function’s meaning is tightly coupled to the class’s domain language.

Version Notes (Python 3.12+)

  • Behavioral stability: @staticmethod itself has not changed in Python 3.12–3.13; it remains a non‑data descriptor that returns the wrapped function on access.

  • Decorator ordering still matters: Decorators are applied bottom‑up (nearest to def runs first). If you stack descriptors with type‑checker‑oriented decorators (e.g., typing.deprecated from PEP 702), ensure the deprecation decorator sees a plain function. Prefer:

    from typing_extensions import deprecated  # Python 3.12; use typing.deprecated on 3.13+
    
    @staticmethod          # outer: wraps returned callable as a staticmethod
    @deprecated("Use new_api()")
    def old_api(...): ...
    

    This lets type checkers flag deprecated usage while runtime calls still go through the staticmethod wrapper. Avoid placing @deprecated outside @staticmethod, which would decorate a staticmethod object and may confuse tooling.

  • Typing & linters: Modern type checkers (3.12+ stdlib types, pyright, mypy) correctly model @staticmethod. Annotate arguments/returns explicitly to improve inference and API docs.

Best Practices Checklist

  • Default to clarity. Use @staticmethod only when the logic is tightly coupled to the class’s domain yet needs no self/cls. Otherwise, prefer a module-level function.
  • Design for tests. Static helpers should be pure, side‑effect free, and easy to mock or replace in unit tests. If you need DI, keep it at module scope.
  • Separate construction from parsing. Use staticmethods for parse/validate/normalize steps; keep object creation in @classmethod constructors.
  • Avoid stealth refactors. Converting an instance/class method into a staticmethod in a base class can silently break subclasses that relied on binding.
  • Mind performance last. Minor call‑overheads rarely matter outside tight loops. Measure with timeit on your hardware before optimizing.
  • Type everything. Add explicit type hints to staticmethods to improve IDE help, API docs, and reviewer confidence.
  • Keep the API small. Don’t over‑namescape utilities inside classes; promote to module scope when usage crosses class boundaries.
  • Document intent. One line above the staticmethod stating why it’s static helps reviewers and future maintainers.

Troubleshooting & Debugging

Error: TypeError: add_numbers() takes 2 positional arguments but 3 were given

This usually happens when a function defined like an instance method is called as if it were static, or vice‑versa. Example: def add_numbers(x, y) inside a class but called as obj.add_numbers(1, 2) causes Python to pass self automatically, becoming three arguments. Fixes: (1) add @staticmethod; (2) include self as the first parameter and call on an instance; or (3) move the function to module scope and call it directly.

Common misuses in large codebases

  • Treating staticmethods as polymorphic APIs. They are not—no self/cls, no super().
  • Hiding broadly reusable utilities inside classes, increasing coupling and awkward imports.
  • Refactoring an instance/class method in a base class into a staticmethod, silently breaking subclasses that relied on binding.
  • Stacking decorators in the wrong order (e.g., deprecation decorators outside @staticmethod) and confusing tooling/type checkers.
  • Using staticmethods to reach shared state via globals/singletons; prefer explicit parameters or dependency injection.

@staticmethod in Larger Systems

Django class‑based views (CBV)

Use staticmethods for stateless helpers that parse/normalize request data, leaving HTTP orchestration to CBV methods. This keeps view logic readable and unit‑testable.

from django.views import View
from django.http import JsonResponse

class UserView(View):
    @staticmethod
    def _parse_page(request):
        try:
            return max(1, int(request.GET.get("page", 1)))
        except ValueError:
            return 1

    def get(self, request):
        page = self._parse_page(request)
        # fetch data for page ...
        return JsonResponse({"page": page, "items": []})

FastAPI with Pydantic validators

In Pydantic v2, validators are typically classmethods (@field_validator), but staticmethods shine as pure helpers that validators call. Keep validation rules composable and testable.

from pydantic import BaseModel, field_validator

class User(BaseModel):
    email: str

    @staticmethod
    def _normalize_email(v: str) -> str:
        v = v.strip().lower()
        if "@" not in v:
            raise ValueError("invalid email")
        return v

    @field_validator("email", mode="before")
    def normalize(cls, v: str) -> str:
        return cls._normalize_email(v)

Static helper _normalize_email carries no framework context and is easy to unit test; the classmethod validator wires it into the model lifecycle.

AI/ML Use Cases for @staticmethod

Static methods are valuable in AI/ML codebases because they provide predictable, framework‑agnostic helpers that don’t depend on object state. This makes them easy to test, reuse across pipelines, and wire into CI without hidden side effects. Below are concrete places where @staticmethod improves reliability and developer velocity.

  1. Organizing utility functions in ML pipelines (data checks, preprocessing)
    In training/serving pipelines you often need small, deterministic helpers: shape/type checks, normalization, clipping, and categorical handling. Static methods keep these utilities close to the domain model (discoverable on the class API) without binding to instances.

    class DataValidator:
        @staticmethod
        def check_input_shape(tensor, expected_dim: int) -> bool:
            """Raise if tensor rank mismatches; return True on success."""
            # Works for numpy-like and torch-like tensors with `.ndim`
            if getattr(tensor, "ndim", None) != expected_dim:
                raise ValueError(f"Expected {expected_dim}D tensor, got {getattr(tensor, 'ndim', 'unknown')}D")
            return True
    
        @staticmethod
        def normalize_unit_interval_seq(xs: list[float]) -> list[float]:
            """Return values scaled to [0,1]; stable when all inputs are equal."""
            if not xs:
                return []
            lo, hi = float(min(xs)), float(max(xs))
            rng = (hi - lo) or 1.0
            return [(float(x) - lo) / rng for x in xs]
    

    These helpers are trivially unit‑testable and can be called as DataValidator.check_input_shape(t, 2) across preprocessing stages without constructing objects.

  2. AI linting and PR bots (policy enforcement)
    Teams often encode style and safety policies: e.g., don’t read global state in validators, prefer static helpers for pure preprocessing, or use classmethods for constructors. Static methods make these rules enforceable and easy to scan. A bot can parse diffs, locate @staticmethod utilities, and run rule checks (see linter below) to comment on PRs with actionable feedback.

  3. Refactoring helpers for ML libraries (TensorFlow/PyTorch)
    Migration tasks—renaming state dict keys, converting checkpoints, or cleaning metadata—benefit from pure utilities. A static method expresses that no class/instance state is touched.

    class CheckpointTools:
        @staticmethod
        def remap_keys(state: dict[str, float], mapping: dict[str, str]) -> dict[str, float]:
            """Return a new state dict with keys remapped per `mapping`."""
            return {mapping.get(k, k): v for k, v in state.items()}
    
        @staticmethod
        def strip_prefix(state: dict[str, float], prefix: str) -> dict[str, float]:
            return { (k[len(prefix):] if k.startswith(prefix) else k): v for k, v in state.items() }
    

    These are easy to compose in conversion scripts and safe to call from CI without hidden context.

  4. Auto‑generating tests and docs in CI
    Static methods double as deterministic generators for test parameters or documentation snippets. Because they’re pure, CI can call them to create fixtures or validate docstrings.

    class Schema:
        SPEC = {"email": str, "age": int}
    
        @staticmethod
        def pytest_params():
            """Yield (field, expected_type) pairs for parametric tests."""
            return tuple(Schema.SPEC.items())
    

    Then in tests:

    import pytest
    
    @pytest.mark.parametrize("field, expected", Schema.pytest_params())
    def test_spec_types(field, expected):
        assert field in {"email", "age"}
        assert expected in (str, int)
    
  5. Auto‑detect misuse of @staticmethod vs @classmethod (AST linter example)
    In larger ML codebases, convention drift creeps in. A lightweight linter can flag likely misuses (e.g., a @classmethod that never reads cls, or a @staticmethod that references self/cls).

    # staticmethod_lint.py
    import ast, sys
    
    def deco_name(node: ast.AST):
        """Resolve decorator base name for @name, @pkg.name, @name(), @pkg.name()."""
        target = node.func if isinstance(node, ast.Call) else node
        if isinstance(target, ast.Name):
            return target.id
        if isinstance(target, ast.Attribute):
            return target.attr
        return None
    
    class MethodRule(ast.NodeVisitor):
        def __init__(self):
            self.issues = []
    
        def visit_ClassDef(self, node: ast.ClassDef):
            for item in node.body:
                if isinstance(item, ast.FunctionDef):
                    decos = {deco_name(d) for d in item.decorator_list}
                    argnames = {a.arg for a in item.args.args}
                    if 'classmethod' in decos and 'cls' not in argnames:
                        self.issues.append((item.name, 'classmethod not using cls → consider @staticmethod'))
                    if 'staticmethod' in decos and ({'self', 'cls'} & argnames):
                        self.issues.append((item.name, 'staticmethod has self/cls → consider instance/class method'))
            self.generic_visit(node)
    
    if __name__ == '__main__':
        path = sys.argv[1]
        with open(path, 'r', encoding='utf-8') as f:
            tree = ast.parse(f.read(), filename=path)
        rule = MethodRule(); rule.visit(tree)
        for name, msg in rule.issues:
            print(f"{path}:{name}: {msg}")
        sys.exit(1 if rule.issues else 0)
    

    Run it in CI against changed files or your ML package to prevent regressions.

  6. CI integration stub (GitHub Actions)
    Wire the linter and tests into your pipeline. This example runs the misuse linter and your test suite; add steps for docs generation as needed.

    # .github/workflows/ci.yml
    name: ci
    on: [push, pull_request]
    jobs:
      build:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - uses: actions/setup-python@v5
            with:
              python-version: '3.12'
          - run: pip install pytest
          - name: Run staticmethod/classmethod misuse checks
            run: |
              python - <<'PY'
              import sys, pathlib, subprocess
              paths = [str(p) for p in pathlib.Path('your_package').rglob('*.py')]
              failed = False
              for p in paths:
                  print(f"linting {p}")
                  r = subprocess.run([sys.executable, 'staticmethod_lint.py', p])
                  if r.returncode != 0:
                      failed = True
              sys.exit(1 if failed else 0)
              PY
          - name: Run tests
            run: pytest -q
          # - name: Generate docs (optional)
          #   run: python scripts/generate_docs.py
    

    This keeps AI‑driven linting and test generation practical and repeatable: small, deterministic, and tied directly to your ML workflow.

FAQ Section

1. What is a static method in Python?

A static method in Python is a method that belongs to a class rather than an instance of the class. This means it can be called directly on the class itself, without the need to create an instance of the class. Static methods are essentially utility functions that are grouped within a class for organizational purposes.

Example:

class MathUtilities:
    @staticmethod
    def calculate_factorial(number):
        # Implementation to calculate the factorial of the input number
        pass

# Calling a static method without creating an instance
MathUtilities.calculate_factorial(5)

2. How is a static method different from a class method?

A static method does not have access to the class or instance state, whereas a class method has access to the class state. This means a static method cannot modify or use class or instance variables, whereas a class method can modify class variables.

Example:

class MyClass:
    class_var = "This is a class variable"

    @staticmethod
    def static_method():
        # This method cannot access or modify class_var
        pass

    @classmethod
    def class_method(cls):
        # This method can access and modify class_var
        cls.class_var = "This is a modified class variable"
        pass

3. When should I use a static method in Python?

Use a static method when you need a utility function that is related to a class but does not require access to the class or instance state. This is particularly useful for grouping utility functions that are not specific to an instance but are related to the class.

Example:

class StringUtilities:
    @staticmethod
    def is_palindrome(s):
        # Implementation to check if a string is a palindrome
        pass

# Using a static method to check if a string is a palindrome
StringUtilities.is_palindrome("radar")

4. Can a static method access instance variables?

No, a static method cannot access instance variables because it is not bound to an instance of the class. It can only access class variables if they are explicitly passed as arguments.

Example:

class MyClass:
    def __init__(self, instance_var):
        self.instance_var = instance_var

    @staticmethod
    def static_method(instance_var):
        # This method can access instance_var if it is passed as an argument
        pass

# Creating an instance and passing the instance variable to the static method
my_instance = MyClass("This is an instance variable")
MyClass.static_method(my_instance.instance_var)

5. Why use static methods instead of regular functions?

Use static methods instead of regular functions when you want to group utility functions within a class for better organization and readability. This is particularly useful when the utility functions are closely related to the class but do not require access to the class or instance state.

6. Is @staticmethod faster than @classmethod?

Usually, yes—but the difference is tiny and rarely matters outside tight loops. @classmethod must create a bound method that supplies cls, while @staticmethod returns the original function unchanged. In microbenchmarks, you’ll often see: module‑level function ≤ staticmethod < classmethod ≈ instance method. In real applications, readability and correct design dominate. Measure your own workload with timeit before optimizing, and keep hot paths at module scope if speed truly matters.

7. When should I prefer a free (module‑level) function over @staticmethod?

Choose a free function when the helper is broadly reusable across modules, needs simple mocking/memoization, or you want to reduce coupling to a class’s import path. Module‑level functions also help avoid awkward circular imports and are marginally faster to call. Use @staticmethod only when the behavior is tightly bound to a class’s domain language and benefits from being discovered via the class API (documentation, namespacing, discoverability).

8. Can @staticmethod be overridden in subclasses?

Yes, via name shadowing: defining the same attribute name in the subclass replaces the base’s staticmethod. However, staticmethods are not polymorphic—they don’t receive self or cls, and super() doesn’t apply. If you need dynamic dispatch that respects inheritance, use an instance method or @classmethod. To call a base implementation explicitly, reference it directly (e.g., Base.helper(...)) rather than relying on super.

Conclusion

Static methods are a useful tool in Python for organizing utility functions within a class. They are particularly useful for grouping utility functions that are not specific to an instance but are related to the class. They can be created using the @staticmethod decorator or the staticmethod() function.

To learn more about Python classes and objects, check out these articles:

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the author(s)

Shubham
Shubham
Author
Anish Singh Walia
Anish Singh Walia
Editor
Sr Technical Writer
See author profile

I help Businesses scale with AI x SEO x (authentic) Content that revives traffic and keeps leads flowing | 3,000,000+ Average monthly readers on Medium | Sr Technical Writer @ DigitalOcean | Ex-Cloud Consultant @ AMEX | Ex-Site Reliability Engineer(DevOps)@Nutanix

Vinayak Baranwal
Vinayak Baranwal
Editor
See author profile

Building future-ready infrastructure with Linux, Cloud, and DevOps. Full Stack Developer & System Administrator @ DigitalOcean | GitHub Contributor | Passionate about Docker, PostgreSQL, and Open Source | Exploring NLP & AI-TensorFlow | Nailed over 50+ deployments across production environments.

Category:
Tags:

Still looking for an answer?

Was this helpful?

Why are you adding the numbers in the first example?

- A Discord User#4063

What does w.r.t mean?

- Mike

Thanks …! Wonderful explanation.

- Shweta

Good explanation! But the method is called multiplyNums, but, actually, it is summing the numbers?!

- WA

good explanation

- himanshu

@staticmethod line also the program is same output

- nag

Actually, this example works even if the @staticmethod decorator is removed. However, if the class had a mixture of static and non-static methods then the decorator would be necessary.

- Ken

Amazing explanation

- BitchBark

Creative CommonsThis work is licensed under a Creative Commons Attribution-NonCommercial- ShareAlike 4.0 International License.
Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.