web-dev-qa-db-fra.com

Quelle est la différence entre une pile et un cadre?

Dans quelles situations voudrais-je utiliser l'un sur l'autre?

Quelle est la différence entre:

>>> import inspect
>>> print(inspect.getouterframes(inspect.currentframe()))
[(<frame object at 0x8fc262c>, '<stdin>', 1, '<module>', None, None)]

Et:

>>> import traceback
>>> traceback.extract_stack()
[('<stdin>', 1, '<module>', None)]

Mise à jour:

Un autre:

>>> import sys
>>> print(sys._getframe().f_trace,sys._getframe().f_code)
(None, <code object <module> at 0x8682a88, file "<stdin>", line 1>)

Je ne comprends pas les nuances ici:

  • Cadre de pile
  • Objet Frame
  • Trace de la pile

mise à jour 2, un peu de temps depuis que la question a été posée, mais très pertinente

46
jmunsch

D'accord, puisque cela semble être plus sur ce que sont les cadres de pile/piles d'appel en général, passons par ceci:

def f():
    try:
        g()
    except:
        # WE WILL DO THINGS HERE

def g():
    h()

def h():
    raise Exception('stuff')

#CALL
f()

Quand nous sommes dans h(), il y a 4 frames sur le call stack .

[top level]
 [f()]
  [g()]
   [h()] #<-- we're here

(si nous essayions de mettre plus de sys.getrecursionlimit() frames sur la pile, nous obtiendrions un RuntimeError, qui est la version python de StackOverflow ;-))

"Outer" se réfère à tout ce qui est au-dessus de nous (littéralement: la direction "vers le haut") dans la pile des appels. Donc dans l'ordre, g, puis f, puis le niveau supérieur (module). De même, "interne" fait référence à tout vers le bas dans la pile des appels. Si nous interceptons une exception dans f(), cet objet traceback aura des références à tous les cadres de pile internes qui ont été déroulés pour nous amener à ce point.

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

Cela donne:

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

Comme prévu, les trois cadres intérieurs f, g et h. Maintenant, nous pouvons prendre ce dernier objet frame (celui de h()) et demander ses cadres externes :

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

Alors voilà, c'est tout ce qui se passe: nous naviguons simplement dans la pile des appels. A titre de comparaison, voici ce que donne traceback.extract_stack(f()[-1][0]):

[('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')")]

Remarquez l'ordre inversé ici par rapport à getouterframes et la sortie réduite. En fait, si vous plissez les yeux, cela ressemble à un traçage régulier (et bon, c'est , avec juste un peu plus de mise en forme).

En résumé: inspect.getouterframes Et traceback.extract_stack Contiennent toutes les informations pour reproduire ce que vous voyez généralement dans votre trace quotidienne; extract_stack Supprime simplement les références aux cadres de pile, car il est très courant de ne plus en avoir besoin une fois que vous êtes sur le point de formater votre trace de pile d'un cadre donné vers l'extérieur.

52
roippi

La documentation pour le module inspect dit:

Lorsque les fonctions suivantes renvoient des "enregistrements de trames", chaque enregistrement est un tuple de six éléments: l'objet de trame, le nom de fichier, le numéro de ligne de la ligne actuelle, le nom de la fonction, une liste de lignes de contexte du code source et l'index de la ligne courante dans cette liste.

La documentation pour le module traceback dit:

Une entrée de trace de pile "prétraitée" est un quadruple (nom de fichier, numéro de ligne, nom de fonction, texte)

Par conséquent, la différence est que l'enregistrement de trame comprend également l'objet de trame et certaines lignes de contexte, tandis que le retraçage inclut uniquement le texte des lignes individuelles dans la pile d'appels (c'est-à-dire les appels qui ont conduit à extract_stack appel).

Vous utiliseriez la pile de traceback si vous voulez simplement imprimer un traceback. Comme le suggère la documentation, il s'agit d'informations traitées pour être montrées à l'utilisateur. Vous auriez besoin d'accéder à l'objet frame à partir de inspect si vous vouliez réellement faire quoi que ce soit avec la pile d'appels (par exemple, lire des variables à partir de frames appelants).

10
BrenBarn