在导入时引发自定义异常

时间:2020-02-27 13:03:07

标签: python exception

简短的问题:我有一个带有对象的模块。如果有人从我的模块中导入对象,该怎么办呢?

我想做什么:我写了一个架构框架(可以在Python 2和3中工作)。用于输出的类取决于jinja2外部库。我希望该框架在没有这种依赖性的情况下也可以使用。在包的__init__.py中,我对类RenderLaTeX进行了条件导入(如果jinja2可用,则导入该类,否则不导入)。

这种方法的问题是我有一些使用此类RenderLaTeX的代码,但是当我在全新的设置上运行它时,会收到类似Import error: no class RenderLaTeX could be imported from output的错误。在我回想起必须事先安装jinja2之前,此错误是非常意外且难以理解的。

我考虑过这种解决方案:如果该类不可用,__init__.py可以使用该名称创建一个字符串。如果用户尝试使用常规的类构造函数实例化此对象,则他们将获得更有意义的错误。不幸的是,简单导入

from output import RenderLaTeX

在这种情况下不会引发错误。

您希望提出什么建议,并希望对优点和缺点进行描述?

2 个答案:

答案 0 :(得分:1)

当Python导入模块时,将执行要从中导入文件的所有代码。

因此,如果将RenderLaTeX类放在单独的文件中,则可以自由添加逻辑,以在缺少必需的依赖项时阻止其导入(或运行)。

例如:

try:
    import somethingidonthave
except ImportError:
    raise Exception('You need this module!')

class RenderLaTeX(object):
    pass

您还可以将所需的任何自定义消息添加到异常中,以更好地描述错误。这应该在Python2和Python3中都可以使用。

答案 1 :(得分:0)

经过一年的思考,解决方案出现了。

首先,我认为覆盖异常类型是没有意义的。唯一的好方法是为丢失的导入添加有用的消息。

其次,我认为语法

from framework.renderers import MyRenderer

确实比

from framework.renderers.my_renderer import MyRenderer

因为它隐藏了实现细节并且需要更少的用户代码(我更新了我的问题以反映这一点)。要使前一种语法起作用,我必须在模块的 __init__.py 中导入 MyRenderer

现在,在 my_renderer.py 中,我通常会使用

导入第三方包
import huge_specific_library

在标题中。 PEP 8 需要此语法。但是,这会使整个 framework.renderers 模块依赖于 huge_specific_library

解决方案是违反 PEP 8 并在类本身内部导入库:

class MyRenderer():

    def __init__(self):
        import huge_specific_library
        # ... use that...

在这里您可以捕获重要的异常,更改其消息等。还有另一个好处:存在如何减少导入时间的指南,并且他们提出了这个解决方案(我很久以前读过它们并且忘记)。大型模块需要一些时间来加载。如果你遵循 PEP 8 风格指南(我仍然认为你通常应该这样做),这可能会导致大量的延迟,只是为了将所有导入到你的程序,还没有做任何有用的事情。

唯一的警告是:如果您在 __init__ 中导入库,您还应该将其导入到使用它的其他类方法中,否则它将在那里不可见。

对于那些仍然有疑问的人,我必须补充一点,由于 Python 导入是缓存的,如果您使用导入的方法不经常调用,这不会影响性能。