【问题标题】:Python: determine actual current module (not __main__)Python:确定实际的当前模块(不是 __main__)
【发布时间】:2012-05-31 13:07:41
【问题描述】:

我正在尝试确定函数的实际当前模块(如从其他地方导入的那样),即使当前模块是“top level scripting environment__main__

这听起来可能很奇怪,但背景是我需要在另一台机器上序列化一个函数并反序列化它(包括参数),为此我需要确保正确的模块而不是@987654325 @ 在反序列化之前导入(否则我会收到错误消息 AttributeError: 'module' object has no attribute my_fun)。

到目前为止,我已经尝试过inspection

import inspect
print inspect.getmodule(my_fun)

这给了我

<module '__main__' from 'example.py'>

当然。我还尝试使用globals() 找到有用的东西,但运气不好。

我真正想要的是&lt;module 'example' from 'example.py'&gt;。我想一个 hacky 方法是使用类似

的东西从文件名中解析它
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)。我试图避免这种情况,因为它对我的包的用户来说可能不直观。

【问题讨论】:

  • 你以前看过nose 库的内部结构,特别是importer 模块吗? nose 做了一些与此非常相似的事情,所以它可能是寻找灵感的好地方。

标签: python python-module inspection


【解决方案1】:

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

我用的是:

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

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

【讨论】:

  • 感谢温斯顿,这是我采用的解决方案,从那以后我没有遇到任何令人讨厌的问题。
  • 如果您在文件夹中运行脚本,例如“bar/foo.py”,这将不起作用
  • @jwayne,它会产生什么?
  • @WinstonEwert 假设我创建了一个文件./bar/foo.py,其中包含import os; import __main__; print os.path.splitext(os.path.basename(__main__.__file__))[0]。从. 运行python bar/foo.py 得到foo,而不是bar.foo
  • @jwayne,恐怕foo 是该上下文中模块的正确名称。 Python 不在乎它是否包含在 bar 文件夹中。
【解决方案2】:

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

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

【讨论】:

  • 这仅在作为模块运行时有效(使用python -m),否则__spec__None。但这对我来说已经足够了! (另外,对于我的用例,我可以跳过inspect,直接使用import __main__; print(__main__.__spec__.name)。)
  • 如果你在主模块中,你也可以只使用__spec__.name,不加任何东西,不导入
【解决方案3】:

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

如果这不可能,我认为您的原始解决方案(从__main__.__file__ 检索模块名称)是最好和最简单的。如果您担心它对您的用户来说似乎违反直觉,请将其包装在一个不错的函数中并记录它的用途。

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

【讨论】:

    【解决方案4】:

    您可以做到这一点的一种方法 - 可能不是最好的方法,但它适用于我 - 是使用 __import__ 导入您的模块,并以如下方式使用 getattr

    (这里我使用了this post 中描述的关于动态加载模块的一些想法。)

    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
    

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

    【讨论】:

      【解决方案5】:

      我认为任何现有的答案实际上都没有直接回答这个问题:当模块以__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
      

      【讨论】:

        【解决方案6】:

        从 python 3.4 开始,

        importlib.util.find_spec('__main__').name
        

        【讨论】:

          猜你喜欢
          • 2020-09-07
          • 1970-01-01
          • 2021-01-26
          • 2020-06-26
          • 1970-01-01
          • 2022-10-18
          • 2012-02-17
          • 2016-10-31
          相关资源
          最近更新 更多