如何在不导入的情况下检查python模块是否存在


190

我需要知道是否存在python模块,而不导入它。

导入可能不存在的内容(不是我想要的):

try:
    import eggs
except ImportError:
    pass

4
我很好奇,使用导入有什么缺点?
Chuck 2014年

16
如果您的模块有副作用,则调用import可能会产生不良后果。因此,如果要检查首先运行哪个版本的文件,可以使用以下答案进行检查,然后再进行导入。我并不是说编写带有副作用的模块不是一个好主意-但是我们都是成年人,可以根据自己想编码的危险程度来做出自己的决定。
yarbelk 2014年


1
@ArtOfWarfare我刚刚关闭您链接为该问题重复项的问题。因为这个问题比较清楚,而且这里提出的解决方案也比那里列出的所有其他解决方案都要好。我宁愿指出谁想要这个更好的解决方案的答案,而不是使人们远离它。
Bakuriu 2014年

7
@Chuck此外,该模块可能存在,但本身可能包含导入错误。如上面的代码中所示,捕获ImportErrors可能导致指示模块不存在(如果确实存在但有错误)。
Michael Barton

Answers:


213

Python2

要检查导入是否可以在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(与python3部分大致相同

import pkgutil
eggs_loader = pkgutil.find_loader('eggs')
found = eggs_loader is not None

Python3

Python3≤3.3

您应该使用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)

Python3≥3.4

在Python3.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.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用于python3.3 +的描述 find_loader
  • @enpenax用于python2.7中的pkgutils.find_loader行为

3
这仅适用于顶级模块,不适用于eggs.ham.spam
hemflit 2013年

3
@hemflit如果您想spameggs.ham其中使用imp.find_module('spam', ['eggs', 'ham'])
gitaarik 2014年

5
+1,但imp弃用赞成importlib3.在Python
rvighne

4
如果导入的模块包含实际的“ ImportError”怎么办。那就是我正在发生的事情。然后该模块存在,但不会被“找到”。
enpenax

1
一年后,我遇到了上面提到的相同问题,并在寻找Python 2的解决方案:pkgutil.find_loader("my.package.module")如果包/模块存在,None并且不存在,则返回加载程序。请更新您对Python 2的回答,因为掩盖ImportError昨天让我发疯了xP
enpenax

19

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)

会显示一条消息,看起来像找不到No module named 'eggs'eggs模块;但将打印出类似这样No module named 'eggs.sub'如果只有sub模块无法找到,但eggs包可能被发现。

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


3
这没有回答这个问题,因为它会导入软件包(如果存在的话)
divenex

13

在使用yarbelk的响应后,我做了这个,因为不必import ìmp

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

settings.py例如在Django中很有用。


4
我拒绝投票,因为这掩盖了模块中的导入错误,这使得很难发现错误。
enpenax 2015年

1
Downvote是一个坏主意,一个好的实践是“总是记录捕获的错误”。这是编写所需内容后的示例。
祖鲁语2015年

2
如果导入的模块在第1行上出现ImportError失败,并且您的try catch使它静默失败,那么您将如何记录错误?
enpenax 2015年

我刚刚遇到了现实生活中的掩盖导入错误问题,而且很糟糕(导致测试本应无法通过!)。
大卫

我遇到了有人在使用该错误来触发另一个模块上的Monkeypatch的情况……这真是疯狂的发现
yarbelk

11

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被捕获并使一切静默失败。


可能不清楚,但是除了第一个代码块之外,其他代码块都不依赖ImportError-如果不清楚,请进行编辑。
yarbelk

我看到您在前两个Python 2示例中使用ImportError捕获。那他们为什么在那里?
enpenax

3
如果mod =='not_existing_package.mymodule',则抛出ImportError。请在下面
MarcinRaczyński2016年

1
当然,它会引发导入错误。如果模块不存在,应该会引发导入错误。这样,您就可以在需要时捕获它。其他解决方案的问题在于它们掩盖了其他错误。
enpenax

尝试/例外并不意味着您不必登录或确保一切。您可以完全捕获任何底层的追溯,并可以执行任何操作。
祖鲁语

8

go_as的答案

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

6

我在寻找一种方法来检查是否从命令行加载了模块时遇到了这个问题, 并希望与我分享那些对我的想法并寻找相同想法的想法:

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

#!/usr/bin/env python

help('modules')

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

并用一个 pipetogrep

./module_help.py | grep module_name

调用内置的帮助系统。(此功能仅供交互式使用。)如果未提供任何参数,则交互式帮助系统将在解释器控制台上启动。如果参数是string,则将字符串作为模块,函数,类,方法,关键字或文档主题的名称进行查找,并在控制台上打印帮助页面。如果自变量是任何其他类型的对象,则会在该对象上生成帮助页面。

互动方式:在控制台中加载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文档

然后退出。


5

使用pkgutil中的功能之一,例如:

from pkgutil import iter_modules

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


4

您可以编写一个脚本来尝试导入所有模块,并告诉您哪些模块发生故障以及哪些模块正在工作:

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。因此,只要您知道自己的进口货,就可以为您解决问题。


4

我写了这个辅助函数:

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

2

您也可以importlib直接使用

import importlib

try:
    importlib.import_module(module_name)
except ImportError:
    # Handle error

这有实际导入它的问题。副作用及其他
yarbelk

2

如果不导入其“父包”,则无法可靠地检查“虚线模块”是否可导入。如此说来,对于“如何检查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

1

在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

这满足了我在编程python时的标准问题:WWDD(Django会做什么?),我应该看看那里
yarbelk
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.