可调用模块

51 python module python-import

为什么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
Run Code Online (Sandbox Code Playgroud)

Ale*_*lli 83

Python不允许模块覆盖或添加任何魔术方法,因为保持模块对象简单,规则和轻量级是非常有利的,因为很少有强大的用例出现在那里你可以使用魔术方法.

当这样的使用情况出现,解决的办法是使一个类实例伪装成一个模块.具体来说,编码mod_call.py如下:

import sys

class mod_call:
    def __call__(self):
        return 42

sys.modules[__name__] = mod_call()
Run Code Online (Sandbox Code Playgroud)

现在您的代码导入和调用mod_call工作正常.

  • 注意:使用\ _\_ _ call\_\_ _方法代替类,也可以使用普通函数 (12认同)
  • 您可能希望子类化`types.ModuleType`,以便它仍被视为模块对象.(那你真的会有一个可调用的模块) (9认同)
  • @MichaelScottCuthbert或等效地用`sys.modules [__ name __ = mod_call(__ name __)'替换`sys.modules [__ name __ = mod_call()` (6认同)
  • 注意:此解决方案会隐藏模块中的其他功能(或其他项).这可以通过将它们添加到类来解决 (4认同)
  • 哇。根据您的这个示例发布代码示例几秒钟后:http://stackoverflow.com/questions/880530/can-python-modules-have-properties-the-same-way-that-objects-can/880550 #880550 (2认同)
  • 我喜欢@ TadhgMcDonald-Jensen建议继承`ModuleType`,但要注意,如果你这样做,你需要覆盖`__init__`将模块名称传递给`super().__ init __('moduleName' )` (2认同)

Mil*_*les 35

只有在类型上定义特殊方法时,才能保证特殊方法被隐式调用,而不是在实例上定义.(__call__是模块实例的属性mod_call,而不是<type 'module'>.)您无法将方法添加到内置类型.

http://docs.python.org/reference/datamodel.html#special-method-lookup-for-new-style-classes


Chr*_*ker 10

正如迈尔斯所说,你需要在课堂级别定义调用.因此,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
Run Code Online (Sandbox Code Playgroud)

注意:使用python3.6测试.

  • 这仅适用于Python 3.5及更高版本,因为它依赖于3.5中添加的[特殊情况](https://github.com/python/cpython/blob/v3.5.0/Objects/typeobject.c#L3669)将模块作为C中实现的唯一对象,可以重新分配它们的`__class__`. (3认同)

Nic*_*teo 8

Christoph Bö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
Run Code Online (Sandbox Code Playgroud)

一切正常,包括所有预期的模块属性,如__file__被定义。(这是因为您实际上根本没有更改由导入产生的模块对象,只是将其“转换”为带有__call__方法的子类,这正是我们想要的。)

要使其在低于 3.5 的 Python 版本中类似地工作,您可以修改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()
Run Code Online (Sandbox Code Playgroud)

现在__file____name__定义了其他模块属性(如果只是按照亚历克斯的回答,则不存在),并且您导入的模块对象仍然“是”模块。


Fri*_*ich 6

所有答案仅适用于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()
Run Code Online (Sandbox Code Playgroud)

该解决方案是通过讨论类似问题的答案而得出的。