代码之家  ›  专栏  ›  技术社区  ›  Roger Pate

可调用模块

  •  78
  • Roger Pate  · 技术社区  · 17 年前

    __call__ a(b) 语法查找 像函数、类和对象那样的属性?(模块的查找是否存在不兼容的差异?)

    >>> 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 回复  |  直到 6 年前
        1
  •  110
  •   Boris Verkhovskiy Brian Clapper    6 年前

    Python不允许模块重写或添加 任何

    mod_call.py 如下:

    import sys
    
    class mod_call:
        def __call__(self):
            return 42
    
    sys.modules[__name__] = mod_call()
    

    现在,您的代码导入和调用 mod_call 工作得很好。

        2
  •  46
  •   Boris Verkhovskiy Brian Clapper    6 年前

    只有在类型上而不是实例上定义了特殊方法时,才能保证隐式调用这些方法。 ( __call__ 是模块实例的属性 mod_call <type 'module'>

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

        3
  •  22
  •   Christoph Boeddeker    8 年前

    正如Miles所说,你需要在类级别定义调用。 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
    

        4
  •  10
  •   Boris Verkhovskiy Brian Clapper    6 年前

    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__

    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()
    

    现在 __文件__ , __name__ 并且定义了其他模块属性(如果只是遵循Alex的答案,则不存在),并且导入的模块对象仍然是一个模块。

        5
  •  6
  •   Friedrich -- Слава Україні    4 年前

    所有答案仅适用于 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 类似的问题。

        6
  •  2
  •   Limina102    5 年前

    要将解决方案转化为方便的可重用功能:

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

    好哇!

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


    P.S.在我写下这个答案的几天后,我从代码中删除了这个技巧,因为像Pylint这样的工具很难理解。

        7
  •  0
  •   Con O'Leary    3 年前

        from types import ModuleType
        
        class CallableModule(ModuleType):
            def __init__(self):
                ModuleType.__init__(self, __name__)
                self.__dict__.update(modules[__name__].__dict__)
        
            def __call__(self):
                print("You just called a module UwU")
          
            mod_call= __call__
            __all__= list(set(vars().keys()) - {'__qualname__'})
        modules[__name__]= CallableModule()