Determine o nome da função a partir dessa função (sem usar o rastreamento)


479

No Python, sem usar o tracebackmódulo, existe uma maneira de determinar o nome de uma função de dentro dessa função?

Digamos que eu tenho um módulo foo com uma barra de funções. Ao executar foo.bar(), existe uma maneira de a barra saber o nome da barra? Ou melhor ainda, foo.baro nome?

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

6
Não entendo por que a resposta aceita não é a de Andreas Young (ou qualquer outra que mostre como fazê-lo). Em vez disso, a resposta aceita é "você não pode fazer isso", o que parece errado; o único requisito do OP não estava sendo usado traceback. Nem mesmo os tempos de respostas e comentários parecem confirmar.
— Sancho.s ReinstateMonicaCellio

Respostas:


198

Python não possui um recurso para acessar a função ou seu nome dentro da própria função. Foi proposto, mas rejeitado. Se você não quiser jogar com a pilha, use "bar"ou usebar.__name__ dependendo do contexto.

O aviso de rejeição fornecido é:

Este PEP é rejeitado. Não está claro como ela deve ser implementada ou qual deve ser a semântica precisa nos casos extremos, e não há casos de uso importantes suficientes. a resposta foi morna, na melhor das hipóteses.


17
inspect.currentframe()é uma dessas maneiras.
— Yuval

41
Combinando @ abordagem da CamHart com @ evita de Yuval "escondido" e métodos potencialmente obsoletas em resposta da @ RoshOxymoron, bem como indexação numérica para a pilha de resposta @ neuro / @ de AndreasJung:print(inspect.currentframe().f_code.co_name)
— Placas

2
é possível resumir por que foi rejeitado?
— Charlie Parker

1
por que essa é a resposta escolhida? A pergunta não é sobre como acessar a função atual ou o próprio módulo, apenas o nome. E os recursos de rastreamento de pilha / depuração já possuem essas informações.
— 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
Isso é ótimo porque você também pode fazer [1][3]para obter o nome do chamador.
— Kos

58
Você também pode usar: print(inspect.currentframe().f_code.co_name)ou para obter o nome do chamador: print(inspect.currentframe().f_back.f_code.co_name). Eu acho que deveria ser mais rápido, já que você não recupera uma lista de todos os quadros de pilha como inspect.stack()faz.
— Michael

7
inspect.currentframe().f_back.f_code.co_namenão trabalha com um método decorado enquanto inspect.stack()[0][3]faz ...
— Dustin Wyatt

4
Observe: inspect.stack () pode resultar em sobrecarga de alto desempenho, portanto use com moderação! Na minha caixa de braço levou 240ms para completar (por alguma razão)
— gardarh

Parece-me como algo presente no máquinas recursão Python pode ser empregado para fazer isso de forma mais eficiente
— Tom Russell

160

Existem algumas maneiras de obter o mesmo 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)

Observe que as inspect.stackchamadas são milhares de vezes mais lentas que as 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

5
inspect.currentframe()Parece um bom equilíbrio entre o tempo de execução e uso de membros privados
— FabienAndre

1
@mbdevpl Meus números são 1,25 ms, 1.24ms, 0.5us, 0.16us normal (nonpythonic :)) segundos conformidade (win7x64, python3.5.1)
— Antony Hatchkins

Só assim ninguém acha que @ mbdevpl é doido :) - Enviei uma edição para a saída da 3ª corrida, já que não fazia sentido. Não faço idéia se o resultado deveria ter sido 0.100 usecou de 0.199 usecqualquer maneira - centenas de vezes mais rápido que as opções 1 e 2, comparável à opção 4 (embora Antony Hatchkins tenha encontrado a opção 3 três vezes mais rápida que a opção 4).
— dwanderson

Uso sys._getframe().f_code.co_namemais inspect.currentframe().f_code.co_namesimplesmente porque já importei o sysmódulo. Essa é uma decisão razoável? (considerando que as velocidades parecem bastante semelhantes)
— PatrickT

47

Você pode obter o nome que foi definido usando a abordagem que @Andreas Jung mostra , mas esse pode não ser o nome com o qual a função foi chamada:

import inspect

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

Foo2 = Foo

>>> Foo()
Foo

>>> Foo2()
Foo

Se essa distinção é importante para você ou não, não posso dizer.


3
A mesma situação que com .func_name. Vale lembrar que nomes de classes e funções em Python são uma coisa e variáveis ​​referentes a elas são outra.
— Kos

Às vezes você pode querer Foo2()imprimir Foo. Por exemplo: Foo2 = function_dict['Foo']; Foo2(). Nesse caso, Foo2 é um ponteiro de função para talvez um analisador de linha de comando.
— Harvey

Que tipo de implicação de velocidade isso tem?
— Robert C. Barth

2
Implicação de velocidade em relação a quê? Existe uma situação em que você precisaria ter essas informações em uma situação difícil em tempo real ou algo assim?
— bgporter

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

Eu queria uma coisa muito semelhante, porque queria colocar o nome da função em uma sequência de log que fosse em vários lugares no meu código. Provavelmente não é a melhor maneira de fazer isso, mas aqui está uma maneira de obter o nome da função atual.


2
Totalmente funcionando, apenas usando sys, não é necessário carregar mais módulos, mas não é tão fácil lembrá-lo: V
— m3nda

@ erm3nda Veja minha resposta.
— Nerdfever.com 26/08/18

1
@ nerdfever.com Meu problema não é criar uma função, é não lembrar o que colocar dentro dessa função. Não é fácil de lembrar, então sempre precisarei ver algumas notas para criar isso novamente. Vou tentar ter em mente o fquadro e o cocódigo. Eu não uso que tanto o melhor se apenas i, salvo que, em algum trecho :-)
— m3nda

24

Eu mantenho esse útil utilitário por perto:

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

Uso:

myself()

1
Como isso seria feito com a alternativa proposta aqui? "eu = lambda: sys._getframe (). f_code.co_name" não funciona (a saída é "<lambda>"; acho que porque o resultado é determinado no momento da definição, e não no horário da chamada. Hmm.
— NYCeyes

2
@ prismalytics.io: Se você me chamar (eu mesmo) e não usar apenas seu valor (eu mesmo), obterá o que está procurando.
— IJw

NYCeyes estava certo, o nome é resolvido dentro do lambda e, portanto, o resultado é <lambda>. Os métodos sys._getframe () e inspect.currentframe () DEVEM ser executados diretamente dentro da função que você deseja obter o nome. O método inspect.stack () funciona porque você pode especificar o índice 1, fazendo inspect.stack () [0] [3] também produz <lambda>.
— kikones34

20

Eu acho que inspecté a melhor maneira de fazer isso. Por exemplo:

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

17

Encontrei um invólucro que escreverá o nome da função

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

Isso imprimirá

my_funky_name

STUB


1
Como um noob decorador, gostaria de saber se existe uma maneira de acesso func .__ name__ dentro do contexto do my_funky_name (para que eu possa recuperar seu valor e usá-lo dentro my_funky_name)
— cowbert

A maneira de fazer isso dentro da função my_funky_name é my_funky_name.__name__. Você pode passar a função .__ name__ para a função como um novo parâmetro. func (* args, ** kwargs, meu_nome = func .__ nome__). Para obter o nome do decorador de dentro da sua função, acho que seria necessário usar o Inspecionar. Mas recebendo o nome da função de controlar a minha função dentro de minha função correndo ... bem que apenas soa como o início de uma bela meme :)
— cad106uk

15

Na verdade, isso é derivado das outras respostas à pergunta.

Aqui está a minha opinião:

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

A provável vantagem desta versão em relação ao uso do inspect.stack () é que ele deve ser milhares de vezes mais rápido [veja a postagem e os horários de Alex Melihoff sobre o uso de sys._getframe () versus o uso de inspect.stack ()].


12

print(inspect.stack()[0].function) parece funcionar também (Python 3.5).


11

Aqui está uma abordagem preparada para o futuro.

A combinação das sugestões de @ CamHart e @ Yuval com a resposta aceita do @ RoshOxymoron tem o benefício de evitar:

  • _hidden e métodos potencialmente obsoletos
  • indexação na pilha (que pode ser reordenada em futuros pitães)

Então, acho que isso funciona bem com futuras versões do python (testadas em 2.7.3 e 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())

Teste:

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

Resultado:

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

No IDE, o código gera

olá, eu sou foo, papai é

olá, eu sou bar, papai é foo

olá, eu sou bar, papai é


8

Você pode usar um 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'

Como isso está respondendo à pergunta do pôster? Você pode expandir isso para incluir um exemplo de como o nome da função é conhecido de dentro da função?
— parvus

@parvus: a minha resposta como é um exemplo que demonstra uma resposta à pergunta de OP
— Douglas Denhartog

Ok, minha_função é a função aleatória do OP do usuário. Culpe isso pela minha falta de compreensão dos decoradores. Onde o @? Como isso funcionará para funções cujos argumentos você não deseja adaptar? Como entendo sua solução: quando quero saber o nome da função, tenho que anexá-lo com @get_function_name e adicionar o argumento name, esperando que ele ainda não esteja lá para outro propósito. Provavelmente estou perdendo alguma coisa, desculpe por isso.
— parvus

Sem iniciar meu próprio curso de python dentro de um comentário: 1. funções são objetos; 2. você pode anexar um atributo de nome à função, imprimir / registrar o nome ou fazer várias coisas com o "nome" dentro do decorador; 3. decoradores podem ser anexados de várias maneiras (por exemplo, @ ou no meu exemplo); 4. os decoradores podem usar @wraps e / ou serem eles mesmos classes; 5. Eu poderia continuar, mas, programação feliz!
— Douglas Denhartog 30/03/19

Isso apenas parece uma maneira complicada de obter o __name__atributo de uma função. O uso requer saber o que você está tentando obter, o que não me parece muito útil em casos simples em que as funções não são definidas em tempo real.
— Avi

3

Eu faço minha própria abordagem usada para chamar super com segurança dentro do cenário de herança múltipla (coloquei todo o 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

uso da amostra:

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

testando:

a = C()
a.test()

resultado:

called from C
called from A
called from B

Dentro de cada método decorado @with_name, você tem acesso a self .__ fname__ como o nome da função atual.


3

Recentemente, tentei usar as respostas acima para acessar a documentação de uma função a partir do contexto dessa função, mas como as perguntas acima estavam retornando apenas a string de nome, ela não funcionou.

Felizmente, encontrei uma solução simples. Se como eu, você deseja se referir à função em vez de simplesmente obter a string que representa o nome que pode aplicar eval () à string do nome da função.

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

3

Sugiro não confiar nos elementos da pilha. Se alguém usar seu código em contextos diferentes (intérprete python por exemplo), sua pilha mudará e quebrará seu índice ([0] [3]).

Eu sugiro que você algo assim:

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

Isso é muito fácil de realizar com um 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')
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.