Pythonクラスを動的にロードする方法


167

たとえばPythonクラスの文字列が与えられた場合、my_package.my_module.MyClassそれをロードする最良の方法は何ですか?

言い換えれば、私はClass.forName()JavaでPythonの関数で同等のものを探しています。Google App Engineで動作する必要があります。

これは、クラスのFQNを文字列として受け入れ、クラスへの参照を返す関数であることが望ましいです。

my_class = load_class('my_package.my_module.MyClass')
my_instance = my_class()

クラス参照を変数にも割り当てることができるようにする必要があります。
pjesi

1
これは次の複製のようです:stackoverflow.com/questions/452969/…–
cdleary

あなたは正しい、それは複製です、それを見つけてくれてありがとう
pjesi

1
Pythonでこのようなクラスをロードする必要はありませんでした。あなたはモジュールがどこにあるか知っているので、なぜモジュールをロードしてPythonのようにそのクラスを使用するのではなく、はるかに単純です
Adam Spence

22
これを行う必要がない場合は、まだ十分に興味深いプログラムを作成していません。練習してください。
John Tyree、2014年

回答:


194

pythonのドキュメントから、必要な関数は次のとおりです。

def my_import(name):
    components = name.split('.')
    mod = __import__(components[0])
    for comp in components[1:]:
        mod = getattr(mod, comp)
    return mod

シンプル__import__が機能しない理由は、パッケージ文字列の最初のドットを超えて何かをインポートすると、インポートするモジュールの属性になるためです。したがって、次のようなものは機能しません。

__import__('foo.bar.baz.qux')

上記の関数を次のように呼び出す必要があります。

my_import('foo.bar.baz.qux')

またはあなたの例の場合:

klass = my_import('my_package.my_module.my_class')
some_object = klass()

編集:私はこれで少しずれていました。あなたが基本的にやりたいことはこれです:

from my_package.my_module import my_class

上記の関数は、空の fromlist がある場合にのみ必要です。したがって、適切な呼び出しは次のようになります。

mod = __import__('my_package.my_module', fromlist=['my_class'])
klass = getattr(mod, 'my_class')

my_import( 'my_package.my_module.my_class')を試しましたが、my_classが見つかりません。これは、モジュールではなくクラスであるため、理にかなっています。my_importの呼び出し後にgettattrを使用してクラスを取得できるかどうかはどうでしょう
pjesi

それは変です。最初のドットを過ぎたものはすべてgetattrを使用して呼び出されます。違いはありません。
Jason Baker、

これが最善の方法だと思います。今、私は文字列「my_pakcage.my_module.my_class」をmod_name、klass_nameに分割するための最良の方法だけが必要ですが、私はそれを理解できると思います:)
pjesi

2
importの pythonドキュメント(コード上)はimportlibを使用するように言っています。
それでは

1
リンク@naokoは次を参照しています:docs.python.org/3/library/importlib.html#importlib.__import__
Noel Evans

125

自分でロールしたくない場合は、pydocこれを正確に実行するモジュールで使用可能な関数があります。

from pydoc import locate
my_class = locate('my_package.my_module.MyClass')

ここにリストされている他の方法に対するこのアプローチの利点は、モジュール内の直接のオブジェクトだけでなく、提供された点線のパスで任意の pythonオブジェクトlocateを見つけることです。例えばmy_package.my_module.MyClass.attr

あなたが彼らのレシピが何であるか知りたいなら、ここに関数があります:

def locate(path, forceload=0):
    """Locate an object by name or dotted path, importing as necessary."""
    parts = [part for part in split(path, '.') if part]
    module, n = None, 0
    while n < len(parts):
        nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
        if nextmodule: module, n = nextmodule, n + 1
        else: break
    if module:
        object = module
    else:
        object = __builtin__
    for part in parts[n:]:
        try:
            object = getattr(object, part)
        except AttributeError:
            return None
    return object

それはpydoc.safeimport機能に依存しています。そのためのドキュメントは次のとおりです。

"""Import a module; handle errors; return None if the module isn't found.

If the module *is* found but an exception occurs, it's wrapped in an
ErrorDuringImport exception and reraised.  Unlike __import__, if a
package path is specified, the module at the end of the path is returned,
not the package at the beginning.  If the optional 'forceload' argument
is 1, we reload the module from disk (unless it's a dynamic extension)."""

1
私はこの回答に賛成しました。ところで、これだけでpydocをインポートするのは奇妙に思われるため、safeimportもあるコードを次に示します。github.com
python

私もこの回答に賛成しました。これはすべての関連する回答の中で最高です。
Sunding Wei

これはqualname(モジュール名前空間の最上位にないオブジェクト)も正しく処理できるようです。
MisterMiyagi

はい、できますlocate('my_package.my_module.MyClass.attr.method.etc')
chadrik '23年

109
import importlib

module = importlib.import_module('my_package.my_module')
my_class = getattr(module, 'MyClass')
my_instance = my_class()

9
モジュールを動的にインポートすると、モジュールを介してクラスにアクセスできるようになります
Adam Spence

私の回答をより簡潔にするために編集しました。これは、Pythonでクラスをロードするための最良の方法です。
Adam Spence

BBB-ベニーとスペンス!;)
dKen 2017

すごい!2.7以降の標準ライブラリの一部でもあります。
Apteryx 2017

これは、モジュール/クラスにアクセスする正しい方法です。ドキュメントはこれをここに述べています:docs.python.org/3/library/importlib.html#importlib.__import__
Noel Evans

29
def import_class(cl):
    d = cl.rfind(".")
    classname = cl[d+1:len(cl)]
    m = __import__(cl[0:d], globals(), locals(), [classname])
    return getattr(m, classname)

5
これはクリーンなソリューションです!あなたは使用することを検討できます(modulename, classname) = cl.rsplit('.', 2)
vdboor

それは素晴らしいです)私はさまざまなユーティリティでputilsパッケージを作成し、そこにクラスをインポートしました。必要に応じて、そのパッケージから使用できます。
2012

4
@vdboor rsplit('.', 1)
CarlesBarrobés2013

私はなんとかグローバル/ローカルの代わりに{}を渡しましたが、それでもうまく
いき

17

Djangoを使用している場合は、これを使用できます。はい、私はOPがdjangoを要求しなかったことを知っていますが、Djangoソリューションを探してこの質問に遭遇しましたが、見つかりませんでした。それを見つけた次の男の子/ギャルのためにここに入力しました。

# It's available for v1.7+
# https://github.com/django/django/blob/stable/1.7.x/django/utils/module_loading.py
from django.utils.module_loading import import_string

Klass = import_string('path.to.module.Klass')
func = import_string('path.to.module.func')
var = import_string('path.to.module.var')

.ような、reまたはargparse使用していないものをインポートする場合は、次の点に注意してください。

re = __import__('re')

5

ここで私が見つけたものを共有し__import__importlib、この問題を解決しようとしているときに。

Python 3.7.3を使用しています。

dモジュールのクラスに到達しようとするとa.b.c

mod = __import__('a.b.c')

mod変数は、トップの名前空間を参照してくださいa

クラスに行くためにd、私はする必要があります

mod = getattr(mod, 'b') #mod is now module b
mod = getattr(mod, 'c') #mod is now module c
mod = getattr(mod, 'd') #mod is now class d

私たちがやろうとすると

mod = __import__('a.b.c')
d = getattr(mod, 'd')

私たちは実際に探していa.dます。

を使用する場合importlib、ライブラリが再帰的getattrに実行したと思います。したがって、を使用するとimportlib.import_module、実際には最も深いモジュールのハンドルが取得されます。

mod = importlib.import_module('a.b.c') #mod is module c
d = getattr(mod, 'd') #this is a.b.c.d

1

OK、私にとってはそれが機能した方法です(私はPython 2.7を使用しています):

a = __import__('file_to_import', globals(), locals(), ['*'], -1)
b = a.MyClass()

次に、bはクラス「MyClass」のインスタンスです。


0

目的のクラスのインスタンスがすでにある場合は、「type」関数を使用してそのクラスタイプを抽出し、これを使用して新しいインスタンスを作成できます。

class Something(object):
    def __init__(self, name):
        self.name = name
    def display(self):
        print(self.name)

one = Something("one")
one.display()
cls = type(one)
two = cls("two")
two.display()


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.