2010-02-04 24 views
15

¿Es posible recuperar cualquier información de clase de un objeto de marco? Sé cómo obtener el archivo (frame.f_code.co_filename), function (frame.f_code.co_name) y el número de línea (frame.f_lineno), pero me gustaría poder obtener también el nombre de la clase del objeto activo instancia del marco (o None si no está en una instancia).Python: ¿Cómo recuperar información de clase de un objeto 'frame'?

Respuesta

16

No creo que, en el nivel de objeto marco, hay alguna forma de encontrar el objeto real de la función python que se ha llamado.

Sin embargo, si su código se basan en la convención común: nombrar el parámetro de ejemplar de un método self, entonces se podría hacer lo siguiente:

def get_class_from_frame(fr): 
    import inspect 
    args, _, _, value_dict = inspect.getargvalues(fr) 
    # we check the first parameter for the frame function is 
    # named 'self' 
    if len(args) and args[0] == 'self': 
    # in that case, 'self' will be referenced in value_dict 
    instance = value_dict.get('self', None) 
    if instance: 
     # return its class 
     return getattr(instance, '__class__', None) 
    # return None otherwise 
    return None 

Si no desea utilizar getargvalues, puede use directamente frame.f_locals en lugar de value_dict y frame.f_code.co_varnames[:frame.f_code.co_argcount] en lugar de args.

Tenga en cuenta que esto es todavía sólo un partido basado en la convención, por lo que no es portátil, y propenso a errores:

  • si una función uso no método self como el primer nombre del parámetro, entonces get_class_from_frame va mal devuelve la clase del primer parámetro.
  • puede ser engañoso cuando se trabaja con descriptores (devolverá la clase del descriptor, no de la instancia real a la que se accede).
  • @classmethod y @staticmethod no tomarán un parámetro self y se implementan con descriptores.
  • y seguramente mucho más

Dependiendo de qué es exactamente lo que quiere hacer, es posible que desee tomar algún tiempo para profundizar y encontrar soluciones para todos estos problemas (se puede comprobar la función de marco de existir en el la clase devuelta y comparte la misma fuente, es posible detectar las llamadas del descriptor, lo mismo para los métodos de clase, etc.)

+0

Gracias por su respuesta, Clément; es detallado y reflexivo. ¡Todavía espero obtener más de una respuesta! Pero si no, este tendrá que ser suficiente ... –

+0

OK, esperó el tiempo suficiente. ¡Esta es una buena respuesta! Esperaba un mejor soporte de las estructuras de cuadros, pero es lo que es. Gracias de nuevo, Clément! –

+1

Para agregar uno a la lista, tenga cuidado de que 'self' pueda ser una instancia de una clase derivada, que podría no ser la que nos interesa. – gertjan

7

Esto es un poco más corto, pero tiene más o menos el mismo valor. Devuelve Ninguno si el nombre de la clase no está disponible.

def get_class_name(): 
    f = sys._getframe(1) 

    try: 
     class_name = f.f_locals['self'].__class__.__name__ 
    except KeyError: 
     class_name = None 

    return class_name 
+0

¡Buena respuesta! Mejor aún, podrías hacer '' f.f_locals ['__ clase __'] .__ nombre__'', que también debería cubrir los casos '' @ classmethod'' y '' @ staticmethod''. –

+2

@LucioPaiva que no funciona. No hay clave '' __class __ ''en f_locals. – Pithikos

2

Acabo de encontrar esta publicación porque me enfrentaba con el mismo problema. Sin embargo, no consideré que el método del "yo" sea una solución aceptable por todos los motivos ya enumerados.

El siguiente código muestra un enfoque diferente: dado un objeto de marco, busca en las globales un objeto con un nombre de miembro y un bloque de código coincidentes. La búsqueda no es exhaustiva, por lo que es posible que no se descubran todas las clases, pero las clases que se encuentran deben ser las que buscamos porque verificamos los códigos coincidentes.

Objeto del código es anteponer un nombre de función con su nombre de clase, si se encuentran:

def get_name(frame): 
    code = frame.f_code 
    name = code.co_name 
    for objname, obj in frame.f_globals.iteritems(): 
    try: 
     assert obj.__dict__[name].func_code is code 
    except Exception: 
     pass 
    else: # obj is the class that defines our method 
     name = '%s.%s' % (objname, name) 
     break 
    return name 

Nota el uso de __dict__ en lugar de getattr para evitar la captura de las clases derivadas.

Tenga en cuenta además que se puede evitar una búsqueda global si self = frame.f_locals['self']; obj = self.__class__ da una coincidencia, o cualquier obj in self.__class__.__bases__ o más profundo, por lo que sin duda hay espacio para la optimización/hibridación.

1

Si un método es un método de clase, la clase será el primer argumento. Esto imprime el tipo del primer arg si está presente para cada marco de pila de llamadas:

def some_method(self): 
     for f in inspect.getouterframes(inspect.currentframe()): 
      args, _,_, local_dict = inspect.getargvalues(f[0]) 
      if args: 
       first_arg = args[0] 
       first_value = local_dict[first_arg] 
       print(type(first_value).__name__) 
Cuestiones relacionadas