iMind Developers Blog

iMind開発者ブログ

Pythonのモジュール、関数、クラスを文字列から取得する

概要

Pythonでモジュール、関数、クラスを名称の文字列から取得する。

バージョン情報

  • Python 3.7.4

事前準備

下記のようなコードを書いて foo.py というファイル名で保存する。

class Foo:
    def hello(self):
        print('hello')

サンプルコードではこのモジュールを文字列でimportする。

evalとexecですべては解決する

書き捨てるだけのコードの時は面倒なのでevalとexecで済ませる。

# モジュールのimport
exec('import foo')
foo.Foo().hello()
    #=> hello

# クラスの初期化
foo = eval('foo.Foo()')
foo.hello()
    #=> hello

# メソッドの呼び出し
eval('foo.hello()')
    #=> hello

importlib.import_module

すぐには捨てないコードではお行儀的にはexecよりはマシなimportlibを使ってモジュールやクラスをimportする。

import importlib

# fooモジュールをimport
foo = importlib.import_module('foo')
foo.Foo().hello()
    #=> hello

余談

importlibにあるreloadを使うと一度importしたコードを再度読み直せる。

# fooの読み直し
importlib.reload(foo)

Jupyter等でimport先のコードを修正した時に、autoreloadは使わずにこっちで明示的にやりたいケースがたまにあるようなないような。

getattrでクラスやメソッドの取得

getattrでモジュールからクラスを取得したりメソッドを取得できる。

import importlib

foo = importlib.import_module('foo')

# モジュールから文字列でクラスを取得
Foo = getattr(foo, 'Foo')
Foo().hello()
    #=> hello

# クラスからメソッドを呼び出す
foo_inst = Foo()
hello = getattr(foo_inst, 'hello')
hello()
    #=> hello

モジュールの存在チェック

importlib.util.find_specでモジュールが存在をチェックできる。

import importlib

# fooモジュールが存在するかチェック
if importlib.util.find_spec('foo'):
    foo = importlib.import_module('foo')
else:
    # do something

try importするより良さげ。

読み込み可能なモジュールの一覧を取得する

pkgutilを使うと読み込み可能なモジュールを一覧で取得できる。

import pkgutil

for info in pkgutil.iter_modules():
    print(info)

指定パッケージ配下のモジュールのみ一覧で出すことも可能。

試しにbarパッケージを下記のように作成する。

$ mkdir bar
$ touch bar/__init__.py
$ echo 'hello = lambda : print("hello bar1")' > bar/bar1.py
$ echo 'hello = lambda : print("hello bar2")' > bar/bar2.py

# パス構成確認
$ tree .
.
├── bar
│   ├── __init__.py
│   ├── bar1.py
│   └── bar2.py
└── foo.py

barパッケージのモジュールを表示する。

for x in pkgutil.iter_modules(['bar']): 
    print(x)

    #=> ModuleInfo(module_finder=FileFinder('bar'), name='bar1', ispkg=False)
    #=> ModuleInfo(module_finder=FileFinder('bar'), name='bar2', ispkg=False)

これで文字列で指定されたパッケージ配下のモジュールをまとめてimportできる。

package_name = 'bar'
bar = dict([ (info.name, importlib.import_module('.' + info.name, package=package_name))
    for info in pkgutil.iter_modules([package_name]) ])

bar['bar1'].hello()
    #=> hello bar1

こんなことをするシチュエーションがあるのかどうかは置いておいて。

変数がモジュールなのかクラスなのか確認する

getattrで操作していると取得したものがモジュールなのかクラスなのか関数なのか不明瞭になるので、insepectを使って確認する。

import importlib
import inspect

# モジュールチェック
foo = importlib.import_module('foo')
inspect.ismodule(foo)
    #=> True
inspect.isclass(foo)
    #=> False

# クラスチェック
Foo = getattr(foo, 'Foo')
inspect.isclass(Foo)
    #=> True

# メソッドチェック
foo_inst = Foo()
inspect.ismethod(getattr(foo_inst, 'hello'))
    #=> True
inspect.isfunction(getattr(foo_inst, 'hello'))
    #=> False

# ファンクションチェック
def hoge():
    pass
inspect.isfunction(hoge)
    #=> True
inspect.ismethod(hoge)
    #=> False

他にもisgeneratorとかisbuiltinとかいろいろなタイプがチェックできる。

https://docs.python.org/3/library/inspect.html

Pythonで「どれが関数でどれがメソッドかわかりません」といった初期にありがちな質問に対する回答用としても使える。

改定履歴

Author: Masato Watanabe, Date: 2020-05-05, 記事投稿