Piszę aplikację w języku Python, która przyjmuje jako polecenie argument, na przykład:
$ python myapp.py command1
Chcę, aby aplikacja była rozszerzalna, to znaczy mogła dodawać nowe moduły, które implementują nowe polecenia bez konieczności zmiany głównego źródła aplikacji. Drzewo wygląda mniej więcej tak:
myapp/
__init__.py
commands/
__init__.py
command1.py
command2.py
foo.py
bar.py
Chcę więc, aby aplikacja znalazła dostępne moduły poleceń w czasie wykonywania i uruchomiła odpowiedni.
Python definiuje funkcję __import__ , która pobiera ciąg nazwy modułu:
__import __ (name, globals = None, locals = None, fromlist = (), level = 0)
Funkcja importuje nazwę modułu, potencjalnie używając danych globalnych i lokalnych do określenia, jak interpretować nazwę w kontekście pakietu. Fromlist podaje nazwy obiektów lub podmodułów, które powinny zostać zaimportowane z modułu podanego przez name.
Źródło: https://docs.python.org/3/library/functions.html# import
Więc obecnie mam coś takiego:
command = sys.argv[1]
try:
command_module = __import__("myapp.commands.%s" % command, fromlist=["myapp.commands"])
except ImportError:
# Display error message
command_module.run()
Działa to dobrze, zastanawiam się tylko, czy jest jakiś bardziej idiomatyczny sposób na osiągnięcie tego, co robimy z tym kodem.
Zauważ, że szczególnie nie chcę wchodzić w użycie jaj lub punktów rozszerzenia. To nie jest projekt typu open source i nie oczekuję, że pojawią się „wtyczki”. Chodzi o to, aby uprościć główny kod aplikacji i wyeliminować potrzebę jego modyfikacji za każdym razem, gdy dodawany jest nowy moduł poleceń.
źródło
dir(__import__)
. Lista from powinna być listą nazw do emulacji „z importu nazw ...”.importlib
: stackoverflow.com/a/54956419/687896Odpowiedzi:
W przypadku języka Python starszego niż 2.7 / 3.1 tak to robisz.
Dla nowszych wersjach, patrz
importlib.import_module
na Pythonie 2 i i Pythona 3 .Możesz użyć,
exec
jeśli chcesz.Lub za pomocą
__import__
możesz zaimportować listę modułów, wykonując następujące czynności:Zgrywanie prosto z Dive Into Python .
źródło
__init__
z tego modułu?__import__
. Dokumentacja 2.7: „Ponieważ ta funkcja jest przeznaczona do użycia przez interpreter Pythona, a nie do ogólnego użytku, lepiej jest użyć importlib.import_module () ...” Podczas używania Python3imp
moduł rozwiązuje tę proble, jak wspomina poniżej monkut.foreach
kiedy maszfor element in
imap()
chociaż :)Zalecanym sposobem dla Python 2.7 i 3.1 i nowszych jest użycie
importlib
modułu:na przykład
źródło
__import__
funkcji na rzecz wyżej wymienionego modułu.os.path
; jak ofrom os.path import *
?globals().update(my_module.__dict)
Jak wspomniano, moduł imp zapewnia funkcje ładowania:
Użyłem ich wcześniej, aby wykonać coś podobnego.
W moim przypadku zdefiniowałem konkretną klasę za pomocą zdefiniowanych metod, które były wymagane. Po załadowaniu modułu sprawdziłbym, czy klasa była w module, a następnie utworzyłem instancję tej klasy, mniej więcej tak:
źródło
expected_class
więc możesz to zrobićclass_inst = getattr(py_mod,expected_name)()
zamiast tego.Użyj modułu imp lub bardziej bezpośredniej
__import__()
funkcji.źródło
W dzisiejszych czasach powinieneś używać importlib .
Zaimportuj plik źródłowy
W docs faktycznie stanowić receptę na to, i to idzie tak:
Zaimportuj pakiet
Importowanie pakiet ( np ,
pluginX/__init__.py
) w ramach bieżącego katalogu jest w rzeczywistości bardzo proste:źródło
sys.modules[module_name] = module
na końcu kodu, jeśli chcesz mócimport module_name
późniejJeśli chcesz to w lokalnych mieszkańców:
to samo działałoby
globals()
źródło
locals()
funkcji wyraźnie ostrzega, że „zawartość tego słownika nie powinna być modyfikowana”.Możesz użyć
exec
:źródło
as command_module
na końcu instrukcji importu, a następnie zróbcommand_module.run()
Podobne jak rozwiązanie @monkut, ale wielokrotnego użytku i odporne na błędy opisane tutaj http://stamat.wordpress.com/dynamic-module-import-in-python/ :
źródło
Poniższy kawałek działał dla mnie:
jeśli chcesz zaimportować w skrypcie powłoki:
źródło
Na przykład moje nazwy modułów są takie jak
jan_module
/feb_module
/mar_module
.źródło
Dla mnie działało:
Ładuje moduły z folderu „modus”. Moduły mają jedną klasę o tej samej nazwie co nazwa modułu. Np. Plik modus / moduł1.py zawiera:
Wynikiem jest lista dynamicznie ładowanych klas „adapterów”.
źródło
fl
? Definicja pętli for jest zbyt skomplikowana. Ścieżka jest zakodowana na stałe (i na przykład nieistotna). Używa zniechęconego Pythona (__import__
), po co to wszystkofl[i]
? Jest to w zasadzie nieczytelne i jest niepotrzebnie skomplikowane w przypadku czegoś, co nie jest wcale takie trudne - zobacz najlepiej głosowaną odpowiedź za pomocą jednej linijki.