Python School 2.0.0 documentation

モジュールのインポート方法いろいろ

«  デコレータ   ::   Contents   ::   Tips  »

モジュールのインポート方法いろいろ

モジュールのインポートには 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 モジュールに依存して書かれたスクリプトを移行できます。

自作ライブラリのインポート

自分で記述したスクリプトをインポートして再利用できます。 パターンとしては四つあり、普通は三つ目か四つ目を使います。

  1. 実行時のディレクトリにスクリプトを置く
  2. 環境変数 PYTHONPATH を通す
  3. ディレクトリを用意して、そこに階層化して配置する
  4. 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.pypyschool というディレクトリに置き、 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__ 変数によって変わってきます。 詳しくは公式チュートリアルを参照してください。

setup.py を使ってインストールする

setup.py を使うと、手元の環境にインストールして使えるようになります。 インストールしておくと、環境変数を設定しなくても、どのディレクトリでも使えますから便利です。

詳しくはこちらのガイドを読んでください。

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 のソースコードがあります。

«  デコレータ   ::   Contents   ::   Tips  »