716 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

13voto

Greg Hewgill Punkte 882617

Sie können verwenden exec :

exec("import myapp.commands.%s" % command)

0 Stimmen

Wie erhalte ich über exec ein Handle auf das Modul, so dass ich (in diesem Beispiel) die Methode .run() aufrufen kann?

7 Stimmen

Sie können dann mit getattr(myapp.commands, command) auf das Modul zugreifen.

1 Stimmen

... oder hinzufügen as command_module an das Ende der Importanweisung an und machen dann command_module.run()

13voto

Jonathan Livni Punkte 92274

Wenn Sie es in Ihren Lokalen haben wollen:

>>> mod = 'sys'
>>> locals()['my_module'] = __import__(mod)
>>> my_module.version
'2.6.6 (r266:84297, Aug 24 2010, 18:46:32) [MSC v.1500 32 bit (Intel)]'

Dasselbe gilt für globals()

4 Stimmen

El Dokumentation für den eingebauten locals() Funktion warnt ausdrücklich davor, dass der "Inhalt dieses Wörterbuchs nicht verändert werden darf".

2voto

stamat Punkte 1664

Ähnlich wie @monkut 's Lösung, aber wiederverwendbar und fehlertolerant hier beschrieben http://stamat.wordpress.com/dynamic-module-import-in-python/ :

import os
import imp

def importFromURI(uri, absl):
    mod = None
    if not absl:
        uri = os.path.normpath(os.path.join(os.path.dirname(__file__), uri))
    path, fname = os.path.split(uri)
    mname, ext = os.path.splitext(fname)

    if os.path.exists(os.path.join(path,mname)+'.pyc'):
        try:
            return imp.load_compiled(mname, uri)
        except:
            pass
    if os.path.exists(os.path.join(path,mname)+'.py'):
        try:
            return imp.load_source(mname, uri)
        except:
            pass

    return mod

0voto

PanwarS87 Punkte 311

Das folgende Stück hat bei mir funktioniert:

>>>import imp; 
>>>fp, pathname, description = imp.find_module("/home/test_module"); 
>>>test_module = imp.load_module("test_module", fp, pathname, description);
>>>print test_module.print_hello();

wenn Sie in ein Shell-Skript importieren wollen:

python -c '<above entire code in one line>'

-9voto

Marc de Lignie Punkte 27

Bei mir hat das Folgende funktioniert:

import sys, glob
sys.path.append('/home/marc/python/importtest/modus')
fl = glob.glob('modus/*.py')
modulist = []
adapters=[]
for i in range(len(fl)):
    fl[i] = fl[i].split('/')[1]
    fl[i] = fl[i][0:(len(fl[i])-3)]
    modulist.append(getattr(__import__(fl[i]),fl[i]))
    adapters.append(modulist[i]())

Es lädt Module aus dem Ordner 'modus'. Die Module haben eine einzelne Klasse mit demselben Namen wie der Modulname. Die Datei modus/modu1.py enthält z.B.:

class modu1():
    def __init__(self):
        self.x=1
        print self.x

Das Ergebnis ist eine Liste von dynamisch geladenen Klassen "Adaptern".

15 Stimmen

Bitte vergraben Sie keine Antworten ohne Begründung in den Kommentaren. Damit ist niemandem geholfen.

0 Stimmen

Ich würde gerne wissen, warum das schlecht ist.

0 Stimmen

So wie ich, da ich neu in Python bin und wissen möchte, warum das schlecht ist.

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