Python の import
文は普段何気なく使っていますが、実はカスタマイズできることをご存じですか?
今回は、Python の標準インポートシステムをカスタマイズする方法を、初心者でも分かりやすいようにコード付きで解説します!
カスタムインポートの目標
Python の標準 import
機能を置き換えることで、独自のインポートロジックを作る方法を学びます。
これを応用すれば、例えば次のようなことが可能になります:
- 特定のモジュールだけ暗号化してインポート
- ネットワーク越しにリモートモジュールをダウンロード
- Python コードをデータベースから動的にロード
では、実際に試してみましょう!
ステップ 1: 通常のインポートを確認
まず、Python の標準的な import
がどのように動作するかを見てみます。
(1) シンプルなモジュールを作る
mymodule.py
というファイルを作成し、次のように書きます。
# mymodule.py
def hello():
print("Hello from mymodule!")
(2) import
して実行
次に、このモジュールをインポートし、関数を呼び出してみましょう。
import mymodule
mymodule.hello()
出力
Hello from mymodule!
ステップ 2: sys.meta_path を使ってカスタムインポート
Python では、sys.meta_path
を使うことでインポートの動作を変更できます。
(1) インポートフックを定義
まず、カスタムのインポートローダーを作成してみます。
次のコードを custom_importer.py
というファイルに保存してください。
import sys
import importlib.abc
import importlib.util
import os
class CustomImporter(importlib.abc.MetaPathFinder):
def find_spec(self, fullname, path, target=None):
print(f"カスタムインポート試行中: {fullname}")
# `mymodule` を特別扱いする
if fullname == "mymodule":
filename = fullname + ".py"
if filename in os.listdir():
print(f"{filename} をカスタムローダーでロード")
spec = importlib.util.spec_from_file_location(fullname, filename)
return spec
return None # 他のモジュールは通常の `import` に任せる
# `sys.meta_path` にカスタムインポーターを追加
sys.meta_path.insert(0, CustomImporter())
この CustomImporter
クラスは mymodule
を見つけると、その場所を教えるだけのシンプルなカスタムファインダーです。
(2) カスタムインポートの動作を確認
次に、この custom_importer.py
をスクリプト内で実行し、mymodule
をインポートしてみましょう。
import custom_importer # これでカスタムインポートが有効になる
import mymodule # `mymodule` のインポートを試す
mymodule.hello()
出力
カスタムインポート試行中: mymodule
mymodule.py をカスタムローダーでロード
Hello from mymodule!
このように、通常の import
を横取りして、カスタムローダーが mymodule.py
をロードしていることが分かります。
ステップ 3: カスタムローダーを追加
find_spec
だけでは、モジュールの「場所」を見つけることしかできません。
実際にモジュールをロードするには、カスタムローダーが必要です。
そこで、ローダーを作成し、インポート時の挙動を制御してみましょう。
class CustomLoader(importlib.abc.Loader):
def create_module(self, spec):
return None # デフォルトのモジュール作成を使用
def exec_module(self, module):
print(f"{module.__name__} をカスタムローダーで実行中")
with open(module.__name__ + ".py", "r", encoding="utf-8") as f:
exec(f.read(), module.__dict__)
class CustomImporter(importlib.abc.MetaPathFinder):
def find_spec(self, fullname, path, target=None):
print(f"カスタムインポート試行中: {fullname}")
filename = fullname + ".py"
if filename in os.listdir():
print(f"{filename} をカスタムローダーでロード")
spec = importlib.util.spec_from_file_location(fullname, filename, loader=CustomLoader())
return spec
return None # 他のモジュールは通常の `import` に任せる
# `sys.meta_path` にカスタムインポーターを追加
sys.meta_path.insert(0, CustomImporter())
このコードを使うと、インポート時に exec_module
が呼ばれ、mymodule.py
のコードが実行されます。
ステップ 4: 実際に動かしてみる
スクリプトを実行すると、次のような出力が得られます。
import custom_importer # カスタムインポーターを登録
import mymodule # `mymodule` のインポートを試す
mymodule.hello()
出力
カスタムインポート試行中: mymodule
mymodule.py をカスタムローダーでロード
mymodule をカスタムローダーで実行中
Hello from mymodule!
これで、カスタムローダーが mymodule
を適切に処理できることが確認できました!
まとめ
sys.meta_path
にカスタムファインダーを登録すると、import
の動作を変更できる。find_spec
を実装すれば、特定のモジュールの検索方法をカスタマイズできる。Loader
を実装すると、インポートしたモジュールの実行を制御できる。
この方法を応用すると、例えば:
.zip
ファイル内の Python モジュールを直接インポート- ネットワーク経由でモジュールを取得して実行
- モジュールのコードを暗号化・復号化して安全にインポート
など、いろいろなカスタムインポートの仕組みを作れます!
Python の柔軟なインポートシステムを活用して、独自の機能を実装してみてください! 🚀