パッケージ
【 目次 】
パッケージはライブラリモジュールをデレクトリに整理して管理できるようにしたものと考えれれる。
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ファイルを作成して再度実行。
├─ 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のみをメモっておく
- 2012/07/24 Python 3.3b1 の名前空間パッケージを試してみた - 清水川Web
- どぶお/Pythonで遊ぼう!/論理パッケージを複数に分ける - BioKids Wiki
- Python入門 - パッケージとモジュール
- Python Tips:ライブラリ・モジュールの場所を調べたい - Life with Python
- pkgutil – パッケージユーティリティ - Python Module of the Week
- 2012/07/24 Python 3.3b1 の名前空間パッケージを試してみた - 清水川Web
- 初めてのPython 20章 パッケージインポート 21章 モジュールに関連する高度なテクニック - taketin.py - fukuoka.pyのはてなグループ
- Pythonのモジュールについてまとめてみたよ - くろのて
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について以下に示しておく。