我正在尝试确定函数的实际当前模块(如从其他地方导入时看到的),即使当前模块是“顶级脚本环境”
__main__
。
这听起来可能是一件奇怪的事情,但背景是我需要序列化一个函数并在另一台机器上反序列化它(包括参数),为此我需要确保正确的模块而不是
__main__
是在反序列化之前导入(否则我会收到错误消息 AttributeError: 'module' object has no attribute my_fun
)。
到目前为止,我已经尝试过检查:
import inspect
print inspect.getmodule(my_fun)
这给了我
<module '__main__' from 'example.py'>
当然。我也尝试使用
globals()
找到一些有用的东西,但没有运气。
我真正想要的是
<module 'example' from 'example.py'>
。我想一种巧妙的方法是使用类似 的内容从文件名中解析它
m_name = __main__.__file__.split("/")[-1].replace(".pyc","")
然后按名称找到模块
sys.modules[m_name]
。
有没有更干净/更好的方法来做到这一点?
编辑: 在了解了 ipython 的“FakeModule”并进行了更多的谷歌搜索之后,我发现了this post,它准确地描述了我面临的问题,包括我当前的解决方案(显式导入当前模块
import current_module
和序列化 current_module.my_fun
而不是 my_fun)。我试图避免这种情况,因为它对于我的包的用户来说可能不直观。
我知道这已经过时了,但我在 Python3 中找到了一个对我有用的更简单的解决方案。长话短说,对象的 __spec__ 还存储实际的模块名称,而不是“__main__”。
import inspect
if obj.__class_.__module__ == "__main__":
print(inspect.getmodule(obj).__spec__.name)
我实际上遇到了同样的问题。
我用的是:
return os.path.splitext(os.path.basename(__main__.__file__))[0]
这实际上与您的“黑客”相同。老实说,我认为这是最好的解决方案。
编辑:回想起来,迄今为止最好、最干净的解决方案是首先避免处于这种情况;如果正在序列化的是您的代码,请将所有可序列化函数移至主程序脚本加载的模块。这使得在任何情况下都可以检索该函数的起源,而无需任何黑客攻击或特殊情况。
如果这是不可能的,我认为你原来的解决方案(从
__main__.__file__
检索模块名称)是最好和最简单的。如果您担心它对用户来说似乎违反直觉,请将其包装在一个不错的函数中并记录它的用途。
当您以
__main__
身份运行模块时,Python 实际上不会将其与其正常的模块名称关联起来:如果您 import example
,它将再次加载该文件,就好像它是一个单独的模块一样。事实上,这可能会发生在您的情况下,否则您将无法在 sys.modules
中按名称找到您的模块: Module example
和 module __main__
确实是单独的运行时对象,如果您这样做,您会发现显式更改其中之一的模块变量。
你可以做到这一点的一种方法 - 可能不是最好的方法,但它对我有用 - 是使用
__import__
导入模块并以如下方式使用 getattr
。
(这里我使用这篇文章中描述的一些关于动态加载模块的想法。)
def dynamic_import(name):
mod = __import__(name)
components = name.split('.')
for comp in components[1:]:
mod = getattr(mod, comp)
return mod
tmodule = dynamic_import('modA')
# Print the module name
print tmodule
# Use the module's contents
t = tmodule.myObject()
t.someMethod()
modA.py 看起来像这样:
class myObject():
def someMethod(self):
print "I am module A"
因此您可以看到我们获取了导入的模块的名称,并且仍然以正常方式使用模块内的对象和方法。当我运行这个时,我得到以下信息:
python experiment.py
<module 'modA' from 'modA.pyc'>
I am module A
同样,这可能是也可能不是“理想”的方式,但它运作良好,据我所知,在大多数情况下不会带来任何不良的权衡。希望这有帮助。
我认为任何现有的答案实际上都没有直接回答这个问题:当模块作为
__main__
运行时,如何获取模块的名称?
对大部分步骤使用检查...
import inspect
def module_name(obj):
module_name = obj.__module__
if "__main__" in module_name:
# get parent modules of object
mod_obj = inspect.getmodule(obj) # type: module
# from the filename of the module, get its name
mod_suffix = inspect.getmodulename(inspect.getmodule(obj).__file__)
# join parent to child with a .
module_name = '.'.join([mod_obj.__package__, mod_suffix])
return module_name
编辑:正如 bluenote10 下面指出的那样,如果你没有对导入路径和模块名称做任何奇怪的事情,你可以这样做
inspect.getmodule(obj).__spec__.name
从 python 3.4 开始,
importlib.util.find_spec('__main__').name