Determine el nombre de la función desde esa función (sin usar el rastreo)


479

En Python, sin usar el tracebackmódulo, ¿hay alguna forma de determinar el nombre de una función desde esa función?

Digamos que tengo un módulo foo con una barra de funciones. Al ejecutar foo.bar(), ¿hay alguna forma para que el bar sepa el nombre del bar? O mejor aún, foo.bar¿cómo se llama?

#foo.py  
def bar():
    print "my name is", __myname__ # <== how do I calculate this at runtime?

66
No entiendo por qué la respuesta aceptada no es la de Andreas Young (o cualquier otra que muestre cómo hacerlo). En cambio, la respuesta aceptada es "no puedes hacer eso", lo que parece incorrecto; el único requisito por el OP no era usar traceback. Ni siquiera los tiempos de respuestas y comentarios parecen respaldarlo.
sancho.s ReinstateMonicaCellio

Respuestas:


198

Python no tiene una función para acceder a la función o su nombre dentro de la función misma. Se ha propuesto pero rechazado. Si no quieres jugar con la pila tú mismo, deberías usar "bar"o bar.__name__dependiendo del contexto.

El aviso de rechazo dado es:

Este PEP es rechazado. No está claro cómo debería implementarse o cuál debería ser la semántica precisa en casos extremos, y no hay suficientes casos de uso importantes. La respuesta ha sido tibia en el mejor de los casos.


17
inspect.currentframe()Es una de esas formas.
Yuval

41
Combinando @ enfoque de CamHart con @ evita de Yuval "ocultos" ya los métodos potencialmente obsoletas en la respuesta de @ RoshOxymoron, así como la indexación numérica en la pila de respuesta neuro @ / @ de AndreasJung:print(inspect.currentframe().f_code.co_name)
Placas

2
¿Es posible resumir por qué ha sido rechazado?
Charlie Parker

1
¿Por qué es esta la respuesta elegida? La pregunta no se trata de acceder a la función actual o al módulo en sí, solo al nombre. Y las características stacktrace / debugging ya tienen esta información.
nurettin

411
import inspect

def foo():
   print(inspect.stack()[0][3])
   print(inspect.stack()[1][3]) #will give the caller of foos name, if something called foo

47
Esto es genial porque también puedes hacer [1][3]para obtener el nombre de la persona que llama.
Kos

58
También es posible usar: print(inspect.currentframe().f_code.co_name)o para obtener el nombre de la persona que llama: print(inspect.currentframe().f_back.f_code.co_name). Creo que debería ser más rápido ya que no recuperas una lista de todos los marcos de pila como lo inspect.stack()hace.
Michael

77
inspect.currentframe().f_back.f_code.co_nameno funciona con un método decorado, mientras inspect.stack()[0][3]que ...
Dustin Wyatt

44
Tenga en cuenta que: inspect.stack () puede incurrir en una sobrecarga de alto rendimiento. En mi caja de brazo tardé 240 ms en completarse (por alguna razón)
gardarh

Me parece que algo presente en la maquinaria de recursión de Python podría emplearse para hacer esto de manera más eficiente
Tom Russell

160

Hay algunas formas de obtener el mismo resultado:

from __future__ import print_function
import sys
import inspect

def what_is_my_name():
    print(inspect.stack()[0][0].f_code.co_name)
    print(inspect.stack()[0][3])
    print(inspect.currentframe().f_code.co_name)
    print(sys._getframe().f_code.co_name)

Tenga en cuenta que las inspect.stackllamadas son miles de veces más lentas que las alternativas:

$ python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][0].f_code.co_name'
1000 loops, best of 3: 499 usec per loop
$ python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][3]'
1000 loops, best of 3: 497 usec per loop
$ python -m timeit -s 'import inspect, sys' 'inspect.currentframe().f_code.co_name'
10000000 loops, best of 3: 0.1 usec per loop
$ python -m timeit -s 'import inspect, sys' 'sys._getframe().f_code.co_name'
10000000 loops, best of 3: 0.135 usec per loop

55
inspect.currentframe()parece un buen compromiso entre el tiempo de ejecución y el uso de los miembros privados
FabienAndre

1
@mbdevpl Mis números son 1.25ms, 1.24ms, 0.5us, 0.16us normales (no pitónicos :)) segundos en consecuencia (win7x64, python3.5.1)
Antony Hatchkins

Solo para que nadie piense que @ mbdevpl está loco :): envié una edición para el resultado de la tercera ejecución, ya que no tenía ningún sentido. No tengo idea de si el resultado debería haber sido 0.100 useco de 0.199 useccualquier manera: cientos de veces más rápido que las opciones 1 y 2, comparable con la opción 4 (aunque Antony Hatchkins encontró la opción 3 tres veces más rápido que la opción 4).
dwanderson

Yo uso sys._getframe().f_code.co_namemás inspect.currentframe().f_code.co_namesimplemente porque ya he importado el sysmódulo. ¿Es esa una decisión razonable? (considerando que las velocidades parecen bastante similares)
PatrickT

47

Puede obtener el nombre con el que se definió utilizando el enfoque que muestra @Andreas Jung , pero ese puede no ser el nombre con el que se llamó a la función:

import inspect

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

Foo2 = Foo

>>> Foo()
Foo

>>> Foo2()
Foo

Si esa distinción es importante para usted o no, no puedo decirlo.


3
La misma situación que con .func_name. Vale la pena recordar que los nombres de clase y los nombres de función en Python son una cosa y las variables que se refieren a ellos son otra.
Kos

A veces es posible que desee Foo2()imprimir Foo. Por ejemplo: Foo2 = function_dict['Foo']; Foo2(). En este caso, Foo2 es un puntero de función para quizás un analizador de línea de comando.
Harvey

¿Qué tipo de implicación de velocidad tiene esto?
Robert C. Barth

2
¿Implicación de velocidad con respecto a qué? ¿Hay alguna situación en la que necesite tener esta información en una situación difícil en tiempo real o algo así?
bgporter

44
functionNameAsString = sys._getframe().f_code.co_name

Quería algo muy similar porque quería poner el nombre de la función en una cadena de registro que iba en varios lugares en mi código. Probablemente no sea la mejor manera de hacerlo, pero aquí hay una forma de obtener el nombre de la función actual.


2
Totalmente funcionando, solo usando sys, no es necesario cargar más módulos, pero no es tan fácil recordarlo: V
m3nda

@ erm3nda Ver mi respuesta.
nerdfever.com

1
@ nerdfever.com Mi problema no es crear una función, es no recordar qué poner dentro de esa función. No es fácil de recordar, por lo que siempre tendré que ver alguna nota para construirlo nuevamente. Trataré de tener en cuenta el fmarco y el cocódigo. No lo uso tanto, así que mejor si solo lo
guardo

24

Mantengo esta útil utilidad cerca:

import inspect
myself = lambda: inspect.stack()[1][3]

Uso:

myself()

1
¿Cómo se haría esto con la alternativa propuesta aquí? "mi mismo = lambda: sys._getframe (). f_code.co_name" no funciona (el resultado es "<lambda>"; Creo que porque el resultado se determina en el momento de la definición, no más tarde en el momento de la llamada. Hmm.
NYCeyes

2
@ prismalytics.io: si te llamas a mí mismo (yo mismo) y no solo usas su valor (yo mismo), obtendrás lo que estás buscando.
ijw

NYCeyes tenía razón, el nombre se resuelve dentro de la lambda y, por lo tanto, el resultado es <lambda>. Los métodos sys._getframe () e inspect.currentframe () DEBEN ejecutarse directamente dentro de la función de la que desea obtener el nombre. El método inspect.stack () funciona porque puede especificar el índice 1, al hacer inspect.stack () [0] [3] también se obtiene <lambda>.
kikones34

20

Supongo que inspectes la mejor manera de hacer esto. Por ejemplo:

import inspect
def bar():
    print("My name is", inspect.stack()[0][3])

17

Encontré un contenedor que escribirá el nombre de la función

from functools import wraps

def tmp_wrap(func):
    @wraps(func)
    def tmp(*args, **kwargs):
        print func.__name__
        return func(*args, **kwargs)
    return tmp

@tmp_wrap
def my_funky_name():
    print "STUB"

my_funky_name()

Esto imprimirá

my_funky_name

TALÓN


1
Como decorador novato, me pregunto si hay una manera de acceder a func .__ name__ dentro del contexto de my_funky_name (para poder recuperar su valor y usarlo dentro de my_funky_name)
cowbert

La forma de hacerlo dentro de la función my_funky_name es my_funky_name.__name__. Puede pasar la función .__ name__ a la función como un nuevo parámetro. func (* args, ** kwargs, my_name = func .__ name__). Para obtener el nombre de su decorador desde dentro de su función, creo que requeriría usar inspeccionar. Pero obtener el nombre de la función que controla mi función dentro de mi función de ejecución ... bueno, eso suena como el comienzo de un hermoso meme :)
cad106uk

15

En realidad, esto se deriva de las otras respuestas a la pregunta.

Aquí está mi opinión:

import sys

# for current func name, specify 0 or no argument.
# for name of caller of current func, specify 1.
# for name of caller of caller of current func, specify 2. etc.
currentFuncName = lambda n=0: sys._getframe(n + 1).f_code.co_name


def testFunction():
    print "You are in function:", currentFuncName()
    print "This function's caller was:", currentFuncName(1)    


def invokeTest():
    testFunction()


invokeTest()

# end of file

La probable ventaja de esta versión sobre el uso de inspect.stack () es que debería ser miles de veces más rápido [vea la publicación y los horarios de Alex Melihoff sobre el uso de sys._getframe () versus el uso de inspect.stack ()].



11

Aquí hay un enfoque a prueba de futuro.

Combinar las sugerencias de @ CamHart y @ Yuval con la respuesta aceptada de @ RoshOxymoron tiene el beneficio de evitar:

  • _hidden y métodos potencialmente obsoletos
  • indexación en la pila (que podría reordenarse en futuras pitones)

Así que creo que esto funciona bien con futuras versiones de Python (probado en 2.7.3 y 3.3.2):

from __future__ import print_function
import inspect

def bar():
    print("my name is '{}'".format(inspect.currentframe().f_code.co_name))

11
import sys

def func_name():
    """
    :return: name of caller
    """
    return sys._getframe(1).f_code.co_name

class A(object):
    def __init__(self):
        pass
    def test_class_func_name(self):
        print(func_name())

def test_func_name():
    print(func_name())

Prueba:

a = A()
a.test_class_func_name()
test_func_name()

Salida:

test_class_func_name
test_func_name


10
import inspect

def whoami():
    return inspect.stack()[1][3]

def whosdaddy():
    return inspect.stack()[2][3]

def foo():
    print "hello, I'm %s, daddy is %s" % (whoami(), whosdaddy())
    bar()

def bar():
    print "hello, I'm %s, daddy is %s" % (whoami(), whosdaddy())

foo()
bar()

En IDE el código sale

hola soy foo papi es

hola soy bar, papi es tonto

hola soy bar papi es


8

Puedes usar un decorador:

def my_function(name=None):
    return name

def get_function_name(function):
    return function(name=function.__name__)

>>> get_function_name(my_function)
'my_function'

¿Cómo responde eso a la pregunta del cartel? ¿Puede expandir esto para incluir un ejemplo de cómo se conoce el nombre de la función desde dentro de la función?
parvus

@parvus: mi respuesta como es es un ejemplo que demuestra una respuesta a la pregunta de OP
Douglas Denhartog

Ok, my_function es la función aleatoria del usuario del OP. Culpo esto a mi falta de comprensión de los decoradores. Donde el @? ¿Cómo funcionará esto para funciones cuyos argumentos no desea adaptar? Cómo entiendo su solución: cuando quiero saber el nombre de la función, tengo que agregarlo con @get_function_name y agregar el argumento del nombre, esperando que no esté allí para otro propósito. Probablemente me estoy perdiendo algo, perdón por eso.
parvus

Sin comenzar mi propio curso de Python dentro de un comentario: 1. las funciones son objetos; 2. puede adjuntar un atributo de nombre a la función, imprimir / registrar el nombre o hacer cualquier cantidad de cosas con el "nombre" dentro del decorador; 3. los decoradores se pueden conectar de varias maneras (por ejemplo, @ o en mi ejemplo); 4. los decoradores pueden usar @wraps y / o ser clases ellos mismos; 5. Podría continuar, pero ¡feliz programación!
Douglas Denhartog

Esto parece una forma complicada de llegar al __name__atributo de una función. El uso requiere saber lo que está tratando de obtener, lo que no me parece muy útil en casos simples donde las funciones no están definidas sobre la marcha.
Avi

3

Hago mi propio enfoque utilizado para llamar a super con seguridad dentro del escenario de herencia múltiple (pongo todo el código)

def safe_super(_class, _inst):
    """safe super call"""
    try:
        return getattr(super(_class, _inst), _inst.__fname__)
    except:
        return (lambda *x,**kx: None)


def with_name(function):
    def wrap(self, *args, **kwargs):
        self.__fname__ = function.__name__
        return function(self, *args, **kwargs)
return wrap

muestra de uso:

class A(object):

    def __init__():
        super(A, self).__init__()

    @with_name
    def test(self):
        print 'called from A\n'
        safe_super(A, self)()

class B(object):

    def __init__():
        super(B, self).__init__()

    @with_name
    def test(self):
        print 'called from B\n'
        safe_super(B, self)()

class C(A, B):

    def __init__():
        super(C, self).__init__()

    @with_name
    def test(self):
        print 'called from C\n'
        safe_super(C, self)()

probándolo:

a = C()
a.test()

salida:

called from C
called from A
called from B

Dentro de cada método decorado @with_name tiene acceso a self .__ fname__ como el nombre de la función actual.


3

Recientemente intenté usar las respuestas anteriores para acceder a la cadena de documentación de una función desde el contexto de esa función, pero como las preguntas anteriores solo devolvían la cadena de nombre, no funcionó.

Afortunadamente encontré una solución simple. Si, como yo, desea referirse a la función en lugar de simplemente obtener la cadena que representa el nombre, puede aplicar eval () a la cadena del nombre de la función.

import sys
def foo():
    """foo docstring"""
    print(eval(sys._getframe().f_code.co_name).__doc__)

3

Sugiero no confiar en los elementos de la pila. Si alguien usa su código dentro de diferentes contextos (por ejemplo, el intérprete de Python) su pila cambiará y romperá su índice ([0] [3]).

Te sugiero algo así:

class MyClass:

    def __init__(self):
        self.function_name = None

    def _Handler(self, **kwargs):
        print('Calling function {} with parameters {}'.format(self.function_name, kwargs))
        self.function_name = None

    def __getattr__(self, attr):
        self.function_name = attr
        return self._Handler


mc = MyClass()
mc.test(FirstParam='my', SecondParam='test')
mc.foobar(OtherParam='foobar')

0

Esto es bastante fácil de lograr con un decorador.

>>> from functools import wraps

>>> def named(func):
...     @wraps(func)
...     def _(*args, **kwargs):
...         return func(func.__name__, *args, **kwargs)
...     return _
... 

>>> @named
... def my_func(name, something_else):
...     return name, something_else
... 

>>> my_func('hello, world')
('my_func', 'hello, world')
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.