Behavior of staticmethod in Python 3

Steven D'Aprano steve+comp.lang.python at pearwood.info
Sat Nov 23 08:48:52 EST 2013


On Sat, 23 Nov 2013 09:28:43 +0100, Marco Buttu wrote:

> In Python 3 the following two classes should be equivalent:

They certainly are not equivalent in *any* version of Python, because 
staticmethods are not equivalent to instance methods.


> $ cat foo.py
> class Foo:
>      def foo():
>          pass
>      print(callable(foo))
> 
> class Foo:
>      @staticmethod
>      def foo():
>          pass
>      print(callable(foo))
> 
> But they do not:
> 
> $ python3 foo.py
> True
> False

And Python 2 gives the same result for staticmethods:

[steve at ando ~]$ python2.7
Python 2.7.2 (default, May 18 2012, 18:25:10)
[GCC 4.1.2 20080704 (Red Hat 4.1.2-52)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
py> 
py> class Test(object):
...     @staticmethod
...     def method():
...             pass
...     print callable(method)
...
False


> How come the metaclass does not skip the staticmethod decorator?

What makes you think the metaclass gets the opportunity to skip the 
decorator? By the time the metaclass gets called, the decorator has 
already run.

You seem to be conflating behaviour in Python 2 that doesn't actually 
occur. Staticmethods are not directly callable in any version of Python.

The problem you seem to have is that you want to call a method both 
during and after construction:

class MyClass(object):
    def helper(arg):
        return arg + 1
    x = helper(10)
    y = helper(20)
    def method(self, arg):
        return self.helper(arg)


Here, the attributes x and y rely on calling helper as a function, where 
it does not receive a self argument, but when calling helper from inside 
the instance method, or when calling it like MyClass.helper(), it will 
receive a self argument.

Unfortunately there is no good solution to this using just built-ins. 
staticmethod doesn't work, as it's not callable. However, we can create 
our own callable version of staticmethod:


class callable_staticmethod(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, obj, cls=None):
        return self.func
    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)


class MyClass(object):
    @callable_staticmethod
    def helper(arg):
        return arg + 1
    x = helper(10)
    y = helper(20)
    def method(self, arg):
        return self.helper(arg)


This should now work exactly as you hope.



-- 
Steven



More information about the Python-list mailing list