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?


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.


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

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


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.


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"

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

16
import inspect

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

Onde

  • stack () [0] o chamador

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


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

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.


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


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

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