6

If a variable refers to either a function or a class method, how can I find out which one it is and get the class type in case it is a class method especially when the class is still being declared as in the given example.

eg.

def get_info(function_or_method):
    print function_or_method

class Foo(object):
    def __init__(self):
        pass

    get_info(__init__)

def bar():
    pass

get_info(bar)

Update to question after the first two responses from David and J. F. Sebastian To reemphasize a point which J.F. Sebastian alluded to, I want to be able to distinguish it when the function is being declared within the class (when the type I am getting is a function and not a bound or unbound method). ie. where the first call to get_info(__init__) happens I would like to be able to detect that its a method being declared as a part of a class.

This question came up since I am putting a decorator around it and it gets a handle to the init function and I can't actually figure out if a method is being declared within a class or as a stand alone function

hexchain
  • 96
  • 10
Dhananjay Nene
  • 672
  • 6
  • 15
  • There's no reason why a decorator should work for BOTH unbound functions and method functions. Why are you trying to do this? Why not have two decorators? – S.Lott Mar 30 '09 at 10:21

3 Answers3

13

You can distinguish between the two by checking the type:

>>> type(bar)
<type 'function'>
>>> type(Foo.__init__)
<type 'instancemethod'>

or

>>> import types
>>> isinstance(bar, types.FunctionType)
True
>>> isinstance(bar, types.UnboundMethodType)
True

which is the way you'd do it in an if statement.

Also, you can get the class from the im_class attribute of the method:

>>> Foo.__init__.im_class
__main__.Foo
David Z
  • 128,184
  • 27
  • 255
  • 279
  • It is worth mentioning that "" is a (somewhat inconsistently) string representation of `types.MethodType` class (isinstance(Foo.__init__, types.MethodType) == True). – jfs Mar 30 '09 at 02:07
  • btw, isinstance(bar, types.UnboundMethodType) is False – jfs Mar 30 '09 at 02:08
  • In addition, you can't distinguish `Foo.__init__` (unbound method) and `Foo().__init__` (bound method) using `isinstance(...)`. You should check `im_self` (`__self__` 2.6+) attribute. – jfs Mar 30 '09 at 02:11
9

At the time you are calling get_info(__init__) (inside class definition) the __init__ is an ordinary function.

def get_info(function_or_method):
    print function_or_method

class Foo(object):
    def __init__(self):
        pass
    get_info(__init__)   # function

def bar():
    pass

get_info(Foo.__init__)   # unbound method
get_info(Foo().__init__) # bound method
get_info(bar)            # function

Output (CPython, IronPython):

<function __init__ at ...>
<unbound method Foo.__init__>
<bound method Foo.__init__ of <__main__.Foo object at ...>>
<function bar at ...>

Output (Jython):

<function __init__ 1>
<unbound method Foo.__init__>
<method Foo.__init__ of Foo instance 2>
<function bar 3>
jfs
  • 399,953
  • 195
  • 994
  • 1,670
4

To reemphasize a point which J.F. Sebastian alluded to, I want to be able to distinguish it when the function is being declared within the class (when the type I am getting is a function and not a bound or unbound method). ie. where the first call to get_info(__init__) happens I would like to be able to detect that its a method being declared as a part of a class.

This question came up since I am putting a decorator around it and it gets a handle to the init function and I can't actually figure out if a method is being declared within a class or as a stand alone function

You can't. J.F. Sebastian's answer is still 100% applicable. When the body of the class definition is being executed, the class itself doesn't exist yet. The statements (the __init__ function definition, and the get_info(__init__) call) happen in a new local namespace; at the time the call to get_info occurs, __init__ is a reference to the function in that namespace, which is indistinguishable from a function defined outside of a class.

Community
  • 1
  • 1
Miles
  • 31,360
  • 7
  • 64
  • 74