Jak uzyskać nazwę funkcji jako ciąg?

740

W Pythonie, jak uzyskać nazwę funkcji jako ciąg, bez wywoływania funkcji?

def my_function():
    pass

print get_function_name_as_string(my_function) # my_function is not in quotes

powinien wyjść "my_function".

Czy taka funkcja jest dostępna w Pythonie? Jeśli nie, jakieś pomysły dotyczące implementacji get_function_name_as_stringw Pythonie?

X10
źródło

Odpowiedzi:

944
my_function.__name__

Używanie __name__jest preferowaną metodą, ponieważ stosuje się równomiernie. W przeciwieństwie do func_nametego działa również na wbudowanych funkcjach:

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

Również podwójne podkreślenia wskazują czytelnikowi, że jest to specjalny atrybut. Jako bonus, klasy i moduły również mają __name__atrybut, więc pamiętasz tylko jedną specjalną nazwę.

użytkownik 28409
źródło
420
Ponieważ w niektórych przypadkach dostajesz jakiś obiekt funkcji jako argument do swojej funkcji i musisz wyświetlić / zapisać / manipulować nazwą tej funkcji. Być może generujesz dokumentację, tekst pomocy, historię działań itp. Więc nie, nie zawsze na stałe wpisujesz nazwę funkcji.
mbargiel
2
@mgargiel: Chciałem powiedzieć: załóżmy, że masz klasę, która definiuje 100 metod, przy czym wszystkie metody są tylko opakowaniami, które wywołują metodę prywatną, przekazując nazwę samego opakowania. Coś takiego: def wrapper(kwargs): super(ExtendedClass,self).call('wrapper', kwargs). Masz innego wyboru, czyli: def wrapper(kwargs): super(ExtendedClass,self).call(sys._getframe().f_code.co_name, kwargs). Odpowiedź Alberta Vonpuppa wygląda więc dla mnie lepiej.
Richard Gomes
19
@RichardGomes Jedna odpowiedź jest odpowiednia do uzyskania nazwy w samej funkcji, druga jest odpowiednia do uzyskania jej z odwołania do funkcji. Pytanie OP w formie pisemnej wskazuje na to drugie.
Russell Borogove
22
@RichardGomes Właściwie to przyszedłem na to pytanie, szukając rozwiązania tego problemu. Problem, który próbuję rozwiązać, to utworzenie dekoratora, którego można używać do rejestrowania wszystkich moich połączeń.
ali-hussain
23
Funkcje @RichardGomes są pierwszorzędnymi obiektami, może być z nimi związane nie tylko nazwa. Niekoniecznie tak jest f.__name__ == 'f'.
wim
298

Aby uzyskać nazwę bieżącej funkcji lub metody z jej wnętrza, rozważ:

import inspect

this_function_name = inspect.currentframe().f_code.co_name

sys._getframedziała również zamiast inspect.currentframetego ostatniego, chociaż unika dostępu do funkcji prywatnej.

Aby zamiast tego uzyskać nazwę funkcji wywołującej, rozważ f_backjak w inspect.currentframe().f_back.f_code.co_name.


Jeśli używasz mypy, możesz narzekać, że:

error: Element „None” z „Optional [FrameType]” nie ma atrybutu „f_code”

Aby ukryć powyższy błąd, rozważ:

import inspect
import types
from typing import cast

this_function_name = cast(types.FrameType, inspect.currentframe()).f_code.co_name
Albert Vonpupp
źródło
45
+1: To jest odpowiedź, którą chciałbym zobaczyć. Inne odpowiedzi zakładają, że osoba dzwoniąca zna już nazwę funkcji, co w kontekście tego pytania jest nonsensowne.
Richard Gomes,
110
Richard: nie, nie robi. Zakładasz , że wywołujesz nazwę lub func_name w swojej funkcji bezpośrednio w tym samym zakresie, w jakim został zdefiniowany, co często nie jest prawdą. Pamiętaj, że funkcje są obiektami - mogą być przekazywane jako argumenty do innych funkcji, przechowywane na listach / dyktach w celu późniejszego wyszukiwania lub wykonania itp.
mbargiel
11
@paulus_almighty, kopanie w ramki stosu nie wydaje mi się abstrakcyjne! W rzeczywistości jest to przeciwieństwo abstrakcji. Zobacz szczegółową notatkę dotyczącą implementacji w dokumentach. Nie wszystkie implementacje Pythona będą obejmować sys._getframe- jest on bezpośrednio połączony z wewnętrznymi elementami CPython.
senderle
6
Działa to tylko wewnątrz funkcji, ale pytanie określa, że ​​funkcja nie powinna być wywoływana.
user2357112 obsługuje Monikę
5
Jeśli chcesz zawinąć swoją funkcję w coś bardziej użytecznego i łatwego do zapamiętania, musisz pobrać ramkę 1 sys._getframe(1).f_code.co_name, abyś mógł zdefiniować funkcję podobną get_func_name()i oczekiwać uzyskania żądanej nazwy funkcji, która ją wywołała.
m3nda
44
my_function.func_name

Istnieją również inne zabawne właściwości funkcji. Wpisz, dir(func_name)aby je wyświetlić. func_name.func_code.co_codeto skompilowana funkcja przechowywana jako ciąg.

import dis
dis.dis(my_function)

wyświetli kod w prawie czytelnej formie. :)

Markus Jarderot
źródło
4
Jaka jest różnica między f .__ name__ a f.func_name?
Federico A. Ramponi
14
Sam: nazwy są prywatne, __nazwiska są wyjątkowe, istnieje różnica koncepcyjna.
Matthew Trevor
11
W przypadku, gdy ktoś jest zaskoczony poprzednią odpowiedzią Matthew, system komentarzy zinterpretował niektóre podwójne podkreślenia jako kod pogrubiony. Z ucieczką za pomocą znaku wstecznego wiadomość powinna brzmieć: __names__są prywatne, __namessą wyjątkowe.
gwideman
12
Właściwie uważam, że poprawne jest _namesprywatne (pojedyncze podkreślenie przed, tylko konwencja), __names__wyjątkowe (podwójne podkreślenia przed i po). Nie jestem pewien, czy podwójne podkreślenie wcześniej ma jakieś znaczenie, formalnie czy jako konwencja.
MestreLion
8
func_namejuż nie istnieje w Python3, więc musisz użyć, func.__name__jeśli chcesz kompatybilności
Daenyth,
34

Ta funkcja zwróci nazwę funkcji dzwoniącego.

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

To jest jak odpowiedź Alberta Vonpuppa z przyjaznym opakowaniem.

Demyn
źródło
1
Miałem „<module>” w indeksie [2], ale następujące działały: traceback.extract_stack (Brak, 2) [0] [- 1]
emmagras
3
dla mnie to nie działa, ale działa: traceback.extract_stack () [- 1] [2]
mike01010
Działa to, jeśli zmienisz pierwszy indeks na 1, powinieneś nauczyć się debugowania przed komentowaniem ... traceback.extract_stack (Brak, 2) [1] [2]
Jcc.Sanabria
29

Jeśli interesują Cię również metody klasowe, Python 3.3+ ma __qualname__dodatkowo __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"
Lapis
źródło
20

Lubię używać dekoratora funkcji. Dodałem klasę, która również razy czas funkcji. Załóżmy, że gLog to standardowy program rejestrujący w języku 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

więc teraz wszystko, co musisz zrobić z funkcją, to udekorować ją i voila

@func_timer_decorator
def my_func():
radato
źródło
15

sys._getframe()nie gwarantuje się, że będzie dostępny we wszystkich implementacjach Pythona ( patrz odnośnik ), możesz użyć tracebackmodułu, aby zrobić to samo, np.

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

   return funcName

Wezwanie do stack[-1]zwróci bieżące szczegóły procesu.

sandyc
źródło
Przepraszamy, jeśli sys._getframe()jest niezdefiniowany, traceback.extract_stackto również nie działa. Ten ostatni zapewnia zgrubny nadzór nad funkcjonalnością pierwszego; nie możesz oczekiwać, że zobaczysz jedno bez drugiego. W rzeczywistości w IronPython 2.7 extract_stack()zawsze powraca []. -1
SingleNegationElimination
15
import inspect

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

gdzie

  • stos () [0] rozmówcy

  • stack () [3] nazwa ciągu metody

Ma Guowei
źródło
1
Prosty i prosty. stack()[0]zawsze będzie wywołujący, [3]nazwa ciągu metody.
kontur
14

Jako rozszerzenie odpowiedzi @ Demyn stworzyłem niektóre funkcje narzędziowe, które wypisują nazwę bieżącej funkcji i jej argumenty:

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)])
Jim G.
źródło
8

Chcesz tylko uzyskać nazwę funkcji tutaj jest to prosty kod do tego. powiedzmy, że masz zdefiniowane te funkcje

def function1():
    print "function1"

def function2():
    print "function2"

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

wyjście będzie funkcją 1

Powiedzmy, że masz te funkcje na liście

a = [function1 , function2 , funciton3]

aby uzyskać nazwę funkcji

for i in a:
    print i.__name__

wyjście będzie

funkcja1
funkcja2
funkcja3

Mohsin Ashraf
źródło
5

Widziałem kilka odpowiedzi, które wykorzystywały dekoratorów, choć czułem, że kilka było trochę gadatliwych. Oto coś, czego używam do rejestrowania nazw funkcji, a także ich odpowiednich wartości wejściowych i wyjściowych. Zaadaptowałem to tutaj, aby po prostu wydrukować informacje, zamiast tworzyć plik dziennika, i dostosowałem go do zastosowania w konkretnym przykładzie PO.

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

Wynik:

{'function_name': 'my_function'}
Kody NL23
źródło
1
To, co lubię w tym, w przeciwieństwie do wszystkich innych, to to, że po prostu dodając debugfunkcję, gdy tylko możesz ją dodać, po prostu dodając dekorator do dowolnej funkcji, której potrzebujesz lub chcesz wydrukować nazwę funkcji. To wydaje się najłatwiejsze i najbardziej elastyczne.
spareTimeCoder