web-dev-qa-db-fra.com

Récupère la classe qui a défini la méthode

Comment puis-je obtenir la classe qui a défini une méthode en Python?

Je voudrais que l'exemple suivant imprime "__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

Merci Sr2222 d'avoir souligné que je manquais le point ...

Voici l'approche corrigée qui ressemble à celle d'Alex mais ne nécessite rien d'importer. Je ne pense pas que ce soit une amélioration, à moins qu'il n'y ait une énorme hiérarchie de classes héritées car cette approche s'arrête dès que la classe de définition est trouvée, au lieu de renvoyer tout l'héritage comme le fait getmro. Comme nous l'avons dit, il s'agit d'un très scénario peu probable.

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

Et l'exemple:

>>> 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

La solution Alex renvoie les mêmes résultats. Tant que l'approche Alex peut être utilisée, je l'utiliserais à la place de celle-ci.

7
estani

Je ne sais pas pourquoi personne n'a jamais soulevé cette question ou pourquoi la réponse du haut a 50 votes positifs quand c'est lent, mais vous pouvez également faire ce qui suit:

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

Pour python 3 je crois que cela a changé et vous devrez examiner .__qualname__.

3
Nick Chapman

J'ai commencé à faire quelque chose de similaire, l'idée était de vérifier chaque fois qu'une méthode dans une classe de base avait été implémentée ou non dans une sous-classe. Il s'est avéré que je l'ai fait à l'origine, je n'ai pas pu détecter quand une classe intermédiaire implémentait réellement la méthode.

Ma solution pour cela était assez simple en fait; définir une méthode attribut et tester sa présence ultérieurement. Voici une simplification de l'ensemble:

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

MISE À JOUR: En fait, appelez method() à partir de run_method() (n'est-ce pas l'esprit?) Et faites-lui passer tous les arguments non modifiés à la méthode.

P.S .: Cette réponse ne répond pas directement à la question. À mon humble avis, il y a deux raisons pour lesquelles on voudrait savoir quelle classe a défini une méthode; la première consiste à pointer du doigt une classe dans le code de débogage (comme dans la gestion des exceptions), et la seconde consiste à déterminer si la méthode a été réimplémentée (où méthode est un stub destiné à être implémenté par le programmeur). Cette réponse résout ce deuxième cas d'une manière différente.

1

Dans Python 3, si vous avez besoin de l'objet de classe réel, vous pouvez le faire:

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

Si la fonction peut appartenir à une classe imbriquée, vous devez itérer comme suit:

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
0