En Python, sans utiliser le module traceback
, existe-t-il un moyen de déterminer le nom d'une fonction à partir de cette fonction?
Disons que j'ai un module toto avec une barre de fonctions. Lors de l'exécution de foo.bar()
, y a-t-il un moyen pour bar de connaître le nom de bar? Ou mieux encore, le nom de foo.bar
?
#foo.py
def bar():
print "my name is", __my# <== how do I calculate this at runtime?
Python n'a pas de fonction permettant d'accéder à la fonction ou à son nom dans la fonction elle-même. Il a été proposé mais rejeté. Si vous ne voulez pas jouer vous-même avec la pile, vous devez soit utiliser "bar"
ou bar.__name__
en fonction du contexte.
L'avis de rejet donné est:
Ce PEP est rejeté. On ne sait pas comment cela devrait être implémenté ou quelle devrait être la sémantique exacte dans les cas Edge, et il n'y a pas assez de cas d'utilisation importants donnés. la réponse a été au mieux tiède.
import inspect
def foo():
print(inspect.stack()[0][3])
Il y a plusieurs façons d'obtenir le même résultat:
from __future__ import print_function
import sys
import inspect
def what_is_my_name():
print(inspect.stack()[0][0].f_code.co_name)
print(inspect.stack()[0][3])
print(inspect.currentframe().f_code.co_name)
print(sys._getframe().f_code.co_name)
Notez que les appels inspect.stack
sont des milliers de fois plus lents que les alternatives:
$ python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][0].f_code.co_name'
1000 loops, best of 3: 499 usec per loop
$ python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][3]'
1000 loops, best of 3: 497 usec per loop
$ python -m timeit -s 'import inspect, sys' 'inspect.currentframe().f_code.co_name'
10000000 loops, best of 3: 0.1 usec per loop
$ python -m timeit -s 'import inspect, sys' 'sys._getframe().f_code.co_name'
10000000 loops, best of 3: 0.135 usec per loop
Vous pouvez obtenir le nom avec lequel elle a été définie à l'aide de l'approche indiquée par @Andreas Jung , mais il se peut que ce ne soit pas le nom avec lequel la fonction a été appelée:
import inspect
def Foo():
print inspect.stack()[0][3]
Foo2 = Foo
>>> Foo()
Foo
>>> Foo2()
Foo
Je ne peux pas vous dire si cette distinction est importante pour vous ou non.
functionNameAsString = sys._getframe().f_code.co_name
Je voulais une chose très similaire parce que je voulais mettre le nom de la fonction dans une chaîne de log qui allait à plusieurs endroits dans mon code. Ce n’est probablement pas le meilleur moyen de le faire, mais voici un moyen d’obtenir le nom de la fonction actuelle.
Je garde cet utilitaire pratique à proximité:
import inspect
myself = lambda: inspect.stack()[1][3]
Usage:
myself()
Je suppose que inspect
est le meilleur moyen de le faire. Par exemple:
import inspect
def bar():
print("My name is", inspect.stack()[0][3])
J'ai trouvé un wrapper qui écrira le nom de la fonction
from functools import wraps
def tmp_wrap(func):
@wraps(func)
def tmp(*args, **kwargs):
print func.__name__
return func(*args, **kwargs)
return tmp
@tmp_wrap
def my_funky_name():
print "STUB"
my_funky_name()
Cela va imprimer
mon_funky_name
TALON
print(inspect.stack()[0].function)
semble également fonctionner (Python 3.5).
Ceci est en réalité dérivé des autres réponses à la question.
Voici ma prise:
import sys
# for current func name, specify 0 or no argument.
# for name of caller of current func, specify 1.
# for name of caller of caller of current func, specify 2. etc.
currentFuncName = lambda n=0: sys._getframe(n + 1).f_code.co_name
def testFunction():
print "You are in function:", currentFuncName()
print "This function's caller was:", currentFuncName(1)
def invokeTest():
testFunction()
invokeTest()
# end of file
L'avantage probable de cette version par rapport à l'utilisation de inspect.stack () est qu'elle devrait être des milliers de fois plus rapide [voir l'article et les chronologies d'Alex Melihoff concernant l'utilisation de sys._getframe () par rapport à inspect.stack ()].
import inspect
def whoami():
return inspect.stack()[1][3]
def whosdaddy():
return inspect.stack()[2][3]
def foo():
print "hello, I'm %s, daddy is %s" % (whoami(), whosdaddy())
bar()
def bar():
print "hello, I'm %s, daddy is %s" % (whoami(), whosdaddy())
foo()
bar()
Dans IDE, les sorties de code
bonjour, je suis foo, papa est
bonjour, je suis bar, papa est foo
bonjour, je suis bar, papa est
Voici une approche d'avenir.
La combinaison des suggestions de @ CamHart et de @ Yuval avec celles de @ RoshOxymoron réponse acceptée présente l'avantage d'éviter:
_hidden
et méthodes potentiellement obsolètesJe pense donc que cela joue bien avec les futures versions de python (testées sous 2.7.3 et 3.3.2):
from __future__ import print_function
import inspect
def bar():
print("my name is '{}'".format(inspect.currentframe().f_code.co_name))
import sys
def func_name():
"""
:return: name of caller
"""
return sys._getframe(1).f_code.co_name
class A(object):
def __init__(self):
pass
def test_class_func_name(self):
print(func_name())
def test_func_name():
print(func_name())
Tester:
a = A()
a.test_class_func_name()
test_func_name()
Sortie:
test_class_func_name
test_func_name
Vous pouvez utiliser un décorateur:
def my_function(name=None):
return name
def get_function_name(function):
return function(name=function.__name__)
>>> get_function_name(my_function)
'my_function'
J'ai fait ce que CamHart a dit:
import sys
def myFunctionsHere():
print(sys._getframe().f_code.co_name)
myFunctionsHere()
Sortie:
C:\Python\Python36\python.exe C: /Python/GetFunctionsNames/TestFunctionsNames.py myFunctionsHere
Processus terminé avec le code de sortie 0
Utilisez ceci (basé sur la réponse de #Ron Davis):
import sys
def thisFunctionName():
"""Returns a string with the name of the function it's called from"""
return sys._getframe(1).f_code.co_name
Je ne sais pas pourquoi les gens compliquent les choses:
import sys
print("%s/%s" %(sys._getframe().f_code.co_filename, sys._getframe().f_code.co_name))
J'ai récemment essayé d'utiliser les réponses ci-dessus pour accéder à la chaîne de caractères d'une fonction à partir du contexte de cette fonction, mais les questions ci-dessus ne renvoyant que la chaîne de nom, cela ne fonctionnait pas.
Heureusement j'ai trouvé une solution simple. Si, comme moi, vous souhaitez faire référence à la fonction plutôt que d'obtenir simplement la chaîne représentant le nom, vous pouvez appliquer eval () à la chaîne du nom de la fonction.
import sys
def foo():
"""foo docstring"""
print(eval(sys._getframe().f_code.co_name).__doc__)
Je fais ma propre approche utilisée pour appeler super avec sécurité dans un scénario d'héritage multiple (je mets tout le code)
def safe_super(_class, _inst):
"""safe super call"""
try:
return getattr(super(_class, _inst), _inst.__fname__)
except:
return (lambda *x,**kx: None)
def with_name(function):
def wrap(self, *args, **kwargs):
self.__f= function.__name__
return function(self, *args, **kwargs)
return wrap
utilisation de l'échantillon:
class A(object):
def __init__():
super(A, self).__init__()
@with_name
def test(self):
print 'called from A\n'
safe_super(A, self)()
class B(object):
def __init__():
super(B, self).__init__()
@with_name
def test(self):
print 'called from B\n'
safe_super(B, self)()
class C(A, B):
def __init__():
super(C, self).__init__()
@with_name
def test(self):
print 'called from C\n'
safe_super(C, self)()
le tester:
a = C()
a.test()
sortie:
called from C
called from A
called from B
Dans chaque méthode @with_name, vous avez accès à self .__fas le nom de la fonction en cours.
Je suggère de ne pas compter sur les éléments de la pile. Si quelqu'un utilise votre code dans des contextes différents (interpréteur python par exemple), votre pile va changer et casser votre index ([0] [3]).
Je vous suggère quelque chose comme ça:
class MyClass:
def __init__(self):
self.function_name = None
def _Handler(self, **kwargs):
print('Calling function {} with parameters {}'.format(self.function_name, kwargs))
self.function_name = None
def __getattr__(self, attr):
self.function_name = attr
return self._Handler
mc = MyClass()
mc.test(FirstParam='my', SecondParam='test')
mc.foobar(OtherParam='foobar')