python - 哪个是处理 ImportError - 引发错误或导入链的更好方法?

标签 python error-handling python-import

问题

在python中遇到ImportError,是直接报错让用户安装,还是用import chain?

描述

我在尝试使用 lxml 包在 python 中解析 xml 文件时遇到了这个问题。
在其官方文档中,它说:

If your code only uses the ElementTree API and does not rely on any functionality that is specific to lxml.etree, you can also use (any part of) the following import chain as a fall-back to the original ElementTree:

try:
    from lxml import etree
    print("running with lxml.etree")
except ImportError:
    try:
        import xml.etree.cElementTree as etree
        print("running with cElementTree on Python 2.5+")
    except ImportError:
        ...

在我看来,导入一个替换是个坏主意,因为:
如果您可以导入另一个库作为替代,它可能没有 lxml 的所有方法,那么您的所有脚本只能基于所有包中的那些可用方法

那么引入最强大的包(例如这里的lxml)就没有意义了,我们可以直接引入功能最少的一个,并节省大量代码。或者,如果我们以后想使用其他方法,那么我们应该直接引发 ImportError。

但是,正如 Error handling when importing modules 中的回答,我发现这种方法似乎在 python 编程中经常使用:

it's useful to define multi-level functionality based on what library has been imported in runtime.

但在我看来,多级功能只能通过不断检查是否导入了一个库来实现,这使得整个代码变得复杂和丑陋。

因此,我只是想知道为什么人们有时会使用这样的结构,而不是直接引发错误?

最佳答案

首先回答你的最后一个问题:

When encounter ImportError in python, should I directly raise the error and ask the user to install it, or should I use import chain?

您可以出于多种原因处理 ImportError:

  • 如果你的模块直接依赖于一个模块,让错误发生。如果依赖项的安装非常重要,一些库会使用有用的错误消息重新引发错误。
  • 如果您的模块试图用速度较慢的库替换具有相同 API 的速度较快的库,则没有理由在屏幕上打印任何内容。
  • 如果您的模块需要某个库存在,但您能找到的只有一个明显较慢的库,警告可能有用,让开发人员知道您的模块仍可运行,但不会达到应有的速度.

现在回答你的其他问题:

Then it make less sense to import the most powerful package (e.g. lxml here), we could directly import the least functional one, and save a lot codes.

lxml.etreeElementTreecElementTree 的特定情况下,这三个都实现了相同的 API。他们是彼此的替代品。 ElementTree 是纯 Python 的,将始终有效,但 cElementTree 通常存在并且速度更快。 lxml.etree 更快,但它是一个外部模块。

可以这样想:

try:
    import super_fast_widget as widget
except ImportError:
    try:
        import fast_widget as widget
    except ImportError:
        import slow_widget as widget

从您的代码的角度来看,widget 将始终以相同的方式工作,无论实际上最终导入了哪个库,因此最好尝试导入最快的实现,如果性能不佳,则退回到较慢的实现你关心的事情。

您是正确的,如果您允许后备库,您将无法充分利用 所有 lxml 的功能。这就是为什么使用 lxml.etree 而不仅仅是 lxml 的原因。它有意模仿其他两个库的 API。

这是 Django 代码库中的一个类似示例:

# Use the C (faster) implementation if possible
try:
    from yaml import CSafeLoader as SafeLoader
    from yaml import CSafeDumper as SafeDumper
except ImportError:
    from yaml import SafeLoader, SafeDumper

Python 在内部为许多内置模块执行此操作。有一个较慢的纯 Python 版本用作较快的 C 版本的后备。

However, as answered in Error handling when importing modules , I find this approach seems to be used frequently in python programming:

您的lxml.etree 示例将较慢的库替换为较快的库。链接的示例代码为一堆库定义了一个通用的跨平台接口(interface) (getpass),这些库都执行相同的操作(提示您输入密码)。作者处理了 ImportError,因为根据您的操作系统,这些单独的模块可能不存在。

您可以将一些 try block 替换为 if platform.system() == 'Windows' 和类似代码,但即使在单个操作系统中也可能存在更好的模块执行相同的任务,所以 try block 只是简化它。最后 getpass 仍然使用完全相同的 API 提示用户输入密码,这才是您真正关心的。

关于python - 哪个是处理 ImportError - 引发错误或导入链的更好方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48481700/

相关文章:

C fputc 错误处理?

python - 构建 python 模块,以便可以使用或不使用 -m 开关从命令行运行

一旦我导入任何 PyQt5 模块,Python.exe 就会停止工作

python - 参数未通过 rpy2 传递给 R

python - SQLAlchemy 自动增量未正确插入

python - 将文件与 python 中的程序关联起来

找不到 Python linalg 包?

c# - 使用异常处理(可能改变)库的错误代码

Python:应该从 Exception 类还是从 ValueError 类派生新的 RangeError 异常?

Python 可以导入未安装的模块