Подтвердить что ты не робот

Какую ошибку повышать, если состояние класса недействительно?

В классе Python, какую ошибку я должен поднять из метода экземпляра, когда некоторые другие атрибуты класса должны быть изменены до запуска этого метода?

Я иду из фона С#, где я бы использовал InvalidOperationException, "исключение, которое бросается, когда вызов метода недействителен для текущего состояния объекта", но я не смог найти эквивалент встроенное исключение в Python.

Я поднимал ValueError ( "при ​​поднятии, когда встроенная операция или функция получает аргумент, который имеет правильный тип, но несоответствующее значение" ), когда проблема связана с параметрами функции. Я полагаю, что это технически недопустимое значение для параметра self; это правильный способ лечения? Например, это идиоматика: raise ValueError("self.foo must be set before running self.bar()")?

4b9b3361

Ответ 1

ValueError - лучшая вещь, которую нужно повысить в этом случае. Для python вам следует использовать встроенные типы исключений для создания собственного. Вы должны создавать только новые типы исключений, когда вы ожидаете, что вам нужно будет их поймать и вести себя по-другому, чем вы себя вели, когда будете ловить встроенные типы. В этом случае ситуация не должна возникать - вы не ожидаете этого, потому что это указывает на ошибку при использовании рассматриваемого класса. Для этого не стоит создавать новый тип, чтобы он имел другое имя - это то, что строка сообщения, которую вы передаете в ValueError(), предназначена для.

Возможно ли реструктурировать ваш класс, чтобы такое недопустимое состояние невозможно?

Ответ 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 является более общим исключением и его следует использовать, когда кусок кода не может корректно вести событие, если ему был присвоен правильный ввод, что несколько похоже на ситуацию, когда объект находится в недопустимом состоянии.

Ответ 3

Я думаю, что путинский путь не оставляет объект в таком состоянии, когда вызов метода не будет сбой, несмотря на то, что он находится в ошибочном состоянии. Это самые трудные ошибки для поиска, поскольку точка, в которой программа, наконец, перевернулась, не там, где произошла ошибка.

например.

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

class InvalidOperationException(Exception):
    pass

SYS_STATE = 1

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

Что-то вроде этого? Я не вижу причин, по которым вы не должны исключать sub-class, чтобы создавать свои собственные типы исключений, но это может быть просто старый Oracle PL/SQL Dev во мне, выходящий...

Ответ 5

ValueError подходит мне, но я думаю, что AssertionError более уместен. В принципе, это нарушает утверждение, сделанное дизайнером API.

Ответ 6

Я думаю, вы должны поднять ValueError, когда проблема связана с параметрами функции, как вы это делаете, и AttributeError, когда проблема связана с атрибутом, который должен быть установлен.

Кроме того, вы можете подклассифицировать AttributeError, чтобы сделать более конкретное исключение, но я не вижу в этом необходимости. Исключение AttributeError Исключение с сообщением об ошибке достаточно ясно.