Why can static methods be called through the class "instance" in Python 3?

Question:

Let's say I have the following class:

class Person:
    @staticmethod
    def hello():
        print('Hello!)

When performing the Person().hello() the method is executed normally. But the hello method belongs to the Person class, not an instance of the class. Could anyone tell me why this statement works?

Answer:

This has, in a way, to do with the instance and class attributes, along with the Python language construction, more precisely the decorators. Unlike other languages, the definition of a static method – or a class, using @classmethod – is done through a decorator, not a language keyword. What does it change? Changes that when using a decorator on a method, the reference to this method is not directly accessible, since the decorator's return is a function – and it is this function that invokes the method reference. You can confirm this by doing:

class Person:
    @staticmethod
    def hello():
        print('Hello!')

print(type(Person.hello))
print(type(Person().hello))

See it working on Ideone | Repl.it

The output will be:

<class 'function'>
<class 'function'>

And not method , as would be expected. To better understand the behavior, just remember how a decorator works: a function that returns another function (roughly speaking). So much so that it is possible to call the decorator explicitly.

class Person:
    def __hello():
        print('Hello!')
    hello = staticmethod(__hello)

This code, for practical purposes, is the equivalent of the previous one, which uses the @staticmethod , but in this one it is clear that what is created is a class attribute called hello , which is a function, returned by the decorator. As a class attribute, it will also be accessible on instances – and since it is a class attribute, it maintains the reference to the same object in both the class and the instance. We can confirm this by checking the id of each one:

class Person:
    @staticmethod
    def hello():
        print('Hello!')

print(id(Person.hello))
print(id(Person().hello))

See it working on Ideone | Repl.it

The output is something like:

47914624982008
47914624982008

Which indicates that the objects accessed by the class or the instance are exactly the same.

And why isn't self passed by parameter?

Precisely because the object does not point to a method, but to a function (which is the decorator's return). The first parameter, self , is defined implicitly in a method call by Python, however, as in this case what happens is a function call – and it is the function that invokes the method – such parameter is not passed (because not it makes sense to access an instance in a static method).

We can think of staticmethod as something like:

def staticmethod(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
    return wrapper

Note that in the func call, which would be the ( evil ) invocation of the method, there is no parameter equivalent to self being passed.

But what if you need to access the class's attributes?

If inside the method it is necessary to access the class attributes, it is not a static method that we need, but a class method. Unlike the static method, the class method receives as its first parameter an object that is the reference to the class itself:

class Person:
    attr = "SOpt"

    @classmethod
    def hello(cls):
        print('Hello', cls.attr)

This happens because, unlike @staticmethod , in @classmethod there is the class's passing as the first parameter. It would be something like:

def classmethod(func)
    def wrapper(*args, **kwargs):
        classObject = inspect.get_glass(func) # hipotético
        func(classObject, *args, **kwargs)
    return wrapper
Scroll to Top