Python: ¿Cómo recuperar información de clase de un objeto ‘marco’?

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

No creo que, a nivel de objeto de cuadro, haya alguna forma de encontrar el objeto de función python real al que se ha llamado.

Sin embargo, si su código se basa en la convención común: nombrar el parámetro de instancia de un método self , entonces 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 usar directamente frame.f_locals lugar de value_dict y frame.f_code.co_varnames[:frame.f_code.co_argcount] lugar de args .

Tenga en cuenta que esto todavía depende solo de la convención, por lo que no es portátil y es propenso a errores:

  • Si una función que no es de método usa self como primer nombre de parámetro, entonces get_class_from_frame devolverá erróneamente 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 implementarán con descriptores.
  • y seguramente mucho mas

Dependiendo de lo que desea hacer exactamente, puede dedicar un tiempo a profundizar y encontrar soluciones para todos estos problemas (puede verificar que la función de ttwig existe en la clase devuelta y compartir la misma fuente; es posible detectar llamadas a descriptores Lo mismo para los métodos de clase, etc.)

Esto es un poco más corto, pero hace lo mismo. 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 

Acabo de encontrar esta publicación porque me enfrenté con el mismo problema. Sin embargo, no consideré que el método del “yo mismo” fuera una solución aceptable por todas las razones ya mencionadas.

El siguiente código demuestra un enfoque diferente: dado un objeto de marco, busca en los 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 correspondientes.

El objeto del código es anteponer un nombre de función con su nombre de clase, si se encuentra:

 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 

Tenga en cuenta el uso de __dict__ lugar de getattr para evitar la captura de clases derivadas.

Tenga en cuenta además que se puede evitar una búsqueda global si self = frame.f_locals['self']; obj = self.__class__ 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 ciertamente hay espacio para la optimización / hibridación.

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

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