如何在不导入 Python 模块的情况下检查它是否存在
- 2025-02-14 09:49:00
- admin 原创
- 49
问题描述:
如何在不导入 Python 模块的情况下知道它是否存在?
导入可能不存在的东西(不是我想要的)会导致:
try:
import eggs
except ImportError:
pass
解决方案 1:
TL;DR)使用importlib.util.find_spec(module_name)
(Python 3.4+)。
Python2的:imp.find_module
要检查import是否能在 Python 2 中找到某些内容,请使用imp
:
import imp
try:
imp.find_module('eggs')
found = True
except ImportError:
found = False
要找到点式导入,您需要做更多的事情:
import imp
try:
spam_info = imp.find_module('spam')
spam = imp.load_module('spam', *spam_info)
imp.find_module('eggs', spam.__path__) # __path__ is already a list
found = True
except ImportError:
found = False
您还可以使用pkgutil.find_loader
(与 Python 3 部分大致相同):
import pkgutil
eggs_loader = pkgutil.find_loader('eggs')
found = eggs_loader is not None
Python 3
Python 3≤3.3:importlib.find_loader
你应该使用importlib
。我这样做是这样的:
import importlib
spam_loader = importlib.find_loader('spam')
found = spam_loader is not None
我的期望是,如果你能找到一个加载器,那么它就存在。你也可以更聪明一点,比如过滤出你要接受的加载器。例如:
import importlib
spam_loader = importlib.find_loader('spam')
# only accept it as valid if there is a source file for the module - no bytecode only.
found = issubclass(type(spam_loader), importlib.machinery.SourceFileLoader)
Python 3≥3.4:importlib.util.find_spec
在 Python 3.4 中,importlib.find_loader
Python 文档已弃用,取而代之的是importlib.util.find_spec
。推荐的方法是importlib.util.find_spec
。还有其他方法importlib.machinery.FileFinder
,例如 ,如果您要加载特定文件,则很有用。弄清楚如何使用它们超出了本文的范围。
import importlib.util
spam_spec = importlib.util.find_spec("spam")
found = spam_spec is not None
这也适用于相对导入,但您必须提供起始包,因此您也可以执行以下操作:
import importlib.util
spam_spec = importlib.util.find_spec("..spam", package="eggs.bar")
found = spam_spec is not None
spam_spec.name == "eggs.spam"
尽管我确信这样做是有原因的——但我不确定是什么。
警告
当尝试查找子模块时,它将导入父模块(对于上述所有方法)!
food/
|- __init__.py
|- eggs.py
## __init__.py
print("module food loaded")
## eggs.py
print("module eggs")
were you then to run
>>> import importlib
>>> spam_spec = importlib.util.find_spec("food.eggs")
module food loaded
ModuleSpec(name='food.eggs', loader=<_frozen_importlib.SourceFileLoader object at 0x10221df28>, origin='/home/user/food/eggs.py')
欢迎就如何解决此问题发表评论
致谢
@rvighne 为 importlib
@lucas-guido 对于 Python 3.3+ 已弃用
find_loader
@enpenax 表示 Python 2.7 中的 pkgutils.find_loader 行为
解决方案 2:
Python 3 >= 3.6:ModuleNotFoundError
在Python 3.6ModuleNotFoundError
中已经引入,可以用于以下目的:
try:
import eggs
except ModuleNotFoundError:
# Error handling
pass
当无法找到模块或其父模块之一时,会引发此错误。因此
try:
import eggs.sub
except ModuleNotFoundError as err:
# Error handling
print(err)
No module named 'eggs'
如果eggs
找不到模块,则会打印一条类似于这样的消息;但No module named 'eggs.sub'
如果只是sub
找不到模块,但eggs
可以找到包,则会打印类似的内容。
有关更多信息,请参阅导入系统的文档ModuleNotFoundError
。
解决方案 3:
在使用yarbelk 的回应之后,我已经这样做了,所以我不需要导入ìmp
。
try:
__import__('imp').find_module('eggs')
# Make things with a supposed existing module
except ImportError:
pass
例如,它在Django的settings.py文件中很有用。
解决方案 4:
Python 2,不依赖 ImportError
在当前答案更新之前,以下是Python 2的方法
import pkgutil
import importlib
if pkgutil.find_loader(mod) is not None:
return importlib.import_module(mod)
return None
为什么是另一个答案?
很多答案都利用了捕获ImportError
。这样做的问题是,我们无法知道是什么引发了ImportError
。
如果您导入现有的模块,而模块中恰好有一个ImportError
(例如,第 1 行的拼写错误),则结果将是您的模块不存在。
您需要进行大量的回溯才能确定您的模块存在并且ImportError
被捕获,从而使事情悄悄失败。
解决方案 5:
go_as 的答案是一行:
python -c "help('modules');" | grep module
解决方案 6:
使用pkgutil 中的一个函数,例如:
from pkgutil import iter_modules
def module_exists(module_name):
return module_name in (name for loader, name, ispkg in iter_modules())
解决方案 7:
我写了这个辅助函数:
def is_module_available(module_name):
if sys.version_info < (3, 0):
# python 2
import importlib
torch_loader = importlib.find_loader(module_name)
elif sys.version_info <= (3, 3):
# python 3.0 to 3.3
import pkgutil
torch_loader = pkgutil.find_loader(module_name)
elif sys.version_info >= (3, 4):
# python 3.4 and above
import importlib
torch_loader = importlib.util.find_spec(module_name)
return torch_loader is not None
解决方案 8:
以下是从命令行检查模块是否加载的方法:
Linux/UNIX脚本文件方法:创建文件module_help.py
:
#!/usr/bin/env python
help('modules')
然后确保它是可执行的:chmod u+x module_help.py
并使用pipe
to来调用它grep
:
./module_help.py | grep module_name
调用内置帮助系统。(此函数旨在用于交互式使用。)如果未提供任何参数,则交互式帮助系统在解释器控制台上启动。如果参数是字符串,则将字符串查找为模块、函数、类、方法、关键字或文档主题的名称,并在控制台上打印帮助页面。如果参数是任何其他类型的对象,则生成该对象的帮助页面。
交互方法:在控制台中,加载python
>>> help('module_name')
如果找到,请输入 退出阅读q
。要退出 Python 解释器交互式会话,请按Ctrl
+D
Windows 脚本文件方法,也兼容 Linux/UNIX,并且总体来说更好:
#!/usr/bin/env python
import sys
help(sys.argv[1])
从如下命令调用它:
python module_help.py site
将输出:
模块站点上的帮助:
NAME
site - 将第三方包的模块搜索路径附加到 sys.path。
FILE
/usr/lib/python2.7/site.py
MODULE DOCS
http://docs.python.org/library/site
DESCRIPTION
... :
您必须按下q
才能退出交互模式。
将其用于未知模块,例如,
python module_help.py lkajshdflkahsodf
将输出:
未找到“lkajshdflkahsodf”的 Python 文档
然后退出。
解决方案 9:
您可以编写一个小脚本,尝试导入所有模块并告诉您哪些模块失败了,哪些模块正在运行:
import pip
if __name__ == '__main__':
for package in pip.get_installed_distributions():
pack_string = str(package).split(" ")[0]
try:
if __import__(pack_string.lower()):
print(pack_string + " loaded successfully")
except Exception as e:
print(pack_string + " failed with error code: {}".format(e))
输出:
zope.interface loaded successfully
zope.deprecation loaded successfully
yarg loaded successfully
xlrd loaded successfully
WMI loaded successfully
Werkzeug loaded successfully
WebOb loaded successfully
virtualenv loaded successfully
...
警告:这将尝试导入所有内容,因此您会看到类似这样的内容PyYAML failed with error code: No module named pyyaml
,因为实际的导入名称只是yaml。因此,只要您知道自己的导入内容,这就可以解决问题。
解决方案 10:
没有任何方法可以可靠地检查“带点模块”是否可以导入,而无需导入其父包。话虽如此,对于“如何检查 Python 模块是否存在”的问题,有很多解决方案。
以下解决方案解决了导入的模块即使存在也会引发 ImportError 的问题。我们希望将这种情况与模块不存在的情况区分开来。
Python 2:
import importlib
import pkgutil
import sys
def find_module(full_module_name):
"""
Returns module object if module `full_module_name` can be imported.
Returns None if module does not exist.
Exception is raised if (existing) module raises exception during its import.
"""
module = sys.modules.get(full_module_name)
if module is None:
module_path_tail = full_module_name.split('.')
module_path_head = []
loader = True
while module_path_tail and loader:
module_path_head.append(module_path_tail.pop(0))
module_name = ".".join(module_path_head)
loader = bool(pkgutil.find_loader(module_name))
if not loader:
# Double check if module realy does not exist
# (case: full_module_name == 'paste.deploy')
try:
importlib.import_module(module_name)
except ImportError:
pass
else:
loader = True
if loader:
module = importlib.import_module(full_module_name)
return module
Python 3:
import importlib
def find_module(full_module_name):
"""
Returns module object if module `full_module_name` can be imported.
Returns None if module does not exist.
Exception is raised if (existing) module raises exception during its import.
"""
try:
return importlib.import_module(full_module_name)
except ImportError as exc:
if not (full_module_name + '.').startswith(exc.name + '.'):
raise
解决方案 11:
在django.utils.module_loading.module_has_submodule中:
import sys
import os
import imp
def module_has_submodule(package, module_name):
"""
check module in package
django.utils.module_loading.module_has_submodule
"""
name = ".".join([package.__name__, module_name])
try:
# None indicates a cached miss; see mark_miss() in Python/import.c.
return sys.modules[name] is not None
except KeyError:
pass
try:
package_path = package.__path__ # No __path__, then not a package.
except AttributeError:
# Since the remainder of this function assumes that we're dealing with
# a package (module with a __path__), so if it's not, then bail here.
return False
for finder in sys.meta_path:
if finder.find_module(name, package_path):
return True
for entry in package_path:
try:
# Try the cached finder.
finder = sys.path_importer_cache[entry]
if finder is None:
# Implicit import machinery should be used.
try:
file_, _, _ = imp.find_module(module_name, [entry])
if file_:
file_.close()
return True
except ImportError:
continue
# Else see if the finder knows of a loader.
elif finder.find_module(name):
return True
else:
continue
except KeyError:
# No cached finder, so try and make one.
for hook in sys.path_hooks:
try:
finder = hook(entry)
# XXX Could cache in sys.path_importer_cache
if finder.find_module(name):
return True
else:
# Once a finder is found, stop the search.
break
except ImportError:
# Continue the search for a finder.
continue
else:
# No finder found.
# Try the implicit import machinery if searching a directory.
if os.path.isdir(entry):
try:
file_, _, _ = imp.find_module(module_name, [entry])
if file_:
file_.close()
return True
except ImportError:
pass
# XXX Could insert None or NullImporter
else:
# Exhausted the search, so the module cannot be found.
return False
解决方案 12:
如果你知道文件的位置,并想检查相应的 Python 代码文件是否包含该模块,你可以通过Python 中的astor包进行检查。以下是一个简单的示例:
"""
Check if a module function exists or not without importing a Python package file
"""
import ast
import astor
tree = astor.parse_file('handler.py')
method_to_check = 'handle'
for item in tree.body:
if isinstance(item, ast.FunctionDef):
if item.name == method_to_check:
print('method exists')
break
解决方案 13:
您也可以importlib.util
直接使用
import importlib.util
def module_exists_without_import(module_name):
spec = importlib.util.find_spec(module_name)
return spec is not None
解决方案 14:
来自 Ask Ubuntu 的一个更简单的if语句,如何检查模块是否已安装在 Python 中?:
import sys
print('eggs' in sys.modules)
解决方案 15:
那怎么样:
def module_is_available(the_path,module_folder_name):
return module_folder_name in os.listdir(the_path)
然后如果您想检查 sys.path 中当前的所有路径:
def module_is_available_in_sys_path(module_folder_name):
for p in sys.path:
if module_is_available(p,module_folder_name)
return True
return False