Comment obtenir un nom de fonction sous forme de chaîne?


741

En Python, comment puis-je obtenir un nom de fonction sous forme de chaîne, sans appeler la fonction?

def my_function():
    pass

print get_function_name_as_string(my_function) # my_function is not in quotes

devrait sortir "my_function".

Une telle fonction est-elle disponible en Python? Sinon, des idées sur la façon de mettre en œuvre get_function_name_as_string, en Python?


Réponses:


945
my_function.__name__

L'utilisation __name__est la méthode préférée car elle s'applique uniformément. Contrairement à func_namecela, il fonctionne également sur les fonctions intégrées:

>>> import time
>>> time.time.func_name
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
AttributeError: 'builtin_function_or_method' object has no attribute 'func_name'
>>> time.time.__name__ 
'time'

Les doubles soulignements indiquent également au lecteur qu'il s'agit d'un attribut spécial. En prime, les classes et les modules ont aussi un __name__attribut, donc vous n'avez qu'un seul nom spécial à retenir.


420
Parce que dans certains cas, vous obtenez un objet fonction comme argument de votre fonction, et vous devez afficher / stocker / manipuler le nom de cette fonction. Peut-être que vous générez de la documentation, du texte d'aide, un historique des actions, etc. Donc non, vous ne codez pas toujours en dur le nom de la fonction.
mbargiel

2
@mgargiel: Ce que je voulais dire, c'est: en supposant que vous avez une classe qui définit 100 méthodes, où toutes les méthodes ne sont que des wrappers, qui appellent une méthode privée, en passant le nom du wrapper lui-même. Quelque chose comme ceci: def wrapper(kwargs): super(ExtendedClass,self).call('wrapper', kwargs). Vous avez un autre choix, ce qui est: def wrapper(kwargs): super(ExtendedClass,self).call(sys._getframe().f_code.co_name, kwargs). Ainsi, la réponse d'Albert Vonpupp me semble meilleure.
Richard Gomes

19
@RichardGomes Une réponse est appropriée pour obtenir le nom dans la fonction elle-même, l'autre est appropriée pour l'obtenir à partir d'une référence de fonction. La question du PO telle qu'écrite indique ce dernier.
Russell Borogove

22
@RichardGomes En fait, je suis venu à cette question à la recherche d'une solution à ce problème. Le problème que j'essaie de résoudre est de créer un décorateur qui peut être utilisé pour enregistrer tous mes appels.
ali-hussain

23
Les fonctions @RichardGomes sont des objets de première classe, il peut y avoir plus qu'un nom qui leur est lié. Ce n'est pas nécessairement le cas f.__name__ == 'f'.
wim

298

Pour obtenir le nom de la fonction ou de la méthode actuelle de l'intérieur, considérez:

import inspect

this_function_name = inspect.currentframe().f_code.co_name

sys._getframefonctionne également au lieu de inspect.currentframebien que ce dernier évite d'accéder à une fonction privée.

Pour obtenir le nom de la fonction appelante à la place, considérez f_backcomme dans inspect.currentframe().f_back.f_code.co_name.


S'il utilise également mypy, il peut se plaindre que:

erreur: l'élément "Aucun" de "Facultatif [FrameType]" n'a pas d'attribut "f_code"

Pour supprimer l'erreur ci-dessus, envisagez:

import inspect
import types
from typing import cast

this_function_name = cast(types.FrameType, inspect.currentframe()).f_code.co_name

45
+1: C'est la réponse que j'aimerais voir. D'autres réponses supposent que l'appelant connaît déjà le nom de la fonction, ce qui est absurde dans le contexte de cette question.
Richard Gomes

110
Richard: non, ce n'est pas le cas. VOUS supposez que vous appelez name ou func_name sur votre fonction directement dans la même portée que celle définie, ce qui n'est pas souvent le cas. Gardez à l'esprit que les fonctions sont des objets - elles peuvent être transmises comme arguments à d'autres fonctions, stockées dans des listes / dictés pour des recherches ou des exécutions ultérieures, etc.
mbargiel

11
@paulus_almighty, creuser dans des cadres de pile ne me semble pas abstrait! En fait, c'est un peu le contraire de l'abstrait. Voir la note détaillée sur l' implémentation dans la documentation. Toutes les implémentations de Python n'incluront pas sys._getframe- il est directement connecté aux composants internes de CPython.
senderle

6
Cela ne fonctionne qu'à l'intérieur de la fonction, mais la question spécifie que la fonction ne doit pas être appelée.
user2357112 prend en charge Monica

5
Si vous voulez envelopper votre fonction pour quelque chose de plus utilisable et facile à retenir, vous devez récupérer l'image 1 comme sys._getframe(1).f_code.co_name, afin que vous puissiez définir une fonction comme get_func_name()et vous attendre à obtenir le nom souhaité de la fonction qui a invoqué.
m3nda

44
my_function.func_name

Il existe également d'autres propriétés amusantes des fonctions. Tapez dir(func_name)pour les répertorier. func_name.func_code.co_codeest la fonction compilée, stockée sous forme de chaîne.

import dis
dis.dis(my_function)

affichera le code dans un format lisible par presque l'homme. :)


4
Quelle est la différence entre f .__ name__ et f.func_name?
Federico A. Ramponi,

14
Sam: les noms sont privés, les __noms sont spéciaux, il y a une différence conceptuelle.
Matthew Trevor

11
Dans le cas où quelqu'un est intrigué par la réponse précédente de Matthew, le système de commentaire a interprété certains doubles soulignements comme du code pour le gras. Échappé avec backtick, le message doit se lire: __names__sont privés, __namessont spéciaux.
gwideman

12
En fait, je pense que le correct est _namesprivé (simple soulignement avant, juste une convention), __names__spécial (double soulignement avant et après). Je ne sais pas si le double soulignement avant a un sens, formellement ou comme convention.
MestreLion

8
func_namen'existe plus en python3 donc vous devez utiliser func.__name__si vous voulez la compatibilité
Daenyth

34

Cette fonction renverra le nom de la fonction de l'appelant.

def func_name():
    import traceback
    return traceback.extract_stack(None, 2)[0][2]

C'est comme la réponse d'Albert Vonpupp avec un emballage convivial.


1
J'avais "<module>" à l'index [2], mais les éléments suivants ont fonctionné: traceback.extract_stack (None, 2) [0] [- 1]
emmagras

3
pour moi, cela ne fonctionne pas, mais cela fonctionne: traceback.extract_stack () [- 1] [2]
mike01010

Cela fonctionne si vous changez le premier index en 1, vous devriez apprendre à déboguer avant de commenter ... traceback.extract_stack (None, 2) [1] [2]
Jcc.Sanabria

29

Si vous êtes intéressé par les méthodes de classe aussi, Python 3.3+ a __qualname__en plus __name__.

def my_function():
    pass

class MyClass(object):
    def method(self):
        pass

print(my_function.__name__)         # gives "my_function"
print(MyClass.method.__name__)      # gives "method"

print(my_function.__qualname__)     # gives "my_function"
print(MyClass.method.__qualname__)  # gives "MyClass.method"

20

J'aime utiliser un décorateur de fonction. J'ai ajouté une classe, qui multiplie également le temps de la fonction. Supposons que gLog est un enregistreur Python standard:

class EnterExitLog():
    def __init__(self, funcName):
        self.funcName = funcName

    def __enter__(self):
        gLog.debug('Started: %s' % self.funcName)
        self.init_time = datetime.datetime.now()
        return self

    def __exit__(self, type, value, tb):
        gLog.debug('Finished: %s in: %s seconds' % (self.funcName, datetime.datetime.now() - self.init_time))

def func_timer_decorator(func):
    def func_wrapper(*args, **kwargs):
        with EnterExitLog(func.__name__):
            return func(*args, **kwargs)

    return func_wrapper

maintenant tout ce que vous avez à faire avec votre fonction est de le décorer et le tour est joué

@func_timer_decorator
def my_func():

16
import inspect

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

  • stack () [0] l'appelant

  • stack () [3] le nom de chaîne de la méthode


1
Clair et simple. stack()[0]sera toujours l'appelant, [3]le nom de chaîne de la méthode.
kontur

15

Dans le prolongement de la réponse de @ Demyn , j'ai créé des fonctions utilitaires qui affichent le nom de la fonction actuelle et les arguments de la fonction actuelle:

import inspect
import logging
import traceback

def get_function_name():
    return traceback.extract_stack(None, 2)[0][2]

def get_function_parameters_and_values():
    frame = inspect.currentframe().f_back
    args, _, _, values = inspect.getargvalues(frame)
    return ([(i, values[i]) for i in args])

def my_func(a, b, c=None):
    logging.info('Running ' + get_function_name() + '(' + str(get_function_parameters_and_values()) +')')
    pass

logger = logging.getLogger()
handler = logging.StreamHandler()
formatter = logging.Formatter(
    '%(asctime)s [%(levelname)s] -> %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.INFO)

my_func(1, 3) # 2016-03-25 17:16:06,927 [INFO] -> Running my_func([('a', 1), ('b', 3), ('c', None)])

15

sys._getframe()n'est pas garanti d'être disponible dans toutes les implémentations de Python ( voir ref ), vous pouvez utiliser le tracebackmodule pour faire la même chose, par exemple.

import traceback
def who_am_i():
   stack = traceback.extract_stack()
   filename, codeline, funcName, text = stack[-2]

   return funcName

Un appel à stack[-1]renverra les détails du processus en cours.


Désolé, s'il sys._getframe()n'est pas défini, il traceback.extract_stackest également inutilisable. Ce dernier fournit un surensemble grossier de la fonctionnalité du premier; vous ne pouvez pas vous attendre à voir l'un sans l'autre. Et en fait, dans IronPython 2.7 extract_stack()revient toujours []. -1
SingleNegationElimination

8

Vous voulez juste obtenir le nom de la fonction, voici un code simple pour cela. disons que vous avez défini ces fonctions

def function1():
    print "function1"

def function2():
    print "function2"

def function3():
    print "function3"
print function1.__name__

la sortie sera function1

Maintenant, disons que vous avez ces fonctions dans une liste

a = [function1 , function2 , funciton3]

pour obtenir le nom des fonctions

for i in a:
    print i.__name__

la sortie sera

fonction1
fonction2
fonction3


5

J'ai vu quelques réponses qui utilisaient des décorateurs, même si je trouvais que certaines étaient un peu bavardes. Voici quelque chose que j'utilise pour enregistrer les noms de fonction ainsi que leurs valeurs d'entrée et de sortie respectives. Je l'ai adapté ici pour simplement imprimer les informations plutôt que de créer un fichier journal et je l'ai adapté pour l'appliquer à l'exemple spécifique OP.

def debug(func=None):
    def wrapper(*args, **kwargs):
        try:
            function_name = func.__func__.__qualname__
        except:
            function_name = func.__qualname__
        return func(*args, **kwargs, function_name=function_name)
    return wrapper

@debug
def my_function(**kwargs):
    print(kwargs)

my_function()

Production:

{'function_name': 'my_function'}

1
Ce que j'aime à ce sujet par rapport à tous les autres, c'est qu'en ajoutant simplement la debugfonction une fois que vous pouvez ajouter la fonctionnalité en ajoutant simplement le décorateur à la fonction dont vous avez besoin ou souhaitez imprimer le nom de la fonction. Il semble que ce soit le plus simple et le plus adaptable.
spareTimeCoder
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.