Get defining class of unbound method object in Python 3

Viewed 27332

Say I want to make a decorator for methods defined in a class. I want that decorator, when invoked, to be able to set an attribute on the class defining the method (in order to register it in a list of methods that serve a particular purpose).

In Python 2, the im_class method accomplishes this nicely:

def decorator(method):
  cls = method.im_class
  cls.foo = 'bar'
  return method

However, in Python 3, no such attribute (or a replacement for it) seems to exist. I suppose the idea was that you could call type(method.__self__) to get the class, but this does not work for unbound methods, since __self__ == None in that case.

NOTE: This question is actually a bit irrelevant for my case, since I've chosen instead to set an attribute on the method itself and then have the instance scan through all of its methods looking for that attribute at the appropriate time. I am also (currently) using Python 2.6. However, I am curious if there is any replacement for the version 2 functionality, and if not, what the rationale was for removing it completely.

EDIT: I just found this question. This makes it seem like the best solution is just to avoid it like I have. I'm still wondering why it was removed though.

5 Answers

Since python 3.6 you could accomplish what you are describing using a decorator that defines a __set_name__ method. The documentation states that object.__set_name__ is called when the class is being created.

Here is an example that decorates a method "in order to register it in a list of methods that serve a particular purpose":

>>> class particular_purpose: 
...     def __init__(self, fn): 
...         self.fn = fn 
...      
...     def __set_name__(self, owner, name): 
...         owner._particular_purpose.add(self.fn) 
...          
...         # then replace ourself with the original method 
...         setattr(owner, name, self.fn) 
...  
... class A: 
...     _particular_purpose = set() 
...  
...     @particular_purpose 
...     def hello(self): 
...         return "hello" 
...  
...     @particular_purpose 
...     def world(self): 
...         return "world" 
...  
>>> A._particular_purpose
{<function __main__.A.hello(self)>, <function __main__.A.world(self)>}
>>> a = A() 
>>> for fn in A._particular_purpose: 
...     print(fn(a)) 
...                                                                                                                                     
world
hello

Note that this question is very similar to Can a Python decorator of an instance method access the class? and therefore my answer as well to the answer I provided there.

A small extension for python 3.6 (python 2.7 worked fine) to the great answer of https://stackoverflow.com/a/25959545/4013571

def get_class_that_defined_method(meth):
    if inspect.ismethod(meth):
        for cls in inspect.getmro(meth.__self__.__class__):
            if cls.__dict__.get(meth.__name__) is meth:
                return cls
        meth = meth.__func__  # fallback to __qualname__ parsing
    if inspect.isfunction(meth):
        class_name = meth.__qualname__.split('.<locals>', 1)[0].rsplit('.', 1)[0]
        try:
            cls = getattr(inspect.getmodule(meth), class_name)
        except AttributeError:
            cls = meth.__globals__.get(class_name)
        if isinstance(cls, type):
            return cls
    return None  # not required since None would have been implicitly returned anyway

I found the following adjustment was required for doctest

        except AttributeError:
            cls = meth.__globals__.get(class_name)

As for some reason, when using nose the inspect.getmodule(meth) didn't contain the defining class

Related