[Python入門]パッケージPython入門(2/2 ページ)

» 2023年11月10日 05時00分 公開
[かわさきしんじDeep Insider編集部]
前のページへ 1|2       

パッケージのインポート

 パッケージのインポートといっても、基本的にはモジュールのインポートと同様だ。一番簡単なインポートの仕方は「import パッケージ名」となる。以下に例を示す。

import mypkg

mypkgパッケージのインポート

 既に述べたが、このときに「mypkgパッケージのmymathモジュールのfact関数」はドット「.」を使って「mypkg.mymath.fact」のように記述できる。実際に呼び出してみよう。

print(mypkg.mymath.fact(5))

mypkg.mymath.fact関数を呼び出す

 実行結果を以下に示す。

実行結果 実行結果

 意外かもしれないが、エラー(AttributeError例外)が発生した。エラーメッセージには「mypkgにはmymathという属性はない」となっている。どういうことかというと、「mypkgパッケージだけをインポートしても、その配下にあるモジュールは自動的には読み込んでくれない」ということだ。ということは明示的に、各モジュールやモジュールで定義されている関数などをインポートする必要があるということだ。

 一番簡単なのは次のように「import パッケージ名.モジュール名」を実行することだ。

import mypkg.mymath
print(mypkg.mymath.fact(5))

mypkg.mymathモジュールをインポートして、そのfact関数を呼び出す

 実行結果を以下に示す(ここで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 import文で個別の要素だけをインポート

 実行結果は次のようになる(予想通りのはずだ)。

実行結果 実行結果

__init__.pyファイルと__all__変数とパッケージ内インポート

 では「from mypkg import *」としてインポートを実行したらどうなるだろう。ただし、これを試してみる前に、Python 3カーネルを再起動しておく必要がある(既にインポートしたいろいろな名前をリセットするため)。これには[restart the kernel (with dialog)]ボタンをクリックする。

Python 3カーネルを再起動 Python 3カーネルを再起動

 このボタンをクリックすると、再起動を確認するダイアログが表示されるので[Restart]ボタンをクリックする。

 では、Python 3カーネルを再起動して、以下を実行してみよう。

print(dir())
from mypkg import *
print(dir())

mypkgパッケージから全ての名前をインポート?

 1行目と3行目で使っているdir関数は、引数なしで呼び出すと現在のローカル名前空間に登録されている名前を返すものだ。実行してみると、次のような結果になる。

実行結果 実行結果

 2つの出力結果が変わらないことに注意しよう。つまり、「from mypkg import *」は「何の名前もインポートしていない」ということだ。先ほども述べたが、「パッケージをインポートしても、その配下にあるモジュールまでは自動的には読み込んでくれない」ということだ。

 では「from mypkg import *」でインポートを行ったときに、2つのモジュールにある4つの関数をインポートできるようにするにはどうしたらよいだろう。これにはPythonで特殊な扱いをされている__all__変数に「インポートさせたい関数の名前を列挙」する。そして、このような処理を記述するのに__init__.pyファイルを使用する。

 そこで、[File]メニューから[Open]を選択して、ファイル一覧ページを表示したら、[mypkg]リンクをクリックしてmypkgフォルダに移動して、[__init__.py]リンクをクリックする。すると、以下のようなページが表示される(先ほど、__init__.pyファイルを作成したページを表示したままにしていたら、それを使えばよい)。

__init__.pyファイルの編集画面 __init__.pyファイルの編集画面

 「__all__変数にインポートさせたい関数の名前を列挙」すると述べたが、このときにはリストの要素にそれらの名前を文字列として並べていく。ここではfact/fizzbuzz/fib/helloの4つの関数を公開したいので、このファイルにまずは次のようなコードを書いてみよう。

__all__ = ['fact', 'fizzbuzz', 'fib', 'hello']

公開したい名前を__all__変数に列挙する

 記述したら、[File]メニューから[Save]を選択して、ファイルの内容を保存するのを忘れないようにしよう。では、この状況でPython 3カーネルを再起動して、次のコードをもう一度試してみよう(状況によっては再起動の必要はないかもしれないが、以降はimport文の実行前に環境をリセットするために念のため、再起動をしている)。なお、この後は以下のコードは省略して「先ほどのコード」と記述するだけとする。

print(dir())
from mypkg import *
print(dir())

mypkgパッケージから全ての名前をインポート?(再掲)

 これでうまくいったと思ったら、実はそうではない。実行結果は次のようになる。

実行結果 実行結果

 今度は「mypkgにはfactという属性はない」というエラーになった。この理由も当然、「パッケージをインポートしても、その配下のモジュールは自動的に読み込んでくれない」ことだ。ということは、__init__.pyファイルの内部でmymathモジュールとgreetモジュールを読み込む必要がある。では、__init__.pyファイルを次のように変更してみよう。

import mymath
import greet
__all__ = ['fact', 'fizzbuzz', 'fib', 'hello']

mymathモジュールとgreetモジュールをインポートするように修正した__init__.pyファイル

 そして、これを記述/保存してから、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']

パッケージを絶対インポートするように変更した__init__.pyファイル

 そして、これを保存し、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']

from import文で関数をインポートして、それらを__all__変数に列挙するように修正した__init__.pyファイル

 __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']

__name__変数の値を出力するように修正した__init__.pyファイル

 これまでと同様に、__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ファイルを置くこともある。そうした場合には、「..」で上の階層のパッケージまたはモジュールを表すこともできる(階層がさらに深くなれば「...」のようにドットの数を多くすることで、さらに上位のパッケージやモジュールを参照できる)。

 なお、パッケージ内のモジュールを参照するのは、必ずしも__init__.pyファイルだけで行うとは限らない。パッケージ内のあるモジュールが、同じパッケージ内の別モジュールで定義されている関数を利用するときには、絶対インポートもしくは相対インポートを行う必要があることには注意しよう。

 モジュールをpythonコマンドで実行するときなどには、__name__変数の値が「__main__」となる。このときには、相対インポートを行おうとしても、「.」は「__main__」となってしまい、モジュールの名前とはならない。そのため、そうしたモジュールでは相対インポートはできない点には注意しよう。

まとめ

 今回はPythonのパッケージについて見てきた。実は、Pythonには名前空間パッケージと呼ばれる種類のパッケージもあるが、これについては本連載では取り上げない。次回は「Pythonのオブジェクト」についてまとめよう。

今回のまとめ:パッケージ

  • 「パッケージ」とは、複数のモジュールを束ねたもの
  • モジュールはファイルに対応し、パッケージはフォルダに対応するものと考えられる
  • 一般的には、パッケージはそのルートとなるフォルダに、パッケージを構成するモジュールを含んだファイルと、__init__.pyファイルを配置する
  • パッケージ内にさらにフォルダを作成して、多階層のパッケージとすることも可能
  • __init__.pyファイルに特別な処理を記述していないのであれば、単に「import パッケージ名」としても、その配下にあるモジュールまでは自動的に読み込んではくれない
  • そのため、個別にモジュールやモジュールで定義されている関数などを明示的にインポートするのがよい
  • 「from パッケージ名 import *」形式でインポートするものを指定するには、__init__.pyファイルで__all__変数に、インポートさせたいものを列挙しておく
  • パッケージ内でモジュールを参照するには絶対インポートもしくは相対インポートによってモジュールを指定する必要がある

「Python入門」のインデックス

Python入門

前のページへ 1|2       

Copyright© Digital Advantage Corp. All Rights Reserved.

スポンサーからのお知らせPR

注目のテーマ

Microsoft & Windows最前線2025
AI for エンジニアリング
ローコード/ノーコード セントラル by @IT - ITエンジニアがビジネスの中心で活躍する組織へ
Cloud Native Central by @IT - スケーラブルな能力を組織に
システム開発ノウハウ 【発注ナビ】PR
あなたにおすすめの記事PR

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。