PythonにはJava Class.forName()と同等のものがありますか?


95

私は文字列引数を取り、その文字列で指定されたクラスのオブジェクトをPythonで作成する必要があります。Javaではを使用しますClass.forName().newInstance()。Pythonに同等のものはありますか?


回答ありがとうございます。私が何をしているのか知りたい人に答えるために:コマンドライン引数をクラス名として使用してインスタンス化したいと思います。私は実際にはJythonでプログラミングしてJavaクラスをインスタンス化しているので、問題のJava性です。 getattr()よく働く。どうもありがとう。


参照stackoverflow.com/a/10675081/116891の使用例についてはimportlib.import2.7(へのpython 3からバックポートし、docs.python.org/2/library/importlib.html
パット

回答:


169

Pythonでのリフレクションは、Javaでのリフレクションよりもはるかに簡単で、はるかに柔軟です。

このチュートリアルを読むことをお勧めします

完全修飾クラス名を取得してクラスを返す直接関数(私が知っている)はありませんが、それを構築するために必要なすべての部分があり、それらを接続することができます。

ただし、少しアドバイスがあります。Pythonを使用しているときは、Javaスタイルでプログラミングしないでください。

あなたが何をしようとしているのかを説明できれば、多分私たちはあなたがそれを行うよりパイソン的な方法を見つけるのを助けることができます。

ここにあなたが望むことをする関数があります:

def get_class( kls ):
    parts = kls.split('.')
    module = ".".join(parts[:-1])
    m = __import__( module )
    for comp in parts[1:]:
        m = getattr(m, comp)            
    return m

この関数の戻り値は、クラス自体であるかのように使用できます。

次に使用例を示します。

>>> D = get_class("datetime.datetime")
>>> D
<type 'datetime.datetime'>
>>> D.now()
datetime.datetime(2009, 1, 17, 2, 15, 58, 883000)
>>> a = D( 2010, 4, 22 )
>>> a
datetime.datetime(2010, 4, 22, 0, 0)
>>> 

それはどのように機能しますか?

を使用__import__して、クラスを保持するモジュールをインポートしています。これには、完全修飾名からモジュール名を最初に抽出する必要があります。次に、モジュールをインポートします。

m = __import__( module )

この場合、m最上位モジュールのみを参照し、

たとえば、クラスがfoo.bazモジュール内にある場合、モジュールにmなります。使用foo
に関するリファレンスを簡単に取得できますfoo.bazgetattr( m, 'baz' )

最上位モジュールからクラスに移動するgettatrには、クラス名の部分を再帰的に使用する必要があります

たとえば、クラス名がの場合、次のようにしますfoo.baz.bar.Model

m = __import__( "foo.baz.bar" ) #m is package foo
m = getattr( m, "baz" ) #m is package baz
m = getattr( m, "bar" ) #m is module bar
m = getattr( m, "Model" ) #m is class Model

これはこのループで何が起こっているかです:

for comp in parts[1:]:
    m = getattr(m, comp)    

ループの最後にはm、クラスへの参照が含まれます。これは、m実際にはクラスitslefであることを意味します。たとえば、次のようにできます。

a = m() #instantiate a new instance of the class    
b = m( arg1, arg2 ) # pass arguments to the constructor

6
実行は非常に危険です。スコープ内の名前を動的に上書きすることで簡単に自分を撃ち落とすのは簡単で、ロードアイランドのサイズのセキュリティ欠陥を開くのと同じくらい簡単です。
Cody Brocious

1
ここでexecが安全でない理由は、「;」を使用できるためです。インポートから抜け出すためにクラス名で。これにより、任意のコードの実行が容易になります。コードに損傷を与える可能性があるのは、execが衝突する名前を上書きし、バグやセキュリティ上の欠陥を引き起こすためです。
Cody Brocious

8
うん、これは__import__存在するものです。module-loading-magicを実行するDjangoのようなプロジェクトは、常にそれを使用します。素敵な答え。
cdleary 2009年

5
get_class = lambda name: reduce(getattr, name.split('.')[1:], __import__(name.partition('.')[0]))。'object.from_name'の方がいい名前かもしれませんが。例:get_class( 'decimal.Decimal')、get_class(module_name)。
jfs 2009年

1
7行の関数を定義するだけで済みます。本当に簡単。
Pavel Vlasov、2012

27

クラスがスコープ内にあると想定します。

globals()['classname'](args, to, constructor)

さもないと:

getattr(someModule, 'classname')(args, to, constructor)

編集:注、getattrに「foo.bar」のような名前を付けることはできません。で分割する必要があります。そして、左から右に各ピースでgetattr()を呼び出します。これはそれを処理します:

module, rest = 'foo.bar.baz'.split('.', 1)
fooBar = reduce(lambda a, b: getattr(a, b), rest.split('.'), globals()[module])
someVar = fooBar(args, to, constructor)

それはglobals()['classname']であるべきではありませんか?
oriPを

あなたは確かに正しいです。globals()が実際のグローバルスコープを返さず、マッピングだけを忘れてしまいました。そのような編集-ありがとう!
Cody Brocious

これは、Javaで、何の仮定が輸入されているものについて行われていないと何ではありませんされているJavaのClass.forNameのと同等ではありません
HASEN

1
attrs = 'foo.bar.baz'.split('.'); fooBar = reduce(getattr, attrs[1:], __import__(attrs[0]))
jfs 2009年

1
またはmodule, _, attrs = 'foo.bar.baz'.partition('.'); fooBar = reduce(getattr, attrs, __import__(module))
jfs

15
def import_class_from_string(path):
    from importlib import import_module
    module_path, _, class_name = path.rpartition('.')
    mod = import_module(module_path)
    klass = getattr(mod, class_name)
    return klass

使用法

In [59]: raise import_class_from_string('google.appengine.runtime.apiproxy_errors.DeadlineExceededError')()
---------------------------------------------------------------------------
DeadlineExceededError                     Traceback (most recent call last)
<ipython-input-59-b4e59d809b2f> in <module>()
----> 1 raise import_class_from_string('google.appengine.runtime.apiproxy_errors.DeadlineExceededError')()

DeadlineExceededError: 

1
+1 importlibを使用してクラスを再帰的に検索する必要性(インポートが原因)を防ぐため、これを行うため。受け入れられた回答よりもシンプルで簡潔です(ただし、十分に文書化されていません)。
sage88

4

さらに別の実装。

def import_class(class_string):
    """Returns class object specified by a string.

    Args:
        class_string: The string representing a class.

    Raises:
        ValueError if module part of the class is not specified.
    """
    module_name, _, class_name = class_string.rpartition('.')
    if module_name == '':
        raise ValueError('Class name must contain module part.')
    return getattr(
        __import__(module_name, globals(), locals(), [class_name], -1),
        class_name)

if module_name:あなたは、単にそれらの行を削除した場合、あなたが得るエラーがあるので、LBYLは、かなりredudantですValueError: Empty module name
ブクソール2014

3

最初からではなく、真ん中からアプローチしているようです。あなたは本当に何をしようとしていますか?特定の文字列に関連付けられたクラスを見つけることは、目的を達成するための手段です。

自分のメンタルリファクタリングを必要とする可能性がある問題を明確にした場合、より良い解決策が提示される可能性があります。

たとえば、タイプ名とパラメータセットに基づいて保存されたオブジェクトをロードしようとしていますか?Pythonはこのunpicklingをスペルし、pickleモジュールを確認する必要があります。そして、unpickleプロセスはあなたが説明したとおりに実行しますが、内部でどのように機能するかについて心配する必要はありません。

>>> class A(object):
...   def __init__(self, v):
...     self.v = v
...   def __reduce__(self):
...     return (self.__class__, (self.v,))
>>> a = A("example")
>>> import pickle
>>> b = pickle.loads(pickle.dumps(a))
>>> a.v, b.v
('example', 'example')
>>> a is b
False

1
これは非常に興味深いですが、質問とは少し異なります
Nick

1

これは、Python標準ライブラリにunittest.TestLoader.loadTestsFromNameとして含まれています。残念ながら、このメソッドはさらにテスト関連のアクティビティを実行しますが、これは最初に再利用できるように見えます。テスト関連の機能を削除するためにそれを編集しました:

def get_object(name):
    """Retrieve a python object, given its dotted.name."""
    parts = name.split('.')
    parts_copy = parts[:]
    while parts_copy:
        try:
            module = __import__('.'.join(parts_copy))
            break
        except ImportError:
            del parts_copy[-1]
            if not parts_copy: raise
    parts = parts[1:]

    obj = module
    for part in parts:
        parent, obj = obj, getattr(obj, part)

    return obj

0

で既存のすべてのクラスのオブジェクトを取得する必要がありましたmy_package。必要なすべてのクラスをインポート私はそうmy_packageさん__init__.py

だから私のディレクトリ構造は次のようになります:

/my_package
    - __init__.py
    - module1.py
    - module2.py
    - module3.py

そして、私__init__.pyはこのように見えます:

from .module1 import ClassA
from .module2 import ClassB

次に、次のような関数を作成します。

def get_classes_from_module_name(module_name):
    return [_cls() for _, _cls in inspect.getmembers(__import__(module_name), inspect.isclass)]

どこ module_name = 'my_package'

ドキュメントを検査:https : //docs.python.org/3/library/inspect.html#inspect.getmembers

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