名前を文字列として指定してモジュールをインポートする方法は?


543

たとえば、コマンドとして引数を取る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は、名前で指定されたモジュールからインポートする必要があるオブジェクトまたはサブモジュールの名前を示します。

ソース:https : //docs.python.org/3/library/functions.html# import

だから現在私は次のようなものがあります:

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

command_module.run()

これは問題なく機能します。このコードで実行していることを達成するためのより慣用的な方法があるのではないかと思っています。

特に卵や拡張ポイントを使いたくありません。これはオープンソースプロジェクトではないため、「プラグイン」が存在するとは思わない。重要なのは、メインアプリケーションコードを簡略化し、新しいコマンドモジュールが追加されるたびにコードを変更する必要をなくすことです。


fromlist = ["myapp.commands"]は何をしますか?
PieterMüller12年

@PieterMüller:Pythonシェルで次のように入力します dir(__import__)。fromlistは、「from name import ...」をエミュレートする名前のリストである必要があります。
mawimawi 2012


2019年現在、次を探す必要があります。stackoverflow.comimportlib / a
Brandt

__import__参照を使用しないでくださいPythonのドキュメントが 使用importlib.import_module
FCM

回答:


321

Pythonが2.7 / 3.1よりも古い場合は、それがほとんどの方法です。

新しいバージョンについてはimportlib.import_modulePython 2およびPython 3を参照してください。

exec必要に応じて使用することもできます。

または、__import__次のようにして、モジュールのリストをインポートできます。

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

Dive Into Pythonから直接リッピングしました。


7
エグゼクティブとの違いは何ですか?
user1767754

1
__init__そのモジュールをどのように使用できますか?
Dorian Dore 2015年

7
OPのこのソリューションの1つの問題は、1つまたは2つの不良「コマンド」モジュールの例外をトラップすると、1つの例外でコマンドアプリ全体が失敗することです。個人的には、個々のtryにラップされたインポートごとにforループを作成します。mods= __ import __()\ nexcept ImportError as error:report(error)to allow other commands引き続き作業を続行しながら、悪いものを修正します。
DevPlayer 2015

7
このソリューションのもう1つの問題は、Denis Malinovskyが以下に指摘するように、Pythonドキュメント自体がを使用しないことを推奨していること__import__です。2.7のドキュメント:「この関数はPythonインタプリタで使用するためのものであり、一般的な使用のためではないため、importlib.import_module()...を使用することをお勧めします。」python3を使用する場合、impモジュールはこの問題を解決します。
LiavK

2
@JohnWu foreach持っfor element inているmap()のになぜ必要なのか:)
カウベルト

300

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')

2
どの情報源または当局から推奨されていますか?
michuelnik 2015

66
ドキュメントでは、上記のモジュールを優先__import__関数を使用しないようにアドバイスています。
Denis Malinovsky、2015

8
これはインポートに適していos.pathます。どうfrom os.path import *ですか?
Nam G VU

5
ここで答えを見つけます。stackoverflow.com/ a / 44492879/248616 ie。呼び出しglobals().update(my_module.__dict)
Nam G VU 2017年

2
@NamGVU、これはグローバルを汚染し、同じ名前の識別子をオーバーライドできるため、これは危険な方法です。あなたが投稿したリンクには、このコードの改善されたバージョンがあります。
Denis Malinovsky 2017年

132

注: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

2
良いシンプルなソリューション。私は同様のものを書きました:stamat.wordpress.com/dynamic-module-import-in-pythonしかし、あなたにはいくつかの欠陥があります:例外はどうですか?IOErrorとImportError?最初にコンパイル済みのバージョンをチェックしてから、ソースバージョンをチェックしませんか。クラスを期待すると、ケースの再利用性が低下します。
stamat 2013年

3
ターゲットモジュールでMyClassを作成する行で、クラス名に冗長な参照を追加しています。すでに保存されているexpected_classため、class_inst = getattr(py_mod,expected_name)()代わりに実行できます。
Andrew

1
適切に一致するバイトコンパイルされたファイル(拡張子が.pycまたは.pyo)が存在する場合は、指定されたソースファイルを解析する代わりに使用されますhttps://docs.python.org/2/library/imp.html#imp.load_source
cdosborn 2015年

1
ヘッドアップ:このソリューションは機能します。しかし、IMPモジュールは、IMPのページを参照して、インポートLIBのために廃止されようとしている:「」「推奨されていませんバージョン3.4以降:IMPパッケージはのimportlibの賛成で廃止を保留している。」「」
リカルド


15

最近は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)

この方法は、あなたが(例えば、機能"のメンバーにアクセスすることができhello、あなたのモジュールから「)pluginX.py-と呼ばれているこのスニペットではmodule-その名前空間の下に。例えば、module.hello()

メンバー(たとえば、 " hello")をインポートする場合は、モジュールのメモリ内リストにmodule/ pluginXを含めることができます。

sys.modules[module_name] = module

from pluginX import hello
hello()

パッケージをインポートする

(パッケージのインポートなどpluginX/__init__.pyあなたの現在のディレクトリの下)は、実際に簡単です:

import importlib

pkg = importlib.import_module('pluginX')

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

3
後でsys.modules[module_name] = moduleできるようにしたい場合は、コードの最後に追加してくださいimport module_name
Daniel Braun

@DanielBraunがエラーになる> ModuleNotFoundError
Nam G VU

10

あなたが地元の人にそれを望むなら:

>>> 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()


2
組み込み関数のドキュメントではlocals()、「このディクショナリの内容を変更しないでください」と明示的に警告しています。
マルティノー

9

使用できますexec

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

(この例では).run()メソッドを呼び出せるように、execを介してモジュールのハンドルを取得するにはどうすればよいですか?
Kamil Kisiel 2008年

5
次に、getattr(myapp.commands、command)を実行してモジュールにアクセスできます。
Greg Hewgill、

1
...またはas command_moduleimportステートメントの最後に追加してから行うcommand_module.run()
oxfn

一部のバージョンのPython 3+では、execが関数に変換され、ソースで作成された参照がexec()のlocals()引数に格納されるという利点が追加されました。ローカルコードブ​​ロックlocalsから参照されるexecをさらに分離するには、空のような独自のdictを提供し、そのdictを使用して参照を参照するか、そのdictを他の関数に渡すexec(source、gobals()、command1_dict) .... print(command1_dict ['somevarible'])
DevPlayer

2

@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

0

以下の作品は私のために働きました:

>>>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>'

-2

たとえば、私のモジュール名はjan_module/ feb_module/のようになりmar_moduleます。

month = 'feb'
exec 'from %s_module import *'%(month)

-7

以下は私のために働きました:

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]())

「modus」フォルダからモジュールをロードします。モジュールには、モジュール名と同じ名前の単一のクラスがあります。たとえば、ファイルmodus / modu1.pyには以下が含まれます:

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

結果は、動的にロードされるクラス「アダプター」のリストです。


13
コメントで理由を提供せずに答えを埋めないでください。それは誰の助けにもなりません。
Rebs、2014年

なぜこれが悪いのか知りたいのですが。
DevPlayer 2016

私と同じですが、私はPythonを初めて使用して知りたいので、なぜこれが悪いのでしょうか。
Kosmos、2016年

5
これは悪いpythonであるだけでなく、一般に悪いコードでもあるので悪いです。なにfl?forループの定義は非常に複雑です。パスはハードコーディングされています(例としては関係ありません)。落胆したpython(__import__)を使用していますが、それは何のfl[i]ためのものですか?これは基本的に読み取り不可能であり、それほど難しいことではないために不必要に複雑です-ワンライナーでトップ投票の回答を参照してください。
Phil
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.