web-dev-qa-db-fra.com

Déterminer le nom de la fonction à partir de cette fonction (sans utiliser traceback)

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?
396
Rob

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.

162
Rosh Oxymoron
import inspect

def foo():
   print(inspect.stack()[0][3])
354
Andreas Jung

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
132
Alex Granovsky

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.

43
bgporter
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.

36
Ron Davis

Je garde cet utilitaire pratique à proximité:

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

Usage:

myself()
21
xxyzzy

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

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

14
cad106uk

print(inspect.stack()[0].function) semble également fonctionner (Python 3.5).

12
Pierre Voisin

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

11
Gino
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

10
Lee

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ètes
  • indexation dans la pile (pouvant être réorganisée dans les futurs pythons)

Je 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))
10
hobs
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
9
nordborn

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'
7
Douglas Denhartog

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

5

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
3
nerdfever.com

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))
3
karthik r

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__)
3
John Forbes

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.

3
Mel Viso Martinez

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')
2
Genschi