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

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

try:
    import eggs
except ImportError:
    pass

当前回答

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 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'的消息;但它会打印类似于没有名为“eggs”的模块。如果子模块找不到,但鸡蛋包可以找到,则Sub '。

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

使用importlib.util.find_spec(module_name) (Python 3.4+)。

Python2: imp.find_module

要检查import是否能在python2中找到一些东西,使用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。Python文档已弃用,取而代之的是importlib.util.find_spec。推荐使用importlib.util.find_spec方法。还有其他的,比如进口机械。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行为

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

使用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

例如,它在Django的settings.py文件中就很有用。