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.

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

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 .


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

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.


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 

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)

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

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

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

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.