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?
python
introspection
zs2020
la source
la source
Réponses:
inspect.getframeinfo et d'autres fonctions associées 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'y fier à des fins de fonctionnalité de production.
la source
Version plus courte:
import inspect def f1(): f2() def f2(): print 'caller name:', inspect.stack()[1][3] f1()
(avec merci à @Alex et Stefaan Lippen )
la source
Cela semble fonctionner très bien:
import sys print sys._getframe().f_back.f_code.co_name
la source
inspect.stack
sys
module subisse une refactorisation lourde.J'ai proposé une version légèrement plus longue qui tente de créer un nom de méthode complet, y compris le module et la 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)
la source
stack
, il y a toujours des fuites de cadres en raison de références circulaires, comme expliqué dansJ'utiliserais
inspect.currentframe().f_back.f_code.co_name
. Son utilisation n'a été couverte dans aucune des réponses précédentes qui sont principalement de l'un des trois types suivants:inspect.stack
mais on sait que c'est trop lent .sys._getframe
qui est une fonction privée interne étant donné son trait de soulignement principal, et donc son utilisation est implicitement déconseillée.inspect.getouterframes(inspect.currentframe(), 2)[1][3]
mais on ne sait pas du tout à quoi[1][3]
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 qu'il
cast(FrameType, frame)
est utilisé pour satisfairemypy
.Remerciements: commentaire préalable de 1313e pour une réponse .
la source
Un peu de fusion des choses ci-dessus. Mais voici ma fissure.
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
la source
modules = [(index, inspect.getmodule(stack[index][0])) for index in reversed(range(1, min(stack_size, len(inspect.stack()))))]
pour obtenir les modules.J'ai trouvé un moyen si vous traversez des classes et que vous voulez la classe à laquelle appartient la méthode ET la méthode. Cela demande un peu de travail d'extraction mais cela fait valoir son point de vue. 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()
la source
#!/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$
la source