697 Stimmen

Wie importiert man ein Modul mit seinem Namen als String?

Ich schreibe eine Python-Anwendung, die z. B. einen Befehl als Argument annimmt:

$ python myapp.py command1

Ich möchte, dass die Anwendung erweiterbar ist, d. h., dass neue Module hinzugefügt werden können, die neue Befehle implementieren, ohne dass der Hauptanwendungscode geändert werden muss. Der Baum sieht in etwa so aus:

myapp/
    __init__.py
    commands/
        __init__.py
        command1.py
        command2.py
    foo.py
    bar.py

Ich möchte also, dass die Anwendung zur Laufzeit die verfügbaren Befehlsmodule findet und das entsprechende Modul ausführt.

Python definiert eine __import__() Funktion, die eine Zeichenkette als Modulnamen annimmt:

__import__(name, globals=None, locals=None, fromlist=(), level=0)

Die Funktion importiert das Modul name möglicherweise unter Verwendung der angegebenen globals et locals um zu bestimmen, wie der Name in einem Paketkontext zu interpretieren ist. Die fromlist gibt die Namen von Objekten oder Untermodulen an, die aus dem Modul importiert werden sollen, das durch name .

Quelle: https://docs.python.org/3/library/functions.html#__import_ _

Derzeit habe ich also etwas wie:

command = sys.argv[1]
try:
    command_module = __import__("myapp.commands.%s" % command, fromlist=["myapp.commands"])
except ImportError:
    # Display error message

command_module.run()

Dies funktioniert sehr gut, ich frage mich nur, ob es möglicherweise eine idiomatischere Weise zu erreichen, was wir mit diesem Code tun.

Beachten Sie, dass ich ausdrücklich nicht auf die Verwendung von Eiern oder Verlängerungspunkten eingehen möchte. Dies ist kein Open-Source-Projekt und ich erwarte nicht, dass es "Plugins" gibt. Es geht darum, den Hauptanwendungscode zu vereinfachen und ihn nicht jedes Mal ändern zu müssen, wenn ein neues Befehlsmodul hinzugefügt wird.

0 Stimmen

Was bewirkt die fromlist=["myapp.commands"]?

2 Stimmen

@PieterMüller : Geben Sie in einer Python-Shell Folgendes ein: dir(__import__) . Die fromlist sollte eine Liste von Namen sein, um "from name import ..." zu emulieren.

4 Stimmen

408voto

Harley Holcombe Punkte 165146

Mit Python, das älter als 2.7/3.1 ist, ist das so ziemlich die Art und Weise, wie man es macht.

Für neuere Versionen, siehe importlib.import_module にとって Python 2 et Python 3 .

Sie können verwenden exec wenn Sie das auch wollen.

Oder mit __import__ können Sie auf diese Weise eine Liste von Modulen importieren:

>>> moduleNames = ['sys', 'os', 're', 'unittest'] 
>>> moduleNames
['sys', 'os', 're', 'unittest']
>>> modules = map(__import__, moduleNames)

Direkt entnommen aus Eintauchen in Python .

8 Stimmen

Worin besteht der Unterschied zu einer Führungskraft?

1 Stimmen

Wie könnten Sie die __init__ dieses Moduls?

11 Stimmen

Ein Problem mit dieser Lösung für die OP ist, dass Trapping Ausnahmen für ein oder zwei schlechte "Befehl"-Module macht seine / ihre gesamte Befehls-Anwendung auf eine Ausnahme fehlschlagen. Persönlich würde ich eine for-Schleife über jedes importieren einzeln verpackt in einem Try: mods=__import__() \nexcept ImportError als error: report(error), damit andere Befehle weiter funktionieren, während die Fehler behoben werden.

401voto

Denis Malinovsky Punkte 5394

Der empfohlene Weg für Python 2.7 und 3.1 und später ist die Verwendung von importlib Modul:

importlib.import_module(name, package=None)

Importieren Sie ein Modul. Das Argument name gibt an, welches Modul in absoluten oder relativen Begriffen importiert werden soll (z.B. entweder pkg.mod oder ..mod). Wird der Name in relativen Begriffen angegeben, muss das Argument package auf den Namen des Pakets gesetzt werden, das als Anker für die Auflösung des Paketnamens dienen soll (z.B. import_module('..mod', 'pkg.subpkg') importiert pkg.mod).

z.B..

my_module = importlib.import_module('os.path')

7 Stimmen

Von welcher Quelle oder Behörde empfohlen?

92 Stimmen

Die Dokumentation rät gegen mit __import__ Funktion zu Gunsten des oben genannten Moduls.

10 Stimmen

Dies funktioniert gut bei der Einfuhr os.path ; wie wäre es mit from os.path import * ?

137voto

monkut Punkte 39059

Anmerkung: imp ist seit Python 3.4 veraltet zugunsten von importlib

Wie bereits erwähnt, ist die imp Modul stellt Ihnen Ladefunktionen zur Verfügung:

imp.load_source(name, path)
imp.load_compiled(name, path)

Ich habe sie schon einmal benutzt, um etwas Ähnliches zu tun.

In meinem Fall habe ich eine bestimmte Klasse mit bestimmten Methoden definiert, die erforderlich waren. Sobald ich das Modul geladen hatte, prüfte ich, ob die Klasse im Modul enthalten war, und erstellte dann eine Instanz dieser Klasse, etwa so:

import imp
import os

def load_from_file(filepath):
    class_inst = None
    expected_class = 'MyClass'

    mod_name,file_ext = os.path.splitext(os.path.split(filepath)[-1])

    if file_ext.lower() == '.py':
        py_mod = imp.load_source(mod_name, filepath)

    elif file_ext.lower() == '.pyc':
        py_mod = imp.load_compiled(mod_name, filepath)

    if hasattr(py_mod, expected_class):
        class_inst = getattr(py_mod, expected_class)()

    return class_inst

2 Stimmen

Eine gute und einfache Lösung. Ich habe eine ähnliche Lösung geschrieben: stamat.wordpress.com/dynamisches-modul-import-in-python Aber Ihr Bericht hat einige Schwachstellen: Was ist mit Ausnahmen? IOError und ImportError? Warum wird nicht zuerst die kompilierte Version geprüft und dann die Quellversion. Die Erwartung einer Klasse reduziert in Ihrem Fall die Wiederverwendbarkeit.

3 Stimmen

In der Zeile, in der Sie MyClass im Zielmodul konstruieren, fügen Sie einen redundanten Verweis auf den Klassennamen hinzu. Er ist bereits gespeichert in expected_class Sie könnten also Folgendes tun class_inst = getattr(py_mod,expected_name)() stattdessen.

1 Stimmen

Wenn eine passende byte-kompilierte Datei (mit der Endung .pyc oder .pyo) existiert, wird diese verwendet, anstatt die angegebene Quelldatei zu parsen . https://docs.python.org/2/library/imp.html#imp.load_source

49voto

Brandt Punkte 3729

Heutzutage sollten Sie importlib .

Importieren einer Quelldatei

Le site docs Es gibt sogar ein Rezept dafür, und das geht so:

import sys
import importlib.util

file_path = 'pluginX.py'
module_name = 'pluginX'

spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)

# check if it's all there..
def bla(mod):
    print(dir(mod))
bla(module)

Auf diese Weise können Sie auf die Mitglieder (z. B. eine Funktion " hello ") von Ihrem Modul pluginX.py -- in diesem Schnipsel heißt das module -- unter seinem Namensraum; z.B, module.hello() .

Wenn Sie die Mitglieder importieren möchten (z. B. " hello ") können Sie einschließen module / pluginX in der speicherinternen Liste der Module:

sys.modules[module_name] = module

from pluginX import hello
hello()

Ein Paket importieren

Importieren eines Pakets ( z.B.. , pluginX/__init__.py ) unter Ihrem aktuellen Verzeichnis ist eigentlich ganz einfach:

import importlib

pkg = importlib.import_module('pluginX')

# check if it's all there..
def bla(mod):
    print(dir(mod))
bla(pkg)

6 Stimmen

Hinzufügen. sys.modules[module_name] = module an das Ende Ihres Codes anfügen, wenn Sie in der Lage sein wollen import module_name danach

0 Stimmen

@DanielBraun dabei bekomme ich Fehler > ModuleNotFoundError

18voto

gimel Punkte 78080

Verwenden Sie die Koboldmodul oder die direktere __import__() Funktion.

7 Stimmen

Verwenden Sie nicht __import__ siehe Python-Doku ein verwenden importlib.import_module

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X