【问题标题】:How to check if a Python module exists without importing it如何在不导入的情况下检查 Python 模块是否存在
【发布时间】:2012-12-27 06:17:22
【问题描述】:

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

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

try:
    import eggs
except ImportError:
    pass

【问题讨论】:

  • 我很好奇,使用 import 有什么缺点?
  • 如果您的模块有副作用,调用 import 可能会产生不良后果。因此,如果您想检查首先运行哪个版本的文件,您可以使用以下答案进行检查,然后再进行导入。我并不是说编写带有副作用的模块是个好主意——但我们都是成年人,可以自己决定编写代码的危险程度。
  • @ArtOfWarfare 我刚刚关闭了您链接的 那个 问题,它是 this 的一个副本。因为这个问题更清楚,而且这里提出的解决方案也比那里列出的所有其他解决方案都要好。我宁愿指出谁想要这个更好的解决方案的答案,而不是让人们远离它。
  • @Chuck 此外,该模块可能存在,但本身可能包含导入错误。在上面的代码中捕获 ImportErrors 可能会导致指示模块不存在,如果它确实存在但有错误。

标签: python python-import


【解决方案1】:

Python2

要检查 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。我开始这样做:

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

在 Python 3.4 中,importlib.find_loader Python documentation 已弃用,取而代之的是 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 for Python 3.3+ 弃用 find_loader
  • @enpenax 用于 Python 2.7 中的 pkgutils.find_loader 行为

【讨论】:

  • 这仅适用于顶级模块,不适用于eggs.ham.spam
  • @hemflit 如果你想在eggs.ham 中找到spam,你可以使用imp.find_module('spam', ['eggs', 'ham'])
  • +1,但 imp 在 Python 3 中是 deprecated 支持 importlib
  • 如果导入的模块包含实际的“ImportError”怎么办。这就是发生在我身上的事情。然后模块存在但不会被“找到”。
  • 一年后,我遇到了我刚才提到的同样的问题,并正在寻找 Python 2 的解决方案:pkgutil.find_loader("my.package.module") 如果包/模块存在则返回一个加载器,如果不存在则None。请更新您对 Python 2 的答案,因为昨天屏蔽 ImportError 让我发疯了 xP
【解决方案2】:

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 的更多信息,请参阅documentation of the import system

【讨论】:

  • 这不回答问题,因为它会导入包(如果存在)
  • 如果另一个模块在导入期间引发此异常,您将无法(轻松)分辨出差异 - 您的模块是否不存在,或者它的某些依赖项不存在。
【解决方案3】:

我写了这个辅助函数:

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

【讨论】:

  • 解释一下。例如,想法/要点是什么?与之前的答案相比如何?来自the Help Center“...总是解释为什么你提出的解决方案是合适的以及它是如何工作的”。请通过editing (changing) your answer 回复,而不是在 cmets 中(without "Edit:"、"Update:" 或类似的 - 答案应该看起来像是今天写的)。
【解决方案4】:

如果您知道文件的位置并想检查相应的 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

【讨论】:

    【解决方案5】:

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

    Linux/UNIX脚本文件方法:制作文件module_help.py

    #!/usr/bin/env python
    
    help('modules')
    

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

    并用pipegrep 调用它:

    ./module_help.py | grep module_name
    

    调用内置的help system。 (此函数用于交互式使用。)如果没有给出参数,交互式帮助系统将在解释器控制台上启动。如果参数是 string,则将该字符串查找为 module、函数、类、方法、关键字或文档主题以及帮助页面的名称打印在控制台上。如果参数是任何其他类型的对象,则会生成有关该对象的帮助页面。

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

    然后退出。

    【讨论】:

    • 好主意。喜欢它。缺点:至少在我的 Win10 上,help('modules') 命令需要 10 秒才能收集所有可用模块。如果这没问题,那么很酷的命令。
    【解决方案6】:

    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

    如果你导入你的 existent 模块并且你的模块中恰好有一个ImportError(例如,第 1 行的错字),结果将是你的模块不存在。

    您需要进行相当多的回溯才能确定您的模块存在并且ImportError 被捕获并使事情静默失败。

    【讨论】:

    • 可能不清楚,但除了第一个代码块之外的所有代码块都不依赖于ImportError- 如果您不清楚,请编辑。
    • 我看到你在前两个 Python 2 示例中使用了 ImportError 捕获。那他们为什么会在那儿呢?
    • 如果 mod == 'not_existing_package.mymodule' 会引发 ImportError。查看我的完整版solution below
    • 当然会引发导入错误。如果模块不存在,它应该抛出一个导入错误。这样你就可以在需要时抓住它。其他解决方案的问题在于它们掩盖了其他错误。
    • Try/except 并不意味着你不能不记录或确保事情。您可以完全捕获任何底层回溯并做任何您想做的事情。
    【解决方案7】:

    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 (What would Django Do?) 我应该看看那里
    • 它的要点是什么?例如,它与以前的答案相比如何(鉴于其复杂性增加)?还是应该在不了解的情况下将其用作黑匣子?请通过editing (changing) your answer 回复,而不是在 cmets 中(without "Edit:"、"Update:" 或类似的 - 答案应该看起来像是今天写的)。
    【解决方案8】:

    go_as's answer 单线:

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

    【讨论】:

      【解决方案9】:

      使用functions from pkgutil 之一,例如:

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

      【讨论】:

        【解决方案10】:

        您可以编写一个小脚本,尝试导入所有模块并告诉您哪些模块失败,哪些模块正常工作:

        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。所以只要你知道你的进口,这应该可以为你解决问题。

        【讨论】:

          【解决方案11】:

          使用yarbelk's response后,我做了这个,所以我不必导入ìmp

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

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

          【讨论】:

          • 我投了反对票,因为这掩盖了模块中的导入错误,因此很难发现错误。
          • 拒绝投票是个坏主意,一个好的做法是“始终记录捕获的错误”。这是一个你想怎么写之后的例子。
          • 如果导入的模块在第 1 行失败并出现 ImportError 并且您的 try catch 使其静默失败,您将如何记录错误?
          • 我刚刚在现实生活中遇到了 masking-import-errors 问题,这很糟糕(导致本应无法通过的测试!)。
          • 我遇到了有人使用该错误来触发不同模块上的猴子补丁......这是 疯狂 找到
          【解决方案12】:

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

          【讨论】:

          • 它基本上完全按照 OP 所说的去做
          【解决方案13】:

          来自 Ask Ubuntu 的更简单的 if 语句,How do I check whether a module is installed in Python?

          import sys
          print('eggs' in sys.modules)
          

          【讨论】:

          • 这里只测试是否安装,不测试是否可安装。
          • 实际上,它测试它是否已经导入,与提出的问题完全相反。
          • 问题是“如何在不导入的情况下检查 python 模块是否存在”。不问模块是否可安装?
          【解决方案14】:

          你也可以直接使用importlib

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

          【讨论】:

          • 这个有实际导入的问题。副作用和所有
          猜你喜欢
          • 2012-12-12
          • 2013-02-18
          • 1970-01-01
          • 2013-08-22
          • 2014-11-14
          • 2018-05-30
          • 2013-10-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多