Python: 00. プログラム構造 - ikymrkw/pydepot GitHub Wiki

ソースコードは .py という拡張子のファイルに書くのが通例。

標準的なスタイルでは、インデントはスペース4つで構成される。インデントにタブ文字を使ってはいけない。

ソースコードの冒頭に、そのソースコードを符号化している文字エンコーディングを書くことができる。

 # -*- coding: utf-8 -*-

が、 PEP では UTF-8 で書くことになっており、上記のようにファイル冒頭で指定するのは非推奨らしい。

ドキュメンテーション文字列

関数やクラスなどの最初に書かれた文字列は、プログラムの実行上は意味がないが、ドキュメンテーション文字列として使われる。

 def func(x, y):
     """Calculates some value from x and y."""
     return x + y

後述のモジュールやパッケージにもドキュメンテーション文字列を書くことができる。

ドキュメンテーション文字列は主に以下のケースで利用される。

  • help(obj) で返される
  • pydoc で文書化される
  • IDE などのヘルプ表示に使われる

モジュール

モジュールは1個の *.py ファイルに相当する。import文で py ファイルを読み込み、実行し、現在のモジュールのスコープに別のスコープの名前を取り込むことができる。

mod1.py というファイルがあったとする。

 import os
 ...
 def func_a(...): ...
 def func_b(...): ...

これをインポートして使えるようにするには、以下のようにする。

 # モジュールをインポート
 import mod1
 mod1.func_a(...)
 
 # モジュール内の特定のメンバーのみをインポート
 from mod1 import func_b
 func_b(...)
 
 # モジュール内の全メンバーをインポート
 from mod1 import *
 func_a(...), func_b(...)
 
 # 名前を変えてインポート
 import mod1 as mmm
 mmm.func_a(...)
 
 from mod1 import func_b as bbb
 bbb(...)

読み込む側から見ると、読み込むモジュール名(たとえば上記の mod1 など)は変数名にすぎない。ただの変数なので、代入文などで上書きすることが可能。

読み込まれる側(モジュール側)では、pyファイルのトップレベルインデントの文の列が実行されることになる。定義・代入された変数(関数も変数の一種)は読み込む側ではモジュール名の変数が指すオブジェクトの要素として追加される。モジュール内での import も代入の一種なので、モジュール外の変数・名前には影響を与えない。

トップレベルインデントだがモジュールとして読み込まれるときには実行されたくない文は、次のように記述する。

 if __name__ == '__main__':
     # ここに記述、たとえば
     print "Hello World!"

これはpyファイルを直接実行したときのみ実行されるので、モジュールのテストコードなどを書くのに使われる。

パッケージ

複数のモジュールを集めたものをパッケージと呼ぶ。パッケージは、ファイル構成上はディレクトリに該当する。パッケージとして扱いたいディレクトリには __init__.py を配置する必要がある。

たとえば次のようなファイル配置のとき

abcd/
abcd/__init__.py
abcd/xyz.py
test.py

test.py には次のいずれかを書くことができる(同時に書いてもいいが、わかりにくいし、あまり意味はない)。

# ケース1
import abcd.xyz
abcd.xyz.func()

# ケース2
from abcd import xyz
xyz.func()

# ケース3
from abcd.xyz import func
func()

またいずれも as nnn を加えて短いまたは別の名前でインポートしても構わない。

すでにパッケージ内にあるモジュール (abc.py) で兄弟モジュールや親子パッケージ内のモジュールをインポートする場合、完全なパッケージ名を書くほかに、from 節に ... を使える。

from . import mod1  # 兄弟モジュール mod1
from .. import mod2  # 親パッケージのモジュール mod2
from .mod1 import xyz  # 兄弟モジュール efg のメンバー
from ..pkg3 import mod3  # 兄弟パッケージのモジュール

__init__.py

上記の配置のとき、 abcd はパッケージでありモジュールではないので、そのままではメンバーを持てない。つまり

import abcd
abcd.xyz.func()     # abcd.xyz が存在しないのでエラー

のように、abcd.xyz という参照はできない(これはimport文でのみ可能な参照である)。

ただし、__init__.py 内でメンバーを持たせることができる。

# __init__.py において:
import abcd.xyz as xyz  # 相対パスではないことに注意
from abcd.xyz import func
NNN = 999

# test.py において:
import abcd
abcd.xyz.func()
abcd.func()
abcd.NNN

Star import from abcd import * を許容するには、 __init__.py に次のように書く。

# __init__.py:
__all__ = ["xyz"]

* の対象は指定したモジュールだけなので、ディレクトリ内の余計なモジュールまでロードされてしまうことを防げる。ただし、モジュールへのアクセスを禁止できるわけではなく、 from abcd import secret_module.py のように指定すればロードできる。

import されるディレクトリ

では、import はどのディレクトリからモジュールやパッケージを読み込むのだろうか?

これは、「カレントディレクトリ」、次に「sys.path のリストにあるディレクトリ」と決まっている。

「カレントディレクトリ」は、 python dir1/dir2/script.py のように実行した場合はスクリプトのあるディレクトリ dir1/dir2/ になる。

sys.pathstr のリストであり、通常はシステムの Python インストール先にある site-packages などが指定されている。環境変数 PYTHONPATH があると、その値が sys.path の先頭に設定される(PYTHONPATH の書式は PATH と同じ)。sys.path はただのリストなので、Pythonコードの実行中に書き換えることもできる。

独自のモジュールを作るとき、実行して試すのに python pak1/pak2/module3.py のようにしていると、 import utilpak1/pak2/util.py をインポートできてしまう。しかしこれは間違いで、 module3.py では import pak1.pak2.util または from . import util と書くべきである。実行は次のいずれかをする:

  • python -m pak1.pak2.module3
  • import pak1.pak2.module3 を含む実行用のスクリプトを別途用意して、それを実行する