可调用模块

78 投票
7 回答
27197 浏览
提问于 2025-04-15 12:34

为什么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

7 个回答

23

正如Miles所说,你需要在类的层面上定义调用。

所以,Alex提到的另一种方法是把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中测试过。

110

Python不允许模块去覆盖或添加任何“魔法方法”,这是因为保持模块对象简单、常规和轻量级是非常有利的,毕竟在实际使用中,能够用到魔法方法的情况非常少。

当这种情况确实出现时,解决办法是让一个类的实例假装成一个模块。具体来说,你可以这样编写你的 mod_call.py 文件:

import sys

class mod_call:
    def __call__(self):
        return 42

sys.modules[__name__] = mod_call()

现在,你的代码在导入和调用 mod_call 时就可以正常工作了。

46

特殊方法只有在它们被定义在类型上时,才会被隐式调用,而不是在实例上。比如,__call__ 是模块实例 mod_call 的一个属性,而不是 <type 'module'> 的属性。你不能给内置类型添加方法。

https://docs.python.org/reference/datamodel.html#special-lookup

撰写回答