¿Cómo obtener un nombre de función como una cadena?

741

En Python, ¿cómo obtengo el nombre de una función como una cadena, sin llamar a la función?

def my_function():
    pass

print get_function_name_as_string(my_function) # my_function is not in quotes

debería salir "my_function".

¿Esta función está disponible en Python? Si no, ¿alguna idea sobre cómo implementar get_function_name_as_string, en Python?

X10
fuente

Respuestas:

945
my_function.__name__

El uso __name__es el método preferido ya que se aplica de manera uniforme. A diferencia func_name, también funciona en funciones integradas:

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

También los guiones bajos dobles indican al lector que este es un atributo especial. Como beneficio adicional, las clases y los módulos también tienen un __name__atributo, por lo que solo debe recordar un nombre especial.

usuario28409
fuente
420
Porque en algunos casos, obtienes algún objeto de función como argumento para tu función, y necesitas mostrar / almacenar / manipular el nombre de esa función. Quizás esté generando documentación, texto de ayuda, un historial de acciones, etc. Así que no, no siempre está codificando el nombre de la función.
mbargiel
2
@mgargiel: Lo que quise decir es: suponiendo que tienes una clase que define 100 métodos, donde todos los métodos son solo contenedores, que llaman a un método privado, pasando el nombre del contenedor. Algo como esto: def wrapper(kwargs): super(ExtendedClass,self).call('wrapper', kwargs). Usted tiene otra opción, que es: def wrapper(kwargs): super(ExtendedClass,self).call(sys._getframe().f_code.co_name, kwargs). Entonces, la respuesta de Albert Vonpupp me parece mejor.
Richard Gomes
19
@RichardGomes Una respuesta es apropiada para obtener el nombre dentro de la función misma, la otra es apropiada para obtenerlo de una referencia de función. La pregunta del OP como está escrita indica lo último.
Russell Borogove
22
@RichardGomes En realidad, llegué a esta pregunta en busca de una solución a este problema. El problema que estoy tratando de solucionar es crear un decorador que pueda usarse para registrar todas mis llamadas.
ali-hussain
23
Las funciones de @RichardGomes son objetos de primera clase, puede haber más de un nombre vinculado a ellas. No es necesariamente el caso que f.__name__ == 'f'.
wim
298

Para obtener el nombre de la función o método actual desde adentro, considere:

import inspect

this_function_name = inspect.currentframe().f_code.co_name

sys._getframetambién funciona en lugar de inspect.currentframeaunque este último evita acceder a una función privada.

Para obtener el nombre de la función de llamada, considere f_backcomo en inspect.currentframe().f_back.f_code.co_name.


Si también lo usa mypy, puede quejarse de que:

error: el elemento "Ninguno" de "Opcional [FrameType]" no tiene el atributo "f_code"

Para suprimir el error anterior, considere:

import inspect
import types
from typing import cast

this_function_name = cast(types.FrameType, inspect.currentframe()).f_code.co_name
Albert Vonpupp
fuente
45
+1: Esta es la respuesta que me gustaría ver. Otras respuestas suponen que la persona que llama ya conoce el nombre de la función, lo cual no tiene sentido en el contexto de esta pregunta.
Richard Gomes
110
Richard: no, no lo hace. USTED asume que está llamando a name o func_name en su función directamente en el mismo alcance que se definió, lo que a menudo no es el caso. Tenga en cuenta que las funciones son objetos: se pueden pasar como argumentos a otras funciones, almacenarse en listas / dictados para búsquedas posteriores o ejecución, etc.
mbargiel
11
@paulus_almighty, cavar en marcos de pila no me parece abstracto. De hecho, es lo contrario de lo abstracto. Consulte la nota detallada de implementación en los documentos. No todas las implementaciones de Python incluirán sys._getframe: está directamente conectado a las partes internas de CPython.
senderle
66
Esto solo funciona dentro de la función, pero la pregunta especifica que no se debe llamar a la función.
user2357112 es compatible con Monica
55
En caso de que desee ajustar su función a algo más útil y fácil de recordar, debe recuperar el cuadro 1 como sys._getframe(1).f_code.co_name, para que pueda definir una función como get_func_name()y esperar obtener el nombre deseado de la función que invocó.
m3nda
44
my_function.func_name

También hay otras propiedades divertidas de funciones. Escriba dir(func_name)para enumerarlos. func_name.func_code.co_codees la función compilada, almacenada como una cadena.

import dis
dis.dis(my_function)

mostrará el código en un formato legible para humanos. :)

Markus Jarderot
fuente
44
¿Cuál es la diferencia entre f .__ name__ y f.func_name?
Federico A. Ramponi
14
Sam: los nombres son privados, los nombres son especiales, hay una diferencia conceptual.
Matthew Trevor el
11
En caso de que alguien se desconcierte por la respuesta anterior de Matthew, el sistema de comentarios ha interpretado algunos guiones bajos dobles como código para negrita. Escapado con la tecla de retroceso, el mensaje debería leer: __names__son privados, __namesson especiales.
gwideman
12
En realidad, creo que lo correcto es _namesprivado (subrayado simple antes, solo una convención), __names__son especiales (subrayados dobles antes y después). No estoy seguro si el doble subrayado antes tiene algún significado, formalmente o como convención.
MestreLion
8
func_nameya no existe en python3, por lo que debe usarlo func.__name__si desea compatibilidad
Daenyth
34

Esta función devolverá el nombre de la función del llamante.

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

Es como la respuesta de Albert Vonpupp con una envoltura amigable.

Demyn
fuente
1
Tenía "<module>" en el índice [2], pero funcionó lo siguiente: traceback.extract_stack (Ninguno, 2) [0] [- 1]
emmagras
3
para mí esto no funciona, pero sí: traceback.extract_stack () [- 1] [2]
mike01010
Esto funciona si cambia el primer índice a 1, ustedes deberían aprender a depurar antes de comentar ... traceback.extract_stack (Ninguno, 2) [1] [2]
Jcc.Sanabria
29

Si también está interesado en los métodos de clase, Python 3.3+ tiene __qualname__además __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"
lapislázuli
fuente
20

Me gusta usar un decorador de funciones. Agregué una clase, que también multiplica el tiempo de la función. Suponga que gLog es un registrador de python estándar:

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

así que ahora todo lo que tienes que hacer con tu función es decorarlo y listo

@func_timer_decorator
def my_func():
radato
fuente
16
import inspect

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

dónde

  • apilar () [0] la persona que llama

  • stack () [3] el nombre de cadena del método

Ma Guowei
fuente
1
Llano y simple. stack()[0]siempre será la persona que llama, [3]el nombre de la cadena del método.
kontur
15

Como una extensión de la respuesta de @ Demyn , creé algunas funciones de utilidad que imprimen el nombre de la función actual y los argumentos de la función actual:

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.
fuente
15

sys._getframe()no se garantiza que esté disponible en todas las implementaciones de Python ( consulte la referencia ), puede usar el tracebackmódulo para hacer lo mismo, por ejemplo.

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

   return funcName

Una llamada a stack[-1]devolverá los detalles del proceso actual.

sandyc
fuente
Lo siento si sys._getframe() no está definido, entonces traceback.extract_stacktampoco funciona. El último proporciona un superconjunto aproximado de la funcionalidad del primero; no puedes esperar ver uno sin el otro. Y de hecho, en IronPython 2.7 extract_stack()siempre regresa []. -1
SingleNegationElimination
8

Solo desea obtener el nombre de la función aquí, es un código simple para eso. digamos que tienes estas funciones definidas

def function1():
    print "function1"

def function2():
    print "function2"

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

la salida será function1

Ahora digamos que tiene estas funciones en una lista

a = [function1 , function2 , funciton3]

para obtener el nombre de las funciones

for i in a:
    print i.__name__

la salida será

función1
función2
función3

Mohsin Ashraf
fuente
5

He visto algunas respuestas que utilizan decoradores, aunque sentí que algunas eran un poco detalladas. Aquí hay algo que uso para registrar nombres de funciones, así como sus respectivos valores de entrada y salida. Lo he adaptado aquí para imprimir la información en lugar de crear un archivo de registro y lo he adaptado para aplicarlo al ejemplo específico de 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()

Salida:

{'function_name': 'my_function'}
NL23codes
fuente
1
Lo que me gusta de esto en comparación con todos los demás, es que simplemente agregando la debugfunción una vez que puede agregar la funcionalidad simplemente agregando el decorador a cualquier función que necesite o desee imprimir el nombre de la función. Parece ser el más fácil y el más adaptable.
spareTimeCoder