¿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?


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.


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

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. :)


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.


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"

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

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


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

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.


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


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

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.