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
??
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__
, andim_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.
In Python 3 there's no reliable way to determine that just from the function object (since any function defined in a class is just a function like any other).
Perhaps a sub-optimal approach is to inspect the signature and check for self
as the first parameter:
import inspect
def is_unbound_method(obj):
signature = inspect.signature(obj)
if not signature.parameters:
return False
return next(iter(signature.parameters), None) == "self"
But of course, this depends on the first parameter being named self
(and on other functions not using self
as the first parameter), which is just a convention.
If you already know the object is defined within a class, perhaps a better approach is to check whether it's a callable that's not a classmethod
or a staticmethod
:
import inspect
def is_unbound_method_from(cls, obj):
return bool(
callable(obj)
and not isinstance(obj, (classmethod, staticmethod))
and inspect.getmembers(cls, lambda m: m is obj)
)
You should reorder the clauses in the conjunction above from what you believe is least likely to most likely (to avoid unnecessary computation).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With