首页 文章

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

提问于
浏览
19

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

我来自C#背景,我会使用 InvalidOperationException ,"the exception that is thrown when a method call is invalid for the object's current state",但我找不到等效的built-in exception in Python .

当问题出在函数参数时,我一直在提高 ValueError ("raised when a built-in operation or function receives an argument that has the right type but an inappropriate value") . 我认为这在技术上是 self 参数的无效值;这是治疗它的正确方法吗?例如,这是惯用语: raise ValueError("self.foo must be set before running self.bar()")

6 回答

  • 5

    在这种情况下, ValueError 是最好的事情 . 对于python,您应该更喜欢使用built-in exception types而不是创建自己的 . 你应该只创建新的异常类型,当你期望你需要捕获它并且行为方式与你出现的方式非常不同时 - 你're not expecting to catch this because it would indicate an error in using the class in question. For this it'不值得创建一个新类型只是为了让它有另一个名字 - 这就是消息字符串你传给 ValueError() 是为了 .

    是否有可能重构您的课程,以便无法实现这种无效状态?

  • 1

    ValueError 对我来说没问题,但我认为 AssertionError 更合适 . 基本上,它违反了API设计者的断言 .

  • 21
    class InvalidOperationException(Exception):
        pass
    
    SYS_STATE = 1
    
    def something_being_run():
        if SYS_STATE < 2:
            raise InvalidOperationException
    

    你的意思是什么?我没有理由为什么你不应该使用子类异常来创建自己的Exception类型,但这可能就是我出来的旧的Oracle PL / SQL Dev ......

  • 2

    我发现 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

    我认为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和迭代器就是一个例子 .

  • -1

    我认为当问题出在函数参数上时,你应该提出一个ValueError,当问题是必须设置的属性时AttributeError .

    此外,您可以将 AttributeError 子类化为更具体的异常,但我认为没有必要 . 带有错误消息的 AttributeError 异常足够清楚 .

相关问题