Python:确定实际的当前模块(不是 __main__)

问题描述 投票:0回答:6

我正在尝试确定函数的实际当前模块(如从其他地方导入时看到的),即使当前模块是“顶级脚本环境

__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)。我试图避免这种情况,因为它对于我的包的用户来说可能不直观。

python python-module inspection
6个回答
11
投票

我知道这已经过时了,但我在 Python3 中找到了一个对我有用的更简单的解决方案。长话短说,对象的 __spec__ 还存储实际的模块名称,而不是“__main__”。

import inspect
if obj.__class_.__module__ == "__main__":
    print(inspect.getmodule(obj).__spec__.name)

8
投票

我实际上遇到了同样的问题。

我用的是:

return os.path.splitext(os.path.basename(__main__.__file__))[0]

这实际上与您的“黑客”相同。老实说,我认为这是最好的解决方案。


6
投票

编辑:回想起来,迄今为止最好、最干净的解决方案是首先避免处于这种情况;如果正在序列化的是您的代码,请将所有可序列化函数移至主程序脚本加载的模块。这使得在任何情况下都可以检索该函数的起源,而无需任何黑客攻击或特殊情况。

如果这是不可能的,我认为你原来的解决方案(从

__main__.__file__
检索模块名称)是最好和最简单的。如果您担心它对用户来说似乎违反直觉,请将其包装在一个不错的函数中并记录它的用途。

当您以

__main__
身份运行模块时,Python 实际上不会将其与其正常的模块名称关联起来:如果您
import example
,它将再次加载该文件,就好像它是一个单独的模块一样。事实上,这可能会发生在您的情况下,否则您将无法在
sys.modules
中按名称找到您的模块: Module
example
和 module
__main__
确实是单独的运行时对象,如果您这样做,您会发现显式更改其中之一的模块变量。


2
投票

你可以做到这一点的一种方法 - 可能不是最好的方法,但它对我有用 - 是使用

__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

同样,这可能是也可能不是“理想”的方式,但它运作良好,据我所知,在大多数情况下不会带来任何不良的权衡。希望这有帮助。


1
投票

我认为任何现有的答案实际上都没有直接回答这个问题:当模块作为

__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


-1
投票

从 python 3.4 开始,

importlib.util.find_spec('__main__').name
© www.soinside.com 2019 - 2024. All rights reserved.