Make staticmethod objects callable?

Steven D'Aprano steve at REMOVEMEcyber.com.au
Wed Mar 1 03:31:28 EST 2006


Nicolas Fleury wrote:

> Hi everyone,
> I was wondering if it would make sense to make staticmethod objects 
> callable, so that the following code would work:

This is one of the more unintuitive areas of Python, 
with side effects and different behaviour depending on 
whether code is called inside or outside a class:

 >>> class Parrot:
...     def speak():
...             return "dead parrot"
...     speak = staticmethod(speak)
...     def playdead():
...             return "still dead"
...
 >>> type(Parrot.speak)
<type 'function'>
 >>> type(Parrot.playdead)
<type 'instancemethod'>

So, based on this evidence, staticmethod() converts an 
instance method into an ordinary function. Parrot.speak 
certainly behaves like an ordinary function.

 >>> callable(Parrot.speak)
True
 >>> Parrot.speak()
'dead parrot'


But now try to do the same outside of a class:

 >>> f = staticmethod(Parrot.playdead)
 >>> type(f)
<type 'staticmethod'>

f is not a function?

 >>> Parrot.playdead = staticmethod(Parrot.playdead)
 >>> type(Parrot.playdead)
<type 'instancemethod'>

So, based on this evidence, staticmethod() inside a 
class definition converts instance methods to 
functions. Outside a class definition, staticmethod() 
does one of two things: it either converts an instance 
method to a static method, or if the output is assigned 
to a class attribute, it leaves it as an instance method.

Hmmm.



> class A:
>     @staticmethod
>     def foo(): pass
>     bar = foo()

Here is a work around:

 >>> class A:
...     def foo(): return "foo foo foo"
...     foo = staticmethod(foo)
...     def __init__(self):
...             if not hasattr(self.__class__, "bar"):
...                     self.__class__.bar = self.foo()
...
 >>> dir(A)
['__doc__', '__init__', '__module__', 'foo']
 >>> a = A()
 >>> dir(A)
['__doc__', '__init__', '__module__', 'bar', 'foo']
 >>> a.foo()
'foo foo foo'
 >>> a.bar
'foo foo foo'


Here is a more interesting example:

 >>> class B:
...     def foo():
...         return lambda x: x+1
...     foo = staticmethod(foo)
...     def __init__(self):
...             if not hasattr(self.__class__, "bar"):
...                     self.__class__.bar = \
...                     staticmethod(self.foo())
...
 >>> dir(B)
['__doc__', '__init__', '__module__', 'foo']
 >>> b = B()
 >>> dir(B)
['__doc__', '__init__', '__module__', 'bar', 'foo']
 >>> b.foo()
<function <lambda> at 0xb7f70c6c>
 >>> b.bar
<function <lambda> at 0xb7f70c34>
 >>> b.bar(3)
4


Hope this helps.



-- 
Steven.




More information about the Python-list mailing list