简短回答:使用proxy_tools
proxy_tools 包尝试提供@module_property 功能。
它安装在
pip install proxy_tools
对@Marein 的示例稍作修改,在the_module.py 中我们将
from proxy_tools import module_property
@module_property
def thing():
print(". ", end='') # Prints ". " on each invocation
return 'hello'
现在从另一个脚本,我可以做到
import the_module
print(the_module.thing)
# . hello
意外行为
此解决方案并非没有注意事项。也就是说,the_module.thing 是不是字符串!它是一个proxy_tools.Proxy 对象,其特殊方法已被覆盖,因此它模仿了一个字符串。以下是一些说明这一点的基本测试:
res = the_module.thing
# [No output!!! Evaluation doesn't occur yet.]
print(type(res))
# <class 'proxy_tools.Proxy'>
print(isinstance(res, str))
# False
print(res)
# . hello
print(res + " there")
# . hello there
print(isinstance(res + "", str))
# . True
print(res.split('e'))
# . ['h', 'llo']
在内部,原函数存储到the_module.thing._Proxy__local:
print(res._Proxy__local)
# <function thing at 0x7f729c3bf680>
进一步的想法
老实说,我对为什么模块没有内置此功能感到困惑。我认为问题的症结在于the_module 是types.ModuleType 类的一个实例。设置“模块属性”相当于在此类的 instance 上设置属性,而不是在 types.ModuleType 类本身上设置属性。详情请见this answer。
我们实际上可以在types.ModuleType 上实现属性,如下所示,虽然结果不是很好。我们不能直接修改内置类型,但是可以curse他们:
# python -m pip install forbiddenfruit
from forbiddenfruit import curse
from types import ModuleType
# curse has the same signature as setattr.
curse(ModuleType, "thing2", property(lambda module: f'hi from {module.__name__}'))
这给了我们一个存在于所有模块中的属性。这有点笨拙,因为我们打破了所有模块的设置行为:
import sys
print(sys.thing2)
# hi from sys
sys.thing2 = 5
# AttributeError: can't set attribute