在Python类中,在运行该方法之前必须更改类的某些其他属性时,我应该从实例方法中引出什么类型的错误?
我来自C#背景,我将使用InvalidOperationException
,“当方法调用对于对象的当前状态无效时引发的异常”,但我找不到等效的{{ 3}}
当问题与函数参数有关时,我一直在提高ValueError
(“当内置操作或函数接收到具有正确类型但不合适值的参数时引发”)。我认为这在技术上是self
参数的无效值;这是治疗它的正确方法吗?例如,这是惯用的:raise ValueError("self.foo must be set before running self.bar()")
?
答案 0 :(得分:22)
ValueError
是最好的事情。对于python,您应该更喜欢使用built-in exception types而不是创建自己的。您应该只创建新的异常类型,当您希望捕获它并且行为方式与捕获内置类型时的行为完全不同时。在这种情况下,不应出现这种情况 - 您不希望发现这种情况,因为它表示使用相关类时出错。为此,不值得创建一个新类型只是为了让它有另一个名称 - 这就是你传递给ValueError()
的消息字符串的用途。
是否可以重构您的课程,以便无法实现这种无效状态?
答案 1 :(得分:6)
我发现RuntimeError
是所有内置异常中最合适的,表示无效状态。
请参阅CPython中如何使用它的示例:
Python 2.7.10 (default, Jul 13 2015, 12:05:58)
[GCC 4.2.1 Compatible Apple LLVM 6.1.0 (clang-602.0.53)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from threading import Thread
>>> Thread().join()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/Cellar/python/2.7.10_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py", line 938, in join
raise RuntimeError("cannot join thread before it is started")
RuntimeError: cannot join thread before it is started
重要的是要注意,即使CPython实现本身与库之间使用特定异常类型也不一致。有时使用ValueError
代替,但在我看来,它从Python文档中的描述表明它的使用是为其他情况保留的。 RuntimeError
是一个更普遍的例外,当一段代码无法正确运行时,如果给出了正确的输入,则应该使用它,这有点类似于对象处于无效状态时的情况。
答案 2 :(得分:2)
class InvalidOperationException(Exception):
pass
SYS_STATE = 1
def something_being_run():
if SYS_STATE < 2:
raise InvalidOperationException
你的意思是什么?我没有理由为什么你不应该使用子类异常来创建自己的异常类型,但这可能就是我出来的旧的Oracle PL / SQL Dev ......
答案 3 :(得分:2)
我认为pythonic方式不是让对象处于这样一种状态,即方法调用不会崩溃,尽管处于错误状态。这些是最难找到的错误,因为程序最终推翻的地方并不是错误发生的地方。
例如
class PseudoTuple(object):
"""
The sum method of PseudoTuple will raise an AttributeError if either x or y have
not been set
"""
def setX(self, x):
self.x = x
def setY(self, y):
self.y = y
def sum(self):
"""
In the documentation it should be made clear that x and y need to have been set
for sum to work properly
"""
return self.x + self.y
class AnotherPseudoTuple(PseudoTuple):
"""
For AnotherPseudoTuple sum will now raise a TypeError if x and y have not been
properly set
"""
def __init__(self, x=None, y=None):
self.x = x
self.y = y
不应该做的事情就像是
class BadPseudoTuple(PseudoTuple):
"""
In BadPseudoTuple -1 is used to indicate an invalid state
"""
def __init__(self, x=-1, y=-1):
self.x = x
self.y = y
def sum(self):
if self.x == -1 or self.y == -1:
raise SomeException("BadPseudoTuple in invalid state")
else:
return self.x + self.y
我认为这是在pythonic的座右铭:
要求宽恕比获得许可更容易
如果异常状态是在正常执行过程中可能发生的事情,而不是通过滥用该类而导致用户错误,那么您应该创建自己的异常似乎是合理的。 StopIteration和迭代器就是一个例子。
答案 4 :(得分:1)
ValueError
对我来说没问题,但我认为AssertionError
更合适。基本上,它违反了API设计者的断言。
答案 5 :(得分:-1)
我认为当问题与你正在进行的函数参数时你应该提出ValueError
,当问题是必须设置的属性时,你应该AttributeError
。
此外,您可以将AttributeError
子类化为更具体的例外,但我认为没有必要。带有错误消息的AttributeError
异常非常清楚。