python - most - ¿Cuál es la diferencia entre una pila y un marco?
traceback python 3 (2)
De acuerdo, ya que esto parece ser más sobre lo que son en general las estructuras de pila / las pilas de llamadas, veamos esto:
def f():
try:
g()
except:
# WE WILL DO THINGS HERE
def g():
h()
def h():
raise Exception(''stuff'')
#CALL
f()
Cuando estamos en h()
, hay 4 fotogramas en la pila de llamadas .
[top level]
[f()]
[g()]
[h()] #<-- we''re here
(Si tratamos de poner más que marcos sys.getrecursionlimit()
en la pila, obtendríamos un RuntimeError
, que es la versión de StackOverflow
de Python ;-))
"Exterior" se refiere a todo lo que está sobre nosotros (literalmente: la dirección "arriba") en la pila de llamadas. Entonces, en orden, g
, luego f
, luego el nivel superior (módulo). Del mismo modo, "interno" se refiere a todo lo que está abajo en la pila de llamadas. Si detectamos una excepción en f()
, ese objeto de rastreo tendrá referencias a todos los marcos de pila internos que se desenrollaron para llevarnos a ese punto.
def f():
try:
g()
except:
import inspect
import sys
#the third(last) item in sys.exc_info() is the current traceback object
return inspect.getinnerframes(sys.exc_info()[-1])
Esto da:
[(<frame object at 0xaad758>, ''test.py'', 3, ''f'', ['' g()/n''], 0),
(<frame object at 0x7f5edeb23648>, ''test.py'', 10, ''g'', ['' h()/n''], 0),
(<frame object at 0x7f5edeabdc50>, ''test.py'', 13, ''h'', [" raise Exception(''stuff'')/n"], 0)]
Como era de esperar, los tres marcos internos f, g y h. Ahora, podemos tomar ese último objeto de marco (el de h()
) y pedir sus marcos externos :
[(<frame object at 0x7f6e996e6a48>, ''test.py'', 13, ''h'', [" raise Exception(''stuff'')/n"], 0),
(<frame object at 0x1bf58b8>, ''test.py'', 10, ''g'', ['' h()/n''], 0),
(<frame object at 0x7f6e99620240>, ''test.py'', 7, ''f'', ['' return inspect.getinnerframes(sys.exc_info()[-1])/n''], 0),
(<frame object at 0x7f6e99725438>, ''test.py'', 23, ''<module>'', [''print(inspect.getouterframes(f()[-1][0]))/n''], 0)]
Entonces, ahí lo tienes, eso es todo lo que está pasando: simplemente estamos navegando por la pila de llamadas. A modo de comparación, esto es lo que traceback.extract_stack(f()[-1][0])
ofrece:
[(''test.py'', 23, ''<module>'', ''print(traceback.extract_stack(f()[-1][0]))''),
(''test.py'', 7, ''f'', ''return inspect.getinnerframes(sys.exc_info()[-1])''),
(''test.py'', 10, ''g'', ''h()''),
(''test.py'', 13, ''h'', "raise Exception(''stuff'')")]
Observe el orden invertido aquí en comparación con getouterframes
, y la salida reducida. De hecho, si entorna los ojos, básicamente se ve como un traceback regular (y bueno, lo es , con solo un poco más de formateo).
En resumen: both inspect.getouterframes
y inspect.getouterframes
contienen toda la información para reproducir lo que generalmente se ve en su rastreo diario; extract_stack
simplemente elimina las referencias a los marcos de pila, ya que es muy común que ya no los necesites una vez que llegas al punto de formatear tu traza de pila from-a-given-frame-outwards.
¿En qué situaciones quisiera usar una sobre la otra?
Cuál es la diferencia entre:
>>> import inspect
>>> print(inspect.getouterframes(inspect.currentframe()))
[(<frame object at 0x8fc262c>, ''<stdin>'', 1, ''<module>'', None, None)]
Y:
>>> import traceback
>>> traceback.extract_stack()
[(''<stdin>'', 1, ''<module>'', None)]
Actualizar:
Otro:
>>> import sys
>>> print(sys._getframe().f_trace,sys._getframe().f_code)
(None, <code object <module> at 0x8682a88, file "<stdin>", line 1>)
No entiendo los matices aquí:
- Marco de pila
- Objeto de marco
- Stack Trace
La documentación para el módulo de inspect
dice:
Cuando las siguientes funciones devuelven "registros de cuadros", cada registro es una tupla de seis elementos: el objeto marco, el nombre del archivo, el número de línea actual, el nombre de la función, una lista de líneas de contexto del código fuente y el índice de la línea actual dentro de esa lista.
La documentación del módulo de traceback
dice:
Una entrada de seguimiento de pila "preprocesada" es una tupla de 4 (nombre de archivo, número de línea, nombre de función, texto)
Por lo tanto, la diferencia es que el registro de cuadro también incluye el objeto de marco y algunas líneas de contexto, mientras que el trazado de seguimiento solo incluye el texto de las líneas individuales en la pila de llamadas (es decir, las llamadas que llevaron a la llamada extract_stack
).
Utilizaría la pila de traceback
si solo quiere imprimir un traceback. Como sugiere la documentación, esta es información procesada para mostrar al usuario. Necesitaría acceso al objeto de marco para inspect
si realmente quería hacer algo con la pila de llamadas (por ejemplo, leer variables de los marcos de llamadas).