290 votes

Comment obtenir le nom de la méthode de l'appelant dans la méthode appelée ?

Python : Comment obtenir le nom de la méthode de l'appelant dans la méthode appelée ?

Supposons que j'ai 2 méthodes :

def method1(self):
    ...
    a = A.method2()

def method2(self):
    ...

Si je ne veux pas faire de changement pour la méthode1, comment obtenir le nom de l'appelant (dans cet exemple, le nom est méthode1) dans la méthode2 ?

7 votes

339voto

Alex Martelli Points 330805

inspect.getframeinfo et d'autres fonctions connexes dans inspect peut vous aider :

>>> import inspect
>>> def f1(): f2()
... 
>>> def f2():
...   curframe = inspect.currentframe()
...   calframe = inspect.getouterframes(curframe, 2)
...   print('caller name:', calframe[1][3])
... 
>>> f1()
caller name: f1

cette introspection est destinée à faciliter le débogage et le développement ; il n'est pas conseillé de s'en servir à des fins de fonctionnalité de production.

27 votes

"il n'est pas conseillé de s'y fier pour des raisons de fonctionnalité de production." Pourquoi pas ?

36 votes

@beltsonata il dépend de l'implémentation de CPython, donc le code l'utilisant sera cassé si vous essayez d'utiliser PyPy ou Jython ou d'autres runtimes. c'est bien si vous développez et déboguez localement mais ce n'est pas vraiment quelque chose que vous voulez dans votre système de production.

2 votes

@EugeneKrevenets Au delà de la version python, j'ai rencontré un problème qui fait que le code qui s'exécute en moins d'une seconde s'exécute en plusieurs minutes une fois introduit. c'est très inefficace

129voto

Todd Owen Points 4477

Version courte :

import inspect

def f1(): f2()

def f2():
    print 'caller name:', inspect.stack()[1][3]

f1()

(avec des remerciements à @Alex, et Stefaan Lippen )

0 votes

Bonjour, j'obtiens l'erreur suivante lorsque j'exécute ce programme : File "/usr/lib/python2.7/inspect.py", line 528, in findsource if not sourcefile and file[0] + file[-1] != '<>' : IndexError : string index out of range Can u please provide suggestion. Merci d'avance.

0 votes

Cette approche m'a donné une erreur : KeyError : ' principal '

1 votes

En Python 3, il faut : print('nom de l'appelant:', inspect.stack()[1][3])

89voto

UGS Points 830

Cela semble fonctionner parfaitement :

import sys
print sys._getframe().f_back.f_code.co_name

8 votes

Cela semble être beaucoup plus rapide que inspect.stack

4 votes

Pourtant, il utilise un membre protégé, ce qui n'est généralement pas conseillé, car il pourrait échouer après que la fonction sys a fait l'objet d'un remaniement en profondeur.

3 votes

C'est un point valable. Merci de l'avoir mentionné ici comme un piège potentiel à long terme de cette solution.

60voto

A-B-B Points 797

J'utiliserais inspect.currentframe().f_back.f_code.co_name . Son utilisation n'a pas été abordée dans les réponses précédentes qui sont principalement de l'un des trois types suivants :

  • Certaines réponses antérieures utilisent inspect.stack mais il est connu pour être trop lent .
  • Certaines réponses antérieures utilisent sys._getframe qui est une fonction privée interne étant donné son trait de soulignement principal, et donc son utilisation est implicitement découragée.
  • Une réponse antérieure utilise inspect.getouterframes(inspect.currentframe(), 2)[1][3] mais il n'est pas clair du tout ce que [1][3] est en train d'accéder.

    import inspect from types import FrameType from typing import cast

    def caller_name() -> str: """Return the calling function's name."""

    Ref: https://stackoverflow.com/a/57712700/

    return cast(FrameType, cast(FrameType, inspect.currentframe()).f_back).f_code.co_name

    if name == 'main': def _test_caller_name() -> None: assert caller_name() == '_test_caller_name' _test_caller_name()

Notez que cast(FrameType, frame) est utilisé pour satisfaire mypy .


Accusé de réception : commentaire de 1313e pour une responder .

0 votes

Je me demandais quel est le nom de l'appelant si la fonction est appelée directement. En l'imprimant, on obtient <module> . Cela sera-t-il toujours le cas lorsque la fonction est directement appelée par son nom ? Ou existe-t-il des cas où le nom du module apparaîtra ?

1 votes

@VahagnTumanyan Si vous l'appelez depuis un module (en dehors de toute fonction), il dira ce que vous avez vu. Cette réponse ne montrera cependant pas le nom du module. Vous pouvez l'appeler depuis une fonction pour voir le nom de la fonction appelante.

3 votes

C'est la meilleure solution, je suppose, puisqu'il n'y a pas de chiffres magiques comme calframe[1][3] qui peuvent changer à l'avenir.

38voto

techtonik Points 2945

J'ai trouvé une version un peu plus longue qui essaie de construire un nom de méthode complet incluant module et classe.

https://gist.github.com/2151727 (rev 9cccbf)

# Public Domain, i.e. feel free to copy/paste
# Considered a hack in Python 2

import inspect

def caller_name(skip=2):
    """Get a name of a caller in the format module.class.method

       `skip` specifies how many levels of stack to skip while getting caller
       name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.

       An empty string is returned if skipped levels exceed stack height
    """
    stack = inspect.stack()
    start = 0 + skip
    if len(stack) < start + 1:
      return ''
    parentframe = stack[start][0]    

    name = []
    module = inspect.getmodule(parentframe)
    # `modname` can be None when frame is executed directly in console
    # TODO(techtonik): consider using __main__
    if module:
        name.append(module.__name__)
    # detect classname
    if 'self' in parentframe.f_locals:
        # I don't know any way to detect call from the object method
        # XXX: there seems to be no way to detect static method call - it will
        #      be just a function call
        name.append(parentframe.f_locals['self'].__class__.__name__)
    codename = parentframe.f_code.co_name
    if codename != '<module>':  # top level usually
        name.append( codename ) # function or a method

    ## Avoid circular refs and frame leaks
    #  https://docs.python.org/2.7/library/inspect.html#the-interpreter-stack
    del parentframe, stack

    return ".".join(name)

0 votes

Génial, cela a bien fonctionné pour moi dans mon code de journalisation, où je peux être appelé depuis de nombreux endroits différents. Merci beaucoup.

1 votes

A moins que vous ne supprimiez aussi stack mais il y a toujours des fuites de trames dues aux réfs circulaires comme expliqué dans la page d'accueil. inspecter-docs

0 votes

@ankostis avez-vous du code de test pour prouver cela ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X