Wie kann ich ein Modul importieren, das seinen Namen dynamisch als Zeichenfolge erhält?

Lesezeit: 8 Minuten

Benutzeravatar von Kamil Kisiel
Kamil Kissel

Ich schreibe eine Python-Anwendung, die einen Befehl als Argument verwendet, zum Beispiel:

$ python myapp.py command1

Ich möchte, dass die Anwendung erweiterbar ist, dh dass neue Module hinzugefügt werden können, die neue Befehle implementieren, ohne die Hauptanwendungsquelle ändern zu müssen. 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 ausführt.

Python definiert ein __import__() Funktion, die einen String als Modulnamen akzeptiert:

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

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

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

Also aktuell habe ich so etwas:

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

command_module.run()

Das funktioniert gut, ich frage mich nur, ob es möglicherweise einen idiomatischeren Weg gibt, um das zu erreichen, was wir mit diesem Code tun.

Beachten Sie, dass ich ausdrücklich nicht auf die Verwendung von Eiern oder Erweiterungspunkten eingehen möchte. Dies ist kein Open-Source-Projekt und ich erwarte nicht, dass es “Plugins” gibt. Der Punkt besteht darin, den Hauptanwendungscode zu vereinfachen und die Notwendigkeit zu beseitigen, ihn jedes Mal zu modifizieren, wenn ein neues Befehlsmodul hinzugefügt wird.


Siehe auch: Wie importiere ich ein Modul mit dem vollständigen Pfad?

  • Was bedeutet fromlist=[“myapp.commands”] tun?

    – Peter Müller

    6. Juni 2012 um 10:29 Uhr

  • @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.

    – Mawimawi

    9. August 2012 um 7:13 Uhr

  • Modul aus Stringvariable importieren

    – Sturm_m2138

    12. August 2015 um 18:28 Uhr

  • Ab 2019 sollten Sie suchen importlib: stackoverflow.com/a/54956419/687896

    – Brandt

    13. März 2019 um 9:42 Uhr

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

    – fcm

    28. Juni 2019 um 15:00 Uhr

Benutzeravatar von Harley Holcombe
Harley Holcombe

Mit Python, das älter als 2.7/3.1 ist, machen Sie es ungefähr so.

Für neuere Versionen siehe importlib.import_module zum Python 2 und Python 3.

Sie können verwenden exec wenn du das auch willst.

Oder verwenden __import__ Sie können eine Liste von Modulen importieren, indem Sie Folgendes tun:

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

Direkt abgerissen Tauchen Sie ein in Python.

  • was ist der unterschied zu exec?

    – Benutzer1767754

    17. September 2014 um 7:10 Uhr

  • Ein Problem bei dieser Lösung für das OP besteht darin, dass das Abfangen von Ausnahmen für ein oder zwei fehlerhafte „Befehls“-Module dazu führt, dass seine/ihre gesamte Befehls-App bei einer Ausnahme fehlschlägt. Persönlich würde ich für Schleife über jeden importieren einzeln verpackt in try: mods=__import__()\naußer ImportError als Fehler: report(error), damit andere Befehle weiter funktionieren, während die schlechten behoben werden.

    – DevPlayer

    8. April 2015 um 13:38 Uhr

  • Ein weiteres Problem bei dieser Lösung ist, wie Denis Malinovsky weiter unten hervorhebt, dass die Python-Dokumentation selbst davon abrät, sie zu verwenden __import__. Die 2.7-Dokumente: “Da diese Funktion für den Python-Interpreter und nicht für den allgemeinen Gebrauch gedacht ist, ist es besser, importlib.import_module() zu verwenden …” Bei Verwendung von python3, the imp Modul löst dieses Problem, wie Moncut weiter unten erwähnt.

    – LiavK

    8. Juli 2015 um 15:14 Uhr


  • @JohnWu warum brauchst du foreach wenn du. .. hast for element in und map() obwohl 🙂

    – Kuhbert

    19. Februar 2018 um 22:59 Uhr

  • Beachten Sie, dass die Karte in Python 3 NICHT funktioniert, da die Karte jetzt eine verzögerte Auswertung verwendet.

    – schw

    13. November 2018 um 21:52 Uhr

Benutzeravatar von Denis Malinovsky
Denis Malinowski

Das empfohlen Weg für Python 2.7 und 3.1 und höher zu verwenden importlib Modul:

importlib.import_module(name, package=None)

Importieren Sie ein Modul. Das Namensargument gibt an, welches Modul absolut oder relativ importiert werden soll (z. B. entweder pkg.mod oder ..mod). Wenn der Name relativ angegeben wird, muss das Paketargument auf den Namen des Pakets gesetzt werden, das als Anker für die Auflösung des Paketnamens dienen soll (z import_module('..mod', 'pkg.subpkg') wird importieren pkg.mod).

z.B

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

  • Von welcher Quelle oder Behörde empfohlen?

    – Michelnik

    8. April 2015 um 13:21 Uhr

  • Dokumentation rät gegen verwenden __import__ Funktion zugunsten des oben genannten Moduls.

    – Denis Malinowski

    9. April 2015 um 12:42 Uhr

  • Dies funktioniert gut für den Import os.path ; wie wäre es mit from os.path import *?

    – Name G VU

    12. Juni 2017 um 6:31 Uhr

  • Ich bekomme die Antwort hier stackoverflow.com/a/44492879/248616 dh. Berufung globals().update(my_module.__dict)

    – Name G VU

    12. Juni 2017 um 6:46 Uhr


  • @NamGVU, dies ist eine gefährliche Methode, da sie Ihre Globals verschmutzt und alle Bezeichner mit denselben Namen überschreiben kann. Der von Ihnen gepostete Link enthält eine bessere, verbesserte Version dieses Codes.

    – Denis Malinowski

    19. Juni 2017 um 17:11 Uhr


Benutzeravatar von Moncut
Mönch

Notiz: Kobold ist seit Python 3.4 zugunsten von veraltet importlib

Wie erwähnt die Kobold Modul bietet Ihnen Ladefunktionen:

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

Ich habe diese zuvor verwendet, um etwas Ähnliches durchzuführen.

In meinem Fall habe ich eine bestimmte Klasse mit definierten Methoden definiert, die erforderlich waren. Sobald ich das Modul geladen habe, würde ich prüfen, ob die Klasse im Modul enthalten ist, und dann eine Instanz dieser Klasse erstellen, 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

  • Gute und einfache Lösung. Ich habe ein ähnliches geschrieben: stamat.wordpress.com/dynamic-module-import-in-python Aber Ihres hat einige Mängel: Was ist mit Ausnahmen? IOError und ImportError? Warum nicht zuerst nach der kompilierten Version und dann nach der Quellversion suchen. Das Erwarten einer Klasse verringert die Wiederverwendbarkeit in Ihrem Fall.

    – stamat

    30. Juni 2013 um 20:40 Uhr

  • In der Zeile, in der Sie MyClass im Zielmodul konstruieren, fügen Sie einen redundanten Verweis auf den Klassennamen hinzu. Es ist bereits eingespeichert expected_class also könntest du machen class_inst = getattr(py_mod,expected_name)() stattdessen.

    – Andreas

    16. Oktober 2013 um 7:11 Uhr

  • Beachten Sie, dass, wenn eine richtig übereinstimmende Byte-kompilierte Datei (mit dem Suffix .pyc oder .pyo) vorhanden ist, diese verwendet wird, anstatt die angegebene Quelldatei zu analysieren. https://docs.python.org/2/library/imp.html#imp.load_source

    – cdosborn

    30. Juni 2015 um 21:07 Uhr


  • Achtung: Diese Lösung funktioniert; das imp-Modul wird jedoch zugunsten von importlib veraltet sein, siehe imps Seite:

    – nsx

    20. November 2016 um 9:50 Uhr

Benutzeravatar von Brandt
Brandt

Heutzutage sollte man verwenden importlib.

Importieren Sie eine Quelldatei

Das Dokumente tatsächlich ein Rezept dafür bereitstellen, und es 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 zugreifen (z. B. eine Funktion “hello“) aus Ihrem Modul pluginX.py — in diesem Snippet wird aufgerufen module — unter seinem Namensraum; Z.B, module.hello().

Wenn Sie die Mitglieder importieren möchten (z. B. “hello“) können Sie einfügen module/pluginX in der In-Memory-Liste der Module:

sys.modules[module_name] = module

from pluginX import hello
hello()

Importieren Sie ein Paket

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

import importlib

pkg = importlib.import_module('pluginX')

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

Verwenden Sie die Imp-Moduloder je direkter __import__() Funktion.

Benutzeravatar von Jonathan Livni
Jonathan Livni

Wenn Sie es in Ihren Lokalen 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 würde mit funktionieren globals()

Benutzeravatar von Georgy
Georgi

Sie können verwenden exec:

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

  • Wie erhalte ich über die exec einen Handle auf das Modul, damit ich (in diesem Beispiel) die Methode .run() aufrufen kann?

    – Kamil Kisiel

    19. November 2008 um 6:19 Uhr

  • Sie können dann getattr(myapp.commands, command) ausführen, um auf das Modul zuzugreifen.

    – Greg Hewgill

    19. November 2008 um 6:20 Uhr

  • … oder hinzufügen as command_module bis zum Ende der Import-Anweisung und dann tun command_module.run()

    – oxfn

    16. Oktober 2013 um 10:43 Uhr


  • In einigen Versionen von Python 3+ Exekutive wurde in eine Funktion mit dem zusätzlichen Vorteil umgewandelt, dass das in source erstellte Ergebnis, auf das verwiesen wird, im locals()-Argument von exec() gespeichert wird. Um die exec, auf die verwiesen wird, weiter von den lokalen Codeblock-Locals zu isolieren, können Sie Ihr eigenes Diktat bereitstellen, z. B. ein leeres, und die Referenzen mit diesem Diktat referenzieren oder dieses Diktat an andere Funktionen übergeben exec(source, gobals(), command1_dict) …. print(command1_dict[‘somevarible’])

    – DevPlayer

    8. April 2015 um 13:43 Uhr

1436550cookie-checkWie kann ich ein Modul importieren, das seinen Namen dynamisch als Zeichenfolge erhält?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy