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

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

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

try:
    import eggs
except ImportError:
    pass

【问题讨论】:

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

标签: python python-import


【解决方案1】:

Python2

使用imp检查导入是否可以在python2中找到东西

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 docs 被弃用,取而代之的是 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')

欢迎 cmets 解决这个问题

致谢

  • @rvighne 用于 importlib
  • @lucas-guido for python3.3+ 贬低find_loader
  • @enpenax 用于 python2.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】:

使用 yarbelk 的回复后,我做了这个,因为不必导入 ìmp

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

在 Django 的 settings.py 中很有用。

【讨论】:

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

如果你导入你的 existant 模块并且你的模块中恰好有一个ImportError(例如第 1 行的错字),结果将是你的模块不存在。您需要进行相当多的回溯才能确定您的模块存在并且ImportError 被捕获并使事情无声无息地失败。

【讨论】:

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

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

    并用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 秒才能收集所有可用模块。如果这没问题,那么很酷的命令。
    【解决方案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】:

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

      【讨论】:

        【解决方案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】:

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

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

          【讨论】:

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

          【讨论】:

          • 这满足了我在编写 python 时的标准问题:WWDD (What would Django Do?) 我应该看看那里
          【解决方案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】:

          如果您知道文件的位置并想检查相应的python代码文件是否具有该模块,您可以通过python中的astor包简单地检查,这里是一个简单的例子:

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

          【讨论】:

            【解决方案14】:

            你也可以直接使用importlib

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

            【讨论】:

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