Python:在 __init__ 中引发异常是一种不好的形式吗?

问题描述 投票:0回答:8

__init__
内提出异常是否被认为是不好的形式?如果是这样,那么当某些类变量初始化为
None
或类型不正确时,可接受的抛出错误的方法是什么?

python exception
8个回答
215
投票

__init__()
内引发异常绝对没问题。没有其他好的方法来指示初始化程序中的错误情况,并且标准库中有数百个示例,其中初始化对象可能会引发异常。

当然,要提出的错误类别取决于您。如果初始化程序传递了无效参数,则

ValueError
是最好的。


38
投票

确实,在构造函数中指示错误的唯一正确方法是引发异常。不过,您需要注意异常安全。在C++等面向对象语言中,如果对象的构造函数抛出异常(意味着对象的初始化不完整),则不会调用析构函数。在Python中,析构函数总是被调用。例如,如果

AttributeError
失败,以下代码会在
self.stream.close()
处抛出
self.socket.connect()

class Connection:
    def __init__(self, address):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.connect(address)
        self.stream = self.socket.makefile()

    def __del__(self):
        self.stream.close()
        self.socket.close()

原因是在连接尝试失败之后、在初始化流属性之前调用了不完整对象的析构函数。

编辑: 正如人们在评论中指出的那样,最好使用上下文管理器来管理 Python 中的资源。下面的替代方案使用上下文管理器。它不需要在构造函数中引发异常,并且如果

__enter__()
引发异常,则不会调用
__exit__()

import socket

class Connection:
    def __init__(self, address):
        self.address = address

    def __enter__(self):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.connect(self.address)
        self.stream = self.socket.makefile()
        return self.stream

    def __exit__(self, exc_type, exc_value, exc_traceback):
        self.stream.close()
        self.socket.close()
        return False

with Connection(("localhost", 22)) as c:
    print(c.readline())

11
投票

我看不出有任何理由认为它是不好的形式。

相反,与返回错误代码相反,众所周知,异常的优点之一是错误代码通常不能由构造函数返回。因此,至少在 C++ 这样的语言中,引发异常是发出错误信号的唯一方法。


9
投票

标准库说:

>>> f = file("notexisting.txt")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: [Errno 2] No such file or directory: 'notexisting.txt'

而且我真的不明白为什么它应该被认为是不好的形式。


4
投票

我应该认为这是内置

ValueError
异常的完美案例。


4
投票

在某些情况下,从 init 引发错误是不可避免的,但在 init 中做太多工作是一种不好的风格。您应该考虑创建一个工厂或伪工厂 - 一个返回已设置对象的简单类方法。


3
投票

我同意以上所有内容。

除了引发异常之外,确实没有其他方法可以表明对象初始化过程中出现了问题。

在大多数程序类中,类的状态完全依赖于该类的输入,我们可能期望引发某种 ValueError 或 TypeError。

如果(例如)网络设备不可用或画布对象无法写入,具有副作用的类(例如,执行网络或图形操作的类)可能会在 init 中引发错误。这对我来说听起来很明智,因为您通常想尽快了解故障情况。


0
投票

不要在上下文管理器调用的构造函数中引发异常!

示例: 如果

contextlib.closing
这样使用:

with closing(Foobar()) as foobar:
    foobar.my_method()

那么如果在对象构造函数中引发异常,

foobar.close()
NOT被调用!

© www.soinside.com 2019 - 2024. All rights reserved.