Python:在__init__中引发异常是否不好?


128

在其中引发异常是否被认为是不好的形式__init__?如果是这样,那么当某些类变量初始化为None错误类型或类型错误时,可以接受的引发错误的方法是什么?


在C语言中,在析构函数中引发异常是不好的形式,但构造函数应该可以。
2009年

6
@Calyth,你的意思是C ++ -有在C.没有构造函数和析构函数
亚历克斯·马尔泰利

4
...或例外情况。
Matt Wilding 2013年

@Calyth:大声笑,请删除无辜的错字;-)
lpapp 2014年

4
@lpapp是的。C ++。FML。而且我无法编辑该评论。因此,互联网应记录我的愚蠢行为;)
Calyth 2014年

Answers:


159

在内部引发异常__init__()是绝对可以的。在构造函数中没有其他好的方法来指示错误情况,并且标准库中有数百个示例,在这些示例中构建对象会引发异常。

当然,要提出的错误类别由您决定。ValueError如果向构造函数传递了无效的参数,则最好。


14
构造函数中最常用的异常是ValueErrorTypeError
Denis Otkidach 09年

9
只是指出那__init__不是构造函数,而是它的初始化器。您可能需要编辑该行没有其他好的方法来指示构造函数中的错误情况,..
Haris

26

确实,在构造函数中指示错误的唯一正确方法是引发异常。这就是为什么在C ++和其他考虑到异常安全性设计的面向对象的语言中,如果在对象的构造函数中抛出异常(表示对象的初始化不完整),则不会调用析构函数。在脚本语言(例如Python)中通常不是这种情况。例如,如果socket.connect()失败,以下代码将引发AttributeError:

class NetworkInterface:
    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中编写完全安全的异常代码。一些Python开发人员完全避免使用析构函数,但这是另一个争论的问题。


这个答案真的很有用。它不仅在谈论“绿灯”,而且还提到了“析构函数”的例子。
lpapp 2014年

您的python代码编写失败,__del__因为它写得不好。如果this->p_socket->close()在C ++中使用析构函数,则会遇到完全相同的问题。在C ++中,您不会这样做-让成员对象销毁自己。在python中做同样的事情。
埃里克(Eric)

1
@Eric我在C ++中不会有问题,因为C ++不会破坏未正确初始化的对象。实际上,在C ++中,在构造函数中分配资源并在析构函数中分配资源是一种非常常见的习惯用法。您建议成员对象销毁自身(在其析构函数中分配资源)-然后,在编写成员类时会出现相同的问题。
Seppo Enarvi

11

我看不出任何形式的错误。

相反,与返回错误代码相反,已知异常处理得很好的原因之一是,构造函数通常无法返回错误代码。因此,至少在像C ++这样的语言中,引发异常是发出错误的唯一途径。


6

标准库说:

>>> 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'

我也没有真正看到任何理由将其视为错误的形式。


3

我应该认为这是内置ValueError异常的完美案例。


2

我同意以上所有观点。

除了引发异常外,实际上没有其他方法可以表明对象的初始化出错。

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

如果(例如)网络设备不可用或无法写入画布对象,则具有副作用的类(例如,进行网络或图形处理的类)可能会在初始化中引发错误。这对我来说听起来很合理,因为您通常希望尽快了解故障情况。


2

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.