Importieren Sie dynamisch eine Methode aus einer Zeichenfolge in eine Datei

80

Ich habe eine Schnur, sagen wir : abc.def.ghi.jkl.myfile.mymethod. Wie importiere ich dynamisch mymethod?

So habe ich es gemacht:

def get_method_from_file(full_path):
    if len(full_path) == 1:
        return map(__import__,[full_path[0]])[0]
    return getattr(get_method_from_file(full_path[:-1]),full_path[-1])


if __name__=='__main__':
    print get_method_from_file('abc.def.ghi.jkl.myfile.mymethod'.split('.'))

Ich frage mich, ob der Import einzelner Module überhaupt erforderlich ist.

Bearbeiten: Ich verwende Python Version 2.6.5.

Lakshman Prasad
quelle

Antworten:

108

Ab Python 2.7 können Sie die Funktion importlib.import_module () verwenden. Sie können ein Modul importieren und mit dem folgenden Code auf ein darin definiertes Objekt zugreifen:

from importlib import import_module

p, m = name.rsplit('.', 1)

mod = import_module(p)
met = getattr(mod, m)

met()
frm
quelle
2
Erwähnenswert ist, dass die Python-Dokumente die Verwendung von importlib.import_module()over __import__(): docs.python.org/2/library/functions.html#__import__ - für 2.7+ empfehlen.
BoltzmannBrain
4
import_module + rsplit = Der einzig wahre Weg.
Cecil Curry
32

Sie müssen die einzelnen Module nicht importieren. Es reicht aus, das Modul zu importieren, aus dem Sie einen Namen importieren möchten, und das folgende fromlistArgument anzugeben:

def import_from(module, name):
    module = __import__(module, fromlist=[name])
    return getattr(module, name)

abc.def.ghi.jkl.myfile.mymethodRufen Sie für Ihr Beispiel diese Funktion als auf

import_from("abc.def.ghi.jkl.myfile", "mymethod")

(Beachten Sie, dass Funktionen auf Modulebene in Python als Funktionen und nicht als Methoden bezeichnet werden.)

Für eine so einfache Aufgabe bietet die Verwendung des importlibModuls keinen Vorteil .

Sven Marnach
quelle
import_from () hat mich in die richtige Richtung gelenkt. myClass = getattr(__import__("module.to.import", fromlist=["myClassName"]), "myClassName"). Danke für die Hilfe!
Fydo
1
Der __import__Ansatz funktioniert. Aber versuche zu rennen help(__import__). Darin heißt es: "Da diese Funktion für den Python-Interpreter und nicht für den allgemeinen Gebrauch bestimmt ist, ist es besser, importlib.import_module()ein Modul programmgesteuert zu importieren."
Twasbrillig
5
@twasbrillig: Als ich diese Frage beantwortete, waren Python 2.5 und 2.6 noch weit verbreitet. Heute ist es sicherlich am besten, importlibstattdessen zu verwenden .
Sven Marnach
1
Cool, danke. Es könnte eine gute Idee sein, die Antwort zu aktualisieren, um dies widerzuspiegeln.
Twasbrillig
26

Für Python <2.7 kann die integrierte Methode __ import__ verwendet werden:

__import__('abc.def.ghi.jkl.myfile.mymethod', fromlist=[''])

Für Python> = 2.7 oder 3.1 wurde die praktische Methode importlib.import_module hinzugefügt. Importieren Sie Ihr Modul einfach wie folgt:

importlib.import_module('abc.def.ghi.jkl.myfile.mymethod')

Update : Aktualisierte Version gemäß Kommentaren ( Ich muss zugeben, dass ich die zu importierende Zeichenfolge erst am Ende gelesen habe und die Tatsache übersehen habe, dass eine Methode eines Moduls importiert werden sollte und nicht ein Modul selbst) :

Python <2.7:

mymethod = getattr(__import__("abc.def.ghi.jkl.myfile", fromlist=["mymethod"]))

Python> = 2.7:

mymethod = getattr(importlib.import_module("abc.def.ghi.jkl.myfile"), "mymethod")
Gecco
quelle
6
Keine dieser Lösungen wird nicht funktionieren , weil der erste Parameter sein , muss Modulnamen in beide __import__()und importlib.import_module().
Kris Hardy
1
importlib.import_module ('abc.def.ghi.jkl.myfile.mymethod') funktioniert nicht, da Sie "welches Modul in absoluten oder relativen Begriffen importieren soll" und nicht den "qualifizierten" Namen der Methode angeben müssen.
vom
Dies funktioniert natürlich nicht, da der erste zu importierende Parameter ein Modul und keine Zeichenfolge sein sollte.
Lakshman Prasad
11

Es ist unklar, was Sie mit Ihrem lokalen Namespace tun möchten. Ich nehme an, Sie wollen nur my_methodals Einheimischer tippen output = my_method()?

# This is equivalent to "from a.b.myfile import my_method"
the_module = importlib.import_module("a.b.myfile")
same_module = __import__("a.b.myfile")
# import_module() and __input__() only return modules
my_method = getattr(the_module, "my_method")

# or, more concisely,
my_method = getattr(__import__("a.b.myfile"), "my_method")
output = my_method()

Während Sie nur my_methodden lokalen Namespace hinzufügen , laden Sie die Kette von Modulen. Sie können Änderungen anzeigen, indem Sie die Tasten sys.modulesvor und nach dem Import beobachten. Ich hoffe, das ist klarer und genauer als Ihre anderen Antworten.

Der Vollständigkeit halber fügen Sie auf diese Weise die gesamte Kette hinzu.

# This is equivalent to "import a.b.myfile"
a = __import__("a.b.myfile")
also_a = importlib.import_module("a.b.myfile")
output = a.b.myfile.my_method()

# This is equivalent to "from a.b import myfile"
myfile = __import__("a.b.myfile", fromlist="a.b")
also_myfile = importlib.import_module("a.b.myfile", "a.b")
output = myfile.my_method()

Wenn Sie __import__()Ihren Suchpfad nach dem Start des Programms verwenden und geändert haben, müssen Sie ihn möglicherweise verwenden __import__(normal args, globals=globals(), locals=locals()). Das Warum ist eine komplexe Diskussion.

Charles Merriam
quelle
Ihr erstes Beispiel für the_moduleund same_moduleist falsch und führt zu unterschiedlichen Ergebnissen. Downvoting.
Sleepycal
7
from importlib import import_module

name = "file.py".strip('.py')
# if Path like : "path/python/file.py" 
# use name.replaces("/",".")

imp = import_module(name)

# get Class From File.py
model = getattr(imp, "classNameImportFromFile")

NClass = model() # Class From file 
Alaa Akiel
quelle
2
Vielen Dank für die Antwort, nur ein kleines Problem: Ich denke, dass es .strip()sich in dem von Ihnen angegebenen Fall nicht richtig verhält. Insbesondere wenn die Datei mit "py" beginnt, werden diese Zeichen ebenfalls entfernt. Zum Beispiel "pyfile.py".strip(".py")produziert "file", wo es bevorzugt wäre, dass es "pyfile"in diesem Fall produziert. name.replace(".py","")funktioniert aber gut.
jxmorris12
1

Diese Website hat eine schöne Lösung: load_class . Ich benutze es so:

foo = load_class(package.subpackage.FooClass)()
type(foo) # returns FooClass

Wie angefordert, ist hier der Code vom Weblink:

import importlib

def load_class(full_class_string):
    """
    dynamically load a class from a string
    """

    class_data = full_class_string.split(".")
    module_path = ".".join(class_data[:-1])
    class_str = class_data[-1]

    module = importlib.import_module(module_path)
    # Finally, we retrieve the Class
    return getattr(module, class_str)
kingaj
quelle
Bitte geben Sie die Zusammenfassung / den Code des angegebenen Links an. Nur-Link-Antworten sind nicht großartig, da sie dazu neigen, Fäulnis zu verknüpfen.
Noch ein Benutzer
0

Verwenden Sie importlib(nur 2.7+).

Daniel Roseman
quelle
1
Ich benutze 2.6.5. Kann ich etwas from __future__tun?
Lakshman Prasad
5
Nein, __future__ist für Sprachfunktionen, nicht für neue stdlib-Module.
ThiefMaster
1
Verwenden Sie das Buit-In __import__wie in den obigen Beispielen. Es funktioniert wieder auf 2.5 und davor ohne Schlüsselwörter.
Charles Merriam
0

Die Art und Weise, wie ich dazu neige (sowie eine Reihe anderer Bibliotheken wie Pylone und Einfügen, wenn mein Speicher mir richtig dient), besteht darin, den Modulnamen vom Funktions- / Attributnamen zu trennen, indem ein ':' zwischen ihnen verwendet wird . Siehe folgendes Beispiel:

'abc.def.ghi.jkl.myfile:mymethod'

Dies import_from(path)erleichtert die Verwendung der folgenden Funktion.

def import_from(path):
    """
    Import an attribute, function or class from a module.
    :attr path: A path descriptor in the form of 'pkg.module.submodule:attribute'
    :type path: str
    """
    path_parts = path.split(':')
    if len(path_parts) < 2:
        raise ImportError("path must be in the form of pkg.module.submodule:attribute")
    module = __import__(path_parts[0], fromlist=path_parts[1])
    return getattr(module, path_parts[1])


if __name__=='__main__':
    func = import_from('a.b.c.d.myfile:mymethod')
    func()
Kris Hardy
quelle
1
Warum sollte irgendjemand das tun? Dazu muss der Aufrufer den Modulnamen und den Attributnamen mit einem Doppelpunkt verbinden, und die Funktion muss am Doppelpunkt erneut aufgeteilt werden. Warum nicht einfach zwei Parameter verwenden?
Sven Marnach
1
In vielen Fällen tritt dies auf, wenn Sie Rückrufe von einem Framework mithilfe von Konfigurationsdateien (.yaml, .ini usw.) an Ihre Anwendung senden. Anschließend können Sie mit einer einzelnen Zeichenfolge in Ihrer Konfigurationsdatei angeben, welche Funktion das Framework usw. aufrufen soll.
Kris Hardy
Ja, vielleicht möchten Sie dies als Syntax für die Konfigurationsdatei auswählen. Aber wie hängt das mit der Frage des OP zusammen? (Und selbst wenn ich dies als meine Syntax für Konfigurationsdateien gewählt hätte, würde ich es vorziehen, es von meinem Parser für Konfigurationsdateien analysieren zu lassen, nicht von zufälligen API-Funktionen.)
Sven Marnach
-1

Wie wäre es damit :

def import_module(name):

    mod = __import__(name)
    for s in name.split('.')[1:]:
        mod = getattr(mod, s)
    return mod
Aymen Alsaadi
quelle
Von der Verwendung der magischen Methode wird abgeraten. Verwenden Sie stattdessen importlib.
Dephinera
Können Sie näher erläutern, warum dies entmutigend ist? @dephinera
Aymen Alsaadi
Nun, weil magische Methoden vom Interpreter aufgerufen werden sollen, nicht explizit von unserem Code. Wenn wir sie explizit aufrufen, verlassen wir uns darauf, dass sich ihre Signatur niemals ändert, jedoch möglicherweise in zukünftigen Sprachversionen.
Dephinera