web-dev-qa-db-ja.com

メソッドを定義したクラスを取得

Pythonでメソッドを定義したクラスを取得するにはどうすればよいですか?

次の例で「__main__.FooClass ":

class FooClass:
    def foo_method(self):
        print "foo"

class BarClass(FooClass):
    pass

bar = BarClass()
print get_class_that_defined_method(bar.foo_method)
73
Jesse Aldridge
import inspect

def get_class_that_defined_method(meth):
    for cls in inspect.getmro(meth.im_class):
        if meth.__in cls.__dict__: 
            return cls
    return None
62
Alex Martelli

私が指摘していなかったことを指摘してくれたSr2222に感謝します...

これは、アレックスと同じですが、何もインポートする必要のない修正されたアプローチです。ただし、getmroのように継承全体を返すのではなく、定義クラスが見つかるとすぐにこのアプローチが停止するため、継承されたクラスの巨大な階層がない限り、それは改善だとは思いません。前述のように、これは非常にありそうもないシナリオです。

def get_class_that_defined_method(method):
    method_name = method.__name__
    if method.__self__:    
        classes = [method.__self__.__class__]
    else:
        #unbound method
        classes = [method.im_class]
    while classes:
        c = classes.pop()
        if method_name in c.__dict__:
            return c
        else:
            classes = list(c.__bases__) + classes
    return None

そして例:

>>> class A(object):
...     def test(self): pass
>>> class B(A): pass
>>> class C(B): pass
>>> class D(A):
...     def test(self): print 1
>>> class E(D,C): pass

>>> get_class_that_defined_method(A().test)
<class '__main__.A'>
>>> get_class_that_defined_method(A.test)
<class '__main__.A'>
>>> get_class_that_defined_method(B.test)
<class '__main__.A'>
>>> get_class_that_defined_method(C.test)
<class '__main__.A'>
>>> get_class_that_defined_method(D.test)
<class '__main__.D'>
>>> get_class_that_defined_method(E().test)
<class '__main__.D'>
>>> get_class_that_defined_method(E.test)
<class '__main__.D'>
>>> E().test()
1

アレックスソリューションは同じ結果を返します。 Alexのアプローチを使用できる限り、このアプローチの代わりに使用します。

7
estani

なぜ誰もこれを提起したことがないのか、なぜそれが地獄のように遅いときにトップアンサーが50のアップ投票を持っているのか分かりませんが、次のこともできます:

def get_class_that_defined_method(meth):
    return meth.im_class.__name__

python 3これは変更されたと思うので、.__qualname__

3
Nick Chapman

似たようなことを始めました。基本的には、基本クラスのメソッドがサブクラスに実装されているかどうかをチェックするという考え方です。当初の方法が判明したため、中間クラスが実際にメソッドを実装していることを検出できませんでした。

私の回避策は実際には非常に簡単でした。メソッドを設定します属性とその存在を後でテストします。全体の簡略化を以下に示します。

_class A():
    def method(self):
        pass
    method._orig = None # This attribute will be gone once the method is implemented

    def run_method(self, *args, **kwargs):
        if hasattr(self.method, '_orig'):
            raise Exception('method not implemented')
        self.method(*args, **kwargs)

class B(A):
    pass

class C(B):
    def method(self):
        pass

class D(C):
    pass

B().run_method() # ==> Raises Exception: method not implemented
C().run_method() # OK
D().run_method() # OK
_

更新:実際にmethod()run_method()から呼び出し(その精神ではないでしょうか?)、すべての引数を変更せずにメソッドに渡します。

追伸:この回答は質問に直接回答するものではありません。私見では、どのクラスがメソッドを定義したかを知りたい2つの理由があります。 1つ目は、デバッグコード(例外処理など)でクラスに指を向けることであり、2つ目は、メソッドが再実装されているかどうかを判断することです(メソッドは、プログラマーによって実装されるスタブです)。この答えは、2番目のケースを別の方法で解決します。

Python 3で、実際のクラスオブジェクトが必要な場合は、以下を実行できます。

import sys
f = Foo.my_function
vars(sys.modules[f.__module__])[f.__qualname__.split('.')[0]]  # Gets Foo object

関数がネストされたクラスに属する場合、次のように繰り返す必要があります。

f = Foo.Bar.my_function
vals = vars(sys.modules[f.__module__])
for attr in f.__qualname__.split('.'):
    vals = vals[attr]
# vals is now the class Foo.Bar