可调用模块

发布于 2024-07-26 07:14:03 字数 587 浏览 6 评论 0 原文

为什么 Python 不允许模块有 __call__< /a> 方法? (显然,直接导入并不容易。)具体来说,为什么不使用 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

Why doesn't Python allow modules to have a __call__ method? (Beyond the obvious that it wouldn't be easy to import directly.) Specifically, why doesn't using a(b) syntax find the __call__ attribute like it does for functions, classes, and objects? (Is lookup just incompatibly different for modules?)

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

心不设防 2024-08-02 07:14:03

Python 不允许模块重写或添加任何魔术方法,因为考虑到可以使用魔术方法的强用例很少出现,保持模块对象简单、常规和轻量级是非常有利的。

当出现此类用例时,解决方案是将类实例伪装成模块。 具体来说,对 mod_call.py 进行如下编码:

import sys

class mod_call:
    def __call__(self):
        return 42

sys.modules[__name__] = mod_call()

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

Python doesn't allow modules to override or add any magic method, because keeping module objects simple, regular and lightweight is just too advantageous considering how rarely strong use cases appear where you could use magic methods there.

When such use cases do appear, the solution is to make a class instance masquerade as a module. Specifically, code your mod_call.py as follows:

import sys

class mod_call:
    def __call__(self):
        return 42

sys.modules[__name__] = mod_call()

Now your code importing and calling mod_call works fine.

放低过去 2024-08-02 07:14:03

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

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

Special methods are only guaranteed to be called implicitly when they are defined on the type, not on the instance. (__call__ is an attribute of the module instance mod_call, not of <type 'module'>.) You can't add methods to built-in types.

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

尘世孤行 2024-08-02 07:14:03

正如迈尔斯所说,您需要在类级别定义调用。
因此,Alex post 的替代方法是将 sys.modules[__name__] 类更改为 sys.modules[__name__] 类型的子类(应该是 <代码>类型.ModuleType)。

这样做的优点是模块是可调用的,同时保留模块的所有其他属性(例如访问函数、变量……)。

import sys

class MyModule(sys.modules[__name__].__class__):
    def __call__(self):  # module callable
        return 42

sys.modules[__name__].__class__ = MyModule

注:使用python3.6测试。

As Miles says, you need to define the call on class level.
So an alternative to Alex post is to change the class of sys.modules[__name__] to a subclass of the type of sys.modules[__name__] (It should be types.ModuleType).

This has the advantage that the module is callable while keeping all other properties of the module (like accessing functions, variables, ...).

import sys

class MyModule(sys.modules[__name__].__class__):
    def __call__(self):  # module callable
        return 42

sys.modules[__name__].__class__ = MyModule

Note: Tested with python3.6.

看轻我的陪伴 2024-08-02 07:14:03

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

一切正常,包括定义所有预期的模块属性,例如 __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()

现在定义了 __file____name__ 和其他模块属性(如果仅遵循 Alex 的答案,则这些属性不存在),并且您导入的模块对象仍然“是一个”模块。

Christoph Böddeker's answer seems to be the best way to create a callable module, but as a comment says, it only works in Python 3.5 and up.

The benefit is that you can write your module like normal, and just add the class reassignment at the very end, i.e.

# 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

and everything works, including all expected module attributes like __file__ being defined. (This is because you're actually not changing the module object resulting from the import at all, just "casting" it to a subclass with a __call__ method, which is exactly what we want.)

To get this to work similarly in Python versions below 3.5, you can adapt Alex Martelli's answer to make your new class a subclass of ModuleType, and copy all the module's attributes into your new module instance:

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

Now __file__, __name__ and other module attributes are defined (which aren't present if just following Alex's answer), and your imported module object still "is a" module.

不弃不离 2024-08-02 07:14:03

所有答案仅适用于 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()

该解决方案是通过 类似问题的答案

All answers work only for import mod_call. To get it working simultaneously for from mod_call import *, the solution of @Alex Martelli can be enhanced as follow

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

This solution was derived with the discussion of an answer of a similar problem.

放血 2024-08-02 07:14:03

要将解决方案转变为方便的可重用函数:

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

万岁!

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


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

To turn the solution into a convenient reusable function:

def set_module(cls, __name__):
    import sys

    class cls2(sys.modules[__name__].__class__, cls):
        pass

    sys.modules[__name__].__class__ = cls2

save it to, say, util.py. Then in your module,

import util

class MyModule:
    def __call__(self):  # module callable
        return 42

util.set_module(MyModule, __name__)

Hurray!

I wrote this because I need to enhance a lot of modules with this trick.


P.S. Few days after I wrote this answer, I removed this trick from my code, since it is so tricky for tools like Pylint to understand.

⒈起吃苦の倖褔 2024-08-02 07:14:03

使用 Python 版本 3.10.8,按如下方式格式化我的代码使我能够:

  • 使我的模块可调用(谢谢 Alex
  • 保留所有模块可访问的属性(例如方法)(谢谢 Nick
  • 允许模块在用作 CallableModule 中的 时工作import x, y, z,尽管不是 from CallableModule import * (我尝试使用 弗里德里希的解决方案
    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()

Using Python version 3.10.8, formatting my code as below allowed me to:

  • Make my module callable (thanks Alex)
  • Keep all properties of the module accessible (e.g. methods) (thanks Nick)
  • Allow the module to work when used as from CallableModule import x, y, z, although not as from CallableModule import * (I tried to employ Friedrich's solution)
    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()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文