类状态无效时会引发什么错误?

时间:2012-05-23 19:55:31

标签: python exception

在Python类中,在运行该方法之前必须更改类的某些其他属性时,我应该从实例方法中引出什么类型的错误?

我来自C#背景,我将使用InvalidOperationException,“当方法调用对于对象的当前状态无效时引发的异常”,但我找不到等效的{{ 3}}

当问题与函数参数有关时,我一直在提高ValueError(“当内置操作或函数接收到具有正确类型但不合适值的参数时引发”)。我认为这在技术上是self参数的无效值;这是治疗它的正确方法吗?例如,这是惯用的:raise ValueError("self.foo must be set before running self.bar()")

6 个答案:

答案 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异常非常清楚。