How to test if an object is a function vs. an unbound method?
Question:
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
??
Answers:
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.
This is what I came up with… According to comments in correct answer, valid for 2.x only
def is_unbound_method(func):
"""Test if ``func`` is an unbound method.
>>> def f(): pass
>>> class MyClass(object):
... def f(self): pass
>>> is_unbound_method(f)
False
>>> is_unbound_method(MyClass().f)
False
>>> is_unbound_method(MyClass.f)
True
"""
return getattr(func, 'im_self', False) is None
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).
I know that this is a very old question, but it shows up in Google, so I will add another way: you can take a look at functions __qualname__
attribute.
Here is my code:
class Test:
def method(self, x: int):
print(self, x)
def free_func(x: int):
print("I'm a func!", x)
def top_func():
def local_func(x: int):
print("I'm a func!", x)
show_contents(local_func)
def show_contents(obj):
print(getattr(obj, '__name__', ''))
print(getattr(obj, '__qualname__', ''))
t = Test()
print('--- Instance ---')
show_contents(t.method)
print('--- Class ---')
show_contents(Test.method)
print('--- Function ---')
show_contents(free_func)
print('--- Nested Function ---')
top_func()
And here is the output:
--- Instance ---
method
Test.method
--- Class ---
method
Test.method
--- Function ---
free_func
free_func
--- Nested Function ---
local_func
top_func.<locals>.local_func
It’s more than a little hacky to use, but __qualname__
has been available since 3.3, so it should work, at least.
So you could use a function like this:
def is_method(func) -> bool:
if not callable(func):
raise ValueError(f"{func!r} is not a callable object")
qualname = func.__qualname__
name = func.__name__
if qualname == name: # it's a top-level function
return False
elif qualname.endswith('.'+name): # it's either a nested function or a method
prefix = qualname[:-len(name) - 1]
return not prefix.endswith('<locals>')
else: # what is it, even?
raise ValueError(f"Can't tell if {func!r} is a 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
??
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.
This is what I came up with… According to comments in correct answer, valid for 2.x only
def is_unbound_method(func):
"""Test if ``func`` is an unbound method.
>>> def f(): pass
>>> class MyClass(object):
... def f(self): pass
>>> is_unbound_method(f)
False
>>> is_unbound_method(MyClass().f)
False
>>> is_unbound_method(MyClass.f)
True
"""
return getattr(func, 'im_self', False) is None
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).
I know that this is a very old question, but it shows up in Google, so I will add another way: you can take a look at functions __qualname__
attribute.
Here is my code:
class Test:
def method(self, x: int):
print(self, x)
def free_func(x: int):
print("I'm a func!", x)
def top_func():
def local_func(x: int):
print("I'm a func!", x)
show_contents(local_func)
def show_contents(obj):
print(getattr(obj, '__name__', ''))
print(getattr(obj, '__qualname__', ''))
t = Test()
print('--- Instance ---')
show_contents(t.method)
print('--- Class ---')
show_contents(Test.method)
print('--- Function ---')
show_contents(free_func)
print('--- Nested Function ---')
top_func()
And here is the output:
--- Instance ---
method
Test.method
--- Class ---
method
Test.method
--- Function ---
free_func
free_func
--- Nested Function ---
local_func
top_func.<locals>.local_func
It’s more than a little hacky to use, but __qualname__
has been available since 3.3, so it should work, at least.
So you could use a function like this:
def is_method(func) -> bool:
if not callable(func):
raise ValueError(f"{func!r} is not a callable object")
qualname = func.__qualname__
name = func.__name__
if qualname == name: # it's a top-level function
return False
elif qualname.endswith('.'+name): # it's either a nested function or a method
prefix = qualname[:-len(name) - 1]
return not prefix.endswith('<locals>')
else: # what is it, even?
raise ValueError(f"Can't tell if {func!r} is a method")