Python3エンジニア基礎試験_模擬試験の復習⑥

# モジュールとは

<font color="Red">**モジュール**</font>とは、関数のような機能をまとめ、必要な時に読み込んで使えるようにする仕組みです。
拡張子に`.py`を指定したファイルなどに関数などを定義することでモジュールとして使用できます。
モジュールを使用するには、初めに`import`してモジュールを読み込む必要があります。

https://docs.python.org/ja/3/tutorial/modules.html#modules

```import.py
import math
math.sqrt(4) # sqrt()は平方根を計算するための関数
# 2.0
```

# インポートの仕組み

モジュールのグローバル変数には、`__name__=モジュール名`という内容が格納されています。
この`__name__`にセットされているモジュール名を使って`import モジュール名`を実行することで、そのモジュール内の関数が一度だけ実行されます。これにより、モジュール内に定義されている関数などが利用できるようになります。

`globals()`関数で、importしてあるモジュールを確認できます。
globals()は、現在のグローバルスコープ内の全ての変数や関数を含む辞書を返す組み込み関数です。

```globals.py
globals()
#  'math': <module 'math' (built-in)>,
```

※globals()関数は、デバッグやインスペクション、動的な変数操作など、さまざまな目的で使用されます。ただし、グローバルスコープはプログラム内のどこからでもアクセス可能であるため、注意が必要です。プログラムの大規模化や保守性の向上を考える場合には、適切なスコープやモジュールの設計を検討することが重要です。

# インポート方法

1. モジュール自体をインポートする。
2. モジュール内の関数を直接インポートする
3. モジュール内の名前をすべて取り込む
   →`アンダーバー（_）`以外で始まるすべての関数やクラスがインポートされます。

```import1.py
import <モジュール名> # 1の例
```

```import2.py
import <モジュール名> from <関数名> # 2の例
```

```import3.py
import <モジュール名> from * # 3の例
```

# モジュールをスクリプトとして実行する方法

①猫の年齢を人間の年齢に換算する関数を、別ファイルで`cats_age.py`として作成する。

```cats_age.py
def cats_age(cat_age):
    return 24 + (cat_age - 2) * 4
```

②このモジュールをスクリプトとして実行できるよう以下のコードを追記します。

```cats_age.py
if __name__ == "__main__":
    import sys
    print(cats_age(int(sys.argv[1])))
```

● 追記したコードの解説

1. `__name__ `変数は、モジュールの実行方法に応じて特定の値を持ちます。
直接実行されたスクリプトの場合、 `__name__ `変数は `"__main__"` となります。
この条件を利用して、<font color="Red">**スクリプトが直接実行されたか他のモジュールからインポートされたかを判断しています。**</font>

2. `if __name__ == "__main__": `の条件文は、スクリプトが直接実行された場合のみ以下のコードブロックが実行されるようにします。ファイルをモジュールとしてではなくスクリプトとして実行したい場合は、この構文をモジュールに追記します。

3. `import sys` は、Pythonの標準ライブラリである `sys` モジュールをインポートしています。このモジュールには、コマンドライン引数を取得するための `argv` 属性が含まれています。

4. `sys.argv[1]` は、スクリプトのコマンドライン引数の2番目（インデックス1）を取得しています。コマンドライン引数は文字列として渡されるため、 `int() `関数を使って整数に変換しています。

5. `cats_age() `関数は、引数として与えられた年齢を使って、人間の年齢に換算する関数。

6. `print(cats_age(int(sys.argv[1])))` は、`cats_age()`関数に引数を渡してその結果を表示しています。

③Goolge Colaboratory（以下、Colabo）で実行してみる。
※Colaboで実行する場合は、先頭に`!`をつける必要があります。

```colabo.py
!python cats_age.py 3
# 28
```

※ここで、globals()関数を実行してみると、下記のようになっている。

```globals.py
globals()
#  '!python cats_age.py 3',
```

# モジュールの検索パスについて

モジュールの検索パスとは、Pythonがモジュールを探索するための場所や順序を定義するリストです。

● 基本的には、以下の4つの順で検索される。

1. 組み込みモジュールの検索パス：
Pythonは最初に組み込みのモジュール（例：`math`や`sys`など）を検索します。これらのモジュールはPythonのインストールに含まれており、常に利用可能です。

2. カレントディレクトリの検索：
Pythonは現在の作業ディレクトリ（スクリプトが実行されたディレクトリ）を検索パスの最初に追加します。これにより、スクリプトと同じディレクトリにあるモジュールをインポートすることができます。


3. PYTHONPATHの検索：
`PYTHONPATH` 環境変数に指定されたディレクトリが、次に検索パスに追加されます。`PYTHONPATH` 環境変数は、カンマで区切られたディレクトリのリストを指定することができます。指定されたディレクトリ内のモジュールが利用可能となります。

4. インストールされたモジュールの検索：
Pythonは、Pythonのインストール場所やサードパーティライブラリのインストール場所など、事前に指定されたディレクトリを検索します。これにより、グローバルにインストールされたモジュールを利用できます。

● モジュールの検索パスは`sys.path`に登録されています。

```sys_path.py
import sys
print(sys.path)
# ['/content', '/env/python', '/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', '/usr/local/lib/python3.10/dist-packages/IPython/extensions', '/root/.ipython']
```

`/content`は、Colaboでのデフォルトの作業フォルダです。

新規フォルダを作成して、そのフォルダでモジュールを作成して実行した場合
下記のようなエラーが発生することがあります。

```error.py
# ModuleNotFoundError: No module named 'モジュール名'
```

これは、`sys.path`にパスを追加していないためにエラーが発生しています。
`sys.path`にパスを追加する必要があります。

```sys_path.py
import sys
sys.path.append("new_folder/") # new_folder/は新規フォルダ名
```

再度、`sys.path`を確認すると、new_folderが追加されているのが分かります。

```sys_path.py
import sys
print(sys.path)
# ['/content', '/env/python', '/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', '/usr/local/lib/python3.10/dist-packages/IPython/extensions', '/root/.ipython', 'new_folder/']
```

# モジュールのキャッシュ機能について

Pythonには、コンパイル済みのモジュールをキャッシュすることで<font color="Red">**モジュールの読み込みを高速化**</font>する機能があります。コンパイル済みのモジュールは`__pycache__`に保存されます。

Colaboで`!ls -a`と実行すると、`__pycache__`が作成されているか分かります。
作成されていない場合は、`!python -m compileall`でモジュールのコンパイルを実行できます。
`cats_age.py`をコンパイルしてみます。

```colabo.py
!python -m compileall cats_age.py
!ls -a # 作成されたか確認
# .   cats_age.py  .ipynb_checkpoints  __pycache__
# ..  .config	 new_folder	     sample_data
```

`__pycache__`の中身を確認してみます。

```colabo.py
!ls __pycache__/
# cats_age.cpython-310.pyc
```

この`.pyc`というファイルにコンパイル済みのモジュールがキャッシュされています。
Pythonではこのファイルを参照することで、モジュールの読み込みを高速化しています。

# dir()関数について

`dir()` 関数は、オブジェクトが持つ属性やメソッドの一覧を返す組み込み関数です。これにより、Pythonのオブジェクトの構造や利用可能な属性やメソッドを調べることができます。
（モジュールが定義している名前を確認することができる。）

```cats_age.py
import cats_age
dir(cats_age)
# ['__builtins__','__cached__','__doc__','__file__','__loader__','__name__','__package__','__spec__','cats_age']
```

dir() 関数は、デバッグやオブジェクトの構造の理解、ドキュメントの調査など、さまざまな目的で使用されます。また、特定のオブジェクトの属性やメソッドを調べる際にも便利です。