davidvgalbraith
davidvgalbraith

Reputation: 461

Does the @staticmethod decorator do anything?

I made these two classes:

class A:
    @staticmethod
    def f(x):
        print("x is", x)

class B:
    def f(x):
        print("x is", x)

And used them like this:

>>> A.f(1)
x is 1
>>> B.f(1)
x is 1

It looks like f became a static method on B even without the decorator. Why would I need the decorator?

Upvotes: 14

Views: 7579

Answers (3)

User051209
User051209

Reputation: 2503

The OP's invocation:

A.f(1)
B.f(1)

are examples of Class Attribute references, where the attribute in these cases is the method f() of the class A (@staticmethod) and the method f() (non static) of the class B.
As showed below, the execution of these invocations gives different output for Python2 and Python3.

Python 2.7.17

With Python 2.7.17 the execution of the code of the OP gives the following output:

>>> from classdef import A, B
>>> A.f(1)
('x is', 1)
>>> B.f(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method f() must be called with B instance as first argument (got int instance instead)

This output shows that with Python2 the decorator @staticmethod (present only in the method f of the class A) is able to avoid the runtime error.

Python 3.6.9

With Python 3.6.9 the execution of the code of the OP gives the following output:

>>> from classdef import A,B
>>> A.f(1)
x is 1
>>> B.f(1)
x is 1

So the OP uses a Python3 interpreter which gives the same result with or without the decorator @staticmethod.

How @staticmethod works with instance of a class (the same for Python2 and Python3)

The following information are true for Python2 and Python3.

As explained in an other answer, the @staticmethod decorator causes that (when the method is called) it is not passed to it a reference to the object use for the calling. This means that you must explicitly pass it a correct reference.

In the following code example is showed how to call a staticmethod of a class by a reference to an instance of the class:

class MyClass:
    attributeMyClass = "this is a Class attribute"

    def __init__(self):
        self.attributeMyClass = "this is an instance attribute"

    @staticmethod
    def show_attribute(self):
        print(self.attributeMyClass)

# x is an instance of the class MyClass
x = MyClass()

# the argument is a MyClass object
x.show_attribute(MyClass)

# the argument is an instance of the class MyClass
x.show_attribute(x)

The output of the code execution is:

this is a Class attribute
this is an instance attribute

The print() instruction of the staticmethod show_attribute() prints a different variable in the two calls.

Upvotes: 0

All Іѕ Vаиітy
All Іѕ Vаиітy

Reputation: 26372

Try these two classes, both having a cry method, one as a classmethod and another as a staticmethod with self passed on

class Cat:

    def __init__(self):
        self.sound = "meow"

    def cry(self):
        print(self.sound)

x = Cat()
x.cry()
meow

and with another class

class Dog:
    def __init__(self):
        self.sound = "ruff-ruff"

    @staticmethod
    def cry(self):
        print(self.sound)

x = Dog()
x.cry()
TypeError: cry() missing 1 required positional argument: 'self'

and we can see the @staticmethod decorator basically removed the passed in self

Upvotes: 4

user2357112
user2357112

Reputation: 280227

It used to matter more back in Python 2, where the instance-ness of instance methods was enforced more strongly:

>>> class B:
...     def f(x):
...         print("x is", x)
...
>>> B.f(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method f() must be called with B instance as first argument (
got int instance instead)

You had to mark static methods with @staticmethod back then.

These days, @staticmethod still makes it clearer that the method is static, which helps with code readability and documentation generation, and it lets you call the method on instances without the system trying to bind self.

Upvotes: 10

Related Questions