パッケージのインポートといっても、基本的にはモジュールのインポートと同様だ。一番簡単なインポートの仕方は「import パッケージ名」となる。以下に例を示す。
import mypkg
既に述べたが、このときに「mypkgパッケージのmymathモジュールのfact関数」はドット「.」を使って「mypkg.mymath.fact」のように記述できる。実際に呼び出してみよう。
print(mypkg.mymath.fact(5))
実行結果を以下に示す。
意外かもしれないが、エラー(AttributeError例外)が発生した。エラーメッセージには「mypkgにはmymathという属性はない」となっている。どういうことかというと、「mypkgパッケージだけをインポートしても、その配下にあるモジュールは自動的には読み込んでくれない」ということだ。ということは明示的に、各モジュールやモジュールで定義されている関数などをインポートする必要があるということだ。
一番簡単なのは次のように「import パッケージ名.モジュール名」を実行することだ。
import mypkg.mymath
print(mypkg.mymath.fact(5))
実行結果を以下に示す(ここでAttributeError例外が発生したら、mymath.pyファイルを保存しているかを確認してほしい)。
この方式のインポートでは、関数を呼び出すのに「パッケージ名.モジュール名.関数名」と全ての名前をフルパスで記述しなければならないので、記述が煩雑になり、面倒だ(その代わりに、どのパッケージ/どのモジュール/どの関数かが明確ではある)。そこで、使えるのが「from import」文だ。
「from」キーワード付きのインポートでは、「from パッケージ名 import モジュール名」や「from パッケージ名.モジュール名 import 識別子」などの形式でモジュールや関数などを個別にインポートできる。以下に例を示す。
from mypkg import mymath # mypkgパッケージのmymathモジュールをインポート
from mypkg.mymath import fact # mypkg.mymathモジュールからfact関数をインポート
print(fact(5))
print(mymath.fizzbuzz(15))
実行結果は次のようになる(予想通りのはずだ)。
では「from mypkg import *」としてインポートを実行したらどうなるだろう。ただし、これを試してみる前に、Python 3カーネルを再起動しておく必要がある(既にインポートしたいろいろな名前をリセットするため)。これには[restart the kernel (with dialog)]ボタンをクリックする。
このボタンをクリックすると、再起動を確認するダイアログが表示されるので[Restart]ボタンをクリックする。
では、Python 3カーネルを再起動して、以下を実行してみよう。
print(dir())
from mypkg import *
print(dir())
1行目と3行目で使っているdir関数は、引数なしで呼び出すと現在のローカル名前空間に登録されている名前を返すものだ。実行してみると、次のような結果になる。
2つの出力結果が変わらないことに注意しよう。つまり、「from mypkg import *」は「何の名前もインポートしていない」ということだ。先ほども述べたが、「パッケージをインポートしても、その配下にあるモジュールまでは自動的には読み込んでくれない」ということだ。
では「from mypkg import *」でインポートを行ったときに、2つのモジュールにある4つの関数をインポートできるようにするにはどうしたらよいだろう。これにはPythonで特殊な扱いをされている__all__変数に「インポートさせたい関数の名前を列挙」する。そして、このような処理を記述するのに__init__.pyファイルを使用する。
そこで、[File]メニューから[Open]を選択して、ファイル一覧ページを表示したら、[mypkg]リンクをクリックしてmypkgフォルダに移動して、[__init__.py]リンクをクリックする。すると、以下のようなページが表示される(先ほど、__init__.pyファイルを作成したページを表示したままにしていたら、それを使えばよい)。
「__all__変数にインポートさせたい関数の名前を列挙」すると述べたが、このときにはリストの要素にそれらの名前を文字列として並べていく。ここではfact/fizzbuzz/fib/helloの4つの関数を公開したいので、このファイルにまずは次のようなコードを書いてみよう。
__all__ = ['fact', 'fizzbuzz', 'fib', 'hello']
記述したら、[File]メニューから[Save]を選択して、ファイルの内容を保存するのを忘れないようにしよう。では、この状況でPython 3カーネルを再起動して、次のコードをもう一度試してみよう(状況によっては再起動の必要はないかもしれないが、以降はimport文の実行前に環境をリセットするために念のため、再起動をしている)。なお、この後は以下のコードは省略して「先ほどのコード」と記述するだけとする。
print(dir())
from mypkg import *
print(dir())
これでうまくいったと思ったら、実はそうではない。実行結果は次のようになる。
今度は「mypkgにはfactという属性はない」というエラーになった。この理由も当然、「パッケージをインポートしても、その配下のモジュールは自動的に読み込んでくれない」ことだ。ということは、__init__.pyファイルの内部でmymathモジュールとgreetモジュールを読み込む必要がある。では、__init__.pyファイルを次のように変更してみよう。
import mymath
import greet
__all__ = ['fact', 'fizzbuzz', 'fib', 'hello']
そして、これを記述/保存してから、Python 3環境を再起動し、再度、先ほどのコードを実行する。実行結果は次のようになる。
今度は「mymathというモジュールはない」というエラー(ModuleNotFoundError例外)が発生した。この原因はPythonがmymathモジュール(やgreetモジュール)を発見できていないからだ。
__init__.pyファイルとmymathモジュール(mymath.pyファイル)やgreetモジュール(greet.pyファイル)は同じ「pkg」フォルダにあるので、単に「import mymath」や「import greet」と指定したくなるが、そうではなく、モジュールのフルパス(パッケージ名.モジュール名)を指定する必要があるのだ。このように、パッケージ内でフルパスでモジュール名を指定することを「絶対インポート」と呼ぶ。これに対して「相対インポート」もあるが、これについては後で述べる。そこで、__init__.pyファイルを次のように修正する。
import mypkg.mymath
import mypkg.greet
__all__ = ['fact', 'fizzbuzz', 'fib', 'hello']
そして、これを保存し、Python 3環境を再起動してから、先ほどのコードを実行してみよう。すると、次のようにまだエラーが発生する。
これは、「import mypkg.mymath」形式のインポートでは「fact」などの名前がインポートされていないからだ。from import文で公開したいものを個別にインポートした上で、それらを__all__変数に列挙すれば、この問題は解決される。よって、__init__.pyファイルは次のようになる。
from mypkg.mymath import fact, fizzbuzz, fib
from mypkg.greet import hello
__all__ = ['fact', 'fizzbuzz', 'fib', 'hello']
__init__.pyファイルを保存して、Python 3カーネルを再起動して、先ほどのコードを実行してみよう。すると、次のように4つの関数がインポートされたことが分かる。
このように、__init__.pyファイルは、パッケージの読み込み時に行いたい処理を記述するために使われる。また、「from パッケージ名 import *」形式でインポートを行ったときに、「パッケージを作成した側」が何をインポートさせたいかは__all__変数で制御できる。
最後に、相対インポートについて簡単に見ておこう。相対インポートとは「現在のモジュールやパッケージの名前に対して相対的にモジュールを指定する」ことを意味する。「現在のモジュールやパッケージの名前」はPythonがプログラム実行時やモジュール読み込み時に自動的に設定する「__name__」変数で調べられる。そこで先ほどの__init__.pyファイルを次のように修正する。
print(__name__)
from mypkg.mymath import fact, fizzbuzz, fib
from mypkg.greet import hello
__all__ = ['fact', 'fizzbuzz', 'fib', 'hello']
これまでと同様に、__init__.pyファイルを保存し、Python 3環境を再起動して、先ほどのコードを実行してみよう。すると、mypkgパッケージから関数をインポートするタイミング(2つ目の出力)で、__init__.pyファイルの__name__変数の値が表示される。
これによると、__name__変数の値は「mypkg」となっている。相対インポートでは、これに対して相対的にモジュールのパスを指定するということだ。絶対インポートでは、「mypkg.mymath」「mypkg.greet」としてモジュールを指定していたが、相対インポートでは次のようにして、モジュールのパスを指定できる。
from .相対パス import ……
ドット「.」が現在のパッケージもしくはモジュールを表す。「相対パス」の部分には、「そのパッケージもしくはモジュール配下のモジュール」を指定する。「mypkg.mymath」であれば、「.」は「mypkg」を表すので、「相対パス」には「mymath」と指定できる。このとき、ドット「.」と「相対パス」の間には空白文字を含めてもよい。よって、先ほどの__init__.pyファイルは次のようにも書けるということだ。
from .mymath import fact, fizzbuzz, fib
from . greet import hello
__all__ = ['fact', 'fizzbuzz', 'fib', 'hello']
パッケージの仮想が深くなると、最上位レベルのフォルダの下にさらにフォルダを作成して(サブモジュール/サブパッケージを作成して)、そこにモジュールを置き、__init__.pyファイルを置くこともある。そうした場合には、「..」で上の階層のパッケージまたはモジュールを表すこともできる(階層がさらに深くなれば「...」のようにドットの数を多くすることで、さらに上位のパッケージやモジュールを参照できる)。
なお、パッケージ内のモジュールを参照するのは、必ずしも__init__.pyファイルだけで行うとは限らない。パッケージ内のあるモジュールが、同じパッケージ内の別モジュールで定義されている関数を利用するときには、絶対インポートもしくは相対インポートを行う必要があることには注意しよう。
モジュールをpythonコマンドで実行するときなどには、__name__変数の値が「__main__」となる。このときには、相対インポートを行おうとしても、「.」は「__main__」となってしまい、モジュールの名前とはならない。そのため、そうしたモジュールでは相対インポートはできない点には注意しよう。
今回はPythonのパッケージについて見てきた。実は、Pythonには名前空間パッケージと呼ばれる種類のパッケージもあるが、これについては本連載では取り上げない。次回は「Pythonのオブジェクト」についてまとめよう。
「Python入門」
Copyright© Digital Advantage Corp. All Rights Reserved.