简体   繁体   English

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

[英]Which is the better way to handle ImportError - Raise error or import chain?

Question

When encounter ImportError in python, should I directly raise the error and ask the user to install it, or should I use import chain? 当在python中遇到ImportError时,我应该直接引发错误并要求用户安装它,还是应该使用导入链?

Description 描述

I came across this question when I tried to use lxml package to parse xml file in python. 当我尝试使用lxml包解析python中的xml文件时遇到了这个问题。
In its official documentation, it says: 在其官方文档中说:

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: 如果您的代码仅使用ElementTree API,并且不依赖于lxml.etree特有的任何功能,则还可以使用以下导入链(的任何部分)作为原始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:
        ...

It seems to me that it's a bad idea to import a substitution since: 在我看来,导入替代品不是一个好主意,因为:
if you can import another library as a substitution, which may not have all the methods as lxml, then all your script can only based on those available methods in all the packages . 如果您可以导入另一个库作为替代库,而该库可能不具有lxml的所有方法,则所有脚本只能基于所有软件包中的那些可用方法

Then it make less sense to import the most powerful package (eg lxml here), we could directly import the least functional one, and save a lot codes. 然后,导入最强大的程序包(例如此处的lxml)变得没有意义,我们可以直接导入功能最差的程序包,并节省很多代码。 Or if we want to use additional methods later, we then should directly raise the ImportError. 或者,如果以后要使用其他方法,则应直接引发ImportError。

However, as answered in Error handling when importing modules , I find this approach seems to be used frequently in python programming: 但是,正如导入模块时的错误处理中所回答的那样,我发现这种方法似乎在python编程中经常使用:

it's useful to define multi-level functionality based on what library has been imported in runtime. 根据在运行时导入的库定义多级功能非常有用。

But it seems to me that, the multi-level functionality can only be achieved by constantly checking whether one library has been imported, which makes the whole codes complicated and ugly. 但是在我看来,只能通过不断检查是否已导入一个库来实现多级功能,这会使整个代码变得复杂而丑陋。

As a result, I just wondered why people sometimes use such structure, instead of raise the error directly? 结果,我只是想知道为什么人们有时会使用这种结构,而不是直接引发错误?

To answer your last question first: 首先要回答您的最后一个问题:

When encounter ImportError in python, should I directly raise the error and ask the user to install it, or should I use import chain? 当在python中遇到ImportError时,我应该直接引发错误并要求用户安装它,还是应该使用导入链?

You can handle ImportError s for many reasons: 您可以处理ImportError的原因很多:

  • If your module directly depends on a module, let the error happen. 如果您的模块直接依赖于模块,请让错误发生。 Some libraries re-raise the error with a helpful error message if the dependency's installation is non-trivial. 如果依赖项的安装很简单,某些库会使用有用的错误消息重新引发该错误。
  • If your module is trying to substitute slower libraries for faster ones with identical APIs, there's no reason to print anything to screen. 如果您的模块尝试使用相同的API将较慢的库替换为较快的库,则没有理由在屏幕上打印任何内容。
  • If your module expects a certain library to exist but a significantly slower one is the only one you can find, a warning may be useful to let the developer know that your module will still function but will not be as fast as it should. 如果您的模块期望某个特定的库存在,但是您可以找到的唯一库明显要慢得多,那么警告可能会很有用,以使开发人员知道您的模块仍然可以运行,但运行得不那么快。

Now for your other questions: 现在,对于您的其他问题:

Then it make less sense to import the most powerful package (eg lxml here), we could directly import the least functional one, and save a lot codes. 然后,导入最强大的程序包(例如此处的lxml)变得没有意义,我们可以直接导入功能最差的程序包,并节省很多代码。

In the specific case of lxml.etree , ElementTree , and cElementTree , all three implement the same API. lxml.etreeElementTreecElementTree的特定情况下,所有三个都实现相同的API。 They're substitutes for one another. 他们是彼此的替代品。 ElementTree is pure-Python and will always work, but cElementTree is usually present and is faster. ElementTree是纯Python的,将始终运行,但是cElementTree通常存在并且速度更快。 lxml.etree is even faster but is an external module. lxml.etree甚至更快,但它是一个外部模块。

Think of it like this: 这样想:

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

From your code's perspective, widget will always work the same regardless of which library actually ended up getting imported, so it's best to try to import the fastest implementation and fall back on slower ones if performance is something you care about. 从代码的角度来看,无论最终导入哪个库, widget都将始终工作相同,因此,如果您在乎性能,那么最好尝试导入最快的实现,而退回到较慢的实现。

You are correct in that you can't fully utilize all of lxml 's features if you allow fallback libraries. 正确的是,如果允许使用回lxml ,则无法充分利用lxml所有功能。 This is why lxml.etree is being used instead of just lxml . 这就是为什么使用lxml.etree而不是lxml It intentionally mimics the API of the other two libraries. 它有意模仿其他两个库的API。

Here's a similar example from Django's codebase: 这是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 internally does this for a lot of built-in modules. Python内部为许多内置模块执行此操作。 There's a slower, pure Python version that's used as a fallback for the faster C version. 有一个较慢的纯Python版本,用作较快的C版本的后备。

However, as answered in Error handling when importing modules , I find this approach seems to be used frequently in python programming: 但是,正如导入模块时的错误处理中所回答的那样,我发现这种方法似乎在python编程中经常使用:

Your lxml.etree example substituted slower libraries for faster ones. 您的lxml.etree示例将较慢的库替换为较快的库。 The linked example code defines a common, cross-platform interface ( getpass ) to a bunch of libraries that all do the same thing (prompt you for your password). 链接的示例代码为一堆相同的库定义了一个通用的跨平台接口( getpass )(提示您输入密码)。 The author handles the ImportError s because those individual modules may not exist depending on your operating system. 作者处理ImportError因为根据您的操作系统,这些单独的模块可能不存在。

You could replace some of the try blocks with if platform.system() == 'Windows' and similar code, but even among a single OS there may be better modules that perform an identical task so the try blocks just simplify it. 您可以使用if platform.system() == 'Windows'和类似的代码替换某些try块,但是即使在单个OS中,也可能会有更好的模块执行相同的任务,因此try块只是对其进行了简化。 In the end getpass still prompts the user for their password with the exact same API, which is all you really care about. 最后, getpass仍会使用完全相同的API提示用户输入密码,这是您真正关心的全部。

I usually use import chains because the output is more controlled. 我通常使用导入链,因为输出受到更多控制。

Raising Errors 引发错误

Traceback (most recent call last):
  File "core.py", line 1, in <module>
ImportError: <error description>

Import Chains 进口链

i Importing "lxml.etree"
x Error Importing "lxml.etree"
i Importing "xml.etree.cElementTree" on Python 2.5+
x Error Importing "xml.etree.cElementTree" on Python 2.5+
i Please Install "lxml.etree" or "xml.etree.xElementTree" on Python 2.5+
i Exit with code 1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM