Chamando uma função de um módulo usando seu nome (uma sequência)

1735

Qual é a melhor maneira de chamar uma função, dada uma string com o nome da função em um programa Python. Por exemplo, digamos que eu tenho um módulo fooe uma string cujo conteúdo é "bar". Qual é a melhor maneira de ligar foo.bar()?

Preciso obter o valor de retorno da função, e é por isso que não uso apenas eval. Eu descobri como fazer isso usando evalpara definir uma função temp que retorna o resultado dessa chamada de função, mas espero que exista uma maneira mais elegante de fazer isso.

ricree
fonte

Respostas:

2079

Supondo módulo foocom o método bar:

import foo
method_to_call = getattr(foo, 'bar')
result = method_to_call()

Você pode reduzir as linhas 2 e 3 para:

result = getattr(foo, 'bar')()

se isso faz mais sentido para o seu caso de uso.

Você pode usar getattrdessa maneira em métodos vinculados à instância de classe, métodos em nível de módulo, métodos de classe ... a lista continua.

Patrick Johnmeyer
fonte
9
hasattr ou getattr pode ser usado para determinar se uma função está definida. Eu tinha um mapeamento de banco de dados (eventType e manuseio functionName) e eu queria ter certeza que eu nunca "esqueceu" para definir um manipulador de eventos no meu python
Shaun
9
Isso funciona se você já souber o nome do módulo. No entanto, se você desejar que o usuário forneça o nome do módulo como uma sequência, isso não funcionará.
precisa saber é o seguinte
7
Se você precisar evitar que a exceção NoneType não seja chamada, você também pode empregar a forma de três argumentos de getattr: getattr (foo, 'bar', lambda: None). Peço desculpas pela formatação; o aplicativo android stackexchange é aparentemente terrível.
geekofalltrades
3
Veja também a resposta fornecida por @sastanin se você se importa apenas, por exemplo, com as funções do seu módulo local / atual.
NuSkooler
6
@akki Sim, se você estiver no módulo que você pode usar para fazer isso:fooglobals()methodToCall = globals()['bar']
Ben Hoyt
543
locals()["myfunction"]()

ou

globals()["myfunction"]()

locals retorna um dicionário com uma tabela de símbolos local atual. globals retorna um dicionário com tabela de símbolos global.

sastanina
fonte
53
Esse método com globais / locais é bom se o método que você precisa chamar estiver definido no mesmo módulo do qual você está chamando.
Joelmob
@ Joelmob existe alguma outra maneira de obter um objeto por string fora do espaço para nome raiz?
Nick T
@ NickT Eu só conheço esses métodos, acho que não existem outros que preencham a mesma função que esses, pelo menos não consigo pensar em uma razão pela qual deveria haver mais.
Joelmob
Eu tenho uma razão para você (na verdade, o que me levou aqui): O módulo A possui uma função F que precisa chamar uma função pelo nome. O módulo B importa o módulo A e chama a função F com uma solicitação para chamar a função G, definida no módulo B. Essa chamada falha porque, aparentemente, a função F é executada apenas com os globais definidos no módulo F - portanto, globais () ['G'] = Nenhum.
David Stein
338

A solução de Patrick é provavelmente a mais limpa. Se você também precisar pegar o módulo dinamicamente, poderá importá-lo como:

module = __import__('foo')
func = getattr(module, 'bar')
func()
HS.
fonte
93
Eu não entendo esse último comentário. __import__ tem seu próprio direito e a próxima frase nos documentos mencionados diz: "O uso direto de __import __ () é raro, exceto nos casos em que você deseja importar um módulo cujo nome é conhecido apenas em tempo de execução". Então: +1 para a resposta dada.
Hoffmaje 5/05
50
Use importlib.import_module. Os documentos oficiais dizem sobre __import__: "Esta é uma função avançada que não é necessária na programação diária do Python, ao contrário do importlib.import_module ()." docs.python.org/2/library/functions.html#__import__
glarrain
8
@glarrain Contanto que você esteja bem, apenas com suporte 2.7 ou superior.
Xiong Chiamiov
1
@Xiong Chaimiov, importlib.import_moduleé suportado em 3.6. Veja docs.python.org/3.6/library/…
cowlinator
7
@cowlinator Sim, o 3.6 faz parte do "2.7 and up", tanto na semântica estrita de versão quanto nas datas de lançamento (veio seis anos depois). Também não existia por três anos após o meu comentário. ;) Na ramificação 3.x, o módulo existe desde 3.1. 2.7 e 3.1 são agora bastante antigos; você ainda encontrará servidores por aí que suportam apenas o 2.6, mas provavelmente vale a pena ter o importlib como o conselho padrão hoje em dia.
Xiong Chiamiov 5/10
114

Apenas uma contribuição simples. Se a classe que precisamos instanciar estiver no mesmo arquivo, podemos usar algo como isto:

# Get class from globals and create an instance
m = globals()['our_class']()

# Get the function (from the instance) that we need to call
func = getattr(m, 'function_name')

# Call it
func()

Por exemplo:

class A:
    def __init__(self):
        pass

    def sampleFunc(self, arg):
        print('you called sampleFunc({})'.format(arg))

m = globals()['A']()
func = getattr(m, 'sampleFunc')
func('sample arg')

# Sample, all on one line
getattr(globals()['A'](), 'sampleFunc')('sample arg')

E, se não uma classe:

def sampleFunc(arg):
    print('you called sampleFunc({})'.format(arg))

globals()['sampleFunc']('sample arg')
Sourcegeek
fonte
101

Dada uma string, com um caminho python completo para uma função, foi assim que obtive o resultado dessa função:

import importlib
function_string = 'mypackage.mymodule.myfunc'
mod_name, func_name = function_string.rsplit('.',1)
mod = importlib.import_module(mod_name)
func = getattr(mod, func_name)
result = func()
roda ferrosa
fonte
1
Isso me ajudou. É uma versão leve da __import__função.
Pankaj Bhambhani
Eu acho que essa foi a melhor resposta.
Saeid 22/01
55

A melhor resposta de acordo com as perguntas frequentes sobre programação em Python seria:

functions = {'myfoo': foo.bar}

mystring = 'myfoo'
if mystring in functions:
    functions[mystring]()

A principal vantagem dessa técnica é que as seqüências de caracteres não precisam corresponder aos nomes das funções. Essa também é a técnica principal usada para emular uma construção de caso


fonte
43

A resposta (espero) que ninguém nunca quis

Eval como comportamento

getattr(locals().get("foo") or globals().get("foo"), "bar")()

Por que não adicionar a importação automática

getattr(
    locals().get("foo") or 
    globals().get("foo") or
    __import__("foo"), 
"bar")()

Caso tenhamos dicionários extras, queremos verificar

getattr(next((x for x in (f("foo") for f in 
                          [locals().get, globals().get, 
                           self.__dict__.get, __import__]) 
              if x)),
"bar")()

Precisamos ir mais fundo

getattr(next((x for x in (f("foo") for f in 
              ([locals().get, globals().get, self.__dict__.get] +
               [d.get for d in (list(dd.values()) for dd in 
                                [locals(),globals(),self.__dict__]
                                if isinstance(dd,dict))
                if isinstance(d,dict)] + 
               [__import__])) 
        if x)),
"bar")()
00500005
fonte
isso poderia ser melhorado através da varredura recursiva da árvore de diretórios e da montagem automática de unidades USB
wilks
27

Quanto vale a pena, se você precisar passar o nome da função (ou classe) e o nome do aplicativo como uma sequência, faça o seguinte:

myFnName  = "MyFn"
myAppName = "MyApp"
app = sys.modules[myAppName]
fn  = getattr(app,myFnName)
trubliphone
fonte
Apenas um pouco mais genérico éhandler = getattr(sys.modules[__name__], myFnName)
lony
21

Tente isso. Enquanto isso ainda usa eval, ele apenas a utiliza para convocar a função do contexto atual . Então, você tem a função real para usar como desejar.

O principal benefício para mim disso é que você receberá erros relacionados à avaliação no momento de convocar a função. Então você receberá apenas os erros relacionados à função quando ligar.

def say_hello(name):
    print 'Hello {}!'.format(name)

# get the function by name
method_name = 'say_hello'
method = eval(method_name)

# call it like a regular function later
args = ['friend']
kwargs = {}
method(*args, **kwargs)
tvt173
fonte
1
Isso seria arriscado. string pode ter qualquer coisa e eval acabaria avaliando-a sem nenhuma consideração.
iankit
4
Claro, você deve estar atento ao contexto em que o está usando, se isso será apropriado ou não, dados esses riscos.
Tvt173
1
Uma função não deve ser responsável por validar seus parâmetros - esse é o trabalho de uma função diferente. Dizer que é arriscado usar eval com uma string está dizendo que o uso de todas as funções é arriscado.
precisa saber é
Você nunca deve usar a evalmenos que estritamente necessário. getattr(__module__, method_name)é uma escolha muito melhor nesse contexto.
moi
14

nada do que foi sugerido me ajudou. Eu descobri isso embora.

<object>.__getattribute__(<string name>)(<params>)

Eu estou usando python 2.66

Espero que isto ajude

Natdrip
fonte
13
Em que aspecto isso é melhor que getattr ()?
V13 29/07
1
Exatamente o que eu queria. Funciona como um encanto! Perfeito!! self.__getattribute__('title')é igual aself.title
ioaniatr 6/08/19
self.__getattribute__('title')não funciona em nenhum caso (não sei por que) afinal, mas func = getattr(self, 'title'); func();funciona. Então, talvez seja melhor usar em getattr()vez disso
ioaniatr 16/08/18
10
As pessoas que não conhecem python podem parar de votar neste lixo eletrônico? Use em getattrvez disso.
Aran-Fey
6

Como esta pergunta Como chamar métodos dinamicamente dentro de uma classe usando a atribuição de nome do método para uma variável [duplicado] marcada como duplicada como esta, estou postando uma resposta relacionada aqui:

O cenário é que um método em uma classe deseja chamar outro método dinamicamente na mesma classe. Adicionei alguns detalhes ao exemplo original, que oferece um cenário e clareza mais amplos:

class MyClass:
    def __init__(self, i):
        self.i = i

    def get(self):
        func = getattr(MyClass, 'function{}'.format(self.i))
        func(self, 12)   # This one will work
        # self.func(12)    # But this does NOT work.


    def function1(self, p1):
        print('function1: {}'.format(p1))
        # do other stuff

    def function2(self, p1):
        print('function2: {}'.format(p1))
        # do other stuff


if __name__ == "__main__":
    class1 = MyClass(1)
    class1.get()
    class2 = MyClass(2)
    class2.get()

Saída (Python 3.7.x)

function1: 12

function2: 12

Serjik
fonte
-7

Esta é uma resposta simples, isso permitirá que você limpe a tela, por exemplo. Existem dois exemplos abaixo, com eval e exec, que imprimirão 0 na parte superior após a limpeza (se você estiver usando o Windows, mude clearpara cls, usuários de Linux e Mac saem como estão, por exemplo) ou apenas execute-o, respectivamente.

eval("os.system(\"clear\")")
exec("os.system(\"clear\")")
Arquivo numérico
fonte
3
Não é isso que o op pede.
Tuncay Göncüoğlu 14/11/19