为什么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
答案 0 :(得分:80)
Python不允许模块覆盖或添加任何魔术方法,因为保持模块对象简单,规则和轻量级是非常有利的,因为很少有强大的用例出现在你可以使用魔术方法的地方那里。
当出现 do 这样的用例时,解决方案是将类实例伪装成模块。具体来说,按照以下方式对mod_call.py
进行编码:
import sys
class mod_call(object):
def __call__(self):
return 42
sys.modules[__name__] = mod_call()
现在您的代码导入和调用mod_call
工作正常。
答案 1 :(得分:34)
只保证在类型上定义特殊方法时隐式调用特殊方法,而不是在实例上定义。 (__call__
是模块实例mod_call
的属性,而不是<type 'module'>
的属性。)您无法将方法添加到内置类型。
http://docs.python.org/reference/datamodel.html#special-method-lookup-for-new-style-classes
答案 2 :(得分:7)
正如迈尔斯所说,你需要在课堂级别定义调用。
因此,Alex帖子的替代方法是将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进行测试。
答案 3 :(得分:1)
所有答案仅适用于import mod_call
。为了使它也可以在from mod_call import *
上使用,可以如下增强@Alex Martelli的解决方案
import sys
class mod_call:
def __call__(self):
return 42
mod_call = __call__
__all__ = list(set(vars().keys()) - {'__qualname__'}) # for python 2 and 3
sys.modules[__name__] = mod_call()
此解决方案是通过讨论类似问题的answer得出的。
答案 4 :(得分:0)
Christoph Böddeker's answer似乎是创建可调用模块的最佳方法,但是正如a comment所说,它仅适用于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__
方法将其“广播”到子类中,这正是我们想要的。)
要使它在3.5以下的Python版本中也能类似地工作,您可以改编Alex Martelli's answer以使新类成为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的回答就不存在),并且导入的模块对象仍然“是”模块。
答案 5 :(得分:0)
要将解决方案变成一个方便的可重复使用的功能:
def set_module(cls, __name__):
import sys
class cls2(sys.modules[__name__].__class__, cls):
pass
sys.modules[__name__].__class__ = cls2
将其保存到 util.py
。然后在你的模块中,
import util
class MyModule:
def __call__(self): # module callable
return 42
util.set_module(MyModule, __name__)
万岁!
我写这个是因为我需要用这个技巧来增强很多模块。