22

So this code:

from inspect import *

class X(object):
  def y(self):
    pass

methods = getmembers(X, predicate=ismethod)
functions = getmembers(X, predicate=isfunction)

print("%r" % methods)
print("%r" % functions)

From python2.7 yields:

[('y', <unbound method X.y>)]
[]

and from python3.3 yields:

[]
[('y', <function X.y at 0x1006ee3b0>)]

I've nosed around, but I can't see any obvious reason for this change in behavior.

Specifically, why is python 3 treating my method as a function?

Is there any cross-runtime way of fetching the list of methods on a class?

(ie. something that returns the same when run on both python2.X and 3.X)

Edit: Example of getmembers() not working:

from inspect import *

class X(object):
  def y(self):
    pass

methods = getmembers(X)
for i in methods:
  if ismethod(i):
    print("Method: %s" % str(i))
  else:
    print("Not a method: %s" % str(i))

Prints:

Not a method: ('__class__', <attribute '__class__' of 'object' objects>)
Not a method: ('__delattr__', <slot wrapper '__delattr__' of 'object' objects>)
Not a method: ('__dict__', <attribute '__dict__' of 'X' objects>)
Not a method: ('__dir__', <method '__dir__' of 'object' objects>)
Not a method: ('__doc__', None)
Not a method: ('__eq__', <slot wrapper '__eq__' of 'object' objects>)
Not a method: ('__format__', <method '__format__' of 'object' objects>)
Not a method: ('__ge__', <slot wrapper '__ge__' of 'object' objects>)
Not a method: ('__getattribute__', <slot wrapper '__getattribute__' of 'object' objects>)
Not a method: ('__gt__', <slot wrapper '__gt__' of 'object' objects>)
Not a method: ('__hash__', <slot wrapper '__hash__' of 'object' objects>)
Not a method: ('__init__', <slot wrapper '__init__' of 'object' objects>)
Not a method: ('__le__', <slot wrapper '__le__' of 'object' objects>)
Not a method: ('__lt__', <slot wrapper '__lt__' of 'object' objects>)
Not a method: ('__module__', '__main__')
Not a method: ('__ne__', <slot wrapper '__ne__' of 'object' objects>)
Not a method: ('__new__', <built-in method __new__ of type object at 0x1001e0640>)
Not a method: ('__reduce__', <method '__reduce__' of 'object' objects>)
Not a method: ('__reduce_ex__', <method '__reduce_ex__' of 'object' objects>)
Not a method: ('__repr__', <slot wrapper '__repr__' of 'object' objects>)
Not a method: ('__setattr__', <slot wrapper '__setattr__' of 'object' objects>)
Not a method: ('__sizeof__', <method '__sizeof__' of 'object' objects>)
Not a method: ('__str__', <slot wrapper '__str__' of 'object' objects>)
Not a method: ('__subclasshook__', <method '__subclasshook__' of 'object' objects>)
Not a method: ('__weakref__', <attribute '__weakref__' of 'X' objects>)
Not a method: ('y', <function X.y at 0x1006ee3b0>)
Doug
  • 32,844
  • 38
  • 166
  • 222
  • 1
    All of the answers here are correct, however the check also fails because `getmembers` returns a list of tuples `(name, object)` and `ismethod(some_tuple)` is always `False`. – Joe G Oct 19 '15 at 02:56

2 Answers2

22

Not specifically a difference with inspect but Python 3 in general see here

The concept of “unbound methods” has been removed from the language. When referencing a method as a class attribute, you now get a plain function object.

My suggestion for cross-platform would be:

getmembers(X, predicate=lambda x: isfunction(x) or ismethod(x))
jamylak
  • 128,818
  • 30
  • 231
  • 230
  • yes, but "Is there any cross-runtime way of fetching the list of methods on a class?" – georg Jun 10 '13 at 08:53
  • @thg435 My best guess is just check both to account for the change in Python 3 – jamylak Jun 10 '13 at 08:57
  • 7
    Yes, this appears to be the only way, unless someone fixes `ismethod` to return what's advertised: ["true if the object is a bound method"](http://docs.python.org/2/library/inspect.html#inspect.ismethod). – georg Jun 10 '13 at 09:18
8

Because essentialy there is no difference between a function and an unbound method. This idea of unbound methods exists in Python 2 mostly for historical reasons and was removed in Python 3.

This email by the BDFL himself goes into some details.


Answering your updated question. I think its best to use inspect.isroutine as it matches both unbound methods and functions with an additional benefit of also matching methods/functions implemented in C.

Start with inspect.getmembers and filter its output as you need. It also takes an optional predicate parameter.

kirelagin
  • 13,248
  • 2
  • 42
  • 57
  • Um... I want a list of methods, not to check a known name. – Doug Jun 10 '13 at 08:55
  • @Doug Just use it together with `inspect.getmembers`. – kirelagin Jun 10 '13 at 08:56
  • @Doug oh, that's by bad. `ismethod` checks for _bound_ methods. It's strange that it works in Python 2 for unbound ones too. I'd suggest using `isroutine` then. – kirelagin Jun 10 '13 at 09:01
  • @Doug That can't be true. That lambda won't work for C methods (try with `list.append`). `isroutine` matches _any_ function. – kirelagin Jun 10 '13 at 09:05
  • My code is at the top of the page; run it yourself with isroutine; it dumps the same result. – Doug Jun 10 '13 at 09:08
  • @Doug `getmembers` returns _pairs_ of names and objects, you can't use it this way. Use `predicate=inspect.isroutine` or replace your `if` with `if ismethod(i[1])` in the loop. – kirelagin Jun 10 '13 at 09:12