Лучшая практика в Python для возвращаемого значения на ошибке по сравнению с успехом

Запишите, какой бы ни более ясно состояния Ваше намерение.

После Ваших работ программы, выясните то, что является медленным, и сделайте это быстрее.

не делают этого наоборот.

48
задан Bhargav Rao 22 June 2015 в 15:02
поделиться

5 ответов

Во-первых, все, что вы делаете, не возвращает результат и сообщение об ошибке. Это действительно плохой способ справляться с ошибками, который вызовет бесконечную головную боль. Если вам нужно указать ошибку, всегда вызывайте исключение .

Я обычно стараюсь избегать появления ошибок, если в этом нет необходимости. В вашем примере выдача ошибки на самом деле не требуется. Пересечение пустого списка с непустым не является ошибкой. Результатом является пустой список, и это правильно. Но предположим, что вы хотите заняться другими делами. Например, если метод получил тип, отличный от списка. В этом случае лучше вызвать исключение. Нечего бояться исключений.

Мой совет - посмотрите библиотеку Python на предмет похожих функций и посмотрите, как Python обрабатывает эти особые случаи. Например, взгляните на метод пересечения в наборе, он имеет тенденцию прощать. Здесь я пытаюсь пересечь пустой набор с пустым списком:

>>> b = []
>>> a = set()
>>> a.intersection(b)
set([])

>>> b = [1, 2]
>>> a = set([1, 3])
>>> a.intersection(b)
set([1])

Ошибки выдаются только при необходимости:

>>> b = 1
>>> a.intersection(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable

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

EDIT:

OP говорит:

Я хочу вернуть что-то, чтобы указать Есть случаи, когда возврат True или False в случае успеха или неудачи может быть хорошим. Но очень важно быть последовательным. Функция всегда должна возвращать один и тот же тип или структуру. Очень запутанно иметь функцию, которая может возвращать список или логическое значение. Или верните тот же тип, но значение этого значения может быть другим в случае ошибки.

EDIT:

OP говорит:

Я хочу вернуть что-то, чтобы указать Есть случаи, когда возврат True или False в случае успеха или неудачи может быть хорошим. Но очень важно быть последовательным. Функция всегда должна возвращать один и тот же тип или структуру. Очень запутанно иметь функцию, которая может возвращать список или логическое значение. Или верните тот же тип, но значение этого значения может быть другим в случае ошибки.

EDIT:

OP говорит:

Я хочу вернуть что-то, чтобы указать параметры были неправильными.

Ничто не говорит об ошибке лучше, чем исключение. Если вы хотите указать, что параметры неверны, используйте исключения и поместите полезное сообщение об ошибке. Возврат результата в этом случае просто сбивает с толку. Могут быть и другие случаи, когда вы хотите указать, что ничего не произошло, но это не ошибка. Например, если у вас есть метод, который удаляет записи из таблицы, а запись, запрошенная для удаления, не существует. В этом случае может быть нормально просто вернуть True или False в случае успеха или неудачи. Это зависит от приложения и предполагаемого поведения

59
ответ дан 26 November 2019 в 18:39
поделиться

Исключения определенно лучше (и более питоничны), чем возвращаемый статус. Подробнее об этом: Исключения и возврат состояния

11
ответ дан 26 November 2019 в 18:39
поделиться

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

class IntersectException(Exception):
    def __init__(self, msg):
        self.msg = msg
    def __str__(self):
        return self.msg

def intersect_two_lists(self, list1, list2):
    if not list1: raise IntersectException("list1 must not be empty.")
    if not list2: raise IntersectException("list2 must not be empty.")

    #http://bytes.com/topic/python/answers/19083-standard
    return filter(lambda x:x in list1,list2)

В этом конкретном случае я бы, вероятно, просто отказался от тестов. На самом деле нет ничего плохого в пересечении пустых списков. Кроме того, лямбда в наши дни отчасти не одобряется, а не списком. См. Найти пересечение двух списков? , где есть несколько способов написать это без использования лямбда .

21
ответ дан 26 November 2019 в 18:39
поделиться

Мне нравится возвращать кортеж:

(True, some_result)

(False, some_useful_response)

Объект some_useful_response может использоваться для обработки условия возврата или может служить для отображать отладочную информацию.

ПРИМЕЧАНИЕ : этот метод применяется к возвращаемым значениям любого типа. Его не следует путать с исключениями .

На принимающей стороне вам просто нужно распаковать:

Код, Response = some_function (...)

Этот метод применяется для «нормальный» поток управления: необходимо использовать функцию исключения, когда происходят некоторые неожиданные вводы / обработки.

Также стоит отметить: этот метод помогает нормализовать возвращаемые функции. И программист, и пользователь функций знают, чего ожидать .

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я пришел из опыта Erlang:

13
ответ дан 26 November 2019 в 18:39
поделиться

В общем случае создаются исключения для исключительных обстоятельств. Хотелось бы, чтобы я мог вспомнить точную цитату (или того, кто ее сказал), но вы должны стремиться к функциям, которые принимают столько значений и типов, сколько разумно, и поддерживают очень узко определенное поведение. Это вариант того, о чем Надя говорила . Рассмотрим следующие варианты использования вашей функции:

  1. correct_two_lists (None, None)
  2. Intercct_two_lists ([], ())
  3. Intercct_two_lists ('12 ',' 23 ')
  4. correct_two_lists ([1, 2 ], {1: 'one', 2: 'two'})
  5. correct_two_lists (False, [1])
  6. correct_two_lists (None, [1])

Я ожидал, что (5) бросит исключение, поскольку передача False является ошибкой типа. Остальные, однако, имеет некоторый смысл, но это действительно зависит от контракта, который устанавливает функция. Если correct_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» в качестве необязательного аргумента. Я бы не советовал делать это, если вы не хотите использовать Дизайн на основе политик . Я действительно хотел упомянуть об этом на случай, если вы раньше не рассматривали этот вариант.

Если контракт для correct_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 об исключениях. Эта конкретная запись дает вам еще пищу для размышлений об исключениях.

затем будьте явными и бросайте исключения, если контракт нарушается:

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

затем будьте явными и бросайте исключения, если контракт нарушается:

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

Я обычно предпочитаю создавать исключения, когда контракт нарушается или мне дают значение, которое я действительно не могу использовать. Если ценности, которые мне дают, разумны, я стараюсь сделать что-нибудь разумное взамен. Вы также можете прочитать C ++ FAQ Lite об исключениях. Эта конкретная запись дает вам еще пищу для размышлений об исключениях.

Я обычно предпочитаю создавать исключения, когда контракт нарушается или мне дают значение, которое я действительно не могу использовать. Если ценности, которые мне дают, разумны, я стараюсь сделать что-нибудь разумное взамен. Вы также можете прочитать C ++ FAQ Lite об исключениях. Эта конкретная запись дает вам еще пищу для размышлений об исключениях.

3
ответ дан 26 November 2019 в 18:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: