Общий случай - выбросить исключения для исключительных обстоятельств. Я хотел бы, чтобы я мог вспомнить точную цитату (или кто это сказал), но вы должны стремиться к функциям, которые принимают столько значений и типов, сколько это разумно, и поддерживают очень узко определенное поведение. Это вариант того, что Надя говорила о . Рассмотрим следующие варианты использования вашей функции:
intersect_two_lists(None, None)
intersect_two_lists([], ())
intersect_two_lists('12', '23')
intersect_two_lists([1, 2], {1: 'one', 2: 'two'})
intersect_two_lists(False, [1])
intersect_two_lists(None, [1])
Я ожидаю, что (5) выдает исключение, поскольку передача False
является ошибкой типа. Остальные, однако, имеют некоторый смысл, но это действительно зависит от контракта, который устанавливает функция. Если intersect_two_lists
было определено как возвращающее пересечение двух итераций , то все, кроме (5) должно работать, пока вы сделаете None
действительным представлением пустого набора, Реализация будет выглядеть примерно так:
def intersect_two_lists(seq1, seq2):
if seq1 is None: seq1 = []
if seq2 is None: seq2 = []
if not isinstance(seq1, collections.Iterable):
raise TypeError("seq1 is not Iterable")
if not isinstance(seq2, collections.Iterable):
raise TypeError("seq1 is not Iterable")
return filter(...)
Я обычно пишу вспомогательные функции, которые обеспечивают выполнение любого контракта, а затем вызываю их, чтобы проверить все предварительные условия. Что-то вроде:
def require_iterable(name, arg):
"""Returns an iterable representation of arg or raises an exception."""
if arg is not None:
if not isinstance(arg, collections.Iterable):
raise TypeError(name + " is not Iterable")
return arg
return []
def intersect_two_lists(seq1, seq2):
list1 = require_iterable("seq1", seq1)
list2 = require_iterable("seq2", seq2)
return filter(...)
Вы также можете расширить эту концепцию и передать "policy" в качестве необязательного аргумента. Я бы не советовал делать это, если вы не хотите принять Дизайн на основе политик . Я хотел бы упомянуть об этом на тот случай, если вы раньше не рассматривали эту опцию.
Если контракт для intersect_two_lists
заключается в том, что он принимает только два непустых параметра list
, тогда будьте явными и генерируйте исключения, если контракт нарушен:
def require_non_empty_list(name, var):
if not isinstance(var, list):
raise TypeError(name + " is not a list")
if var == []:
raise ValueError(name + " is empty")
def intersect_two_lists(list1, list2):
require_non_empty_list('list1', list1)
require_non_empty_list('list2', list2)
return filter(...)
Я думаю, что мораль этой истории что бы вы ни делали, делайте это последовательно и будьте откровенны . Лично я обычно выступаю за повышение исключений, когда нарушается договор или мне дают значение, которое я действительно не могу использовать. Если значения, которые мне дают, являются разумными, то я стараюсь сделать что-то разумное взамен. Возможно, вы также захотите прочитать C ++ FAQ Lite об исключениях. Эта конкретная статья дает вам еще пищу для размышлений об исключениях.