Какое исключение я должен вызывать для неверных / недопустимых комбинаций аргументов в Python?

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

def import_to_orm(name, save=False, recurse=False):
    """
    :param name: Name of some external entity to import.
    :param save: Save the ORM object before returning.
    :param recurse: Attempt to import associated objects as well. Because you
        need the original object to have a key to relate to, save must be
        `True` for recurse to be `True`.
    :raise BadValueError: If `recurse and not save`.
    :return: The ORM object.
    """
    pass

Единственное, что раздражает, это то, что в каждом пакете есть свой BadValueError, обычно немного отличающийся. Я знаю, что в Java существует java.lang.IllegalArgumentException - хорошо ли понятно, что каждый будет создавать свои собственные BadValueError на Python, или есть другой, предпочтительный метод?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
623
0
273 511
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Я в основном только что видел встроенный ValueError, используемый в этой ситуации.

Я бы унаследовал от ValueError

class IllegalArgumentError(ValueError):
    pass

Иногда лучше создать свои собственные исключения, но унаследовать их от встроенного, что максимально близко к тому, что вы хотите.

Если вам нужно отловить эту конкретную ошибку, полезно иметь имя.

Прекратить писать классы и пользовательские исключения - pyvideo.org/video/880/stop-writing-classes

Hamish Grubijan 15.11.2012 18:50

@HamishGrubijan, видео ужасное. Когда кто-нибудь предлагал хорошо использовать класс, он просто блеял: «Не используйте классы». Блестяще. Классы хорошие. Но не верьте мне на слово.

Rob Grant 23.02.2016 12:41

@RobertGrant Нет, ты этого не понимаешь. Это видео не совсем буквально о том, чтобы «не использовать классы». Речь идет о том, чтобы не усложнять слишком много вещей.

RayLuo 31.08.2016 07:06

@RayLuo, возможно, вы проверили, что говорится в видео, и превратили это в приятное, разумное альтернативное сообщение, но именно об этом говорится в видео, и это то, что уйдет от человека, у которого нет большого опыта и здравого смысла. с.

Rob Grant 07.09.2016 15:14

@RobertGrant Насколько я понимаю, он выступает против неправильного использования классов, а не против любого использования классов (см. С минуты 12:48). И он сказал, что есть риск чрезмерного упрощения при ответе на первый вопрос. Итак, ключ в том, чтобы найти баланс, не так ли?

Samuel 28.07.2017 18:05

@SamuelSantana, как я уже сказал, каждый раз, когда кто-нибудь поднимает руку и говорит: "А как насчет X?" где X был хорошей идеей, он просто сказал: «не создавайте еще один класс». Довольно ясно. Я согласен, что главное - это баланс; проблема в том, что это слишком расплывчато, чтобы жить по нему :-)

Rob Grant 29.07.2017 11:25

Не совсем то, что OP хотел отметить. Значения аргументов может быть в порядке, но проблема комбинации аргументов. Так что IllegalArgument - неправильное слово, BadArguments больше похоже на него. Я так много думаю об этой идее, что предлагаю ее как другой ответ.

BobHy 05.10.2017 18:06

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

Raised when a built-in operation or function receives an argument that has the right type but an inappropriate value, and the situation is not described by a more precise exception such as IndexError.

- Документация ValueError

Сравните google.com/codesearch?q=lang:python+class \ + \ wОшибка (([^ E] ‌ \ w * | E [^ x] \ w)): с google.com/codesearch?q=lang:python+class \ + \ w * Ошибка (исключение):

Markus Jarderot 02.11.2008 02:41

@dbr: Да, я думаю, они имеют в виду «(встроенная операция или функция)», а не «(встроенная операция) или функция». Я бы подумал, что они бы противопоставили это, сказав «определяется пользователем» во втором случае.

cdleary 02.11.2008 02:49

@MizardX: Это интересно, как и google.com/codesearch?q=lang%3Apython+class \ + IllegalArgument‌ Exception - они разделяются между наследованием от ValueError, Exception и BaseException.

cdleary 02.11.2008 02:55

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

Ignacio Vazquez-Abrams 02.11.2008 04:50

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

James Bennett 02.11.2008 09:48

@James Bennett: Мы процитировали несколько проектов в приведенных выше поисках кода Google, которые не используют ValueError напрямую, поэтому, по крайней мере, кажется, что есть необходимость в разъяснении.

cdleary 03.11.2008 04:19

Эээ, если мы собираемся использовать поиск Google Code, чтобы аргументировать это: google.com/codesearch?q=lang%3Apython+raise%5C+ValueError # 66 300 случаев возникновения ValueError, включая Zope, xen, Django, Mozilla (и это только с первой страницы результатов). Если подходит встроенное исключение, используйте его.

dbr 04.11.2008 11:29

Как сказано, документация неоднозначна. Он должен был быть написан либо как «Повышается, когда встроенная операция или встроенная функция получает», либо как «Повышается, когда функция или встроенная операция получает». Конечно, каким бы ни было первоначальное намерение, текущая практика превзошла его (как указывает @dbr). Так что его следует переписать как второй вариант.

Eponymous 22.02.2013 04:37

Это должен быть комментарий, а не ответ.

Paul Rooney 02.09.2016 06:31

Ничего о процитированном документе предотвращает вы не поднимаете ValueError в коде, написанном пользователем. И широко распространенное использование в сообществе Python является для использования в собственном коде.

BobHy 05.10.2017 17:57
Ответ принят как подходящий

Я бы просто поднял ValueError, если вам не нужно более конкретное исключение ..

def import_to_orm(name, save=False, recurse=False):
    if recurse and not save:
        raise ValueError("save must be True if recurse is True")

На самом деле нет смысла делать class BadValueError(ValueError):pass - ваш собственный класс идентичен использованию ValueError, так почему бы не использовать его?

> "Так почему бы не использовать это?" - Специфика. Возможно, я хочу поймать какой-нибудь внешний слой «MyValueError», но не любой / весь «ValueError».

Kevin Little 02.11.2008 18:11

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

cdleary 18.03.2009 03:35

Я не уверен, что этот аргумент верен: если кто-то звонит math.sqrt(-1), это ошибка программирования, которую все равно необходимо исправить. ValueError не предназначен для перехвата при нормальном выполнении программы, иначе он будет производным от RuntimeError.

ereOn 28.05.2015 17:53

Если ошибка относится к ЧИСЛУ аргументов для функции с переменным числом аргументов ... например, функции, в которой аргументы должны быть четным числом аргументов, то для согласованности следует поднять исключение TypeError. И не создавайте свой собственный класс, если: а) у вас нет варианта использования или б) вы экспортируете библиотеку для использования другими. Преждевременная функциональность - это смерть кода.

Erik Aronesty 12.12.2016 17:58

Будет ли утверждение приемлемым в этом случае или есть конкретная причина использовать вместо него ValueError?

vlizana 18.08.2020 19:29

Согласитесь с предложением Маркуса свернуть собственное исключение, но в тексте исключения следует пояснить, что проблема заключается в списке аргументов, а не в отдельных значениях аргументов. Я бы предложил:

class BadCallError(ValueError):
    pass

Используется, когда отсутствуют аргументы ключевого слова, необходимые для конкретного вызова, или значения аргументов действительны по отдельности, но несовместимы друг с другом. ValueError все равно будет прав, если конкретный аргумент имеет правильный тип, но выходит за пределы допустимого диапазона.

Разве это не должно быть стандартным исключением в Python?

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

Я бы поднял KeyError для ключевого слова, которое не найдено (поскольку отсутствующее явное ключевое слово семантически идентично dict **kwargs, в котором отсутствует этот ключ).

cowbert 31.03.2018 00:27

Я думаю, что лучший способ справиться с этим - это то, как сам python справляется с этим. Python вызывает TypeError. Например:

$ python -c 'print(sum())'
Traceback (most recent call last):
File "<string>", line 1, in <module>
TypeError: sum expected at least 1 arguments, got 0

Наш младший разработчик только что нашел эту страницу в поисковике Google по запросу «неправильные аргументы исключения python», и я удивлен, что очевидный (для меня) ответ не был предложен за десять лет с тех пор, как был задан этот вопрос.

Меня ничего не удивляет, но я на 100% согласен с тем, что TypeError является правильным исключением, если тип неверен для некоторых аргументов, переданных в функцию. ValueError будет подходящим, если переменные имеют правильный тип, но их содержимое и значения не имеют смысла.

user3504575 25.04.2019 00:59

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

Nobody 25.04.2019 14:41

Как сказали @ user3504575 и @Nobody, TypeError используется, если аргументы не соответствуют сигнатуре функции (неправильное количество позиционных аргументов, аргументы ключевого слова с неправильным именем, неправильный тип аргумента), но ValueError используется при вызове функции совпадает с подписью, но значения аргументов недействительны (например, вызов int('a')). источник

goodmami 24.10.2019 05:47

Поскольку вопрос OP относится к «недопустимым комбинациям аргументов», кажется, что ошибка TypeError будет подходящей, поскольку это был бы случай, когда сигнатура функции по существу неверна для переданных аргументов.

J Bones 25.10.2019 21:21

В вашем примере вызывается sum() без аргументов, то есть TypeError, но OP был обеспокоен «недопустимыми» комбинациями значений аргументов, когда типы аргументов верны. В этом случае и save, и recurse являются булевыми, но если recurse - это True, то save не должен быть False. Это ValueError. Я согласен с тем, что на некоторую интерпретацию названия вопроса ответит TypeError, но не для представленного примера.

goodmami 29.10.2019 12:42

Это зависит от того, в чем проблема с аргументами.

Если аргумент имеет неправильный тип, вызовите TypeError. Например, когда вы получаете строку вместо одного из этих логических значений.

if not isinstance(save, bool):
    raise TypeError(f"Argument save must be of type bool, not {type(save)}")

Обратите внимание, однако, что в Python мы редко проводим подобные проверки. Если аргумент действительно неверен, за нас, вероятно, пожалуется какая-нибудь более глубокая функция. И если мы проверим только логическое значение, возможно, какой-то пользователь кода позже просто скармливает ему строку, зная, что непустые строки всегда истинны. Это могло бы спасти его от гипсовой повязки.

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

if recurse and not save:
    raise ValueError("If recurse is True, save should be True too")

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

if recurse and not save:
    logging.warning("Bad arguments in import_to_orm() - if recurse is True, so should save be")
    save = True

Думаю, это наиболее точный ответ. Это явно недооценено (пока 7 голосов, включая мой).

Siu Ching Pong -Asuka Kenji- 17.05.2020 05:48

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