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


89

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)

どのバージョンのPythonを使用していますか?2.2より前では、im_classを使用できましたが、バインドされた自己オブジェクトのタイプを表示するように変更されました。
キャシーヴァンストーン

1
知っておくと良い。しかし、私は2.6を使用しています。
Jesse Aldridge

回答:


71
import inspect

def get_class_that_defined_method(meth):
    for cls in inspect.getmro(meth.im_class):
        if meth.__name__ in cls.__dict__: 
            return cls
    return None

1
おかげで、これを自分で理解するのに時間がかかったでしょう
David

すべてのクラスが実装しているわけではないことに注意してください__dict__!時々__slots__使用されます。getattrメソッドがクラスにあるかどうかをテストするために使用する方がおそらく良いでしょう。
Codie CodeMonkey 2013年

16
についてはPython 3この回答を参照しください。
Yoel 2014

27
私が得ているもの:'function' object has no attribute 'im_class'
Zitrax 2015年

5
Python 2.7では、機能しません。'im_class'の欠落に関する同じエラー。
RedX 2016年

8

私が要点を逃していたことを指摘してくれたSr2222に感謝します...

これは、Alexと同じですが、何もインポートする必要がない修正されたアプローチです。ただし、継承クラスの巨大な階層がない限り、このアプローチは、継承全体を返すのではなく、定義するクラスが見つかるとすぐに停止するため、改善されたとは思いません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ソリューションは同じ結果を返します。アレックスのアプローチが使用できる限り、私はこれの代わりにそれを使用します。


Cls().meth.__self__Cls特定のインスタンスにバインドされているインスタンスを提供するだけですmeth。に類似していCls().meth.im_classます。を持っている場合はclass SCls(Cls)、インスタンスではなくインスタンスSCls().meth.__self__を取得SClsClsます。OPが望んでいるのは、取得することですCls。これは、@ AlexMartelliのようにMROを歩くことによってのみ利用できるようです。
Silas Ray

@ sr2222その通りです。Alexソリューションの方がコンパクトだと思いますが、すでに始めたので答えを変更しました。
estani 2012

1
インポートを回避する必要がある場合は良い解決策ですが、基本的にはMROを再実装するだけなので、永久に機能することは保証されていません。MROはおそらく同じままですが、Pythonの過去に一度変更されており、再度変更すると、このコードは微妙で広範囲にわたるバグになります。
Silas Ray

プログラミングでは、「非常にありそうもないシナリオ」が何度も発生します。めったに、災害を引き起こしません。一般的に、「XY.Z%それは決して起こらない」という思考パターンは、コーディングを行う際の非常にひどい思考ツールです。使用しないでください。100%正しいコードを書いてください。
ulidtko

@ulidtko説明を読み間違えたと思います。それは正確さではなくスピードです。すべてのケースに適合する「完璧な」ソリューションはありません。そうでない場合、たとえば、ソートアルゴリズムは1つだけになります。ここで提案するソリューションは、「まれな」ケースの方が高速であるはずです。読みやすさの後で、速度はすべてのケースの99%の割合で発生するため、このソリューションは、そのまれなケースの「のみ」でより良いソリューションになる可能性があります。あなたがそれを読んでいない場合のコードは、それがあなたが恐れていたものであるならば、100%正しいです。
estani

6

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

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

Python 3の場合、これは変更されたと思います.__qualname__。調査する必要があります。


2
うーん。Python 2.7でそれを行うと、定義クラスが表示されません-メソッドが呼び出されたクラスを取得していますが、定義されているクラスではありません...
F1Rumors 2018

5

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('.')[:-1]:
    vals = vals[attr]
# vals is now the class Foo.Bar

1

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

それに対する私の回避策は実際には非常に簡単でした。メソッド属性を設定し、後でその存在をテストします。全体を簡略化すると、次のようになります。

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()fromを呼び出しrun_method()(それは精神ではありませんか?)、すべての引数を変更せずにメソッドに渡します。

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


1

Python 3

非常に簡単な方法でそれを解決しました:

str(bar.foo_method).split(" ", 3)[-2]

これは与える

'FooClass.foo_method'

ドットで分割して、クラスと関数名を別々に取得します


なんて節約だ!
user3712978
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.