如何检查Python模块是否存在而不导入它

我如何知道Python模块是否存在,而不导入它?

导入一些可能不存在(不是我想要的)的东西会导致:

try:
import eggs
except ImportError:
pass
244059 次浏览

TL;DR)使用importlib.util.find_spec(module_name) (Python 3.4+)

Python2: imp.find_module

要检查进口是否能在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
spam_spec = importlib.util.find_spec("spam")
found = spam_spec is not None

这也适用于相对导入,但你必须提供初始包,所以你还可以这样做:

import importlib
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 for importlib
  • @lucas-guido for Python 3.3+弃用find_loader
  • @enpenax for pkgutils。Python 2.7中的find_loader行为

使用pkgutil中的函数中的一个,例如:

from pkgutil import iter_modules


def module_exists(module_name):
return module_name in (name for loader, name, ispkg in iter_modules())

在使用yarbelk的反应之后,我已经这样做了,所以我不需要导入ìmp

try:
__import__('imp').find_module('eggs')
# Make things with a supposed existing module
except ImportError:
pass

例如,它在Djangosettings.py文件中很有用。

下面是一种检查模块是否从命令行加载的方法:

Linux/UNIX脚本文件方法:创建一个文件module_help.py:

#!/usr/bin/env python


help('modules')

然后确保它是可执行的:chmod u+x module_help.py

并使用pipegrep调用它:

./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

将输出:

模块网站帮助:

< p > NAME site -将第三方包的模块搜索路径追加到sys.path < p > FILE /usr/lib/python2.7/site.py < / p > < p > MODULE DOCS http://docs.python.org/library/site < / p > < p > DESCRIPTION ... : < / p >

你必须按q来退出交互模式。

将它用于未知模块,例如,

python module_help.py lkajshdflkahsodf

将输出:

没有找到'lkajshdflkahsodf'的Python文档

并退出。

go_as的回答作为一行程序:

 python -c "help('modules');" | grep module

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被捕获并使事情无声地失败。

没有任何方法可以可靠地检查“虚线模块”是否;在不导入其父包的情况下可导入。说到这里,对于“如何检查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

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

你可以写一个小脚本,尝试导入所有的模块,并告诉你哪些是失败的,哪些是工作的:

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。只要知道导入,这个就能帮到你。

你也可以直接使用importlib.util

import importlib.util


def module_exists_without_import(module_name):
spec = importlib.util.find_spec(module_name)
return spec is not None

Python 3 >= 3.6: ModuleNotFoundError

ModuleNotFoundError已在Python 3.6中引入,可用于以下目的:

try:
import eggs
except ModuleNotFoundError:
# Error handling
pass

当无法找到模块或它的父模块之一时将引发错误。所以

try:
import eggs.sub
except ModuleNotFoundError as err:
# Error handling
print(err)

如果找不到eggs模块,将打印一条类似No module named 'eggs'的消息;但如果只有sub模块找不到,但可以找到eggs包,它就会打印类似No module named 'eggs.sub'的东西。

有关ModuleNotFoundError的更多信息,请参阅导入系统的文件

我写了这个helper函数:

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

如果你知道文件的位置,想要检查相应的Python代码文件是否有该模块,你可以简单地通过Python中的阿斯特包进行检查。这里有一个简单的例子:

"""
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