web-dev-qa-db-fra.com

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 method1, comment obtenir le nom de l'appelant (dans cet exemple, le nom est method1) dans method2?

151
zs2020

inspect.getframeinfo et d'autres fonctions connexes dans inspect peuvent 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 à aider au débogage et au développement; il n'est pas conseillé de s'en servir pour les fonctionnalités de production.

197
Alex Martelli

Version plus courte:

import inspect

def f1(): f2()

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

f1()

(avec merci à @Alex, et Stefaan Lippen )

81
Todd Owen

Cela semble bien fonctionner:

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

J'ai mis au point une version légèrement plus longue qui tente de créer un nom de méthode complet, y compris module et classe.

https://Gist.github.com/2151727 (rév 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)
25
anatoly techtonik

Peu de fusion des choses ci-dessus. Mais voici ma solution à cela.

def print_caller_name(stack_size=3):
    def wrapper(fn):
        def inner(*args, **kwargs):
            import inspect
            stack = inspect.stack()

            modules = [(index, inspect.getmodule(stack[index][0]))
                       for index in reversed(range(1, stack_size))]
            module_name_lengths = [len(module.__name__)
                                   for _, module in modules]

            s = '{index:>5} : {module:^%i} : {name}' % (max(module_name_lengths) + 4)
            callers = ['',
                       s.format(index='level', module='module', name='name'),
                       '-' * 50]

            for index, module in modules:
                callers.append(s.format(index=index,
                                        module=module.__name__,
                                        name=stack[index][3]))

            callers.append(s.format(index=0,
                                    module=fn.__module__,
                                    name=fn.__name__))
            callers.append('')
            print('\n'.join(callers))

            fn(*args, **kwargs)
        return inner
    return wrapper

Utilisation:

@print_caller_name(4)
def foo():
    return 'foobar'

def bar():
    return foo()

def baz():
    return bar()

def fizz():
    return baz()

fizz()

la sortie est

level :             module             : name
--------------------------------------------------
    3 :              None              : fizz
    2 :              None              : baz
    1 :              None              : bar
    0 :            __main__            : foo
10
migpok35

Je voudrais utiliser inspect.currentframe().f_back.f_code.co_name. Son utilisation n’a été traitée dans aucune des réponses précédentes qui sont principalement de l’un des trois types suivants:

  • Certains utilisent inspect.stack Mais on le sait trop lent .
  • Certains utilisent sys._getframe, Qui est une fonction privée interne en raison de son soulignement, et son utilisation est donc implicitement déconseillée.
  • On utilise inspect.getouterframes(inspect.currentframe(), 2)[1][3] mais on ignore encore à quoi [1][3] Accède.
import inspect
import types
from typing import cast


def caller_name() -> str:
    """Return the calling function's name."""
    # Ref: https://stackoverflow.com/a/57712700/
    return cast(types.FrameType, inspect.currentframe()).f_back.f_code.co_name


if __== '__main__':
    def _test_caller_name() -> None:
        assert caller_name() == '_test_caller_name'
    _test_caller_name()

Remerciements: commentaire préalable de 1313e pour un réponse .

2
Acumenus

J'ai trouvé un moyen si vous allez à travers les classes et que vous voulez que la classe à laquelle appartient la méthode ET la méthode. Cela prend un peu de travail d’extraction mais c’est clair. Cela fonctionne dans Python 2.7.13.

import inspect, os

class ClassOne:
    def method1(self):
        classtwoObj.method2()

class ClassTwo:
    def method2(self):
        curframe = inspect.currentframe()
        calframe = inspect.getouterframes(curframe, 4)
        print '\nI was called from', calframe[1][3], \
        'in', calframe[1][4][0][6: -2]

# create objects to access class methods
classoneObj = ClassOne()
classtwoObj = ClassTwo()

# start the program
os.system('cls')
classoneObj.method1()
1
Michael Swartz
#!/usr/bin/env python
import inspect

called=lambda: inspect.stack()[1][3]

def caller1():
    print "inside: ",called()

def caller2():
    print "inside: ",called()

if __name__=='__main__':
    caller1()
    caller2()
shahid@shahid-VirtualBox:~/Documents$ python test_func.py 
inside:  caller1
inside:  caller2
shahid@shahid-VirtualBox:~/Documents$

vous pouvez imprimer une liste de fonctions telles que celles répondues ici https://stackoverflow.com/a/56897183/4039061

import inspect;print(*['\n\x1b[0;36;1m| \x1b[0;32;1m{:25}\x1b[0;36;1m| \x1b[0;35;1m{}'.format(str(x.function), x.filename+'\x1b[0;31;1m:'+str(x.lineno)+'\x1b[0m') for x in inspect.stack()])
0
MohitGhodasara