Behavior of staticmethod in Python 3

Steven D'Aprano steve+comp.lang.python at pearwood.info
Sun Nov 24 15:18:07 CET 2013


On Sun, 24 Nov 2013 11:30:14 +0100, Antoon Pardon wrote:

> Foo.foo() is legal here. So Foo.foo is callable. 

Incorrect. Foo.foo() is legal for *any* identifiers Foo and foo. Since 
Python is an extremely dynamic language, the compiler cannot (easily, or 
at all) prohibit "illegal" combinations. The only combinations which are 
illegal are those which are not legal identifier, e.g.:

while.spam
abc$xyz.eggs

Otherwise, any pair of legal identifiers are legal and will be accepted 
by the compiler. Only at runtime does the lookup succeed or fail:

str.length  # likely to fail, unless str has been shadowed
str.find  # likely to succeed, unless str has been shadowed

If the lookup has succeeded, then and only then does a second lookup 
occur, namely:

type(str.find).__call__

and if that succeeds it is called.

It isn't helpful to talk about function or method calls being "legal" or 
"illegal" in Python, since such things are normally determined in terms 
of *success* or *failure*, not permitted versus prohibited. Either the 
full chain of lookups and function call will succeed, or something will 
raise an exception and it will fail.

 
> Foo.foo() being legal and Foo.foo not being callable is IMO a bug in
> python. No matter what explanation you have for the behaviour.

I don't know why you keep going on about this point, since this is NOT 
the behaviour the original poster is talking about. With foo decorated as 
a staticmethod in class Foo, Foo.foo *is* callable. It takes 30 seconds 
to try it yourself:


py> class Foo(object):  # inherit from object necessary in Python 2
...     @staticmethod
...     def foo():
...             return "Success!"
...
py>
py> Foo.foo()
'Success!'
py> Foo().foo()
'Success!'


This is not what the OP is talking about. Try this instead:

py> Foo.__dict__['foo']()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'staticmethod' object is not callable


What's going on here? Let's have a look:

py> Foo.__dict__['foo']
<staticmethod object at 0x9d83194>
py> Foo.foo
<function foo at 0x9d80294>


You CANNOT understand this behaviour without understanding the descriptor 
protocol, which is *fundamental* to Python, and has been since Python 
2.2. Regular instance methods, class methods, static methods and 
properties are all defined in terms of descriptors. Trying to understand 
them without knowledge of descriptors is like trying to understand 
generators and iterators without knowledge of StopIteration -- you can go 
only so far before you get stuck and confused.

The OP discovered that you can call a regular function inside a class 
body, outside of a method, during the class statement:


class MyClass:
    def function():
        return "stuff"

    attr = function()


This works, but then you can't call MyClass().function() as it will 
raise. So the OP tried making it a staticmethod:

class MyClass:
    @staticmethod
    def function():
        return "stuff"

    attr = function()


but that fails, because *staticmethod instances are not callable*. They 
are descriptors. Only after the descriptor protocol gets a chance to run 
do you get a callable function.

*This is not a bug.* Making staticmethod instances callable is a feature 
enhancement, not a bug fix. Or you could just define your own callable-
staticmethod descriptor, it's easy enough to do.



-- 
Steven



More information about the Python-list mailing list