Pointers to static methods in Python

function-pointers, python, static-methods

Solution

I like to view this behaviour from the "bottom up".

A function in Python acts as a "descriptor object". As such, it has a `__get__()` method.

A read access to a class attribute which has such a `__get__()` method is "redirected" to this method. A attribute access to the class is executed as `attribute.__get__(None, containing_class)`, while an attribute access to the instance is mapped to `attribute.__get__(instance, containing_class)`.

A function's `__get__()` method's task is to wrap the function in a method object which wraps away the `self` parameter - for the case of an attribute access to the instance. This is called a bound method.

On a class attribute access on 2.x, a function's `__get__()` returns an unbound method wrapper, while, as I learned today, on 3.x, it returns itself. (Note that the `__get__()` mechanism still exists in 3.x, but a function just returns itself.) That's nearly the same, if you look at how it is called, but an unbound method wrapper additionally checks for the correct type of the `self` argument.

A `staticmethod()` call just creates an object whose `__get__()` call is designed to return the originally given object so that it undoes the described behaviour. That's how HYRY's trick works: the attribute acces undoes the `staticmethod()` wrapping, the call does it again so that the "new" attribute has the same status as the old one, although in this case, `staticmethod()` seems to be applied twice (but really isn't).

(BTW: It even works in this weird context:

s = staticmethod(8)
t = s.__get__(None, 2) # gives 8

although `8` is not a function and `2` is not a class.)

In your question, you have two situations:

cmd = Cmd.cmdOne
cmd() # works fine

accesses the class and asks for its `cmdOne` attribute, a `staticmethod()` object. This is queried via its `__get__()` and returns the original function, which is then called. That's why it works fine.

Cmd.cmd = Cmd.cmdOne
Cmd.cmd() # unbound error

does the same, but then assigns this function to `Cmd.cmd`. The next line is an attribute access - which does, again, the `__get__()` call to the function itself and thus returns an unbound method, which must be called with a correct `self` object as first argument.

Problem

Why is it that in the following code, using a class variable as a method pointer results in unbound method error, while using an ordinary variable works fine: ``` class Cmd: cmd = None @staticmethod def cmdOne(): print 'cmd one' @staticmethod def cmdTwo(): print 'cmd two' def main(): cmd = Cmd.cmdOne cmd() # works fine Cmd.cmd = Cmd.cmdOne Cmd.cmd() # unbound error !! if __name__=="__main__": main() ``` The full error: ``` TypeError: unbound method cmdOne() must be called with Cmd instance as first argument (got nothing instead) ```

Original source