Chiamare una funzione di un modulo usando il suo nome (una stringa)


1735

Qual è il modo migliore per chiamare una funzione assegnata a una stringa con il nome della funzione in un programma Python. Ad esempio, diciamo che ho un modulo fooe ho una stringa il cui contenuto è "bar". Qual è il modo migliore per chiamare foo.bar()?

Devo ottenere il valore di ritorno della funzione, motivo per cui non mi limito a utilizzare eval. Ho capito come farlo usando evalper definire una funzione temporanea che restituisce il risultato di quella chiamata di funzione, ma spero che ci sia un modo più elegante per farlo.

Risposte:


2079

Supponendo il modulo foocon il metodo bar:

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

È possibile abbreviare le righe 2 e 3 per:

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

se questo ha più senso per il tuo caso d'uso.

È possibile utilizzare getattrin questo modo metodi associati a istanze di classe, metodi a livello di modulo, metodi di classe ... l'elenco potrebbe continuare.


9
hasattr o getattr possono essere usati per determinare se una funzione è definita. Avevo una mappatura del database (eventType e handling functionName) e volevo assicurarmi di non aver mai "dimenticato" di definire un gestore eventi nel mio pitone
Shaun,

9
Funziona se conosci già il nome del modulo. Tuttavia, se si desidera che l'utente fornisca il nome del modulo come stringa, ciò non funzionerà.
Blairg23,

7
Se è necessario evitare un'eccezione NoneType non richiamabile, è possibile utilizzare anche la forma a tre argomenti di getattr: getattr (foo, 'bar', lambda: None). Mi scuso per la formattazione; l'app Android StackExchange è apparentemente terribile.
geekofalltrades

3
Vedi anche la risposta fornita da @sastanin se ti preoccupi solo per esempio delle funzioni del tuo modulo locale / corrente.
NuSkooler,

6
@akki Sì, se siete nel modulo che si può usare per fare questo:fooglobals()methodToCall = globals()['bar']
Ben Hoyt

543
locals()["myfunction"]()

o

globals()["myfunction"]()

la gente del posto restituisce un dizionario con una tabella dei simboli locale corrente. globals restituisce un dizionario con tabella dei simboli globale.


53
Questo metodo con globals / local è buono se il metodo che devi chiamare è definito nello stesso modulo da cui stai chiamando.
Joelmob,

@Joelmob c'è un altro modo per estrarre un oggetto per stringa dallo spazio dei nomi di root?
Nick T,

@NickT Sono solo a conoscenza di questi metodi, non penso che ce ne siano altri che svolgono la stessa funzione di questi, almeno non riesco a pensare a un motivo per cui dovrebbe essercene di più.
Joelmob,

Ho una ragione per te (in realtà cosa mi ha portato qui): il modulo A ha una funzione F che deve chiamare una funzione per nome. Il modulo B importa il modulo A e richiama la funzione F con una richiesta per chiamare la funzione G, che è definita nel modulo B. Questa chiamata fallisce perché, apparentemente, la funzione F funziona solo con i globi definiti nel modulo F, quindi globals () ['G'] = Nessuno.
David Stein,

338

La soluzione di Patrick è probabilmente la più pulita. Se devi ritirare dinamicamente anche il modulo, puoi importarlo come:

module = __import__('foo')
func = getattr(module, 'bar')
func()

93
Non capisco quell'ultimo commento. __import__ ha i suoi diritti e la frase successiva nei documenti citati dice: "L'uso diretto di __import __ () è raro, tranne nei casi in cui si desidera importare un modulo il cui nome è noto solo in fase di esecuzione". Quindi: +1 per la risposta data.
hoffmaje,

50
Usa importlib.import_module. I documenti ufficiali dicono __import__: "Questa è una funzione avanzata che non è necessaria nella programmazione quotidiana di Python, a differenza di importlib.import_module ()." docs.python.org/2/library/functions.html#__import__
glarrain

8
@glarrain Finché stai bene con solo il supporto 2.7 e versioni successive.
Xiong Chiamiov

1
@Xiong Chaimiov, importlib.import_moduleè supportato in 3.6. Vedi docs.python.org/3.6/library/…
cowlinator il

7
@cowlinator Sì, 3.6 fa parte di "2.7 e versioni successive", sia nella semantica del versioning rigoroso che nelle date di rilascio (arrivò circa sei anni dopo). Inoltre non esisteva per tre anni dopo il mio commento. ;) Nel ramo 3.x, il modulo esiste da 3.1. 2.7 e 3.1 sono ora piuttosto antichi; troverai ancora server in giro che supportano solo 2.6, ma probabilmente vale la pena avere importlib come consiglio standard al giorno d'oggi.
Xiong Chiamiov,

114

Solo un semplice contributo. Se la classe che dobbiamo istanziare è nello stesso file, possiamo usare qualcosa del genere:

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

Per esempio:

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 non una classe:

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

globals()['sampleFunc']('sample arg')

101

Data una stringa, con un percorso python completo per una funzione, ecco come ho ottenuto il risultato di detta funzione:

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

1
Questo mi ha aiutato. È una versione leggera della __import__funzione.
Pankaj Bhambhani,

Penso che questa sia stata la migliore risposta.
Ha detto il

55

La migliore risposta secondo le FAQ sulla programmazione di Python sarebbe:

functions = {'myfoo': foo.bar}

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

Il vantaggio principale di questa tecnica è che le stringhe non devono necessariamente corrispondere ai nomi delle funzioni. Questa è anche la tecnica principale utilizzata per emulare un costrutto case


43

La risposta (spero) che nessuno abbia mai voluto

Comportamento simile al male

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

Perché non aggiungere l'importazione automatica

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

Nel caso in cui abbiamo dizionari extra che vogliamo controllare

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

Dobbiamo andare più a fondo

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

questo potrebbe essere migliorato scansionando ricorsivamente l'albero delle directory e auto-montando le unità USB
wilks

27

Per quello che vale, se hai bisogno di passare il nome della funzione (o della classe) e il nome dell'app come stringa, puoi farlo:

myFnName  = "MyFn"
myAppName = "MyApp"
app = sys.modules[myAppName]
fn  = getattr(app,myFnName)

Un po 'più generico èhandler = getattr(sys.modules[__name__], myFnName)
lony

21

Prova questo. Mentre questo usa ancora eval, lo usa solo per evocare la funzione dal contesto corrente . Quindi, hai la vera funzione da usare come desideri.

Il vantaggio principale per me da questo è che otterrai errori relativi ad eval al momento di convocare la funzione. Quindi riceverai solo gli errori relativi alla funzione quando chiami.

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)

1
Questo sarebbe rischioso. la stringa può avere qualsiasi cosa ed eval finirebbe per valutarla senza alcuna considerazione.
iankit,

4
Certo, devi essere consapevole del contesto in cui lo stai usando, sia che sia appropriato o meno, dati quei rischi.
tvt173,

1
Una funzione non dovrebbe essere responsabile della convalida dei suoi parametri: questo è il compito di una funzione diversa. Dire che è rischioso usare eval con una stringa significa che l'uso di ogni funzione è rischioso.
red777,

Non si dovrebbe mai usare evalse non strettamente necessario. getattr(__module__, method_name)è una scelta molto migliore in questo contesto.
moi,

14

niente di ciò che mi è stato suggerito mi ha aiutato. L'ho scoperto però.

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

Sto usando Python 2.66

Spero che sia di aiuto


13
In quale aspetto è meglio di getattr ()?
V13,

1
Esattamente quello che volevo. Funziona come un fascino! Perfetto!! self.__getattribute__('title')è uguale aself.title
ioaniatr il

self.__getattribute__('title')dopotutto non funziona (non so perché), ma func = getattr(self, 'title'); func();funziona. Quindi, forse è meglio usare getattr()invece
ioaniatr

10
Le persone che non conoscono Python possono smettere di votare questa spazzatura? Usa getattrinvece.
Aran-Fey,

6

Come questa domanda Come chiamare dinamicamente i metodi all'interno di una classe usando l'assegnazione nome metodo a una variabile [duplicato] contrassegnata come duplicata come questa, sto pubblicando una risposta correlata qui:

Lo scenario è che un metodo in una classe vuole chiamare un altro metodo sulla stessa classe in modo dinamico, ho aggiunto alcuni dettagli all'esempio originale che offre uno scenario più ampio e chiarezza:

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

Output (Python 3.7.x)

funzione1: 12

funzione2: 12


-7

Questa è una risposta semplice, ad esempio ti permetterà di cancellare lo schermo. Di seguito sono riportati due esempi, con eval ed exec, che stamperanno 0 in alto dopo la pulizia (se si utilizza Windows, si clearpassa a cls, gli utenti Linux e Mac se ne vanno come è per esempio) o semplicemente si eseguono, rispettivamente.

eval("os.system(\"clear\")")
exec("os.system(\"clear\")")

3
Questo non è ciò che chiede op.
Tuncay Göncüoğlu,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.