How would you determine where each property and method of a Python class is defined?
Question: Given an instance of some class in Python, it would be useful to be able to
determine which line of source code _defined_ each method and property (e.g.
to implement [1]). For example, given a module ab.py

    
    
    class A(object):
        z = 1
        q = 2
        def y(self): pass
        def x(self): pass
    
    class B(A):
        q = 4
        def x(self): pass
        def w(self): pass
    

define a function whither(class_, attribute) returning a tuple containing the
filename, class, and line in the source code that defined or subclassed
`attribute`. This means the definition in the class body, not the latest
assignment due to overeager dynamism. It's fine if it returns 'unknown' for
some attributes.

    
    
    >>> a = A()
    >>> b = B()
    >>> b.spigot = 'brass'
    >>> whither(a, 'z')
    ("ab.py", <class 'a.A'>, [line] 2)
    >>> whither(b,  'q')
    ("ab.py", <class 'a.B'>, 8)
    >>> whither(b, 'x')
    ("ab.py", <class 'a.B'>, 9)
    >>> whither(b, 'spigot')
    ("Attribute 'spigot' is a data attribute")
    

I want to use this while introspecting Plone, where every object has hundreds
of methods and it would be really useful to sort through them organized by
class and not just alphabetically.

Of course, in Python you can't always reasonably know, but it would be nice to
get good answers in the common case of mostly-static code.


Answer: This is more-or-less impossible without static analysis, and even then, it
won't always work. You can get the line where a function was defined and in
which file by examining its code object, but beyond that, there's not much you
can do. The `inspect` module can help with this. So:

    
    
    import ab
    a = ab.A()
    meth = a.x
    # So, now we have the method.
    func = meth.im_func
    # And the function from the method.
    code = func.func_code
    # And the code from the function!
    print code.co_firstlineno, code.co_filename
    
    # Or:
    import inspect
    print inspect.getsource(meth), inspect.getfile(meth)
    

But consider:

    
    
    def some_method(self):
        pass
    ab.A.some_method = some_method
    ab.A.some_class_attribute = None
    

Or worse:

    
    
    some_cls = ab.A
    some_string_var = 'another_instance_attribute'
    setattr(some_cls, some_string_var, None)
    

Especially in the latter case, what do you want or expect to get?

