Python - How can I query the class in which a meth

2019-07-20 01:48发布

My question is somewhat similar to this one; it concerns object methods rather than module contents. I want to know if I can use the inspect module to get the methods defined only in the class I'm asking about, and not its parent(s).

I need this because my child class defines 'macro' methods, which accesses the parent's methods at a higher level of abstraction, and I don't want the user to have to worry about the lower-level methods defined all the way up the inheritance tree.

Here is a simplified example:

class Foo(object):
    def __init__(self): pass
    def f1(self): return 3
    def f2(self): return 1

class Bar(Foo):
    def __init__(self): Foo.__init__(self)
    def g1(self): return self.f1() + self.f2()
    def g2(self): return self.f1() - self.f2()

import inspect
inspect.getmembers(Bar, inspect.ismethod)

Output:

[('__init__', <unbound method Bar.__init__>),
 ('f1', <unbound method Bar.f1>),
 ('f2', <unbound method Bar.f2>),
 ('g1', <unbound method Bar.g1>),
 ('g2', <unbound method Bar.g2>)]

The user need not know or care about the existence of the fs since she's only ever going to be interested in the gs. (Of course, this output makes sense in the vast majority of contexts, since all these methods will be bound to the object when it is instantiated.) For a long inheritance tree, the returned list can get very long and full of things that aren't relevant to the user.

How can I get it to leave f1 and f2 off this list? Is there an equivalent to the __module__ attribute for the methods defined in classes? Better still, is it possible to do the same thing with instance methods?

2条回答
We Are One
2楼-- · 2019-07-20 01:54

Methods have an im_class attribute, that points to the class in question. You can use that filter on functions that are members of the class:

inspect.getmembers(Bar,
    lambda m: inspect.ismethod(m) and m.__func__ in m.im_class.__dict__.values())

This gives you:

[
    ('__init__', <unbound method Bar.__init__>),
    ('f1', <unbound method Bar.f1>), 
    ('f2', <unbound method Bar.f2>)
]

Of course, you can just bypass getmembers altogether then:

[m for m in Bar.__dict__.values() if inspect.isfunction(m)]

gives:

[<function __init__ at 0x100a28de8>, <function g1 at 0x100a28e60>, <function g2 at 0x100a28ed8>]

This applies to bound or unbound methods, they have same .__func__ (or im_func, the old name) attribute. The difference between bound and unbound is the value of the .__self__ attribute (None when unbound).

These "secret" attributes are all documented in the Python Data Model.

查看更多
仙女界的扛把子
3楼-- · 2019-07-20 02:03

Hopefully someone will come along with a better solution, but what about:

foo_members = inspect.getmembers(Foo,inspect.ismethod)
bar_members = inspect.getmembers(Bar,inspect.ismethod)

set(bar_members) - set(foo_members)

Of course, in a real situation, you might need to walk through Bar.__bases__ to actually get rid of everything you don't want.

e.g.:

set(bar_members) - set(sum([inspect.getmembers(base,inspect.ismethod) for base in Bar.__bases__],[]))
查看更多
登录 后发表回答