Define a simple method:
class Foo:
def bar(self):
print('bar!')
Now use inspect.ismethod
:
>>> from inspect import ismethod
>>> ismethod(Foo.bar)
False
Why is this? Isn't bar
a method?
Consult the documentation:
inspect.ismethod(object)
Return
True
if the object is a bound method written in Python.
So ismethod
is actually only testing for bound methods. That means if you create an instance and access the method through the instance, ismethod
will return True
:
>>> obj = Foo()
>>> ismethod(obj.bar)
True
A "bound" method means a method which is already bound to an object, so that object is provided as the self
parameter. This is how you can call obj.bar()
with no arguments, even though bar
was declared to have one parameter.
We can also see the difference by looking at the types:
>>> Foo.bar
<function Foo.bar at 0x7853771f1ab0>
>>> obj.bar
<bound method Foo.bar of <__main__.Foo object at 0x7853771e4850>>
Only the latter is a bound method. Foo.bar
is a function, it is not considered to be a method by Python's type system.
So why does inspect.ismethod
behave this way?
From Python's perspective, bar
is an ordinary function which happens to be referenced by an attribute of the class Foo
. To make this clear, suppose you define a function outside of the class, and then assign it to a class attribute:
def baz(self):
print('baz!')
Foo.baz = baz
If you call ismethod(baz)
you should expect it to be False
because baz
is obviously just a function declared in the outer scope. And you will get the same result if you call ismethod(Foo.baz)
, because Foo.baz
is just a reference to the same function. In fact, the expression Foo.baz
is evaluated to get a reference to the function before that reference is passed as an argument to ismethod
; of course, ismethod
can't give different answers for the same argument depending on where that argument comes from.
The same applies when the function bar
is declared inside the class Foo
. The way classes work in Python, this is pretty much equivalent to declaring the function outside of the class and then assigning it to a class attribute of the same name as the function. So bar
and baz
work exactly the same way:
>>> obj.baz
<bound method baz of <__main__.Foo object at 0x7853771e4850>>
>>> obj.baz()
baz!
In short, Foo.bar
is "not a method" because in Python, "methods" are just ordinary functions until they are bound to instances.