【備忘録】importlibを使用した動的なインポート

### 事の発端

以前作成したPython形式のファイルをJSON形式のファイルに書き換えたいと思い立ち，

1. 変換対象のファイルから変数（中身は数字，文字，リスト，None，ブーリアン等）をインポート
2. インポートした変数を辞書型に変更
3. JSON形式で保存

を行う変換コードをPythonで作成してみました．変換対象のPythonファイルが1つであれば，変換コードの文頭で静的にインポートして万事解決でしたが，当該のPythonファイルが複数存在していたため，変換コード内でfor文を使用して一つ一つ変換していく運びになりました．

ところが，文頭以外で別のPythonファイルをインポートする方法を知らなかったため，「importlib」というパッケージを使用して文中で動的にインポートするということにしました．


### 変換対象のファイルの例
このようなファイルが複数個存在しています．また，格納先は
/hoge/src/
内となっています．

```/hoge/src/example.py
# 日付と番号
date = ["20230607", 3]
# 名前
name = "佐藤太郎"

# 項目_1
item_1 = 200
# 項目_2
item_2 = [["松", 1], ["竹", 2], ["梅", 3]]
# 項目_3
item_3 = False
# 項目_4（場合によっては二重リストの可能性あり）
item_4 = None
item_4 = [["序", 4], ["破", 5], ["急", 6]]
```

### 作成中の変換のコード（Python形式）の例

こちらのコードは
/hoge/
内に格納されています．for文を使用して対象ファイル一つ一つに対して動的なインポート作業とJSON形式への出力作業を行うようにしています．

```/hoge/main.py
import os
import json
from importlib import import_module

# インポートするPythonファイルを格納しているディレクトリ
dir_src = "/hoge/src/"
# 保存先のディレクトリ
dir_out = "/hoge/json/"


# インポートするPythonファイル名を与えると，当該ファイル内の変数をインポートし，必要に応じて変数を処理するクラス
class FileReader:
    def __init__(self, name_src: str) -> None:
        """
        Import variables in the input python file

        Args:
            name_src (str): name of the source python file
        """
        # 拡張子を除去したPythonファイルの名前
        name_base = os.path.splitext(name_src)[0]
        # Pythonファイルのインポート
        python_file = import_module("src." + name_base)

        # 日付と番号
        self.date = python_file.date
        # 名前
        self.name = python_file.name
        # 項目_1
        self.number = python_file.item_1
        # 項目_2
        self.list = {
            "item_2-1": {
                "sort": python_file.item_2[0][0],
                "number": python_file.item_2[0][1],
            },
            "item_2-2": {
                "sort": python_file.item_2[1][0],
                "number": python_file.item_2[1][1],
            },
            "item_2-3": {
                "sort": python_file.item_2[2][0],
                "number": python_file.item_2[2][1],
            },
        }
        # 項目_3
        self.boolean = python_file.item_3
        # 項目_4
        if python_file.item_4 == None:
            # Noneであれば変更なし
            self.none = python_file.item_4
        else:
            # 二重リストであれば辞書型に変更
            self.none = {}
            for i in range(0, len(python_file.item_4)):
                self.none[python_file.item_4[i][0]] = python_file.item_4[i][1]


for file in os.listdir(dir_src):
    inp = FileReader(name_src=file)
    # インポートしたデータを辞書型へ変更
    out = {
        "general": {"date": inp.date[0], "trial": inp.date[1], "name": inp.name},
        "items": {
            "item_1": inp.number,
            "item_2": inp.list,
            "item_3": inp.boolean,
            "item_4": inp.none,
        },
    }
    # JSON形式で出力
    json_name = inp.date[0] + "_" + str(inp.date[1]).zfill(2) + ".json"
    json_file = os.path.join(dir_out, json_name)
    with open(json_file, "w") as f:
        json.dump(out, f, indent=2, ensure_ascii=False)
```

### 結果

上手く変換できました．

```/hoge/json/20230607_03.json
{
  "general": {
    "date": "20230607",
    "trial": 3,
    "name": "佐藤太郎"
  },
  "items": {
    "item_1": 200,
    "item_2": {
      "item_2-1": {
        "sort": "松",
        "number": 1
      },
      "item_2-2": {
        "sort": "竹",
        "number": 2
      },
      "item_2-3": {
        "sort": "梅",
        "number": 3
      }
    },
    "item_3": false,
    "item_4": {
      "序": 4,
      "破": 5,
      "急": 6
    }
  }
}
```