staticmethod makes my brain hurt

Thomas Rachel nutznetz-0c1b6768-bfa9-48d5-a470-7603bd3aa915 at spamschutz.glglgl.de
Thu Nov 17 04:17:21 EST 2011


Am 17.11.2011 03:30 schrieb Roy Smith:
> When I run this (python 2.6.1):
>
> class C:
>      @staticmethod
>      def foo():
>          pass
>
>      print "inside", foo, callable(foo)
>
> print "outside", C.foo, callable(C.foo)
>
> I get:
>
> inside<staticmethod object at 0x421df0>  False
> outside<function foo at 0x41e6f0>  True

Right. The reason is that on an attribute access, you get a __get__ call 
of the "real" attribute.

That is, if your class has a "regular" method, it is stored there as a 
formal function. But if you access it (C.f), you get 
C.__dict__["f"].__get__(None, C) (not sure about the arguments, but you 
should get the idea).

A functions __get__ returns a unbound or bound method, depending on its 
own arguments.

With a static method, you don't want this to happen. So you wrap your 
"regular" function into a staticmethod object, which has a __get__() 
method itself just returning the wrapped function object.

Look at this:

 >>> class C(object): pass
...
 >>> f=lambda *a:a
 >>> C.f=f
 >>> s=staticmethod(f)
 >>> C.s=s
 >>> # Now we test the access
...
 >>> f
<function <lambda> at 0x00B43E30>
 >>> s
<staticmethod object at 0x00B48A90>
 >>> C.f
<unbound method C.<lambda>>
 >>> C().f
<bound method C.<lambda> of <__main__.C object at 0x00B48810>>
 >>> C.s
<function <lambda> at 0x00B43E30>
 >>> C().s
<function <lambda> at 0x00B43E30>
 >>> f.__get__(None, C)
<unbound method C.<lambda>>
 >>> f.__get__(C(), C)
<bound method C.<lambda> of <__main__.C object at 0x00B48AD0>>
 >>> s.__get__(None, C)
<function <lambda> at 0x00B43E30>
 >>> s.__get__(C(), C)
<function <lambda> at 0x00B43E30>


That's how things work.

If you want to get back the "real" function from a staticmethod, you 
either call its __get__ with an arbitrary argument, or you do it the 
clean way and do a

def deref(s):
     class C(object): s=s
     return s.s

and so do a

class User(Document):
      @staticmethod
      def _get_next_id():
        [blah, blah, blah]
        return id

      user_id = IntField(required=True, default=deref(_get_next_id))


HTH,


Thomas



More information about the Python-list mailing list