パッケージ

【 目次 】

パッケージはライブラリモジュールをデレクトリに整理して管理できるようにしたものと考えれれる。

osのファイルシステムのデレクトリの階層構造を利用して、サブデレクトリに関連するモジュールを分類分けして配置することにより、複数のモジュールをパッケージとしてツリ-構造でまとめて管理できる。

これはjavaのパッケージの概念に似ている。

パッケージはモジュールの検索パスを示すsys.path上のサブデレクトリに配置する。
サブデレクトリに保存するので同名のモジュール名があっても名前がぶつからない。

モジュールが保存されたディレクトリをPythonにパッケージとして認識させるためには、各サブデレクトリにパッケージの初期化スクリプトを記述したファイル __init__.pyを配置しなければならない。
初期化スクリプトが必要ない場合は__init__.py はただの空ファイルで構わない。

__init__.pyは各サブデレクトリに必要な事に注意。

パッケージの中のオブジェクトの呼び出し方はバリエーションがいろいろとあってポイントを把握していないと、何が許されて何が許されないのか間違いやすいので要注意。

パッケージ内のモジュールのimport

まず、パッケージの簡単な例を示す。

パッケージはsys.pathに登録されているのであればどのディレレクトリに配置しても良いが、ここではパッケージを利用する側のメインモジュールの存在するカレントデレクトリにサブデレクトリとして配置することにする。
カレントデレクトリに「my_package」というデレクトリを配置して、以下のようなモジュールを作成,保存する

パッケージmy_package内のモジュールsub.pyのコード

#coding: UTF-8

str = u"my_package.sub.a"

def func():
    print u"my_package.sub.funcを実行しました。"

次にカレントデレクトリにメインモジュールを配置。

メインモジュールmain.pyのコード

import my_package.sub

my_package.sub.func()

メインモジュールでは、パッケージmy_package内のモジュールsub.pyをimportして、sub.pyに定義してある関数funcを呼び出す。

ところが、これが動かない。 エラーになってしまう。

そうかPythonがデレクトリをパッケージと認識させるには、__init__.pyが必要だった。 という事でmy_packageデレクトリに空の__init__.pyファイルを作成して再度実行。

改めて\_\_init\_\_.pyも含めたデレクトリの階層構造を以下に示す。
├─ main.py
└─ my_package
    ├─ __init__.py
    └─ sub.py

実行結果

my_package.sub.funcを実行しました。

パッケージをimportしてもパッケージ内に含まれるモジュールを利用する事はできない

パッケージをimportしてもパッケージ内に含まれるモジュールを利用する事はできない。
importするのはあくまでもパッケージ内のモジュールmy_package.subであって、パッケージmy_packageでは無い。
以下の例はエラーになる。

誤ってパッケージ内のモジュールでは無くパッケージをimportしてエラーになる例

import my_package

my_package.sub.func()

パッケージをimport後、引数無しでdir関数を実行してグローバルシンボルを調べてみる。

print dir()

すると確かにmy_package変数が含まれている。

['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'my_package']

しかしmy_packageオブジェクトよりアクセスできるシンボルを調べるために、

print dir(my_package)

のように、my_packageを引数に指定してdir関数を実行しても、my_package内のsubモジュールにアクセスするためのシンボルが含まれていない。

['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__']

import文によりmy_package変数にパッケージオブジェクトというべきオブジェクトが代入されるが、my_package変数よりアクセスできるシンボルにはパッケージに含まれるsubモジュールのシンボルが含まれていないのでパッケージ内のサブモジュールにアクセスできない。

このため、my_packageを使ってパッケージに含まれるモジュールにアクセスする事はできない。

importにシンボルまでのパスを指定する事はできない。

また、importにシンボルまでのパスを指定する事もできない。
importのパラメータに指定できるのはモジュールであってモジュール内のシンボルでは無い。

以下の例もエラーになる。

importにシンボルまでのパスを指定してエラーになる例

#coding: UTF-8

import my_package.sub.func

func()  # これはダメ

結論として、import文のパラメータとして指定するものはパッケージでは無くモジュールまでのパスでなくてはならない。(パケージやシンボルではダメ)

パッケージ内のモジュールのシンボルにアクセスするにはパッケージ.モジュール.シンボルでアクセスする。

以下の例もエラーになる。

モジュール.シンボルではエラー

#coding: UTF-8

import my_package.sub

sub.func()  # これはエラー

パッケージ内のモジュールのシンボルにアクセスするにはモジュール.シンボル(sub.func)では無く、パッケージ.モジュール.シンボル(my_package.sub.func)のようにアクセスする必要がある。

as - 別名を使う

sub.func()はエラーになると述べたが、以下のように別名を付ける事でアクセスできるようになる。

別名を使う

#coding: UTF-8

import my_package.sub as sub

sub.func()  # これはOK

my_package.subに別名subでアクセスできるようになる。

from ... import ... - from付きのimport文

モジュールの時のようにfrom付きのimport文を使ってグローバルシンボルテーブルにモジュールやシンボルをimport(追加)する事ができる。
from付きのimport文はimport文よりさらに複雑。

from付きのimport文には以下のように2種類の使い方ができる。

  • from パッケージパス import モジュール
    モジュールをimport
  • from パッケージパス.モジュール import シンボル
    シンボルをimport

更に「from パッケージパス import モジュール」には以下のようにモジュールの部分をワイルドカード*を使って指定できる。

  • from パッケージパス import *
    ワイルドカードを使ってパッケージ内の複数のモジュールをimport

from パッケージパス import モジュール 構文

from パッケージパス import モジュール 構文の例を以下に示す。

from my_package import sub

sub.func()

from パッケージパス.モジュール import シンボル 構文

これをfrom パッケージパス.モジュール import シンボルを使うと

from my_package.sub import func

func()

from パッケージパス import モジュールはメインモジュールのグローバルシンボルテーブルに、モジュールオブジェクトを指すシンボルを追加するのに対してfrom パッケージパス.モジュール import シンボルではパッケージ.モジュール内のシンボルを追加する事になる。

from付きのimportで別名を使う。

from付きのimport構文にもasを使って別名を付ける事ができる。

from my_package import sub as sub_alias

sub_alias.func()

または

from my_package.sub import func as func_alias

func_alias()

複数の項目を指定

複数の項目を指定してimportする事ができる。

my_packageにもう一つモジュールを作成。

my_packageにother_module.pyを追加

#coding: UTF-8

str = u"my_package.other_module.str"

def func():
    print u"my_package.other_module.funcを実行しました。"

複数のモジュールをimport

my_packageのsubモジュールとother_moduleをimportして、subモジュールのfunc関数とother_moduleのfunc関数を実行。

from my_package import sub, other_module

sub.func()
other_module.func()

複数のシンボルをimport

my_packageのsubモジュールの関数funcと変数strをimport。

from my_package.sub import func, str

func()
print str

複数の項目に別名を付ける。

form付きのimport構文はimportの後に複数の項目を指定でき、それぞれの項目に別名を指定する事ができる。

subモジュールとother_moduleジュールに別名を

from my_package import sub as sub_mofule1, other_module as sub_module2

sub_mofule1.func()
sub_module2.func()

my_packageのsubモジュールの関数funcと変数strに別名を

from my_package.sub import func as func_alias, str as str_alias

func_alias()
print str_alias

パッケージ内での参照 - 相対パスによる参照

pythonのバージョンの違いでいろいろと異なっているようであるが、本稿は基本的にpython2.7.8で動作確認をしている。

これまでに示してきた例ではモジュールまでのパスにsys.pathで指定された位置(デレクトリ)をルートとしてそこからの絶対パスを指定していた。
しかし、パッケージ内のモジュールが、同一パッケージ内の他のモジュールを参照する時にはモジュールからの相対パスを指定する事ができる。
相対パスが使えるのはfrom付きのimportの場合で、from無しimportでは使えないみたい。

しつこいようだが注意が必要なのは、あくまでも相対パスが使えるのはパッケージ内のモジュールから同一パケージ内にある他のモジュールに対してという事。
パケージ内のサブモジュールをメインモジュールとして実行した場合はエラーになってしまう。

相対パスの例を示す。

まず、同一パケージ内にある他のモジュールを参照するサブモジュールを作成。
ここでは、以前に作成したmy_packageパッケージのsubモジュールを参照するuse_subモジュールをsubモジュールと同一階層に配置する。

同一パケージ内にある他のモジュールを参照するサブモジュール(use_sub.py)

#coding: UTF-8

from . import sub

def foo():
    print u"my_package.use_sub.foo関数を実行しました。"
    sub.func()

サブモジュール(use_sub.py)のfuncを呼び出すメインモジュールは下記のコードとする。

メインモジュール(relative_path_test_main.py)

import my_package.use_sub

my_package.use_sub.foo()

メインモジュール(relative_path_test_main.py)はmy_package内のuse_sub.pyモジュールの関数fooを呼び出す。

ファイルの階層構造を以下に示す。
├─ relative_path_test_main.py
└─ my_package
    ├─ sub.py
    ├─ use_sub.py
    └─ __init__.py

注意しなければならないのは、相対パスを使ったモジュールをメインモジュールとして実行するとエラーになってしまう。
このため、上記、use_sub.pyをメインモジュールとして実行するとエラーになる。

相対パスの指定例を公式ドキュメント より、以下に引用しておく。

from . import echo
from .. import formats
from ..filters import equalizer

from パッケージパス import * 構文 - モジュールにワイルドカード*を指定

「from パッケージパス import *」を使うとパッケージ内の複数のモジュールを一度にimportする事ができる。
importされるモジュールはパッケージの初期化ファイル__init__.pyの__all__ 変数にリスト型で指定する。
_all__ 変数の指定が無い時にはimportされない。

例えば、以下のようなファイル構成の時に
├─ main.py
└─ top_package
    ├─ top_module1.py
    ├─ top_module2.py
    ├─ top_module3.py
    ├─ __init__.py
    └─sub_package
        ├─ sub_module1.py
        ├─ sub_module2.py
        ├─ sub_module3.py
        └─ __init__.py

top_packageの__init__.pyのコードが以下のようだったとする。

top_packageの__init__.pyのコード

__all__ = ["top_module1", "top_module3"]

さらに、sub_packageの__init__.pyのコードが以下のようだったとする。

sub_packageの__init__.pyのコード

__all__ = ["sub_module2", "sub_module3"]

import *を使用してtop_packageのモジュールをimportしてdir関数でメインモジュールのシンボルテーブルを調べてみる。
mainモジュールのコードを以下のように記述。

main.pyのコード

from top_package import *

print dir()

実行結果は以下のようになる。

実行結果

['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'top_module1', 'top_module3']

mainモジュールのグローバルシンボルテーブルにtop_module1とtop_module3の名前が追加されているのがわかる。

サブパッケージのimport

「from パッケージパス import *」はあくまでのそのパッケージ内のモジュールのimportであってそのパッケージのサブパッケージまでたどって サブパッケージのモジュールをimportまではしてくれないようだ。

サブパッケージのモジュールもimportするには、更にサブパッケージのモジュールの為のimport文を追加して。

main.pyのコード

from top_package import *
from top_package.sub_package import *

print dir()

all 変数が定義されていない場合はimport済みのモジュールがimport *の対象に

_init__.pyに__all__ 変数が定義されていない場合には、 「import *」でimportの対象となるモジュールは、パッケージ内のその前にimport済みのモジュールが対象となる。
どうゆう事かというと、

main.pyのコード

import top_package.top_module1
from top_package import *

print dir()

実行結果は以下のようになる。

実行結果

['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'top_module2', 'top_package']

シンボルテーブルにtop_packageがあるのは1行目のimport文の結果として当然として、 2行目の「import *」によりtop_module2もシンボルテーブルに追加されているのがわかる。

更にパッケージを詳しくみていくと

更にパッケージを詳しくみていくと、__path__ だのpkgutil のextend_pathだのを利用していろいろな事ができるらしい。

名前空間パッケージと言って、異なるデレクトリのパッケージを一つのパッケージのように見せる事ができるらしい。
もうごちゃごちゃしてなにがないやらよくわからなくなったので、深入りせずに参考となりそうなURLのみをメモっておく

importされたシンボルの削除

名前,変数名,シンボル」で述べたとおり、シンボルはdel symbol_nameで削除できる。
従って、importの結果追加されたシンボルも削除できる。
以下の例はimport文によりいったん追加したパッケージオブジェクトのシンボルtop_packageを削除する例である。

top_packageの__init__.pyのコード

import top_package.top_module2
from top_package import *

print dir()

del top_package

print dir()

実行結果は以下のようになる。

実行結果

['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'top_module2', 'top_package']
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'top_module2']

del top_packageの実行前と後でtop_packageがシンボルテーブルから削除されているのがわかる。

importされたモジュール,パッケージオブジェクトに定義されているシンボル

importされたモジュールオブジェクト,パッケージオブジェクト内のシンボルの値を以下のようなコードを使って調べてみる。

top_packageの__init__.pyのコード

#coding: UTF-8

import top_package.empty_code

# empty_codeモジュールのシンボルの値を列挙
print "*** dir(top_package.empty_code) ***"
for symbol in dir(top_package.empty_code):
    full_symbol = "top_package.empty_code." + symbol
    print "\t",full_symbol,"->", eval(full_symbol)

# top_packageオブジェクトのシンボルの値を列挙
print "*** dir(top_package) ***"
for symbol in dir(top_package):
    full_symbol = "top_package." + symbol
    print "\t",full_symbol,"->", eval(full_symbol)

empty_code.pyは文字どおり空のファイルである。
実行結果は以下のようになる。

実行結果

*** dir(top_package.empty_code) ***
        top_package.empty_code.__builtins__ -> {'bytearray': <type 'bytearray'>, ...(以下略)
        top_package.empty_code.__doc__ -> None
        top_package.empty_code.__file__ -> [パッケージの親デレクトリ]\top_package\empty_code.pyc
        top_package.empty_code.__name__ -> top_package.empty_code
        top_package.empty_code.__package__ -> None
*** dir(top_package) ***
        top_package.__builtins__ -> {'bytearray': <type 'bytearray'>, 'IndexErro ...(以下略)
        top_package.__doc__ -> None
        top_package.__file__ -> [パッケージの親デレクトリ]\top_package\__init__.pyc
        top_package.__name__ -> top_package
        top_package.__package__ -> None
        top_package.__path__ -> ['[パッケージの親デレクトリ]\\top_package']
        top_package.empty_code -> <module 'top_package.empty_code' from '[パッケージの親デレクトリ]\top_package\empty_code.pyc'>

モジュールのシンボルテーブルには定義した覚えのない変数'__builtins__', '__doc__', '__file__', '__name__', '__package__'が定義されているのがわかる。

以外なのは__package__の値、どちらもパケージ名ではなくNoneになってしまった。

関連する参考URL

これまでに多くの参考URLのリンクを示していて、重複している部分もあるがあらためて前項の「モジュール」と本項の「パッケージ」についての、有益と思われる参考となるURLについて以下に示しておく。

ページのトップへ戻る