Como obter um nome de função como uma string?

741

No Python, como obtenho um nome de função como uma string, sem chamar a função?

def my_function():
    pass

print get_function_name_as_string(my_function) # my_function is not in quotes

deve produzir "my_function".

Essa função está disponível no Python? Se não, alguma idéia de como implementar get_function_name_as_string, em Python?

X10
fonte

Respostas:

945
my_function.__name__

Usar __name__é o método preferido, pois se aplica de maneira uniforme. Ao contrário func_name, ele funciona também em funções internas:

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

Além disso, os sublinhados duplos indicam ao leitor que este é um atributo especial. Como bônus, as classes e os módulos também têm um __name__atributo; portanto, você só lembra de um nome especial.

user28409
fonte
420
Porque, em alguns casos, você obtém algum objeto de função como argumento para sua função e precisa exibir / armazenar / manipular o nome dessa função. Talvez você esteja gerando documentação, texto de ajuda, histórico de ações, etc. Portanto, nem sempre você codifica o nome da função.
Mbargiel 26/07/2013
2
@mgargiel: O que eu quis dizer é: supondo que você tenha uma classe que defina 100 métodos, onde todos os métodos são apenas invólucros, que chamam um método privado, passando o nome do próprio invólucro. Algo parecido com isto: def wrapper(kwargs): super(ExtendedClass,self).call('wrapper', kwargs). Você tem outra escolha, que é: def wrapper(kwargs): super(ExtendedClass,self).call(sys._getframe().f_code.co_name, kwargs). Então, a resposta de Albert Vonpupp parece melhor para mim.
Richard Gomes
19
@RichardGomes Uma resposta é apropriada para obter o nome dentro da própria função, a outra é apropriada para obtê-lo a partir de uma referência de função. A pergunta do OP, como está escrita, indica o último.
Russell Borogove
22
@ Richardhardomes Na verdade, cheguei a essa pergunta procurando uma solução para esse problema. O problema que estou tentando resolver é criar um decorador que possa ser usado para registrar todas as minhas chamadas.
ali-hussain
23
As funções @RichardGomes são objetos de primeira classe; pode haver mais do que nome associado a elas. Não é necessariamente o caso f.__name__ == 'f'.
Wim
298

Para obter o nome da função ou método atual de dentro, considere:

import inspect

this_function_name = inspect.currentframe().f_code.co_name

sys._getframetambém funciona em vez de, inspect.currentframeembora o último evite acessar uma função privada.

Para obter o nome da função de chamada, considere f_backcomo em inspect.currentframe().f_back.f_code.co_name.


Se também estiver usando mypy, pode reclamar que:

erro: o item "Nenhum" de "Opcional [FrameType]" não tem atributo "f_code"

Para suprimir o erro acima, considere:

import inspect
import types
from typing import cast

this_function_name = cast(types.FrameType, inspect.currentframe()).f_code.co_name
Albert Vonpupp
fonte
45
+1: esta é a resposta que eu gostaria de ver. Outras respostas assumem que o chamador já sabe o nome da função, o que não faz sentido no contexto desta pergunta.
Richard Gomes
110
Richard: não, não. VOCÊ está assumindo que está chamando o nome ou nome_da_função em sua função diretamente no mesmo escopo definido, o que geralmente não é o caso. Tenha em mente que as funções são objetos - eles podem ser passadas ao redor como argumentos para outras funções, armazenados em listas / dicts para pesquisas posteriores ou execução, etc.
mbargiel
11
@paulus_almighty, cavar em quadros de pilha não me parece tão abstrato! De fato, é o oposto do abstrato. Veja a nota detalhada da implementação nos documentos. Nem todas as implementações do Python incluem sys._getframe- ele está diretamente conectado às partes internas do CPython.
Senderle
6
Isso funciona apenas dentro da função, mas a pergunta especifica que a função não deve ser chamada.
User2357112 suporta Monica
5
Caso você queira agrupar sua função em algo mais utilizável e fácil de lembrar, você precisa recuperar o quadro 1 como sys._getframe(1).f_code.co_name, para poder definir uma função como get_func_name()e esperar obter o nome desejado da função que chamou.
M3nda
44
my_function.func_name

Existem também outras propriedades divertidas das funções. Digite dir(func_name)para listá-los. func_name.func_code.co_codeé a função compilada, armazenada como uma string.

import dis
dis.dis(my_function)

exibirá o código em formato legível quase humano. :)

Markus Jarderot
fonte
4
Qual é a diferença entre f .__ name__ e f.func_name?
Federico A. Ramponi 30/10/08
14
Sam: os nomes são privados, os nomes são especiais, há uma diferença conceitual.
Matthew Trevor
11
Caso alguém fique intrigado com a resposta anterior de Matthew, o sistema de comentários interpretou alguns sublinhados duplos como código para negrito. Escapada com backtick, a mensagem deve ser: __names__são particulares, __namessão especiais.
gwideman
12
Na verdade, acho que o correto é _namesprivado (sublinhado único antes, apenas uma convenção), __names__são especiais (sublinhado duplo antes e depois). Não tenho certeza se o duplo sublinhado antes tem algum significado, formal ou como uma convenção.
MestreLion 23/08/14
8
func_namenão existe mais no python3 então você precisa usar func.__name__se você quiser compatibilidade
Daenyth
34

Esta função retornará o nome da função do chamador.

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

É como a resposta de Albert Vonpupp com uma embalagem amigável.

Demyn
fonte
1
Eu tinha "<module>" no índice [2], mas o seguinte funcionou: traceback.extract_stack (Nenhum, 2) [0] [- 1]
emmagras
3
para mim isso não funcionar, mas isso faz: traceback.extract_stack () [- 1] [2]
mike01010
Isso funciona se alterar o primeiro índice para 1, vocês devem aprender a depurar antes de comentar ... traceback.extract_stack (Nenhum, 2) [1] [2]
Jcc.Sanabria
29

Se você estiver interessado em métodos de classe também, Python 3.3+ tem __qualname__além __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"
lapis
fonte
20

Eu gosto de usar um decorador de funções. Eu adicionei uma classe, que também calcula o tempo da função. Suponha que o gLog seja um logger python padrão:

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

então agora tudo o que você tem a ver com a sua função é decorá-la e pronto

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

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

Onde

  • stack () [0] o chamador

  • stack () [3] o nome da string do método

Ma Guowei
fonte
1
Claro e simples. stack()[0]sempre será o chamador, [3]o nome da string do método.
28819
15

Como uma extensão da resposta de @ Demyn , criei algumas funções utilitárias que imprimem o nome da função atual e os argumentos da função atual:

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

sys._getframe()não está garantido que esteja disponível em todas as implementações do Python ( consulte ref ), você pode usar o tracebackmódulo para fazer a mesma coisa, por exemplo.

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

   return funcName

Uma chamada para stack[-1]retornará os detalhes do processo atual.

sandyc
fonte
Desculpe, se não sys._getframe()for definido, traceback.extract_stacktambém será inoperante. O último fornece um superconjunto aproximado da funcionalidade do primeiro; você não pode esperar ver um sem o outro. E, de fato, no IronPython 2.7 extract_stack()sempre retorna []. -1
SingleNegationElimination
8

Você só quer obter o nome da função aqui é um código simples para isso. digamos que você tenha essas funções definidas

def function1():
    print "function1"

def function2():
    print "function2"

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

a saída será function1

Agora, digamos que você tenha essas funções em uma lista

a = [function1 , function2 , funciton3]

para obter o nome das funções

for i in a:
    print i.__name__

a saída será

função1
função2
função3

Mohsin Ashraf
fonte
5

Eu já vi algumas respostas que utilizavam decoradores, embora eu achasse que algumas eram um pouco detalhadas. Aqui está algo que eu uso para registrar nomes de funções, bem como seus respectivos valores de entrada e saída. Eu a adaptei aqui para apenas imprimir as informações em vez de criar um arquivo de log e as adaptei para aplicar ao exemplo específico do 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()

Resultado:

{'function_name': 'my_function'}
NL23codes
fonte
1
O que eu gosto sobre isso, em oposição a todos os outros, é que, apenas adicionando a debugfunção, uma vez que você pode adicionar a funcionalidade, simplesmente adicionando o decorador a qualquer função que você precise ou deseje imprimir o nome da função. Parece ser o mais fácil e o mais adaptável.
spareTimeCoder 14/04