Come ottenere un nome di funzione come stringa?


741

In Python, come posso ottenere un nome di funzione come stringa, senza chiamare la funzione?

def my_function():
    pass

print get_function_name_as_string(my_function) # my_function is not in quotes

dovrebbe produrre "my_function".

Tale funzione è disponibile in Python? In caso contrario, qualche idea su come implementare get_function_name_as_string, in Python?


Risposte:


945
my_function.__name__

L'uso __name__è il metodo preferito in quanto si applica in modo uniforme. A differenza func_name, funziona anche su funzioni integrate:

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

Anche i doppi trattini bassi indicano al lettore che questo è un attributo speciale. Come bonus, anche le classi e i moduli hanno un __name__attributo, quindi hai solo un nome speciale da ricordare.


420
Perché in alcuni casi, si ottiene un oggetto funzione come argomento per la propria funzione ed è necessario visualizzare / archiviare / manipolare il nome di quella funzione. Forse stai generando documentazione, testo di aiuto, una cronologia di azioni, ecc. Quindi no, non stai sempre programmando il nome della funzione.
mbargiel,

2
@mgargiel: Quello che intendevo dire è: supponendo che tu abbia una classe che definisce 100 metodi, in cui tutti i metodi sono solo wrapper, che chiamano un metodo privato, passando il nome del wrapper stesso. Qualcosa di simile a questo: def wrapper(kwargs): super(ExtendedClass,self).call('wrapper', kwargs). Hai un'altra scelta, che è: def wrapper(kwargs): super(ExtendedClass,self).call(sys._getframe().f_code.co_name, kwargs). Quindi, la risposta di Albert Vonpupp mi sembra migliore.
Richard Gomes,

19
@RichardGomes Una risposta è appropriata per ottenere il nome all'interno della funzione stessa, l'altra è appropriata per ottenerlo da un riferimento di funzione. La domanda del PO come scritta indica quest'ultima.
Russell Borogove,

22
@RichardGomes In realtà sono arrivato a questa domanda cercando una soluzione a questo problema. Il problema che sto cercando di risolvere è creare un decoratore che può essere utilizzato per registrare tutte le mie chiamate.
ali-hussain,

23
Le funzioni di @RichardGomes sono oggetti di prima classe, possono esserci più di un nome associato a loro. Non è necessariamente così f.__name__ == 'f'.
mercoledì

298

Per ottenere il nome della funzione o del metodo corrente al suo interno, considerare:

import inspect

this_function_name = inspect.currentframe().f_code.co_name

sys._getframefunziona anche invece che inspect.currentframesebbene quest'ultimo eviti di accedere a una funzione privata.

Per ottenere invece il nome della funzione chiamante, considerare f_backcome in inspect.currentframe().f_back.f_code.co_name.


Se si utilizza anche mypy, si può lamentare che:

errore: l'elemento "Nessuno" di "Opzionale [FrameType]" non ha attributo "f_code"

Per eliminare l'errore sopra riportato, considerare:

import inspect
import types
from typing import cast

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

45
+1: Questa è la risposta che vorrei vedere. Altre risposte presuppongono che il chiamante conosca già il nome della funzione, che è una sciocchezza nel contesto di questa domanda.
Richard Gomes,

110
Richard: no non lo è. Stai assumendo che stai chiamando nome o nome_funzione sulla tua funzione direttamente nello stesso ambito in cui è stata definita, il che molto spesso non è il caso. Tieni presente che le funzioni sono oggetti: possono essere passate come argomenti ad altre funzioni, memorizzate in elenchi / dicts per ricerche o esecuzioni successive, ecc.
mbargiel,

11
@paulus_almighty, scavare nei frame dello stack non mi sembra astratto! In effetti, è un po 'l'opposto dell'astratto. Vedi la nota di dettaglio dell'implementazione nei documenti. Non tutte le implementazioni di Python includeranno sys._getframe: è direttamente collegata agli interni di CPython.
mittente

6
Funziona solo all'interno della funzione, ma la domanda specifica che la funzione non deve essere chiamata.
user2357112 supporta Monica

5
Nel caso in cui desideri avvolgere la tua funzione in qualcosa di più utilizzabile e facile da ricordare sys._getframe(1).f_code.co_name, devi recuperare il frame 1 come , in modo da poter definire una funzione come get_func_name()e aspettarti di ottenere il nome desiderato della funzione che ha invocato.
m3nda,

44
my_function.func_name

Ci sono anche altre proprietà divertenti delle funzioni. Digita dir(func_name)per elencarli. func_name.func_code.co_codeè la funzione compilata, memorizzata come stringa.

import dis
dis.dis(my_function)

visualizzerà il codice in un formato quasi leggibile dall'uomo. :)


4
Qual è la differenza tra f .__ name__ e f.func_name?
Federico A. Ramponi,

14
Sam: i nomi sono privati, i nomi __ sono speciali, c'è una differenza concettuale.
Matthew Trevor,

11
Nel caso in cui qualcuno sia perplesso dalla precedente risposta di Matthew, il sistema di commenti ha interpretato alcuni caratteri di sottolineatura come codice per il grassetto. Sfuggito al backtick, il messaggio dovrebbe essere: __names__privato, __namesspeciale.
Gwideman,

12
In realtà, penso che il corretto sia _namesprivato (sottolineatura singola prima, solo una convenzione), __names__sono speciali (sottolineatura doppia prima e dopo). Non sono sicuro che il doppio trattino di sottolineatura prima abbia un significato, formale o convenzionale.
MestreLion,

8
func_namenon esiste più in python3, quindi è necessario utilizzarlo func.__name__se si desidera la compatibilità
Daenyth

34

Questa funzione restituirà il nome della funzione del chiamante.

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

È come la risposta di Albert Vonpupp con un involucro amichevole.


1
Avevo "<module>" all'indice [2], ma il seguente ha funzionato: traceback.extract_stack (None, 2) [0] [- 1]
emmagras,

3
per me questo non funziona, ma questo funziona: traceback.extract_stack () [- 1] [2]
mike01010

Questo funziona se cambi il primo indice in 1, dovresti imparare a eseguire il debug prima di commentare ... traceback.extract_stack (None, 2) [1] [2]
Jcc.Sanabria

29

Se sei interessato anche ai metodi di classe, __qualname__oltre a Python 3.3+ __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

Mi piace usare un decoratore di funzioni. Ho aggiunto una classe, che a volte anche il tempo della funzione. Supponiamo che gLog sia un logger standard di Python:

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

quindi ora tutto ciò che hai a che fare con la tua funzione è decorarla e voilà

@func_timer_decorator
def my_func():

16
import inspect

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

dove

  • stack () [0] il chiamante

  • stack () [3] il nome stringa del metodo


1
Chiaro e semplice. stack()[0]sarà sempre il chiamante, [3]il nome stringa del metodo.
Kontur,

15

Come estensione della risposta di @ Demyn , ho creato alcune funzioni di utilità che stampano il nome della funzione corrente e gli argomenti della funzione corrente:

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()non è garantito per essere disponibile in tutte le implementazioni di Python ( vedi ref ), puoi usare il tracebackmodulo per fare la stessa cosa, ad es.

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

   return funcName

Una chiamata a stack[-1]restituirà i dettagli del processo corrente.


Siamo spiacenti, se sys._getframe()non è definito, allora traceback.extract_stackè anche inutilizzabile. Quest'ultimo fornisce un superset approssimativo della funzionalità del primo; non puoi aspettarti di vederne uno senza l'altro. E infatti, in IronPython 2.7 extract_stack()ritorna sempre []. -1
SingleNegationElimination

8

Vuoi solo ottenere il nome della funzione qui è un semplice codice per questo. supponiamo che tu abbia definito queste funzioni

def function1():
    print "function1"

def function2():
    print "function2"

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

l'uscita sarà function1

Ora supponiamo che tu abbia queste funzioni in un elenco

a = [function1 , function2 , funciton3]

per ottenere il nome delle funzioni

for i in a:
    print i.__name__

l'uscita sarà

funzione1
funzione2
funzione3


5

Ho visto alcune risposte che utilizzavano i decoratori, anche se pensavo che alcuni fossero un po 'prolissi. Ecco qualcosa che uso per registrare i nomi delle funzioni, nonché i rispettivi valori di input e output. L'ho adattato qui per stampare semplicemente le informazioni anziché creare un file di registro e adattarlo per applicare l'esempio specifico 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()

Produzione:

{'function_name': 'my_function'}

1
Quello che mi piace di questo in contrapposizione a tutti gli altri, è che aggiungendo semplicemente la debugfunzione una volta che puoi aggiungere la funzionalità semplicemente aggiungendo il decoratore a qualunque funzione ti serva o desideri stampare il nome della funzione. Sembra essere il più semplice e il più adattabile.
spareTimeCoder
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.