引数としてコマンドを受け取るPythonアプリケーションを書いています。例えば:
$ python myapp.py command1
私はアプリケーションが拡張可能であること、すなわち、メインアプリケーションのソースを変更することなく新しいコマンドを実装する新しいモジュールを追加できることを望みます。木は次のようになります。
myapp/
__init__.py
commands/
__init__.py
command1.py
command2.py
foo.py
bar.py
だから私はアプリケーションが実行時に利用可能なコマンドモジュールを見つけて、適切なものを実行することを望みます。
Pythonは_ IMPORT _関数を定義しています。これはモジュール名に文字列を取ります。
__import __(name、globals = None、locals = None、fromlist =()、level = 0)
この関数はモジュール名をインポートします。パッケージコンテキストで名前を解釈する方法を決定するために、指定されたグローバルとローカルを使用する可能性があります。 fromlistはnameで与えられたモジュールからインポートされるべきオブジェクトあるいはサブモジュールの名前を与えます。
だから今私は何かがあります:
command = sys.argv[1]
try:
command_module = __import__("myapp.commands.%s" % command, fromlist=["myapp.commands"])
except ImportError:
# Display error message
command_module.run()
これは問題なく動作します。このコードを使って行っていることを達成するためのもっと慣用的な方法があるかどうか、私は思っています。
卵やエクステンションポイントを使用したくないということには特に注意してください。これはオープンソースプロジェクトではないので、「プラグイン」があるとは思っていません。重要なのは、メインのアプリケーションコードを単純化し、新しいコマンドモジュールが追加されるたびにそれを変更する必要をなくすことです。
2.7/3.1より古いPythonでは、それはほとんどあなたがそうする方法です。
新しいバージョンについては、 Python 2 および Python 3 のimportlib.import_module
を参照してください。
必要に応じてexec
を使うこともできます。
あるいは__import__
を使ってこれを行うことでモジュールのリストをインポートすることができます:
>>> moduleNames = ['sys', 'os', 're', 'unittest']
>>> moduleNames
['sys', 'os', 're', 'unittest']
>>> modules = map(__import__, moduleNames)
Dive Into Python から直接リッピングしました。
Python 2.7および3.1以降で推奨される方法は、 importlib
モジュールを使用することです。
importlib.import_module(name、package = None)
モジュールをインポートします。 name引数は、どのモジュールを絶対的または相対的な用語でインポートするかを指定します(例:pkg.modまたは..mod)。名前が相対的な用語で指定されている場合、package引数はパッケージ名を解決するためのアンカーとして機能するパッケージの名前に設定する必要があります(例:import_module( '.. mod'、 'pkg.subpkg') pkg.modをインポートします。
例えば.
my_module = importlib.import_module('os.path')
注:impは、Python 3.4以降、importlibに置き換えられて非推奨になりました。
既に述べたように、 imp モジュールはロード関数を提供します。
imp.load_source(name, path)
imp.load_compiled(name, path)
私は以前にこれらを使って似たようなことをした。
私の場合は、必要な定義済みメソッドで特定のクラスを定義しました。モジュールをロードしたら、クラスがモジュール内にあるかどうかを確認してから、そのクラスのインスタンスを作成します。
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
impモジュール 、またはもっと直接的な __import__()
関数を使用してください。
あなたがあなたの地元の人々にそれを望むならば:
>>> 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)]'
globals()
でも同じことが言えます
あなたは exec
を使うことができます:
exec "import myapp.commands.%s" % command
今日では importlib を使うべきです。
のドキュメント は実際にそのレシピを提供しています、そしてそれは次のようになります:
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)
現在のディレクトリにパッケージをインポートする(例:、pluginX/__init__.py
)は、実際には簡単です。
import importlib
pluginX = importlib.import_module('pluginX')
# check if it's all there..
def bla(mod):
print(dir(mod))
bla(module)
@monkutのソリューションと似ていますが、ここで説明されている再利用可能でエラー耐性があります 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
例:私のモジュール名はjan_module/feb_module/mar_moduleのようになります。
月= 'feb'
exec 'from%s_module import *'%(月)
それはあなたが本当に欲しいものがプラグインアーキテクチャであるように聞こえます。
Setuptoolsパッケージが提供する エントリポイント の機能を見てください。それはあなたのアプリケーションのためにロードされているプラグインを発見するための素晴らしい方法を提供します。
下の部分は私のために働きました:
>>>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();
あなたがシェルスクリプトでインポートしたい場合:
python -c '<above entire code in one line>'