Python: ¿Cómo recuperar información de clase de un objeto ''marco''?
introspection (4)
¿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).
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.
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
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 pila 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__)
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, entoncesget_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ámetroself
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 trama existe en la clase devuelta y compartir la misma fuente, es posible detectar llamadas de descriptores, Lo mismo para los métodos de clase, etc.)