可调用模块
为什么 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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
Python 不允许模块重写或添加任何魔术方法,因为考虑到可以使用魔术方法的强用例很少出现,保持模块对象简单、常规和轻量级是非常有利的。
当出现此类用例时,解决方案是将类实例伪装成模块。 具体来说,对
mod_call.py
进行如下编码:现在,您的代码导入和调用
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:Now your code importing and calling
mod_call
works fine.特殊方法只有在类型上定义时才能保证被隐式调用,而不是在实例上定义。 (
__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 instancemod_call
, not of<type 'module'>
.) You can't add methods to built-in types.https://docs.python.org/reference/datamodel.html#special-lookup
正如迈尔斯所说,您需要在类级别定义调用。
因此,Alex post 的替代方法是将 sys.modules[__name__] 类更改为 sys.modules[__name__] 类型的子类(应该是 <代码>类型.ModuleType)。
这样做的优点是模块是可调用的,同时保留模块的所有其他属性(例如访问函数、变量……)。
注:使用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 ofsys.modules[__name__]
(It should betypes.ModuleType
).This has the advantage that the module is callable while keeping all other properties of the module (like accessing functions, variables, ...).
Note: Tested with python3.6.
Christoph Böddeker 的答案似乎是创建可调用模块的最佳方法,但评论说,它仅适用于Python 3.5及更高版本。
好处是您可以像平常一样编写模块,只需在最后添加类重新分配即可,即
一切正常,包括定义所有预期的模块属性,例如
__file__
。 (这是因为您实际上根本没有更改导入产生的模块对象,只是使用__call__
方法将其“转换”到子类,这正是我们想要的。)这在低于 3.5 的 Python 版本中工作类似,您可以调整 Alex Martelli 的答案 使您的新类成为 ModuleType 的子类,并将所有模块的属性复制到新的模块实例中:
现在定义了
__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.
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:
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.所有答案仅适用于
import mod_call
。 为了让它同时工作from mod_call import *
,@Alex Martelli 的解决方案可以如下增强:该解决方案是通过 类似问题的答案。
All answers work only for
import mod_call
. To get it working simultaneously forfrom mod_call import *
, the solution of @Alex Martelli can be enhanced as followThis solution was derived with the discussion of an answer of a similar problem.
要将解决方案转变为方便的可重用函数:
将其保存到,例如,
util.py
。 然后在你的模块中,万岁!
我写这篇文章是因为我需要用这个技巧增强很多模块。
PS 在我写完这个答案几天后,我从代码中删除了这个技巧,因为像 Pylint 这样的工具很难理解它。
To turn the solution into a convenient reusable function:
save it to, say,
util.py
. Then in your module,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.
使用 Python 版本 3.10.8,按如下方式格式化我的代码使我能够:
时工作import x, y, z
,尽管不是from CallableModule import *
(我尝试使用 弗里德里希的解决方案)Using Python version 3.10.8, formatting my code as below allowed me to:
from CallableModule import x, y, z
, although not asfrom CallableModule import *
(I tried to employ Friedrich's solution)