Исключениями гораздо легче управлять, потому что они определяют общие семейства вещей, которые могут пойти не так.
В вашем примере есть только одна возможная проблема, поэтому нет никаких преимуществ в использовании исключений. Но если у вас есть другой класс, который выполняет деление, он должен сигнализировать о том, что вы не можете делить на ноль. Просто вернуть None
больше не получится.
С другой стороны, исключения можно разделить на подклассы, и вы можете перехватить определенные исключения, в зависимости от того, насколько вы заботитесь об основной проблеме. Например, у вас может быть базовое исключение DoesntCompute
и подклассы, такие как InvalidType
и InvalidArgument
. Если вам просто нужен результат, вы можете заключить все вычисления в блок, который улавливает DoesntCompute
, но вы все равно можете выполнять очень специфическую обработку ошибок с такой же легкостью.
Как правило, вы хотите использовать проверку условий для ситуаций, которые понятны, ожидаемы и могут быть обработаны. Вы могли бы использовать исключения для случаев, которые непоследовательны или не поддаются обработке.
Итак, если вы думаете о своей функции «добавить». Он НИКОГДА не должен возвращать null. Это не согласованный результат для добавления двух вещей. В этом случае в переданных аргументах есть ошибка, и функция должна не пытаться делать вид, что все в порядке. Это идеальный случай для исключения исключения.
Вы можете использовать проверку условий и возвращать null, если вы находитесь в обычном или нормальном случае выполнения. Например, IsEqual
может быть хорошим случаем для использования условий и возврата false, если одно из ваших условий не выполняется. I.E.
function bool IsEqual(obj a, obj b)
{
if(a is null) return false;
if(b is null) return false;
if(a.Type != b.Type) return false;
bool result = false;
//Do custom IsEqual comparison code
return result;
}
В этом сценарии вы возвращаете false как для исключительных случаев, так и для случая «объекты не равны». Это означает, что потребитель (вызывающая сторона) не может сказать, было ли сравнение неудачным или объекты просто не равны . Если эти случаи необходимо различать, вам следует использовать исключения вместо условий.
В конечном итоге вы хотите спросить себя, сможет ли потребитель конкретно обработать случай сбоя, с которым вы столкнулись. Если ваш метод / функция не может делать то, что ему нужно , вы, вероятно, захотите сгенерировать исключение.
Отсутствие GeneratedCode можно обойти.
, используя вашу собственную ветвь частичных классов для применения этого атрибута. Это будет означать, что любой добавленный вами пользовательский код (включая реализацию частичных методов) будет исключен. Например: [
namespace MyApp.DB {
[GeneratedCode("LINQ To SQL", "4.0")]
internal partial class MyAppDataContext {
}
// Repeat for each entity
}
-121--4903877- Если вы спрашиваете, вам, вероятно, следует использовать исключения. Исключения используются для обозначения исключительных обстоятельств, конкретного случая, когда все работает иначе, чем другие случаи. Это касается почти всех ошибок, а также многих других вещей.
Во второй реализации sum_
пользователь должен каждый раз проверять значение . Это напоминает шаблон C / Fortran / других языков (и частый источник ошибок), где коды ошибок не проверяются, чего мы избегаем. Вы должны писать такой код на всех уровнях, чтобы иметь возможность распространять ошибки. Это становится беспорядочным, и его особенно избегают в Python.
Еще пара примечаний:
ValueError
и TypeError
. Exception
. Встроенная иерархия исключений здесь . Я бы никогда не реализовал такую функцию, как sum_
, поскольку проверка типов делает ваш код менее гибким, поддерживаемым и идиоматическим.
Я бы просто написал функцию
def sum_ (a, b):
return a + b
, которая работала бы, если бы объекты были совместимы, а если бы нет, она бы уже генерирует исключение, TypeError
, которое все привыкли видеть. Посмотрим, как работает моя реализация
>>> sum_ (1, 4)
5
>>> sum_ (4.5, 5.0)
9.5
>> > sum _ ([1, 2], [3, 4])
[1, 2, 3, 4]
>>> sum_ (3.5, 5) # Эта операция имеет смысл, но у вас ничего не получится
8.5
>>> sum _ ("cat", 7) # Это не имеет смысла и уже является ошибкой.
Traceback (последний вызов последним) :
Файл "", строка 1, в
Файл "", строка 1, в сумме _
TypeError: невозможно объединить 'str' и Объекты int
Мой код был короче и проще, но при этом более надежен и гибок, чем ваш. Вот почему мы избегаем проверки типов в Python.
Вы должны генерировать исключение, если параметр содержит неожиданное значение.
В ваших примерах я бы рекомендовал генерировать исключение, когда два параметра имеют разные типы.
Создание исключения - это элегантный способ прервать службу, не загромождая код.
Может быть sum_
в одиночку выглядит нормально. Что, если, вы знаете, он действительно используется?
#foo.py
def sum_(a, b):
if type(a) == type(b):
return a + b
#egg.py
from foo import sum_:
def egg(c = 5):
return sum_(3, c)
#bar.py
from egg import egg
def bar():
return len(egg("2"))
if __name__ == "__main__":
print bar()
Если вы запустите bar.py
, вы получите:
Traceback (most recent call last):
File "bar.py", line 6, in <module>
print bar()
File "bar.py", line 4, in bar
return len(egg("2"))
TypeError: object of type 'NoneType' has no len()
Видите ли, обычно вызывается функция с намерением воздействовать на ее вывод. Если вы просто «проглотите» исключение и вернете фиктивное значение, у того, кто использует ваш код, возникнут проблемы с устранением неполадок. Во-первых, трассировка совершенно бесполезна. Одного этого должно быть достаточно.
Тот, кто хочет исправить эту ошибку, должен сначала дважды проверить bar.py
, а затем проанализировать egg.py
, пытаясь выяснить, откуда именно появился None. После чтения egg.py
им нужно будет прочитать sum_.py
и, надеюсь, заметить неявный возврат None
; только тогда они понимают проблему: они не прошли проверку типа из-за введенного для них параметра egg.py
.
Добавьте сюда немного реальной сложности, и все очень быстро станет уродливым.
Python, в отличие от C, написан с учетом принципа Проще просить прощения, чем разрешения : если что-то пойдет не так, я получу исключение . Если вы передадите мне None
, где я ожидаю фактического значения, все сломается, исключение произойдет далеко от линии, которая его действительно вызывает, и люди будут проклинать в вашем общем направлении в двадцать различных языков, затем измените код, чтобы создать подходящее исключение ( TypeError («несовместимый тип операнда»)
).
Моя основная причина, по которой я предпочитаю исключения возврату статуса, связана с размышлением о том, что произойдет, если программист забудет выполнить свою работу. За исключением случаев, вы можете не заметить исключение. В этом случае ваша система явно выйдет из строя, и у вас будет возможность подумать, где добавить уловку. При возврате статуса, если вы забудете проверить возврат, он будет молча проигнорирован, и ваш код продолжит работу, возможно, позже загадочным образом выйдет из строя. Я предпочитаю видимый провал невидимому.
Есть и другие причины, которые я объяснил здесь: Исключения против возвратов состояния .