1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonモジュールを呼び出し可能にする:Caduleのご紹介

1
Last updated at Posted at 2025-12-26

Pythonを書いているとき、Node.jsのあの機能が懐かしいと思ったことはありませんか? そう、モジュールをrequireしたら、すぐに呼び出せるようになるあれです (´▽`)

Node.jsだとこんな感じで書けます:

const messUpThings = require('./mess-up-things');
messUpThings(); // 動くじゃん!

「このパターン嫌いだよ〜」という開発者の方もいると思いますが、実は便利な場面もあるんですよね (´・ω・`)

問題点

例えば、mess_up_thingsみたいな「名前だけで何をするか分かる」ヘルパー関数を書きたいとしましょう。選択肢はいくつかあります:

  1. utilやhelperモジュールを作る: util.pyhelper.pyに入れるパターン。でも、この名前好きじゃない人も多いですよね(特にGo開発者の方、怒られそう...)

  2. 専用のファイルを作る: mess_up_things.pyに置く。でもこうなると...

from mess_up_things import mess_up_things

うわっ、同じ名前を二回も書くの? 面倒くさっ (*´Д`)

こんな感じで書けたら嬉しくないですか?

import mess_up_things
mess_up_things()

スッキリ! ✨

解決策を探る

Pythonの魔法メソッド(magic methods)に詳しい方なら、「オブジェクトに__call__メソッドがあれば呼び出し可能になる」と知っているはず。モジュールもtypes.ModuleTypeのインスタンスだし、__getattr__みたいな魔法メソッドもサポートしてるから、モジュールに直接__call__を定義すれば...?

残念ながら、Pythonはデフォルトではこれをサポートしていないんです (;´Д`

実はPEP 713という提案があって、モジュールレベルの__call__サポートを追加しようとしたのですが...採用されませんでした orz

でも! Pythonのランタイムに詳しい方なら、裏技があることに気づくかもしれません (´∀`)

import sys

class MyModule(sys.modules[__name__].__class__):
    def __call__(self):
        print("Messing up things...")

sys.modules[__name__].__class__ = MyModule

実はランタイムでモジュールの__class__を動的に置き換えると、こういうことができるんです!

...でも、毎回これをコピペするのめんどくさくないですか? (´・ω・`)

Cadule登場!

この面倒なボイラープレートを繰り返さなくて済むように、このシンプルな仕組みをCaduleCallable [Mo]dule Lessの略)というパッケージにまとめました φ(.. )

インストール

pip install cadule

使い方

mess_up_things.pyというファイルを作ります:

import cadule

@cadule
def __call__():
    print("Messing up things...")

そしてPythonのREPLまたは別のスクリプトから:

>>> import mess_up_things
>>> mess_up_things()
Messing up things...
>>> callable(mess_up_things)
True

これだけ! mess_up_thingsモジュール全体が呼び出し可能なオブジェクトになりました (*´▽*) モジュールを呼び出すと、デコレートされたcall`関数が実行されます。

引数を渡したり、戻り値を受け取ることもできます:

import cadule

@cadule
def __call__(target):
    return f"Messing up {target}!"
>>> import mess_up_things
>>> mess_up_things("the database")
'Messing up the database!'

どんな時に使う?

Caduleは特にこんな時に便利です:

  • 単一目的のモジュール: モジュールの主目的が1つの関数を公開することである場合
  • DSLや流れるようなインターフェース: より自然なAPIを作りたい時
  • スクリプトやユーティリティ: コマンドラインツールをより直感的に

注意点

この方法はランタイムでのクラス入れ替えに依存しているので、強力ですが考慮すべき点もあります:

  • Pythonの内部sys.modulesメカニズムを使用しています
  • 動的なクラス代入のため、デバッグが少し複雑になるかもしれません
  • 一部の静的解析ツールはこのパターンを理解できないかも (´・ω・`)

まとめ

Pythonの公式デザイン哲学は呼び出し可能なモジュールを受け入れていませんが、Caduleは少しのランタイム魔法で、このパターンをエレガントに実現できることを証明しています。Node.jsのようにモジュールを呼び出し可能にしたい時、Caduleがきっと役に立つはずです ✨

次にfrom module import functionと書きたくなったら、Caduleを使ってもっとクールで表現力豊かなコードを書いてみませんか? (´∀`)b


GitHub: cadule
PyPI: pip install cadule

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?