36

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

我来自 C# 背景,我会使用InvalidOperationException“当方法调用对对象的当前状态无效时引发的异常”,但我在 Python 中找不到等效的内置异常

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

4

6 回答 6

35

ValueError在这种情况下最好提出。对于 python,您应该更喜欢使用内置的异常类型而不是创建自己的异常类型。仅当您期望需要捕获它并且行为与捕获内置类型时的行为非常不同时,您才应该创建新的异常类型。在这种情况下,不应该出现这种情况——您不希望看到这种情况,因为这表明使用相关类时出现错误。为此,仅仅为了让它有另一个名字而创建一个新类型是不值得的——这就是你传递给的消息字符串ValueError()的用途。

是否有可能重组你的班级,使这种无效状态是不可能的?

于 2012-05-23T20:39:34.310 回答
14

我发现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是一个更普遍的例外,当一段代码即使给出正确的输入也不能正确运行时,应该使用它,这有点类似于对象处于无效状态时的情况。

于 2015-09-04T12:40:33.687 回答
2
class InvalidOperationException(Exception):
    pass

SYS_STATE = 1

def something_being_run():
    if SYS_STATE < 2:
        raise InvalidOperationException

你的意思是这样的吗?我看不出你为什么不应该对异常进行子类化来创建自己的异常类型,但这可能只是我的旧 Oracle PL/SQL Dev 出来了......

于 2012-05-23T20:12:36.480 回答
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 和迭代器就是一个例子。

于 2012-05-23T20:46:10.147 回答
1

ValueError对我来说还可以,但我认为AssertionError更合适。基本上,它违反了 API 设计者的断言。

于 2013-04-28T03:04:18.053 回答
-1

我认为你应该ValueError在问题出在函数参数上时提出问题,就像你正在做的那样,AttributeError当问题出在必须设置的属性上时。

此外,您可以对 进行子类化AttributeError以制作更具体的异常,但我认为没有必要。您的错误消息的AttributeError异常很清楚。

于 2012-05-23T20:49:36.463 回答