在构造函数中运行可能失败的代码的不良做法?

Tom*_*Tom 15 python oop constructor exception-handling

我的问题是一个设计问题.在Python中,如果"构造函数"中的代码失败,则对象最终不会被定义.从而:

someInstance = MyClass("test123") #lets say that constructor throws an exception
someInstance.doSomething() # will fail, name someInstance not defined.
Run Code Online (Sandbox Code Playgroud)

我确实有一种情况,如果我从构造函数中删除容易出错的代码,会发生很多代码复制.基本上我的构造函数填充了一些属性(通过IO,很多可能出错),可以使用各种getter访问.如果我从构造函数中删除代码,我将有10个带有复制粘贴代码的getter:

  1. 是属性真的设置?
  2. 执行一些IO操作来填充属性
  3. 返回有问题的变量的内容

我不喜欢这样,因为我的所有getter都会包含很多代码.而不是我在中心位置,构造函数中执行我的IO操作,并填充我的所有属性.

这是一个正确的方法吗?

lis*_*ine 35

C++中的构造函数和__init__Python中的方法之间存在差异.在C++中,构造函数的任务是构造一个对象.如果失败,则不会调用析构函数.因此,如果在抛出异常之前获取了任何资源,则应在退出构造函数之前完成清理.因此,有些人更喜欢两相结构,大多数结构都是在构造函数之外完成的(ugh).

Python有一个更清晰的两阶段构造(构造,然后初始化).但是,许多人将__init__方法(初始化程序)与构造函数混淆.调用Python中的实际构造函数__new__.与C++不同,它不需要实例,而是返回一个实例.任务__init__是初始化创建的实例.如果引发异常__init__,__del__则将按预期调用析构函数(如果有),因为在调用时已经创建了对象(即使它未正确初始化)__init__.

回答你的问题:

在Python中,如果"构造函数"中的代码失败,则对象最终不会被定义.

这不完全正确.如果__init__引发异常,则会创建对象但未正确初始化(例如,未分配某些属性).但是在它被引发的时候,你可能没有对这个对象的任何引用,所以没有分配属性的事实并不重要.只有析构函数(如果有的话)需要检查属性是否确实存在.

这是一个正确的方法吗?

在Python中,初始化对象__init__并不担心异常.在C++中,使用RAII.


更新 [关于资源管理]:

在垃圾收集语言中,如果要处理资源,尤其是数据库连接等有限资源,最好不要在析构函数中释放它们.这是因为对象以非确定性的方式被破坏,如果你碰巧有一个引用循环(这并不总是很容易辨别),并且循环中至少有一个对象定义了析构函数,它们将会永远不会被摧毁 垃圾收集语言还有其他处理资源的方法.在Python中,它是一个with语句.


小智 20

至少在C++中,在构造函数中放置容易出错的代码没有任何问题 - 如果发生错误,您只需抛出异常.如果需要代码来正确构造对象,那么实际上没有其他选择(尽管您可以将代码抽象为子函数,或者更好地将其抽象为子对象的构造函数).最糟糕的做法是半构造对象,然后期望用户调用其他函数以某种方式完成构造.

  • 在C++构造函数中抛出异常不会调用析构函数(因为该对象尚未构造).在投掷之前,你必须确保清理你所设想构建的任何东西.auto_ptrs很方便. (5认同)

And*_*ite 3

我不是 Python 开发人员,但一般来说,最好避免在构造函数中进行复杂/容易出错的操作。解决这个问题的一种方法是在类中放置“LoadFromFile”或“Init”方法,以从外部源填充对象。构造对象后必须单独调用此加载/初始化方法。

  • 拉阿尔托提出的工厂想法可能也是个好主意。工厂将隐藏两阶段构造,因此您的应用程序代码不需要了解它。(我会赞成他的回答,但今天我没有投票了:)。我仍然坚持我的观点,最好不要从构造函数中抛出异常。 (3认同)