Pythonのインポートまわりを理解する

Pythonのインポートまわりを理解する

こんにちは、えびかずきです。

Pythonはその強力なライブラリとモジュールが豊富に揃っていることで知られていますが、それらを適切に活用するためにはimportの仕組みを理解することが重要です。この記事では、Pythonのimportの基本を解説していきます。

では説明していきます。

要点

sys.pathについて

•基本的に実行スクリプトの階層が追加される

•python -m でモジュールとして実行した場合は作業ディレクトリの階層が追加される

相対インポートについて

•相対インポートできるモジュールはsys.path内のディレクトリパスより下の階層のモジュールに限る

import文の基本

Pythonのimport文は他のモジュールの機能を使えるようにする基本的な機能です。例えば、mathモジュールをimportして、その中の関数を利用することができます。

import math
print(math.sqrt(16)) # 4.0

このように、import モジュール名とすることで指定したモジュール全体がimportされます。モジュール内の特定の関数やクラスだけを使いたい場合は、以下のようにfrom モジュール名 import 関数名と記述します。

from math import sqrt
print(sqrt(16)) # 4.0

Pythonのimport文にはasというキーワードを使った使い方もあります。これはモジュールをimportする際に別名をつけるためのものです。この機能は特に、モジュール名が長い場合や、同名の関数やクラスがある場合に有用です。

たとえば、以下のようにimport モジュール名 as 別名と記述することで、モジュール名別名で参照することができます。

import math as m
print(m.sqrt(16)) # 4.0

この例では、mathモジュールをmという名前でimportしています。これにより、モジュール名を短くすることができ、コードの可読性を向上させることができます。

importできるモジュールについて

importできるモジュールはsys.pathに記述されているディレクトリパス配下にあるものに限ります。ターミナルからpython main.pyのように直接スクリプトを実行した場合には、作業ディレクトリパスではなく、スクリプトが存在するディレクトリパスがsys.pathに追加されることを覚えておきましょう。

一方で、python -m moduleのようにスクリプトをモジュールとして実行した場合にはターミナルの作業ディレクトリがsys.pathに追加されます。

Packageをimportしてはいけない

Pythonでは、パッケージを直接importすると、その配下のモジュールがimportできなくなることがあります。これはPythonがパッケージレベルのimportではなく、モジュールレベルのimportを期待しているためです。例えば、以下のようなディレクトリ構造があるとします。

mypackage/
    __init__.py
    mymodule.py

この時、import mypackageとするとmypackage.mymoduleは使用できません。

import mypackage
mypackage.mymodule 

# AttributeError: module 'mypackage' has no attribute 'mymodule'

しかし、パッケージの__init__.pyファイルに配下のモジュールをimportする記述がある場合、この問題は解決します。__init__.pyはパッケージがimportされたときに実行される特別なファイルで、ここにモジュールのimport文を書くことで、パッケージをimportした時に自動的にモジュールもimportされます。

以下のように__init__.pyimport .mymoduleを追加します。

# mypackage/__init__.py
import .mymodule

これで、import mypackageとした時にmypackage.mymoduleも一緒にimportされます。

import mypackage
mypackage.mymodule # This is now possible

よって、パッケージをimportする際は、その配下のモジュールが正しくimportされるように、適切な設定が必要であるという注意点があります。

実際のライブラリでこの現象を確認してみましょう。

例えば、numpyはパッケージを直接importすることができます。

import numpy
print(numpy.random) # <module 'numpy.random' from '/usr/local/lib/python3.6/dist-packages/numpy/random/__init__.py'>

一方、scikit-learnではパッケージを直接importしてもその配下のモジュールはimportできません。

import sklearn
print(sklearn.linear_model) # AttributeError: module 'sklearn' has no attribute 'linear_model'

このように、パッケージをimportする際には、そのパッケージがモジュールを自動的にimportする設定になっているかどうかを確認する必要があります。

相対import

Pythonの相対importとは、同じパッケージに属するモジュールをインポートする方法です。...を使って現在のモジュールの位置から相対的にインポートするモジュールの位置を指定します2

相対importの注意点は、トップレベルのスクリプトでは使えないことです3。トップレベルのスクリプトとは、直接実行されるスクリプトのことです。相対importは、パッケージ内のモジュールをインポートするときにのみ使えます3。ちなみに相対インポートできるモジュールはsys.path内のディレクトリパスより下の階層のモジュールに限ります。つまりパッケージ階層を超えたディレクトリのモジュール、例えばトップレベルのスクリプトと同階層のモジュールなどはインポート出来ません。

では相対importの例を見てみましょう。以下のようなディレクトリ構造があるとします。

my_package/
    __init__.py
    module_a.py
    module_b.py
    sub_package/
        __init__.py
        module_c.py
        module_d.py
main.py

ここで、module_a.pyからmodule_b.pyをインポートするには、以下のように書けます。

# module_a.py
from . import module_b # .は現在のディレクトリを表す

また、module_c.pyからmodule_d.pyをインポートするには、以下のように書けます。

# module_c.py
from . import module_d # .は現在のディレクトリを表す

さらに、module_c.pyからmodule_a.pyをインポートするには、以下のように書けます。

# module_c.py
from .. import module_a # ..は上位のディレクトリを表す

しかし、main.pyからmodule_a.pyをインポートするには、相対importは使えません。main.pyはトップレベルのスクリプトなので、絶対importを使わなければなりません。以下のように書けます。

# main.py
import my_package.module_a # パッケージ名から始める

以上が、Pythonの相対importに関する説明です。相対importは、パッケージ内のモジュールをインポートするときに便利な方法ですが、トップレベルのスクリプトでは使えないことに注意しましょう。

まとめ

Pythonのimportまわりの基本について解説しました。私自身importまわりの仕組みがわからなくなって調べ直すことがよくあります。たぶん3ヶ月後ぐらいにはまた忘れちゃってると思うので、この備忘録で復習することになりそうです。

参考

https://note.nkmk.me/python-import-usage/

その他カテゴリの最新記事