モジュールのインポート方法いろいろ¶
モジュールのインポートには import 文を使いますが、 実行環境によってインポート可能なモジュールが異なる場合もあります。 この問題に対処するために、 try-catch を使って ImportError を使うこともあります。 この章では、インポートのルールやバージョン間の違いについての理解を進めましょう。
標準モジュールのインポート¶
標準モジュールのインポートはこれまでも見てきた通りです。 インタープリタを起動してから即座にインポートできます。
>>> import unittest
>>> type(unittest)
<class 'module'>
type() は組み込み関数で、引数の「型」を返します。 この場合はモジュールであることが分かります。
インポートしたモジュールオブジェクトからは、 dir() 関数でメソッド一覧を取得できます。
>>> availables = dir(unittest)
>>> len(availables)
42
>>> availables[:3]
['BaseTestSuite', 'FunctionTestCase', 'SkipTest']
>>> import math
>>> for i in range(math.ceil(len(availables) / 4)):
... print(' '.join(availables[i*4:i*4+4]))
...
BaseTestSuite FunctionTestCase SkipTest TestCase
TestLoader TestProgram TestResult TestSuite
TextTestResult TextTestRunner _TextTestResult __all__
__builtins__ __cached__ __doc__ __file__
__initializing__ __loader__ __name__ __package__
__path__ __unittest case defaultTestLoader
expectedFailure findTestCases getTestCaseNames installHandler
loader main makeSuite registerResult
removeHandler removeResult result runner
signals skip skipIf skipUnless
suite util
サードパーティモジュールのインポート¶
easy_install あるいは pip でインストールしたモジュールも同様に読み込めます。 API ドキュメントがソースに記述されている場合には、 help() 関数で表示できます。
>>> import flake8
>>> help(flake8)
(スクリーンビューアーに切り替わる)
モジュールを別名でインポートしたい場合には as を使います。
>>> import json as simplejson
>>> type(simplejson)
<class 'module'>
>>> type(json)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'json' is not defined
こうすることで、 simplejson モジュールに依存して書かれたスクリプトを移行できます。
自作ライブラリのインポート¶
自分で記述したスクリプトをインポートして再利用できます。 パターンとしては四つあり、普通は三つ目か四つ目を使います。
- 実行時のディレクトリにスクリプトを置く
- 環境変数 PYTHONPATH を通す
- ディレクトリを用意して、そこに階層化して配置する
- setup.py を使ってインストールする
実行時のディレクトリにスクリプトを置くだけ¶
cmdline.py というファイルに共通処理を記述し、 mylib-sample.py というスクリプトから使う場合は次のようになります。
cmdline.py
# -*- coding: utf-8 -*-
"""Simple Command Line Utility.
"""
import argparse
import logging
def parse_args():
"""Parse arguments and set up logging verbosity.
:rtype: parsed arguments as Namespace object.
"""
parser = argparse.ArgumentParser()
parser.add_argument("-f", "--file", dest="filename",
help="setting file", metavar="FILE")
parser.add_argument("-o", "--output", dest="output",
help="output file", metavar="FILE")
parser.add_argument("-n", "--dryrun", dest="dryrun",
help="dry run", default=False, action="store_true")
parser.add_argument("-v", "--verbose", dest="verbose", default=False,
action="store_true", help="verbose mode")
parser.add_argument("-q", "--quiet", dest="quiet", default=False,
action="store_true", help="quiet mode")
# Add this line from boilerplate.
parser.add_argument("--header", dest="header", default=False,
action="store_true", help="contains header row")
parser.add_argument("--encoding", dest="encoding", default='utf-8',
help="encoding of input file")
parser.add_argument("filename", nargs=1, help="input file path")
args = parser.parse_args()
if args.verbose:
logging.basicConfig(level=logging.DEBUG)
elif not args.quiet:
logging.basicConfig(level=logging.INFO)
return args
# vim: set et ts=4 sw=4 cindent fileencoding=utf-8 :
mylib-sample.py
import cmdline
print("\n----- cmdline module -----\n")
print('* ' + '\n* '.join(dir(cmdline)))
print("\n----- parse_args() document -----\n")
print(cmdline.parse_args.__doc__)
実行結果
$ python mylib-sample.py
----- cmdline module -----
* __builtins__
* __cached__
* __doc__
* __file__
* __initializing__
* __loader__
* __name__
* __package__
* argparse
* logging
* parse_args
----- parse_args() document -----
Parse arguments and set up logging verbosity.
:rtype: parsed arguments as Namespace object.
ディレクトリを用意して、そこに階層化して配置する¶
上記の方法だと、標準モジュールやその他のモジュールと名前がバッティングしてしまう可能性があります。 また、機能の単位やモジュール同士の依存関係などが全く分かりません。
そこで、通常は階層構造を持たせて管理することになります。 モジュールの階層は基本的にはディレクトリの階層に一致しますが、 それぞれのディレクトリには __init__.py という特殊ファイルが配置されていなければなりません。 __init__.py は空のファイルでも構いませんし、そこに Python スクリプトを記述しても構いません。
前述の cmdline.py を pyschool というディレクトリに置き、 pyschool ディレクトリに空の __init__.py ファイルを置きます。
$ tree pyschool/
pyschool/
├── __init__.py
└── cmdline.py
モジュールの情報を確認してみます。
>>> import pyschool.cmdline
>>> help(pyschool.cmdline)
階層が深くなっていくと、いつもパッケージ名を付けるのは冗長です。 そこで、 from を使ってインポートすることもあります。 たとえば、 pyschool.cmdline.parse_args() の場合は次のようにします。
>>> from pyschool.cmdline import parse_args
>>> print parse_args.__module__, parse_args.__name__, parse_args.func_name
pyschool.cmdline parse_args parse_args
あるパッケージからすべての機能をインポートすることも可能です。 この場合、”*” を使います。 しかし、「すべて」が何を意味するかは __init__.py の __all__ 変数によって変わってきます。 詳しくは公式チュートリアルを参照してください。
- 6.4. Packages (The Python Tutorial)
setup.py を使ってインストールする¶
setup.py を使うと、手元の環境にインストールして使えるようになります。 インストールしておくと、環境変数を設定しなくても、どのディレクトリでも使えますから便利です。
詳しくはこちらのガイドを読んでください。
- Python のパッケージ化に関するガイド - IBM developerWorks
Python のバージョン違いを考慮したインポート¶
urlparse モジュールに parse_qs() という関数が定義されています。 URL のクエリストリング (”?” の後ろの部分) を解析してくれる関数です。
Python 2.7 のドキュメント には次のように記述されています。
New in version 2.6: Copied from the cgi module.
[日本語だと] バージョン 2.6 で新しく追加されました。cgi モジュールからコピーしています。
cgi モジュールの parse_qs() のドキュメントには次のように記述されています。
This function is deprecated in this module.
Use urlparse.parse_qs() instead. It is maintained here only for backward compatiblity.
[日本語だと] このモジュールにおけるこの関数は非推奨です。
urlparse.parse_qs() を代わりに使ってください。
ここでの記述は後方互換性のためだけに残されています。
ということで、Python 3.x 系、Python 2.7 それから Python 2.4 で動作するスクリプトを記述するためには、 ちょっとした気遣いが必要になります。
モジュールをインポートできない場合、 ImportError という例外が発生します。 そこで、この例外を捕まえてあげることで、利用可能なモジュールを選択的に読み込むことが可能です。 スクリプトにすると次のようになります。
try:
try:
from urllib.parse import parse_qs # Python 3.x
except ImportError:
from urlparse import parse_qs # Python 2.6 or higher
except ImportError:
from cgi import parse_qs # Legacy Python
ImportError によって利用するモジュールを切り替える書き方は、ライブラリのコードには散見されます。 基本的には six を使ってカプセル化するべきですが、たまに必要かもしれませんので覚えておくと良いでしょう。 例としては、実行時に最適な JSON ライブラリを選択してくれる anyjson のソースコードがあります。
- anyjson 0.3.3 - pypi.python.org
- anyjson/__init__.py - bitbucket.org