Search code examples
pythonpython-3.x

Python: Get unbound class method


How can you get a not bound class method?

class Foo:
    @classmethod
    def bar(cls): pass

>>> Foo.bar
<bound method type.bar of <class '__main__.Foo'>>

Edit: This is python 3. Sorry for the confusion.


Solution

  • Python 3 does not have unbound methods. Forget about classmethods for a moment, and look at this:

    >>> class Foo:
    ...     def baz(self): pass
    >>> Foo.baz
    <function __main__.baz>
    

    In 2.x, this would be <unbound method Foo.baz>, but 3.x does not have unbound methods.

    If you want to get the function out of a bound method, that's easy:

    >>> foo = Foo()
    >>> foo.baz
    <bound method Foo.baz of <__main__.Foo object at 0x104da6850>>
    >>> foo.baz.__func__
    <function __main__.baz>
    

    In the same way:

    >>> class Foo:
    ...     @classmethod
    ...     def bar(cls): pass
    >>> Foo.bar
    <bound method type.bar of <class '__main__.Foo'>>
    >>> Foo.bar.__func__
    <function __main__.bar>
    

    Things are much more interesting in 2.x, because there actually are unbound methods to get. You can't normally see an unbound classmethod, because the whole point is that they get bound to the class at class creation time, instead of being left unbound and then bound to each instance at instance creation time.

    But really, an unbound method is just any instancemethod whose im_self is None. So, just as you can do this:

    class Foo(object):
        def baz(self): pass
    
    foo = Foo()
    bound_baz = foo.baz
    unbound_baz = new.instancemethod(bound_baz.im_func, None, bound_baz.im_class)
    

    Note that bound_baz.im_func is the 2.x version of bound_baz.__func__ in 3.x—but that new.instancemethod does not have a 3.x equivalent.

    The documentation says that new is deprecated in favor of types, for 3.x compatibility, and in fact, you can do this in 2.x:

    unbound_baz = types.MethodType(bound_baz.im_func, None, bound_baz.im_class)
    

    But that doesn't work in 3.x, because MethodType does not take a class parameter, and does not allow its instance parameter to be None. And personally, when I'm doing something that is explicitly 2.x-only and cannot be ported to 3.x, I think using new is clearer.

    Anyway, given a class in 2.x, you can do this:

    class Foo(object):
        @classmethod
        def bar(cls): pass
    
    bound_bar = Foo.bar
    unbound_bar = new.instancemethod(bound_bar.im_func, None, bound_bar.im_class)
    

    If you print it out, you'll see:

    <unbound method type.bar>
    

    Or, using your example, with an old-style class:

    class Foo:
        @classmethod
        def bar(cls): pass
    
    <unbound method classobj.bar>
    

    And yes, maybe it's a bit of a cheat that the im_class of a classmethod for an old-style class is classobj even though that's not Foo.__class__, but it seems like the most reasonable way to get old-style and new-style classes working similarly in all of the usual use cases.