Aufrufen einer Funktion eines Moduls unter Verwendung seines Namens (einer Zeichenfolge)

1734

Was ist der beste Weg, um eine Funktion mit einem String mit dem Namen der Funktion in einem Python-Programm aufzurufen? Angenommen, ich habe ein Modul foound eine Zeichenfolge mit dem Inhalt "bar". Wie rufe foo.bar()ich am besten an ?

Ich muss den Rückgabewert der Funktion erhalten, weshalb ich nicht nur verwende eval. Ich habe herausgefunden, wie das geht, indem ich evaleine temporäre Funktion definiert habe, die das Ergebnis dieses Funktionsaufrufs zurückgibt, aber ich hoffe, dass es einen eleganteren Weg gibt, dies zu tun.

Ricree
quelle

Antworten:

2078

Angenommenes Modul foomit Methode bar:

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

Sie können die Zeilen 2 und 3 verkürzen, um:

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

wenn das für Ihren Anwendungsfall sinnvoller ist.

Sie können getattrauf diese Weise für Klasseninstanz-gebundene Methoden, Methoden auf Modulebene, Klassenmethoden ... verwenden. Die Liste wird fortgesetzt.

Patrick Johnmeyer
quelle
9
Mit hasattr oder getattr kann ermittelt werden, ob eine Funktion definiert ist. Ich hatte eine Datenbankzuordnung (eventType und Handling functionName) und wollte sicherstellen, dass ich nie "vergessen" habe, einen Event-Handler in meinem Python zu definieren
Shaun
9
Dies funktioniert, wenn Sie den Modulnamen bereits kennen. Wenn Sie jedoch möchten, dass der Benutzer den Modulnamen als Zeichenfolge angibt, funktioniert dies nicht.
Blairg23
7
Wenn Sie vermeiden müssen, dass NoneType keine aufrufbare Ausnahme ist, können Sie auch die Form mit drei Argumenten von getattr: getattr (foo, 'bar', lambda: None) verwenden. Ich entschuldige mich für die Formatierung; Die Stackexchange Android App ist anscheinend schrecklich.
Geekofalltrades
3
Siehe auch die Antwort von @sastanin, wenn Sie sich zum Beispiel nur für die Funktionen Ihres lokalen / aktuellen Moduls interessieren.
NuSkooler
6
@akki Ja, wenn Sie in dem fooModul können Sie verwenden , globals()dies zu tun:methodToCall = globals()['bar']
Ben Hoyt
543
locals()["myfunction"]()

oder

globals()["myfunction"]()

Einheimische geben ein Wörterbuch mit einer aktuellen lokalen Symboltabelle zurück. globals gibt ein Wörterbuch mit globaler Symboltabelle zurück.

Sastanin
quelle
53
Diese Methode mit Globals / Locals ist gut, wenn die Methode, die Sie aufrufen müssen, in demselben Modul definiert ist, von dem aus Sie aufrufen.
Joelmob
@Joelmob gibt es eine andere Möglichkeit, ein Objekt per String aus dem Root-Namespace zu entfernen?
Nick T
@NickT Ich kenne nur diese Methoden, ich glaube nicht, dass es andere gibt, die dieselbe Funktion wie diese erfüllen, zumindest kann ich mir keinen Grund vorstellen, warum es mehr geben sollte.
Joelmob
Ich habe einen Grund für Sie (was mich hierher geführt hat): Modul A hat eine Funktion F, die eine Funktion beim Namen nennen muss. Modul B importiert Modul A und ruft die Funktion F mit der Anforderung auf, die in Modul B definierte Funktion G aufzurufen. Dieser Aufruf schlägt fehl, da die Funktion F anscheinend nur mit den in Modul F definierten Globals ausgeführt wird - also globals () ['G'] = Keine.
David Stein
337

Patricks Lösung ist wahrscheinlich die sauberste. Wenn Sie das Modul auch dynamisch aufnehmen müssen, können Sie es wie folgt importieren:

module = __import__('foo')
func = getattr(module, 'bar')
func()
HS.
quelle
93
Ich verstehe diesen letzten Kommentar nicht. __import__ hat ein eigenes Recht und der nächste Satz in den genannten Dokumenten lautet: "Die direkte Verwendung von __import __ () ist selten, außer in Fällen, in denen Sie ein Modul importieren möchten, dessen Name nur zur Laufzeit bekannt ist." Also: +1 für die gegebene Antwort.
hoffmaje
50
Verwenden Sie importlib.import_module. In den offiziellen Dokumenten heißt es __import__: "Dies ist eine erweiterte Funktion, die im Gegensatz zu importlib.import_module () in der täglichen Python-Programmierung nicht benötigt wird." docs.python.org/2/library/functions.html#__import__
Glarrain
8
@glarrain Solange du mit Unterstützung ab 2.7 in Ordnung bist.
Xiong Chiamiov
1
@ Xiong Chaimiov, importlib.import_modulewird in 3.6 unterstützt. Siehe docs.python.org/3.6/library/…
Cowlinator
7
@cowlinator Ja, 3.6 ist Teil von "2.7 und höher", sowohl in der strengen Versionssemantik als auch in den Veröffentlichungsdaten (es kam ungefähr sechs Jahre später). Es gab es auch drei Jahre nach meinem Kommentar nicht. ;) Im 3.x-Zweig gibt es das Modul seit 3.1. 2.7 und 3.1 sind jetzt ziemlich alt; Es gibt immer noch Server, die nur 2.6 unterstützen, aber es lohnt sich wahrscheinlich, importlib heutzutage als Standardempfehlung zu verwenden.
Xiong Chiamiov
114

Nur ein einfacher Beitrag. Wenn sich die Klasse, die wir instanziieren müssen, in derselben Datei befindet, können wir Folgendes verwenden:

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

Zum Beispiel:

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

Und wenn nicht eine Klasse:

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

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

Bei einer gegebenen Zeichenfolge mit einem vollständigen Python-Pfad zu einer Funktion habe ich das Ergebnis dieser Funktion erhalten:

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()
Eisenrad
quelle
1
Das hat mir geholfen. Es ist eine leichte Version der __import__Funktion.
Pankaj Bhambhani
Ich denke, das war die beste Antwort.
Saeid
55

Die beste Antwort laut den häufig gestellten Fragen zur Python-Programmierung wäre:

functions = {'myfoo': foo.bar}

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

Der Hauptvorteil dieser Technik besteht darin, dass die Zeichenfolgen nicht mit den Namen der Funktionen übereinstimmen müssen. Dies ist auch die primäre Technik, die zum Emulieren eines Fallkonstrukts verwendet wird


quelle
43

Die Antwort (ich hoffe) wollte niemand jemals

Eval wie Verhalten

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

Warum nicht den automatischen Import hinzufügen?

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

Falls wir zusätzliche Wörterbücher haben, möchten wir diese überprüfen

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

Wir müssen tiefer gehen

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
quelle
Dies könnte verbessert werden, indem der Verzeichnisbaum rekursiv
gescannt
27

Wenn Sie den Funktions- (oder Klassen-) Namen und den App-Namen als Zeichenfolge übergeben müssen, können Sie Folgendes tun:

myFnName  = "MyFn"
myAppName = "MyApp"
app = sys.modules[myAppName]
fn  = getattr(app,myFnName)
Trubliphon
quelle
Nur ein bisschen allgemeiner isthandler = getattr(sys.modules[__name__], myFnName)
Lony
21

Versuche dies. Während dies immer noch eval verwendet, wird es nur verwendet, um die Funktion aus dem aktuellen Kontext aufzurufen . Dann haben Sie die eigentliche Funktion, die Sie nach Belieben verwenden können.

Der Hauptvorteil für mich besteht darin, dass Sie beim Aufrufen der Funktion alle evalbezogenen Fehler erhalten. Dann erhalten Sie beim Aufruf nur die funktionsbezogenen Fehler.

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
quelle
1
Das wäre riskant. string kann alles haben und eval würde es am Ende ohne Rücksicht bewerten.
iankit
4
Sicher, Sie müssen sich des Kontexts bewusst sein, in dem Sie ihn verwenden, ob dies angesichts dieser Risiken angemessen ist oder nicht.
tvt173
1
Eine Funktion sollte nicht für die Validierung ihrer Parameter verantwortlich sein - das ist die Aufgabe einer anderen Funktion. Zu sagen, dass es riskant ist, eval mit einer Zeichenfolge zu verwenden, bedeutet, dass die Verwendung jeder Funktion riskant ist.
Red777
Sie sollten niemals verwenden, es evalsei denn, dies ist unbedingt erforderlich. getattr(__module__, method_name)ist in diesem Zusammenhang eine viel bessere Wahl.
Moi
14

Nichts von dem, was vorgeschlagen wurde, half mir. Ich habe das allerdings entdeckt.

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

Ich benutze Python 2.66

Hoffe das hilft

Natdrip
quelle
13
Inwiefern ist das besser als getattr ()?
V13
1
Genau das, was ich wollte. Klappt wunderbar! Perfekt!! self.__getattribute__('title')ist gleichself.title
ioaniatr
self.__getattribute__('title')funktioniert in keinem Fall (weiß nicht warum), func = getattr(self, 'title'); func();tut es aber . Also, vielleicht ist es besser, getattr()stattdessen zu verwenden
Ioaniatr
10
Können Leute, die Python nicht kennen, bitte aufhören, diesen Müll zu stimmen? Verwenden Sie getattrstattdessen.
Aran-Fey
6

Da diese Frage, wie Methoden innerhalb einer Klasse mithilfe der Zuweisung von Methodennamen zu einer Variablen [Duplikat], die als Duplikat wie diese markiert ist , dynamisch aufruft , veröffentliche ich hier eine verwandte Antwort:

Das Szenario ist, dass eine Methode in einer Klasse eine andere Methode für dieselbe Klasse dynamisch aufrufen möchte. Ich habe dem ursprünglichen Beispiel einige Details hinzugefügt, die ein breiteres Szenario und Klarheit bieten:

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

Ausgabe (Python 3.7.x)

Funktion1: 12

Funktion2: 12

Serjik
quelle
-7

Dies ist eine einfache Antwort, mit der Sie beispielsweise den Bildschirm löschen können. Im Folgenden finden Sie zwei Beispiele mit eval und exec, bei denen nach dem Bereinigen oben 0 gedruckt wird (wenn Sie Windows verwenden, wechseln Sie clearzu clsLinux- und Mac-Benutzern, lassen Sie diese unverändert ) oder führen Sie sie einfach aus.

eval("os.system(\"clear\")")
exec("os.system(\"clear\")")
Nummer Datei
quelle
3
Dies ist nicht das, was op verlangt.
Tuncay Göncüoğlu