初めてのPython(19章)

モジュール

トップレベルの変数は、モジュールの属性となる。
また、モジュール名は、プログラム中では変数として扱われるので、予約語は使えない。

importステートメントとfromステートメント

importはモジュール全体をインポートするのに対して、fromはモジュール内の特定の属性をインポートする。
そのため、importを使った場合はモジュール名.属性名でアクセスするが、
fromを使った場合は属性名で直接アクセスできる。
fromを使ってimport相当のことをする場合は、from *ステートメントを使う。
ただし、fromを使った場合でも、import 同様に モジュール全体がロードされた上で、対象の属性がコピーされる。

import モジュール名
from モジュール名 import 属性名
from モジュール名 import *

モジュールのロード/実行処理は、importやfromを使った初回のみ行われる。
2回目以降は既にロード済みのオブジェクトが使われる。
そのため、ロード済みオブジェクトの内容を変更した後に、再度インポートしても内容は初期化されない。
再度ロード/実行処理を行いたい場合は、reload関数を利用するが、
その場合は、ロード済みオブジェクトの内容が初期化されてしまうことを認識しておかないといけない。
(test.py)

#!/usr/bin/env python

x = 1
y = [1, 2]
>>> import test
>>> test.x, test.y
(1, [1, 2])
>>> test.x = 42                # モジュールの属性xの参照するオブジェクトを変更
>>> test.y[0] = 42             # モジュールの属性yの参照先オブジェクトの内容を変更
>>> from test import x, y      # 変更が加えられたオブジェクトの属性x, yをロードする
>>> test.x, test.y
(42, [42, 2])
>>> x, y
(42, [42, 2])
>>> y[1] = 10                  # ローカルの属性yの参照先オブジェクトの内容を変更する
>>> test.x, test.y             # モジュールの属性yとローカルの属性yは参照先を共有しているので影響を受ける
(42, [42, 10])
>>> x, y
(42, [42, 10])
>>> x = 50                     # ローカルの属性xの参照するオブジェクトを変更する
>>> test.x, test.y             # モジュールの属性xの参照するオブジェクトは変更されていないので影響を受けない
(42, [42, 10])
>>> x, y
(50, [42, 10])

上記では、import -> fromの順で実施したが、from -> importの順で実施すると、
x = 42の結果は、test.x には影響を及ぼさない。
これは数値(xの参照先オブジェクト)がimmutableなオブジェクトのため参照するオブジェクトが変更されているためである。
y[0] = 42の結果が、test.y に影響を及ぼすのは[1,2](yの参照先オブジェクト)がmutableだから。

>>> from test import x, y
>>> x, y
(1, [1, 2])
>>> x = 42
>>> y[0] = 42
>>> import test
>>> x, y
(42, [42, 2])
>>> test.x, test.y
(1, [42, 2])
>>> 

importと違ってfromはモジュールの個別の属性をローカルの属性として扱えるようにするため、
以下の点に注意が必要

  • ローカルの属性が元はどこに属していたか分かりにくい(→fromでインポートする属性を局所化する必要がある)
  • ローカルの属性と同名の属性は、ローカルの属性の参照先を上書きする
>>> x = 10
>>> from test import x, y
>>> x
1

名前空間としてのモジュール

モジュールは名前(属性)の集まりで、グローバス変数がそのモジュールの属性。
で、この名前空間はディクショナリとして存在していて、ディクショナリの内容は属性名:値

関数とかクラスも、関数名やクラス名の変数にマッピングされているからモジュールの属性。
モジュールがインポートされコードが実行されることでモジュールオブジェクトが作成され、属性にアクセスできるようになる。

モジュールのコードが実行されるのは初回インポート時のみで、このため再インポートしても属性は初期化されない。

モジュールのリロード

モジュールのコードを再実行したい時にリロードを使う。
そのための関数がreload()で、引数にはモジュールオブジェクトを指定する。
reload()を実行すると、モジュールの名前空間の内容に変更が加えられる。