假设我有一个模块的字典(通过vars(mod)
或mod.__dict__
或globals()
),例如:
import mod
d = vars(mod)
鉴于字典d
,我该如何找回模块mod
?即我想编写一个函数get_mod_from_dict(d)
,如果字典属于某个模块,则返回该模块,或者返回None
:
>>> get_mod_from_dict(d)
<module 'mod'>
如果get_mod_from_dict
返回一个模块,则必须满足以下条件:
mod = get_mod_from_dict(d)
assert mod is None or mod.__dict__ is d
我实际上可以这样实现:
def get_mod_from_dict(d):
mods = {id(mod.__dict__): mod for (modname, mod) in sys.modules.items()
if mod and modname != "__main__"}
return mods.get(id(d), None)
但是,对我来说,遍历sys.modules
似乎效率很低。
有更好的方法吗?
我为什么需要这个?
在某些情况下,您只能访问dict。例如。在堆栈框架中。然后,根据您想做什么,也许只是出于检查/调试的目的,找回模块很有帮助。
我为Pickler
写了一些扩展,可以扩展方法,函数等的内容。其中一些引用了模块或模块字典。在腌制过程中,无论我有一个属于模块的字典,我都不想腌制该字典,而要引用该模块。
答案 0 :(得分:5)
每个模块都有一个__name__
属性,可以在导入系统中唯一地标识该模块:
>>> import os
>>> os.__name__
'os'
>>> vars(os)['__name__']
'os'
导入的模块也缓存在sys.modules
中,这是将模块名称映射到模块实例的字典。您可以在此处简单地查找模块的名称:
import sys
def get_mod_from_dict(module_dict):
module_name = module_dict['__name__']
return sys.modules.get(module_name)
有些人表示担心,这可能不适用于软件包中的(子)模块,但确实如此:
>>> import urllib.request
>>> get_mod_from_dict(vars(urllib.request))
<module 'urllib.request' from '/usr/lib/python3.7/urllib/request.py'>
有一个 警告,但是:仅适用于已由导入机制正确导入并缓存的模块。如果某个模块已使用How to import a module given the full path?之类的技巧导入,则该模块可能不会缓存在sys.modules
中,然后您的函数可能会意外返回None
。
答案 1 :(得分:4)
您可以使用importlib.import_module导入模块(给出模块名称)。 numpy
In [77]: import numpy
...: import importlib
In [78]: d = vars(numpy)
In [79]: np = importlib.import_module(d['__name__'])
In [80]: np.array([1,2,3])
Out[80]: array([1, 2, 3])
答案 2 :(得分:1)
为完整起见,通过gc
模块提供了另一个解决方案:
def get_mod_from_dict_3(d):
"""
:param dict[str] d:
:rtype: types.ModuleType|None
"""
objects = gc.get_referrers(d)
for obj in objects:
if isinstance(obj, types.ModuleType) and vars(obj) is d:
return obj
return None
使用gc
可能取决于Python解释器。并非所有的Python解释器都可能具有GC。即使有,我也不确定是否可以保证该模块引用了它的字典(尽管很有可能,但确实如此;它无法真正想到为什么没有该字典的充分理由)。
因此,我认为通过sys.modules[d['__name__']]
的其他解决方案可能会更好。
尽管我检查了CPython和PyPy,但在两种情况下,此解决方案均有效。而且,此解决方案更为通用。即使没有任何对象,它也可以工作(无需检查ModuleType
)。
尽管考虑了不同的Python解释器,但我什至可以想象甚至一个Python解释器,其中vars(mod)
永远不会返回相同的dict,而这将动态地创建dict。这样的功能根本无法实现。不确定。
我收集了所有给定的解决方案以及一些测试代码here。
答案 3 :(得分:0)
您最终可以使用生成器来改善您的解决方案:
def get_mod_from_dict_2(d):
return next((mod for modname, mod in sys.modules.items() if mod and modname != "__main__" and id(mod.__dict__) == id(d)), None)
但这不会帮助您避免使用sys.modules
...
更新:如@Devesh Kumar Singh的回答中所述,您可以使用importlib模块按名称检索已导入的模块(如果尚未导入,则可以导入)。只要模块的词典和目录不是'__main__'模块,它就保存模块的名称和文件。从那里,您可以执行以下操作:
import importlib
import some_module
d = vars(some_module)
print(d['__name__']) # >> 'some_module'
m = importlib.import_module(d['__name__'])
print(m) # >> <module 'some_module' from '/path/to/some_module.py'>