2009-06-07 7 views
52

Wie bekomme ich die Klasse, die eine Methode in Python definiert?Get Klasse, die Methode definiert

Ich würde wollen, dass das folgende Beispiel „__main__.FooClass“ drucken:

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

class BarClass(FooClass): 
    pass 

bar = BarClass() 
print get_class_that_defined_method(bar.foo_method) 
+0

Welche Version von Python verwenden Sie? Vor 2.2 konnten Sie im_class verwenden, aber das wurde geändert, um den Typ des gebundenen Selbstobjekts anzuzeigen. –

+1

Gut zu wissen. Aber ich benutze 2.6. –

Antwort

54
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 
+0

Es funktioniert, danke! –

+0

Gern geschehen! –

+1

Danke, es hätte eine Weile gedauert, das herauszufinden – David

5

Dank Sr2222 für den Hinweis auf ich fehlte den Punkt ...

Hier ist der korrigierte Ansatz, der ist genau wie Alex, aber muss nichts importiert werden. Ich denke jedoch nicht, dass es eine Verbesserung ist, es sei denn, es gibt eine riesige Hierarchie von vererbten Klassen, da dieser Ansatz aufhört, sobald die definierende Klasse gefunden wird, anstatt die gesamte Vererbung zurückzugeben, wie es getmro tut. Wie gesagt, das ist ein sehr unwahrscheinlich Szenario.

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 

Und das Beispiel:

>>> 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 Lösung liefert die gleichen Ergebnisse. Solange Alex Ansatz verwendet werden kann, würde ich es anstelle dieses verwenden.

+0

'Cls(). Meth .__ self__' gibt Ihnen nur die Instanz von' Cls', die an diese spezifische Instanz von 'meth' gebunden ist. Es ist analog zu 'Cls() .meth.im_class'. Wenn Sie 'class SCls (Cls)' haben, wird 'SCls() .meth .__ self__' Ihnen eine 'SCls'-Instanz und keine' Cls'-Instanz liefern. Was das OP will, ist "Cls" zu bekommen, was nur so aussieht, als ob man das MRO wie @Alex Martelli laufen lässt. –

+0

@ sr2222 Sie haben Recht. Ich habe die Antwort geändert, wie ich bereits begonnen habe, aber ich denke Alex Lösung ist kompakter. – estani

+0

Es ist eine gute Lösung, wenn Sie Importe vermeiden müssen, aber da Sie das MRO einfach nur neu implementieren, ist es nicht garantiert, dass es für immer funktioniert. Das MRO wird wahrscheinlich gleich bleiben, aber es wurde bereits einmal in Pythons Vergangenheit geändert, und wenn es erneut geändert wird, wird dieser Code subtile, weit verbreitete Fehler sein. –

1

Ich fing an etwas etwas Ähnliches zu tun, grundsätzlich überprüfte die Idee, wann immer eine Methode in einer Basisklasse implementiert wurde oder nicht in einer Unterklasse. Wie ich es ursprünglich gemacht habe, konnte ich nicht erkennen, wann eine Zwischenklasse die Methode tatsächlich implementiert hat.

Meine Workaround für es war eigentlich ziemlich einfach; Einstellen einer Methode Attribut und Testen seiner Anwesenheit später. Hier ist eine Vereinfachung der ganzen Sache:

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 

UPDATE: Eigentlich method() von run_method() nennen (nicht, dass der Geist?) Und haben es alle Argumente übergeben unverändert an das Verfahren.

S.S .: Diese Antwort beantwortet die Frage nicht direkt. Meiner Meinung nach gibt es zwei Gründe, warum man wissen möchte, welche Klasse eine Methode definiert hat; Zuerst werden Finger auf eine Klasse im Debug-Code (wie in der Ausnahmebehandlung) gezeigt, und zweitens wird festgestellt, ob die Methode neu implementiert wurde (wobei Methode ein Stub ist, der vom Programmierer implementiert werden soll). Diese Antwort löst diesen zweiten Fall auf andere Weise.

2

Ich weiß nicht, warum niemand jemals erzogen hat oder warum die Top-Antwort hat 50 upvotes, wenn es langsam wie die Hölle ist, aber Sie können auch Folgendes tun:

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

für Python 3 Ich glaube, das hat sich geändert und Sie müssen in .__qualname__ suchen.