なぜPythonモジュールに___call__
_を許可しないのですか?(直接インポートするのは簡単ではないことは明らかです。)具体的には、なぜa(b)
構文は、関数、クラス、およびオブジェクトの場合と同じように___call__
_属性を見つけますか?(ルックアップはモジュールと互換性がないだけですか?)
_>>> print open("mod_call.py").read()
def __call__():
return 42
>>> import mod_call
>>> mod_call()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'module' object is not callable
>>> mod_call.__call__()
42
_
特別なメソッドは、インスタンスではなく、型で定義されている場合にのみ暗黙的に呼び出されることが保証されています。 (__call__
はモジュールインスタンスmod_call
の属性であり、<type 'module'>
の属性ではありません。)組み込み型にメソッドを追加することはできません。
http://docs.python.org/reference/datamodel.html#special-method-lookup-for-new-style-classes
Pythonでは、モジュールがany magicメソッドをオーバーライドまたは追加することはできません。モジュールオブジェクトをシンプル、規則的、軽量に保つことは、魔法のメソッドを使用できる強力なユースケースがめったに表示されないことを考えると、あまりにも有利だからです。
このようなユースケースdoが表示された場合の解決策は、クラスインスタンスをモジュールに見せかけることです。具体的には、mod_call.py
を次のようにコーディングします。
import sys
class mod_call(object):
def __call__(self):
return 42
sys.modules[__name__] = mod_call()
これで、コードのインポートとmod_call
の呼び出しが正常に機能します。
Milesが言うように、クラスレベルで呼び出しを定義する必要があります。したがって、Alex postの代わりに、sys.modules[__name__]
のクラスをsys.modules[__name__]
のタイプのサブクラスに変更することもできます(types.ModuleType
である必要があります)。
これには、モジュールの他のすべてのプロパティ(関数、変数へのアクセスなど)を維持しながら、モジュールを呼び出すことができるという利点があります。
import sys
class MyModule(sys.modules[__name__].__class__):
def __call__(self): # module callable
return 42
sys.modules[__name__].__class__ = MyModule
注:python3.6でテスト済み。
ChristophBöddekerの答え 呼び出し可能なモジュールを作成するための最良の方法のようですが、 コメント が言うように、それはPython 3.5以上。
利点は、通常のようにモジュールを記述でき、最後にクラスの再割り当てを追加するだけです。
# coolmodule.py
import stuff
var = 33
class MyClass:
...
def function(x, y):
...
class CoolModule(types.ModuleType):
def __call__(self):
return 42
sys.modules[__name__].__class__ = CoolModule
__file__
のような予想されるすべてのモジュール属性が定義されていることを含め、すべてが機能します。 (これは、インポートの結果としてモジュールオブジェクトを実際に変更するのではなく、__call__
メソッドを使用してサブクラスに「キャスト」するだけであるためです。これはまさに私たちが望んでいることです。)
これをPythonバージョン3.5未満で同様に機能させるには、 Alex Martelliの回答 を適応させて新しいクラスをModuleTypeのサブクラスにし、すべてのモジュールの属性をコピーします。新しいモジュールインスタンスに:
#(all your module stuff here)
class CoolModule(types.ModuleType):
def __init__(self):
types.ModuleType.__init__(self, __name__)
# or super().__init__(__name__) for Python 3
self.__dict__.update(sys.modules[__name__].__dict__)
def __call__(self):
return 42
sys.modules[__name__] = CoolModule()
これで、__file__
、__name__
およびその他のモジュール属性が定義され(Alexの回答に従っている場合は存在しません)、インポートされたモジュールオブジェクトは引き続き「モジュール」です。