可调用模块

时间:2009-06-29 22:01:10

标签: python module

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

6 个答案:

答案 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__)

万岁!

我写这个是因为我需要用这个技巧来增强很多模块。