Search code examples
pythonintrospection

How to test if an object is a function vs. an unbound method?


def is_unbound_method(func):
    pass

def foo(): pass

class MyClass(object):
    def bar(self): pass

What can I put in the body of is_unbound_method so that

is_unbound_method(foo) == False
is_unbound_method(MyClass().bar) == False
is_unbound_method(MyClass.bar) == True

??


Solution

  • An unbound method has __self__ set to None:

    def is_unbound_method(func):
        return getattr(func, '__self__', 'sentinel') is None
    

    Demo:

    >>> foo.__self__
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'function' object has no attribute '__self__'
    >>> is_unbound_method(foo)
    False
    >>> MyClass.bar.__self__
    >>> is_unbound_method(MyClass.bar)
    True
    >>> MyClass().bar.__self__
    <__main__.MyClass object at 0x106c64a50>
    >>> is_unbound_method(MyClass().bar)
    False
    

    The attribute is also available as .im_self, but __self__ is forward compatible.

    Note that in Python 3 unbound methods are gone; accessing MyClass.bar returns the function object. Thus the above function will always return False.

    See the Datamodel documentation, in the User-defined methods section:

    Special read-only attributes: im_self is the class instance object, im_func is the function object

    [...]

    Changed in version 2.6: For Python 3 forward-compatibility, im_func is also available as __func__, and im_self as __self__.

    [...]

    When a user-defined method object is created by retrieving a user-defined function object from a class, its im_self attribute is None and the method object is said to be unbound.