2010-05-06 8 views
11

J'essaie de trouver le nom de la classe qui contient le code de la méthode.nom de la classe qui contient le code de méthode

Dans l'exemple ci-dessous, j'utilise self.__class__.__name__, mais bien sûr cela renvoie le nom de la classe dont self est une instance et non une classe qui contient le code de méthode test(). b.test() va imprimer 'B' pendant que je voudrais obtenir 'A'.

J'ai regardé dans la documentation du module inspect mais je n'ai rien trouvé directement utile.

class A: 
    def __init__(self): 
    pass 
    def test(self): 
    print self.__class__.__name__ 

class B(A): 
    def __init__(self): 
    A.__init__(self) 



a = A() 
b = B() 

a.test() 
b.test() 
+1

Vous le savez au moment du codage, non? C'est A: 'def test (self): affiche" A "'. –

+0

Je veux éviter de répliquer le nom de la classe. Si je change le nom de la classe de 'A' à' AA', je serai le premier à oublier l'instruction '' print ''. – user266940

Répondre

7

3.x Python, vous pouvez simplement utiliser __class__.__name__. Le nom __class__ est légèrement magique, et pas la même chose que l'attribut __class__ de self.

Dans Python 2.x, il n'existe aucun moyen d'obtenir ces informations. Vous pouvez utiliser l'inspection de la pile pour obtenir l'objet code, puis parcourir la hiérarchie des classes à la recherche de la bonne méthode, mais elle est lente et fastidieuse et risque de se casser si vous ne le souhaitez pas. Vous pouvez également utiliser une métaclasse ou un décorateur de classe pour post-traiter la classe d'une manière ou d'une autre, mais ces deux approches sont plutôt intrusives. Et vous pouvez faire quelque chose de vraiment moche, comme accéder à self.__nonexistant_attribute, attraper l'AttributeError et extraire le nom de la classe du nom mutilé. Aucune de ces approches ne vaut vraiment la peine si vous voulez juste éviter de taper le nom deux fois; au moins oublier de mettre à jour le nom peut être un peu plus évident en faisant quelque chose comme:

class C: 
    ... 
    def report_name(self): 
     print C.__name__ 
4

inspect.getmro vous donne un tuple des classes où pourrait venir la méthode de, dans l'ordre. Dès que vous en trouver un d'entre eux qui a le nom de la méthode dans son dict, vous avez terminé:

for c in inspect.getmro(self.__class__): 
    if 'test' in vars(c): break 
return c.__name__ 
+1

Malheureusement, cela vous donne le nom de la première classe dans le MRO qui a un attribut avec ce nom, qui n'est pas nécessairement la même que la méthode que vous utilisez (si votre méthode est plus loin dans le MRO.) –

+0

Nice solution, mais comme Thomas mentionne qu'il ne fonctionne pas lorsque classe 'B' remplace la méthode' test() ': classe A: def __init __ (self): passe test def (auto): importation inspection pour c dans inspect.getmro (self .__ class__): si 'test' dans vars (c): break impression c .__ name__ classe B (A): def __init __ (self): A .__ init __ (self) de test def (auto): A.test (auto-) a = A() b = B() a.test() # imprime A b.test() # imprime B – user266940

+0

désolé pour le désordre dans le commentaire précédent. Ceci est mon premier commentaire de stackoverflow et je n'étais pas au courant de l'impossibilité d'ajouter du code dans les commentaires. – user266940

2

Utilisation __dict__ d'objet de classe elle-même:

class A(object): 
    def foo(self): 
     pass 

class B(A): 
    pass 

def find_decl_class(cls, method): 
    if method in cls.__dict__: 
     return cls 
    for b in cls.__bases__: 
     decl = find_decl_class(b, method) 
     if decl: 
      return decl 

print 'foo' in A.__dict__ 
print 'foo' in B.__dict__ 
print find_decl_class(B, 'foo').__name__ 

imprimera Vrai, Faux, A

+0

Cela a le même problème que la solution MRO d'Alex. –

+0

D'accord. Mais si l'on sait à l'avance qu'il n'y aura qu'un héritage de classe unique, la solution est OK et simple. Merci pour la note. – nkrkv

2

Vous pouvez utiliser (abuser?) private name mangling pour accomplir cet effet. Si vous recherchez un attribut sur self qui commence par __ depuis une méthode, python change le nom de __attribute en _classThisMethodWasDefinedIn__attribute.

Il vous suffit de stocker le nom de classe que vous voulez dans la forme tronquée où la méthode peut le voir. À titre d'exemple, nous pouvons définir une méthode __new__ sur la classe de base qui le fait:

def mangle(cls, attrname): 
    if not attrname.startswith('__'): 
     raise ValueError('attrname must start with __') 
    return '_%s%s' % (cls.__name__, attrname) 

class A(object): 

    def __new__(cls, *args, **kwargs): 
     obj = object.__new__(cls) 
     for c in cls.mro(): 
      setattr(obj, mangle(c, '__defn_classname'), c.__name__) 
     return obj 

    def __init__(self): 
     pass 

    def test(self): 
     print self.__defn_classname 

class B(A): 

    def __init__(self): 
     A.__init__(self) 



a = A() 
b = B() 

a.test() 
b.test() 

qui imprime:

A 
A 
0

Vous pouvez faire

>>> class A(object): 
... def __init__(self): 
...  pass 
... def test(self): 
...  for b in self.__class__.__bases__: 
...  if hasattr(b, 'test'): 
...   return b.__name__ 
...  return self.__class__.__name__ 
... 
>>> class B(A): 
... def __init__(self): 
...  A.__init__(self) 
... 
>>> B().test() 
'A' 
>>> A().test() 
'A' 
>>> 

Gardez à l'esprit que vous pourrait le simplifier en utilisant __class__.__base__, mais si vous utilisez l'héritage multiple, cette version fonctionnera mieux.

Il vérifie simplement d'abord sur ses baseclasses pour test. Ce n'est pas le plus joli, mais ça marche.

Questions connexes