初めてのPython(21章)
モジュールのカプセル化
Pythonではカプセル化は出来ない。
アンダースコア(_)を使うことで、データを隠蔽することは出来るPEP8。
- 1個の場合、from ~ import * でインポートできないようになる
- 2個の場合、モジュール(クラス無し)の時は1個の時と同じ動きだが、クラス属性だと属性名の前に_クラス名が付いた名称に変更される
- ただ、カプセル化で来ている訳ではないので、属性名を指定するとアクセスできてしまう。
__all__属性に該当する属性をリストアップしなければ、アンダースコア1個と同じ動きができる。
けど、__all__属性は__init__.pyに記述するからパッケージインポートの時にしか使えない。
(test_module.py) #!/usr/bin/env python x = 10 _y = 20 __z = 30 (test_class.py) #!/usr/bin/env python class sample1: x = 10 _y = 20 __z = 30 class _sample2: x = 100 _y = 200 __z = 300
[kobakoba0723@fedora13-intel64 pkg_import]$ python Python 2.6.4 (r264:75706, Apr 1 2010, 02:55:51) [GCC 4.4.3 20100226 (Red Hat 4.4.3-8)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> dir() ['__builtins__', '__doc__', '__name__', '__package__'] >>> from test_module import * >>> dir() ['__builtins__', '__doc__', '__name__', '__package__', 'x'] >>> import test_module >>> dir(test_module) ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__z', '_y', 'x'] >>> >>> from test_class import * >>> dir() ['__builtins__', '__doc__', '__name__', '__package__', 'sample1', 'test_module', 'x'] >>> import test_class >>> dir(test_class) ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '_sample2', 'sample1'] >>> >>> sample1 = sample1() >>> dir(sample1) ['__doc__', '__module__', '_sample1__z', '_y', 'x'] >>> sample2 = test_class._sample2() >>> dir(sample2) ['__doc__', '__module__', '_sample2__z', '_y', 'x'] >>> >>> test_module.x, test_module._y, test_module.__z (10, 20, 30) >>> >>> sample1.x, sample1._y, sample1._sample1__z (10, 20, 30) >>> >>> test_module.__z = 100 >>> test_module.x, test_module._y, test_module.__z (10, 20, 100) >>> >>> sample1._sample1__z = 100 >>> sample1.x, sample1._y, sample1._sample1__z (10, 20, 100) >>>
相対インポートと絶対インポート
モジュールの探索先をカレントパスから行うのか、モジュールサーチパスから行うかの違い
種別 | 探索先 |
相対インポート | カレントパス→モジュールサーチパス |
絶対インポート | モジュールサーチパス |
相対インポートだと、標準モジュールと同名のモジュールが”たまたま”カレントパスにあった場合に、標準モジュールが使われない。
それを防ぐために、将来的には通常のインポートが絶対インポートになる。
(from __future__ import absolute_import を使うことで有効化可能)
相対インポートは from ~ import ステートメントでしか使えないインポート方法で、"."をつけることで相対インポートを行う。
"."を1つ追加する毎に1階層親のディレクトリから探索が始まる
以下のようなディレクトリ構造の場合、moduleXから相対インポートをするには、
relative_import
-- __init__.py |
-- main.py |
-- package |
(兄弟モジュールをインポート) from . import moduleY from ..subpackage1 import moduleY (兄弟モジュールの属性をインポート) from .moduleY import Y from ..subpackage1 import Y (兄弟パッケージのモジュールをインポート) from ..subpackage2 import moduleZ (兄弟パッケージのモジュールの属性をインポート) from ..subpackage2.moduleZ import Z (親パッケージのモジュールをインポート) from .. import moduleA (親パッケージのモジュールの属性をインポート) from ..moduleA import A
ただし、以下のようなコードを書くとエラー(*1)が出てしまう(※main.pyに記述して、python main.py とやった場合)
from ...package import bar
(*1) ValueError: Attempted relative import beyond toplevel package
relative_import と同一階層に main.py を配置して同様に実行した場合はエラーが出ない... なんでだろう。
package ディレクトリにも __init__.py を配置してるからパッケージとして扱われると思ってたんだけど。。。
メタプログラム
ビルドイン属性を利用して、他のモジュールを管理/操作するプログラムのこと
メタプログラムは、イントロスペクションとも呼ばれる。
モジュールのある属性にアクセスする場合、以下のような方法がとれる
- モジュール名.属性名
- モジュール名.__dict__['属性名']
- sys.modules['モジュール名'].属性名
- getattr('モジュール名', '属性名')
その他
拡張機能の有効化は以下で行う。
- from __future__ import 機能名
トップレベルファイルとして使われているのか、インポートされて使われているかの切り分けに”__name__"が使える
- トップレベルファイルの場合は __main__ が設定される
- インポートの場合は モジュール名 が設定される
モジュールに別名をつける機能があり、as を使う
- import module名 as 別名
- from module import attribute as 別名
from ステートメントでインポートした属性は reload() の影響を受けない。
reload を使用する場合は、import ステートメントを使う。