2010-05-06 19 views
11

Estoy tratando de encontrar el nombre de la clase que contiene el código de método.nombre de la clase que contiene el código de método

En el ejemplo de abajo utilizo self.__class__.__name__, pero por supuesto esto devuelve el nombre de la clase de la cual self es una instancia y no una clase que contiene el código de método test(). b.test() imprimirá 'B' mientras que me gustaría obtener 'A'.

Miré en la documentación del módulo inspect pero no encontré nada directamente útil.

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

Lo sabe en el momento de la codificación, ¿no? Es A: 'prueba de def (auto): imprimir" A "'. –

+0

Quiero evitar replicar el nombre de clase. Si cambio el nombre de la clase de 'A' a' AA', seré el primero en olvidar adentar la instrucción 'print 'A' '. – user266940

Respuesta

7

En Python 3.x, puede simplemente usar __class__.__name__. El nombre __class__ es levemente mágico, y no es lo mismo que el atributo __class__ de self.

En Python 2.x, no hay una buena forma de obtener esa información. Puede utilizar la inspección de pila para obtener el objeto de código, luego caminar por la jerarquía de clases buscando el método correcto, pero es lento y tedioso y probablemente se rompa cuando no lo desee. También puede usar una metaclase o un decorador de clases para postprocesar la clase de alguna manera, pero ambos son enfoques bastante intrusivos. Y puede hacer algo realmente desagradable, como acceder al self.__nonexistant_attribute, capturar AttributeError y extraer el nombre de clase del nombre destruido. Ninguno de esos enfoques realmente vale la pena si solo quieres evitar escribir el nombre dos veces; al menos olvidar actualizar el nombre se puede hacer un poco más evidentes haciendo algo como:

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

inspect.getmro le da una tupla de las clases en que el método podría tener origen, con el fin. Tan pronto como se encuentra uno de los que tiene el nombre del método en su dict, ya está hecho:

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

Desafortunadamente eso le da el nombre de la primera clase en el MRO que tiene un atributo con ese nombre, que no es necesariamente el mismo que el método desde el que está haciendo esto (si su método está más abajo en el MRO) –

+0

Nice solución, pero como se menciona Thomas no funciona cuando la clase 'B' anula el 'test()' método: clase a: def __init __ (self): pase prueba def (auto): importación inspeccionar de c en inspect.getmro (self .__ class__): si 'prueba' en vars (c): descanso de impresión c .__ name__ clase B (a): def __init __ (self): A .__ init __ (self) prueba def (self): A.test (auto) a = A() b = B() a.test() # imprime A b.test() # imprime B – user266940

+0

disculpa por el desorden en el comentario anterior. Este es mi primer comentario de stackoverflow y no estaba al tanto de la imposibilidad de agregar código en los comentarios. – user266940

2

Uso __dict__ de por sí objeto de clase:

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__ 

imprimirá Verdadero, Falso, Un

+0

Esto tiene el mismo problema que la solución MRO de Alex. –

+0

De acuerdo. Pero si se sabe de antemano que habrá solo una herencia de clase única, la solución es correcta y simple. Gracias por la nota. – nkrkv

2

Puede usar (¿abuso?) private name mangling para lograr este efecto. Si busca un atributo en self que comienza con __ desde dentro de un método, python cambia el nombre de __attribute a _classThisMethodWasDefinedIn__attribute.

Solo de alguna manera esconde el nombre de clase que quieras en forma desgarrada donde el método puede verlo. A modo de ejemplo, podemos definir un método __new__ en la clase base que lo hace:

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

que imprime:

A 
A 
0

Usted puede hacer

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

Tenga en cuenta que usted podría simplificarlo usando __class__.__base__, pero si usa herencia múltiple, esta versión funcionará mejor.

Simplemente comprueba primero sus clases base para test. No es el más bonito, pero funciona.

Cuestiones relacionadas