Wie erhalte ich einen Funktionsnamen als Zeichenfolge?

741

Wie erhalte ich in Python einen Funktionsnamen als Zeichenfolge, ohne die Funktion aufzurufen?

def my_function():
    pass

print get_function_name_as_string(my_function) # my_function is not in quotes

sollte ausgeben "my_function".

Ist eine solche Funktion in Python verfügbar? Wenn nicht, irgendwelche Ideen zur Implementierung get_function_name_as_stringin Python?

X10
quelle

Antworten:

945
my_function.__name__

Die Verwendung __name__ist die bevorzugte Methode, da sie einheitlich angewendet wird. Im Gegensatz func_namedazu funktioniert es auch mit integrierten Funktionen:

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

Auch die doppelten Unterstriche zeigen dem Leser an, dass dies ein besonderes Attribut ist. Als Bonus haben Klassen und Module auch ein __name__Attribut, sodass Sie sich nur einen speziellen Namen merken müssen.

user28409
quelle
420
In einigen Fällen erhalten Sie ein Funktionsobjekt als Argument für Ihre Funktion und müssen den Namen dieser Funktion anzeigen / speichern / bearbeiten. Vielleicht generieren Sie Dokumentation, Hilfetext, einen Verlauf von Aktionen usw. Nein, Sie codieren den Funktionsnamen nicht immer fest.
mbargiel
2
@mgargiel: Was ich sagen wollte ist: Angenommen, Sie haben eine Klasse, die 100 Methoden definiert, wobei alle Methoden nur Wrapper sind, die eine private Methode aufrufen und den Namen des Wrappers selbst übergeben. So etwas wie das : def wrapper(kwargs): super(ExtendedClass,self).call('wrapper', kwargs). Sie haben eine andere Wahl : def wrapper(kwargs): super(ExtendedClass,self).call(sys._getframe().f_code.co_name, kwargs). Die Antwort von Albert Vonpupp sieht für mich also besser aus.
Richard Gomes
19
@RichardGomes Eine Antwort ist geeignet, um den Namen innerhalb der Funktion selbst abzurufen, die andere, um ihn aus einer Funktionsreferenz abzurufen. Die schriftliche Frage des OP weist auf Letzteres hin.
Russell Borogove
22
@RichardGomes Eigentlich bin ich auf diese Frage gekommen und habe nach einer Lösung für dieses Problem gesucht. Das Problem, das ich ansprechen möchte, besteht darin, einen Dekorateur zu erstellen, mit dem alle meine Anrufe protokolliert werden können.
Ali-Hussain
23
@ RichardGomes-Funktionen sind erstklassige Objekte, an die mehr als nur ein Name gebunden sein kann. Das muss nicht unbedingt so sein f.__name__ == 'f'.
wim
298

Beachten Sie Folgendes, um den Namen der aktuellen Funktion oder Methode darin abzurufen:

import inspect

this_function_name = inspect.currentframe().f_code.co_name

sys._getframefunktioniert auch anstatt inspect.currentframeobwohl letzterer den Zugriff auf eine private Funktion vermeidet.

Um stattdessen den Namen der aufrufenden Funktion zu erhalten, betrachten Sie f_backwie in inspect.currentframe().f_back.f_code.co_name.


Wenn auch verwendet mypy, kann es sich beschweren, dass:

Fehler: Element "Keine" von "Optional [FrameType]" hat kein Attribut "f_code"

Beachten Sie Folgendes, um den obigen Fehler zu unterdrücken:

import inspect
import types
from typing import cast

this_function_name = cast(types.FrameType, inspect.currentframe()).f_code.co_name
Albert Vonpupp
quelle
45
+1: Dies ist die Antwort, die ich gerne sehen würde. Andere Antworten setzen voraus, dass der Aufrufer den Funktionsnamen bereits kennt, was im Kontext dieser Frage Unsinn ist.
Richard Gomes
110
Richard: Nein, tut es nicht. SIE gehen davon aus, dass Sie name oder func_name für Ihre Funktion direkt in demselben Bereich aufrufen, in dem sie definiert wurde, was sehr oft nicht der Fall ist. Denken Sie daran, dass Funktionen Objekte sind - sie können als Argumente an andere Funktionen weitergegeben, in Listen / Diktaten für spätere Suchvorgänge oder Ausführung usw.
gespeichert werden
11
@paulus_almighty, das Stöbern in Stapelrahmen erscheint mir nicht abstrakt! In der Tat ist es das Gegenteil von abstrakt. Weitere Informationen finden Sie im Implementierungsdetailhinweis in den Dokumenten. Nicht alle Implementierungen von Python enthalten sys._getframe- es ist direkt mit den Interna von CPython verbunden.
senderle
6
Dies funktioniert nur innerhalb der Funktion, aber die Frage gibt an, dass die Funktion nicht aufgerufen werden sollte.
user2357112 unterstützt Monica
5
Wenn Sie Ihre Funktion in eine benutzerfreundlichere und leichter zu merkende Funktion umschließen möchten, müssen Sie Frame 1 wie abrufen sys._getframe(1).f_code.co_name, damit Sie eine Funktion wie definieren get_func_name()und erwarten können, den gewünschten Namen der aufgerufenen Funktion zu erhalten.
m3nda
44
my_function.func_name

Es gibt auch andere lustige Eigenschaften von Funktionen. Geben Sie ein, dir(func_name)um sie aufzulisten. func_name.func_code.co_codeist die kompilierte Funktion, die als Zeichenfolge gespeichert wird.

import dis
dis.dis(my_function)

zeigt den Code in einem fast für Menschen lesbaren Format an. :) :)

Markus Jarderot
quelle
4
Was ist der Unterschied zwischen f .__ name__ und f.func_name?
Federico A. Ramponi
14
Sam: Namen sind privat, __namen sind speziell, es gibt einen konzeptionellen Unterschied.
Matthew Trevor
11
Falls jemand von der vorhergehenden Antwort von Matthew verwirrt ist, hat das Kommentarsystem einige doppelte Unterstriche als Code für Fettdruck interpretiert. Mit Backtick entkommen, sollte die Nachricht lauten: __names__sind privat, __namessind speziell.
Gwideman
12
Eigentlich denke ich, dass das Richtige _namesprivat ist (einzelner Unterstrich vorher, nur eine Konvention), etwas __names__Besonderes ist (doppelter Unterstrich vorher und nachher). Ich bin mir nicht sicher, ob ein doppelter Unterstrich zuvor formal oder als Konvention eine Bedeutung hat.
MestreLion
8
func_nameexistiert nicht mehr in Python3, daher müssen Sie es verwenden, func.__name__wenn Sie Kompatibilität wünschen
Daenyth
34

Diese Funktion gibt den Funktionsnamen des Anrufers zurück.

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

Es ist wie die Antwort von Albert Vonpupp mit einem freundlichen Umschlag.

Demyn
quelle
1
Ich hatte "<Modul>" bei Index [2], aber Folgendes funktionierte: traceback.extract_stack (None, 2) [0] [- 1]
emmagras
3
Für mich funktioniert das nicht, aber das funktioniert: traceback.extract_stack () [- 1] [2]
mike01010
Dies funktioniert, wenn Sie den ersten Index auf 1 ändern. Sie sollten lernen, vor dem Kommentieren zu debuggen ... traceback.extract_stack (None, 2) [1] [2]
Jcc.Sanabria
29

Wenn Sie sich auch für Klassenmethoden interessieren, bietet Python 3.3+ __qualname__zusätzlich zu __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
quelle
20

Ich benutze gerne einen Funktionsdekorateur. Ich habe eine Klasse hinzugefügt, die auch die Funktionszeit mal. Angenommen, gLog ist ein Standard-Python-Logger:

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

Jetzt müssen Sie nur noch mit Ihrer Funktion dekorieren und voila

@func_timer_decorator
def my_func():
Radato
quelle
15

sys._getframe()Es ist nicht garantiert, dass es in allen Implementierungen von Python verfügbar ist ( siehe Ref. ). Sie können das tracebackModul verwenden, um dasselbe zu tun, z.

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

   return funcName

Ein Aufruf von stack[-1]gibt die aktuellen Prozessdetails zurück.

sandyc
quelle
Sorry, wenn sys._getframe()undefiniert, dann traceback.extract_stackist auch nicht funktionsfähig. Letzteres bietet eine grobe Obermenge der Funktionalität des ersteren; Sie können nicht erwarten, eins ohne das andere zu sehen. Tatsächlich extract_stack()kehrt in IronPython 2.7 immer zurück []. -1
SingleNegationElimination
15
import inspect

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

wo

  • stack () [0] der Aufrufer

  • stack () [3] der Stringname der Methode

Ma Guowei
quelle
1
Schlicht und einfach. stack()[0]wird immer der Aufrufer sein, [3]der Stringname der Methode.
Kontur
14

Als Erweiterung der Antwort von @ Demyn habe ich einige Dienstprogrammfunktionen erstellt, die den Namen der aktuellen Funktion und die Argumente der aktuellen Funktion drucken:

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.
quelle
8

Sie möchten nur den Namen der Funktion erhalten, hier ist ein einfacher Code dafür. Angenommen, Sie haben diese Funktionen definiert

def function1():
    print "function1"

def function2():
    print "function2"

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

Die Ausgabe ist function1

Angenommen, Sie haben diese Funktionen in einer Liste

a = [function1 , function2 , funciton3]

um den Namen der Funktionen zu erhalten

for i in a:
    print i.__name__

die Ausgabe wird sein

Funktion1
Funktion2
Funktion3

Mohsin Ashraf
quelle
5

Ich habe ein paar Antworten gesehen, bei denen Dekorateure zum Einsatz kamen, obwohl ich der Meinung war, dass einige etwas ausführlich waren. Hier ist etwas, das ich zum Protokollieren von Funktionsnamen sowie ihrer jeweiligen Eingabe- und Ausgabewerte verwende. Ich habe es hier angepasst, um nur die Informationen zu drucken, anstatt eine Protokolldatei zu erstellen, und es angepasst, um es auf das OP-spezifische Beispiel anzuwenden.

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

Ausgabe:

{'function_name': 'my_function'}
NL23codes
quelle
1
Was mir im Gegensatz zu allen anderen gefällt, ist, dass debugSie durch einfaches Hinzufügen der Funktion die Funktionalität hinzufügen können, indem Sie einfach den Dekorator zu jeder Funktion hinzufügen, die Sie benötigen oder den Funktionsnamen drucken möchten. Es scheint das einfachste und anpassungsfähigste zu sein.
SpareTimeCoder