はじめに
この実験では、Python モジュールの基本を学びます。モジュールは、関数、クラス、変数の定義を含む Python ファイルで、他の Python プログラムで使用することができます。モジュールは、コードを論理的な単位に整理し、再利用性を高めるのに役立ちます。
この実験の終了時には、独自のモジュールを作成する方法、様々な方法でモジュールをインポートする方法、およびコードに影響を与える重要なモジュール読み込み動作を理解するようになります。
シンプルなモジュールの作成
Python モジュールの旅に、シンプルなモジュールを作成することから始めましょう。Python では、モジュールは本質的に .py 拡張子を持つ Python コードを含むファイルです。関連する関数、クラス、変数をまとめるコンテナと考えてください。これにより、特にプロジェクトが大規模になるにつれて、コードが整理され、管理しやすくなります。
まず、WebIDE を開きます。開いたら、新しいファイルを作成する必要があります。これを行うには、メニューバーの「File」をクリックし、「New File」を選択します。この新しいファイルを
simplemod.pyと名付け、/home/labex/projectディレクトリに保存します。このディレクトリは、この実験に関連するすべてのファイルを保管する場所です。次に、新しく作成した
simplemod.pyファイルにいくつかのコードを追加しましょう。以下のコードは、Python モジュールによく見られるいくつかの基本要素を定義しています。
## simplemod.py
x = 42 ## グローバル変数
## シンプルな関数
def foo():
print('x is', x)
## シンプルなクラス
class Spam:
def yow(self):
print('Yow!')
## スクリプト文
print('Loaded simplemod')
このコードでは:
x = 42は、xという名前のグローバル変数を作成し、それに値42を割り当てます。グローバル変数は、モジュール内のどこからでもアクセスできます。foo()関数は、グローバル変数xの値を出力するように定義されています。関数は、特定のタスクを実行する再利用可能なコードブロックです。Spamクラスは、オブジェクトを作成するためのブループリントです。yow()という名前のメソッドを持ち、単に文字列 'Yow!' を出力します。メソッドは、クラスに属する関数です。print('Loaded simplemod')文はスクリプト文です。モジュールが読み込まれるとすぐに実行され、モジュールが正常に読み込まれたことを確認するのに役立ちます。
- コードを追加したら、ファイルを保存します。これは、キーボードで
Ctrl+Sを押すか、メニューから「File」>「Save」を選択することで行えます。ファイルを保存することで、行ったすべての変更が保存されます。
このモジュールが含む内容をもう少し詳しく見てみましょう:
- 値が
42のグローバル変数x。この変数はモジュール全体で使用でき、正しくインポートすれば他のモジュールからもアクセスできます。 xの値を出力する関数foo()。関数は、同じコードを何度も書かずに繰り返しタスクを実行するのに便利です。- メソッド
yow()を持つクラスSpam。クラスとメソッドは、オブジェクト指向プログラミングの基本的な概念で、複雑なデータ構造と振る舞いを作成することができます。 - モジュールが読み込まれたときに実行される
print文。この文は、モジュールが Python 環境に正常に読み込まれたことを視覚的に示す役割を果たします。
一番下の print 文は、モジュールがいつ読み込まれるかを観察するのに役立ちます。これは、デバッグや Python でのモジュールの動作を理解するために重要です。
モジュールのインポートと使用
モジュールを作成したので、次はそれをインポートしてそのコンポーネントを使用する方法を理解する時です。Python では、モジュールは Python の定義と文を含むファイルです。モジュールをインポートすると、その中で定義されたすべての関数、クラス、変数にアクセスできるようになります。これにより、コードを再利用し、プログラムをより効果的に整理することができます。
まず、WebIDE で新しいターミナルを開く必要があります。このターミナルは、Python コマンドを実行できる作業スペースとして機能します。新しいターミナルを開くには、「Terminal」>「New Terminal」をクリックします。
ターミナルが開いたら、Python インタープリタを起動する必要があります。Python インタープリタは、Python コードを読み取って実行するプログラムです。起動するには、ターミナルに以下のコマンドを入力して Enter キーを押します。
python3
- Python インタープリタが実行されたら、モジュールをインポートできます。Python では、
import文を使用してモジュールを現在のプログラムに取り込みます。Python インタープリタに以下のコマンドを入力します。
>>> import simplemod
Loaded simplemod
出力に「Loaded simplemod」が表示されることに気づくでしょう。これは、simplemod モジュールの print 文がモジュールが読み込まれたときに実行されるためです。Python がモジュールをインポートすると、そのモジュール内のすべてのトップレベルのコード、print 文を含め、実行されます。
- モジュールをインポートした後、ドット表記を使用してそのコンポーネントにアクセスできます。ドット表記は、Python でオブジェクトの属性(変数と関数)にアクセスする方法です。この場合、モジュールはオブジェクトであり、その関数、変数、クラスはその属性です。
simplemodモジュールのさまざまなコンポーネントにアクセスする方法の例を以下に示します。
>>> simplemod.x
42
>>> simplemod.foo()
x is 42
>>> spam_instance = simplemod.Spam()
>>> spam_instance.yow()
Yow!
最初の行では、simplemod モジュールで定義された変数 x にアクセスしています。2 行目では、simplemod モジュールの関数 foo を呼び出しています。3 行目と 4 行目では、simplemod モジュールで定義された Spam クラスのインスタンスを作成し、そのメソッド yow を呼び出しています。
- モジュールをインポートしようとするときに、
ImportErrorに遭遇することがあります。このエラーは、Python がインポートしようとしているモジュールを見つけることができないときに発生します。Python がモジュールを探している場所を調べるには、sys.path変数を調べることができます。sys.path変数は、Python がモジュールを探すときに検索するディレクトリのリストです。Python インタープリタに以下のコマンドを入力します。
>>> import sys
>>> sys.path
['', '/usr/lib/python310.zip', '/usr/lib/python3.10', '/usr/lib/python3.10/lib-dynload', '/usr/local/lib/python3.10/dist-packages', '/usr/lib/python3/dist-packages']
リストの最初の要素(空文字列)は、現在の作業ディレクトリを表しています。ここが Python が simplemod.py ファイルを探す場所です。モジュールが sys.path にリストされているディレクトリのいずれにもない場合、Python はそれを見つけることができず、ImportError が発生します。simplemod.py ファイルが現在の作業ディレクトリまたは sys.path の他のディレクトリのいずれかにあることを確認してください。
モジュールの読み込み動作の理解
Python では、モジュールの読み込み方法にいくつか興味深い特性があります。このステップでは、これらの動作を調べて、Python がモジュールの読み込みをどのように管理しているかを理解しましょう。
- まず、同じ Python インタープリタセッション内でモジュールを再度インポートしようとしたときに何が起こるかを見てみましょう。Python インタープリタを起動すると、Python コードを実行できる作業スペースが開かれるようなものです。一度モジュールをインポートした後、再度インポートするとモジュールが再読み込みされるように思えるかもしれませんが、実際はそうではありません。
>>> import simplemod
今回は「Loaded simplemod」という出力が表示されないことに注意してください。これは、Python はインタープリタセッションごとにモジュールを一度だけ読み込むからです。その後の import 文はモジュールを再読み込みしません。Python はすでにモジュールを読み込んだことを記憶しているので、再度読み込むプロセスを経ません。
- モジュールをインポートした後、その中の変数を変更することができます。Python のモジュールは、変数、関数、クラスを保持するコンテナのようなものです。モジュールをインポートしたら、他の Python オブジェクトと同じように、その変数にアクセスして変更することができます。
>>> simplemod.x
42
>>> simplemod.x = 13
>>> simplemod.x
13
>>> simplemod.foo()
x is 13
ここでは、まず simplemod モジュール内の変数 x の値を確認します。最初は 42 です。次にその値を 13 に変更し、変更が反映されていることを確認します。モジュール内の foo 関数を呼び出すと、x の新しい値が反映されます。
- モジュールを再度インポートしても、その変数に加えた変更はリセットされません。もう一度モジュールをインポートしようとしても、Python はそれを再読み込みしないので、変数に加えた変更は保持されます。
>>> import simplemod
>>> simplemod.x
13
- モジュールを強制的に再読み込みしたい場合は、
importlib.reload()関数を使用する必要があります。時には、モジュールのコードに変更を加え、それらの変更を即座に反映させたいことがあります。importlib.reload()関数を使用すると、それが可能になります。
>>> import importlib
>>> importlib.reload(simplemod)
Loaded simplemod
<module 'simplemod' from 'simplemod.py'>
>>> simplemod.x
42
>>> simplemod.foo()
x is 42
モジュールが再読み込みされ、x の値が 42 にリセットされました。これは、モジュールがソースコードから再度読み込まれ、すべての変数が元の状態に初期化されたことを示しています。
- Python は、すべての読み込まれたモジュールを
sys.modules辞書に記録します。この辞書は、現在のインタープリタセッション中に読み込まれたすべてのモジュールに関する情報を格納するレジストリのようなものです。
>>> 'simplemod' in sys.modules
True
>>> sys.modules['simplemod']
<module 'simplemod' from 'simplemod.py'>
モジュール名が sys.modules 辞書に含まれているかどうかを確認することで、そのモジュールが読み込まれているかどうかを確認できます。また、モジュール名をキーとして辞書にアクセスすることで、そのモジュールに関する情報を取得できます。
- この辞書からモジュールを削除すると、次回のインポート時に Python がそれを再読み込みするように強制できます。
sys.modules辞書からモジュールを削除すると、Python はそのモジュールをすでに読み込んだことを忘れます。したがって、次にそのモジュールをインポートしようとすると、Python はソースコードから再度読み込みます。
>>> del sys.modules['simplemod']
>>> import simplemod
Loaded simplemod
>>> simplemod.x
42
モジュールは sys.modules から削除されたため、再度読み込まれました。これは、モジュールのコードの最新バージョンを使用することを保証する別の方法です。
from module import 構文の使用
Python では、モジュールからコンポーネントをインポートする方法は様々あります。そのうちの 1 つが from module import 構文で、このセクションではこれについて説明します。
モジュールからコンポーネントをインポートする際には、新しい状態から始めることが良いアイデアです。これにより、以前の操作で残っている変数や設定が現在の実験に影響を与えることがなくなります。
- Python インタープリタを再起動して新しい状態にする:
>>> exit()
このコマンドは現在の Python インタープリタセッションを終了します。終了した後、新しいセッションを開始して新しい環境を確保します。
python3
この bash コマンドは新しい Python 3 インタープリタセッションを開始します。これで新しい Python 環境ができたので、モジュールからコンポーネントをインポートし始めることができます。
from module import構文を使用してモジュールから特定のコンポーネントをインポートする:
>>> from simplemod import foo
Loaded simplemod
>>> foo()
x is 42
ここでは、from simplemod import foo 文を使用して、simplemod モジュールから foo 関数のみをインポートしています。foo 関数のみを要求したにもかかわらず、simplemod モジュール全体が読み込まれたことに注意してください。これは「Loaded simplemod」という出力で示されています。これは、Python が foo 関数にアクセスするためにモジュール全体を読み込む必要があるからです。
from module importを使用すると、モジュール自体にアクセスできない:
>>> simplemod.foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'simplemod' is not defined
from module import 構文を使用すると、指定されたコンポーネントのみが直接名前空間に取り込まれます。モジュール名自体はインポートされません。したがって、simplemod.foo() にアクセスしようとすると、Python は simplemod を認識できず、そのようにインポートされていないためエラーが発生します。
- 一度に複数のコンポーネントをインポートすることができる:
>>> from simplemod import x, foo
>>> x
42
>>> foo()
x is 42
from module import 構文を使用すると、1 つの文でモジュールから複数のコンポーネントをインポートすることができます。ここでは、simplemod モジュールから変数 x と関数 foo の両方をインポートしています。インポート後、これらのコンポーネントにコード内で直接アクセスすることができます。
- モジュールから変数をインポートすると、モジュール内の変数へのリンクではなく、オブジェクトへの新しい参照が作成される:
>>> x = 13 ## ローカル変数 x を変更する
>>> x
13
>>> foo()
x is 42 ## 関数はまだモジュールの x を使用しており、ローカルの x ではない
モジュールから変数をインポートすると、実質的にローカル名前空間内で同じオブジェクトへの新しい参照が作成されます。したがって、ローカル変数 x を 13 に変更しても、simplemod モジュール内の x 変数には影響しません。foo() 関数は依然としてモジュールの x 変数(42)を参照しています。この概念を理解することは、コードでの混乱を避けるために重要です。
モジュール再読み込みの制限を探る
モジュールの再読み込みは Python で便利な機能ですが、特にクラスを扱う際にはいくつかの制限があります。このセクションでは、これらの制限を段階的に調べていきます。これらの制限を理解することは、開発環境と本番環境の両方において重要です。
- Python インタープリタを再起動する:
まず、Python インタープリタを再起動する必要があります。このステップは、新しい状態から始めることを保証するために重要です。インタープリタを再起動すると、以前にインポートされたすべてのモジュールと変数がクリアされます。現在の Python インタープリタを終了するには、
exit()コマンドを使用します。その後、ターミナルでpython3コマンドを使用して新しい Python インタープリタセッションを開始します。
>>> exit()
python3
- モジュールをインポートし、
Spamクラスのインスタンスを作成する: 新しい Python インタープリタセッションができたので、simplemodモジュールをインポートします。モジュールをインポートすることで、そのモジュールで定義されたクラス、関数、変数を使用することができます。モジュールをインポートした後、Spamクラスのインスタンスを作成し、そのyow()メソッドを呼び出します。これにより、クラスの初期の動作を確認することができます。
>>> import simplemod
Loaded simplemod
>>> s = simplemod.Spam()
>>> s.yow()
Yow!
- モジュール内の
Spamクラスを変更しましょう。Python インタープリタを終了する: 次に、simplemodモジュール内のSpamクラスに変更を加えます。その前に、Python インタープリタを終了する必要があります。これは、モジュールのソースコードに変更を加え、それがクラスの動作にどのように影響するかを確認するためです。
>>> exit()
- WebIDE で
simplemod.pyファイルを開き、Spamクラスを変更する: WebIDE でsimplemod.pyファイルを開きます。ここにsimplemodモジュールのソースコードがあります。Spamクラスのyow()メソッドを変更して、別のメッセージを出力するようにします。この変更により、モジュールを再読み込みした後のクラスの動作の変化を観察することができます。
## simplemod.py
## ... (ファイルの残りの部分は変更しないでおく)
class Spam:
def yow(self):
print('More Yow!') ## 'Yow!' から変更
- ファイルを保存し、ターミナルに戻る。Python インタープリタを起動し、新しいインスタンスを作成する:
simplemod.pyファイルに変更を加えた後、保存します。その後、ターミナルに戻り、新しい Python インタープリタセッションを開始します。再度simplemodモジュールをインポートし、Spamクラスの新しいインスタンスを作成します。新しいインスタンスのyow()メソッドを呼び出して、更新された動作を確認します。
python3
>>> import simplemod
Loaded simplemod
>>> t = simplemod.Spam()
>>> t.yow()
More Yow!
- モジュール再読み込みの動作を見てみましょう:
モジュール再読み込みがどのように機能するかを確認するために、
importlib.reload()関数を使用します。この関数を使用すると、以前にインポートしたモジュールを再読み込みすることができます。モジュールを再読み込みした後、Spamクラスに加えた変更が反映されているかを確認します。
>>> import importlib
>>> importlib.reload(simplemod)
Loaded simplemod
<module 'simplemod' from 'simplemod.py'>
- Python を終了し、ファイルを再度変更してから、両方のインスタンスをテストする:
もう一度 Python インタープリタを終了します。その後、
simplemod.pyファイル内のSpamクラスに別の変更を加えます。その後、Spamクラスの古いインスタンスと新しいインスタンスの両方をテストして、それらの動作を確認します。
>>> exit()
simplemod.pyファイルを更新する: 再度simplemod.pyファイルを開き、Spamクラスのyow()メソッドを変更して、別のメッセージを出力するようにします。この変更により、モジュール再読み込みの制限をさらに理解することができます。
## simplemod.py
## ... (ファイルの残りの部分は変更しないでおく)
class Spam:
def yow(self):
print('Even More Yow!') ## 再度変更
- ファイルを保存し、ターミナルに戻る:
simplemod.pyファイルの変更を保存し、ターミナルに戻ります。新しい Python インタープリタセッションを開始し、simplemodモジュールをインポートし、Spamクラスの新しいインスタンスを作成します。新しいインスタンスのyow()メソッドを呼び出して、更新された動作を確認します。
python3
>>> import simplemod
Loaded simplemod
>>> s = simplemod.Spam()
>>> s.yow()
Even More Yow!
>>> ## Python を閉じずに終了し、ファイルを編集する
- Python を閉じずに、WebIDE で
simplemod.pyを開き、変更する: Python インタープリタを閉じずに、WebIDE でsimplemod.pyファイルを開き、Spamクラスのyow()メソッドに別の変更を加えます。これにより、モジュールを再読み込みした後の既存のインスタンスと新しいインスタンスの動作の変化を確認することができます。
## simplemod.py
## ... (ファイルの残りの部分は変更しないでおく)
class Spam:
def yow(self):
print('Super Yow!') ## さらに変更
- ファイルを保存し、Python インタープリタに戻る:
simplemod.pyファイルの変更を保存し、Python インタープリタに戻ります。importlib.reload()関数を使用してsimplemodモジュールを再読み込みします。その後、Spamクラスの古いインスタンスと新しいインスタンスの両方をテストして、それらの動作を確認します。
>>> import importlib
>>> importlib.reload(simplemod)
Loaded simplemod
<module 'simplemod' from 'simplemod.py'>
>>> ## 古いインスタンスを試す
>>> s.yow()
Even More Yow! ## まだ古い実装を使用している
>>> ## 新しいインスタンスを作成する
>>> t = simplemod.Spam()
>>> t.yow()
Super Yow! ## 新しい実装を使用している
古いインスタンス s はまだ古い実装を使用しているのに対し、新しいインスタンス t は新しい実装を使用していることに注意してください。これは、モジュールを再読み込みしても既存のクラスインスタンスは更新されないためです。クラスインスタンスが作成されると、その時点のクラスオブジェクトへの参照が保存されます。モジュールを再読み込むと新しいクラスオブジェクトが作成されますが、既存のインスタンスは依然として古いクラスオブジェクトを参照しています。
- 他の異常な動作も観察できます:
isinstance()関数を使用することで、モジュール再読み込みの制限をさらに観察することができます。この関数は、オブジェクトが特定のクラスのインスタンスであるかどうかを確認します。モジュールを再読み込みした後、古いインスタンスsは新しいsimplemod.Spamクラスのインスタンスとは見なされなくなり、新しいインスタンスtは見なされることがわかります。
>>> isinstance(s, simplemod.Spam)
False
>>> isinstance(t, simplemod.Spam)
True
これは、再読み込み後、simplemod.Spam が s を作成するために使用されたクラスオブジェクトとは異なるクラスオブジェクトを参照していることを示しています。
これらの制限により、モジュール再読み込みは主に開発とデバッグに有用ですが、本番コードでの使用は推奨されません。本番環境では、クラスのすべてのインスタンスが同じ最新の実装を使用することを確保することが重要です。モジュール再読み込みは一貫性のない動作を引き起こす可能性があり、デバッグや保守が困難になります。
まとめ
この実験では、Python のモジュールを扱う基本的な方法を学びました。変数、関数、クラスを含む .py ファイルとして Python モジュールを作成する方法や、import 文を使用してモジュールをインポートする方法を学びました。また、Python がインタープリタセッションごとにモジュールを 1 回だけ読み込むこと、および importlib.reload() を使用して強制的に再読み込みできることも理解しました。
さらに、読み込まれたモジュールを追跡するための sys.modules 辞書を調べ、from module import 構文を使用して特定のコンポーネントをインポートし、特にクラスに関するモジュール再読み込みの制限を理解しました。これらの概念は、Python コードを再利用可能なコンポーネントに整理するための基礎であり、大規模なアプリケーションにおけるコード構造の維持と再利用性の促進に不可欠です。