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?
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))
The output will be:
<class 'function'> <class 'function'>
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))
The output is something like:
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