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

StackOverflow https://stackoverflow.com/questions/1630706

  •  06-07-2019
  •  | 
  •  

Вопрос

В Общая информация, допустим, у вас есть метод, подобный приведенному ниже.

def intersect_two_lists(self, list1, list2):
    if not list1:
        self.trap_error("union_two_lists: list1 must not be empty.")
        return False
    if not list2:
        self.trap_error("union_two_lists: list2 must not be empty.")
        return False
    #http://bytes.com/topic/python/answers/19083-standard
    return filter(lambda x:x in list1,list2)

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

Мой вопрос в том, какова наилучшая практика в подобных областях, а не только для списков?Возвращайте все, что я захочу, и убедитесь, что я документирую это для чтения пользователем?:-) Чем занимается большинство из вас, ребята:

  1. Если в случае успеха вы должны были вернуть True или False, и вы поймали ошибку?
  2. Если в случае успеха вы должны были вернуть список, а вы поймали ошибку?
  3. Если в случае успеха вы должны были вернуть дескриптор файла, и вы поймали ошибку?
  4. et cetera
Это было полезно?

Решение

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

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

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

>>> 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 при успехе или неудаче может быть полезным.Но очень важно быть последовательным.Функция всегда должна возвращать один и тот же тип или структуру.Наличие функции, которая могла бы возвращать список или логическое значение, очень сбивает с толку.Или возвращает тот же тип, но значение этого значения может отличаться в случае ошибки.

Редактировать:

В ОП говорится:

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

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

Другие советы

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

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)

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

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

  

(True, some_result)

     

(False, some_useful_response)

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

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

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

  

Code, Response = some_function (...)

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

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

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я родом из Эрланга: -)

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

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

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

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

    Для результатов, не относящихся к коллекции, есть несколько способов обработки условных возвратов:

    <Ол>
  • Как уже объяснялось во многих ответах, использование исключений является одним из способов для решения этой проблемы и является идиоматическим питоном. Тем не менее, я предпочитаю не использовать исключения для потока управления, так как я считаю, что это создает двусмысленность в намерениях исключения. Вместо этого поднимайте исключения в реальных исключительных ситуациях.
  • Другое решение - вернуть None вместо ожидаемого результата, но это вынуждает пользователя добавлять защитные проверки повсюду на своих сайтах вызовов, запутывая фактическую бизнес-логику, которой они являются. пытаясь на самом деле выполнить.
  • Третий способ - использовать тип коллекции, который может содержать только один элемент (или быть явно пустым). Это называется опциональным и является моим предпочтительным методом, потому что он позволяет поддерживать чистоту логики сайта вызовов. Тем не менее, Python не имеет встроенного необязательного типа, поэтому я использую свой собственный. Я опубликовал его в крошечной библиотеке под названием опциональный.py , если кто-нибудь хочет попробовать. Вы можете установить его, используя pip install option.py . Я приветствую комментарии, пожелания и комментарии.
  • Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top