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?
Roubar
fonte
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.

Rosh Oxymoron
fonte
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
Andreas Jung
fonte
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
Alex Granovsky
fonte
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.

bgporter
fonte
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.

Ron Davis
fonte
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()
xxyzzy
fonte
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])
Bjorn
fonte
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

cad106uk
fonte
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 ()].

Gino
fonte
12

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

Pierre Voisin
fonte
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))
fogão
fonte
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
nordborn
fonte
11

Não sei por que as pessoas complicam:

import sys 
print("%s/%s" %(sys._getframe().f_code.co_filename, sys._getframe().f_code.co_name))
karthik r
fonte
talvez porque você esteja usando a função privada do módulo sys, fora dele. Em geral, é considerada uma má prática, não é?
tikej 14/04
@tikej, o uso concorda com as melhores práticas declaradas aqui: docs.quantifiedcode.com/python-anti-patterns/correctness/…
karthik r
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 é

Lee
fonte
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'
Douglas Denhartog
fonte
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.

Mel Viso Martinez
fonte
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__)
John Forbes
fonte
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')
Genschi
fonte
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')
Jeff Laughlin
fonte