Проверка аргументов в числовом коде Python - PullRequest
3 голосов
/ 29 октября 2009

Я постоянно пишу один и тот же код проверки аргументов для обработки чисел:

def myfun(a, b):
    if a < 0:
        raise ValueError('a cannot be < 0 (was a=%s)' % a)
    # more if.. raise exception stuff here ...
    return a + b

Есть ли лучший способ? Мне сказали не использовать «assert» для этих вещей (хотя я не вижу проблемы, за исключением того, что не знаю значения переменной, которая вызвала ошибку).

edit: Чтобы уточнить, аргументы обычно являются просто числами, и условия проверки ошибок могут быть сложными, нетривиальными и не обязательно приведут к исключению позже, а просто к неправильному результату. (нестабильные алгоритмы, бессмысленные решения и т. д.)

Ответы [ 3 ]

4 голосов
/ 29 октября 2009

assert оптимизируется, если вы запускаете с python -O (скромная оптимизация, но иногда приятно иметь). Одна из предпочтительных альтернатив, если у вас есть шаблоны, которые часто повторяются, может заключаться в использовании декораторов - отличный способ исключить повторение. Например, скажем, у вас есть zillion функции, которые должны вызываться с аргументами по позициям (а не по ключевым словам) и должны иметь свои первые аргументы положительными; то ...:

def firstargpos(f):
  def wrapper(first, *args):
    if first < 0:
      raise ValueError(whateveryouwish)
    return f(first, *args)
  return wrapper

тогда вы говорите что-то вроде:

@ firstargpos def myfun (a, b): ...

и проверки выполняются в декораторах (точнее, закрытии оболочки, которое он возвращает) раз и навсегда. Итак, единственная сложная задача - выяснить, что именно проверяет ваши функции, и как лучше всего вызывать декоратор (ы), чтобы выразить их (трудно сказать, не видя набор функций, которые вы определяете, и набор проверок, каждый из которых необходим). ! -). Помните, что DRY («Не повторяйте себя») находится на первом месте среди руководящих принципов в разработке программного обеспечения, и Python имеет разумную поддержку, позволяющую вам внедрить DRY и избежать ненужного, повторяющегося кода! -)

0 голосов
/ 29 октября 2009

Я не уверен, что это ответит на ваш вопрос, но мне кажется, что проверка большого количества аргументов в начале функции не очень pythonic .

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

def myfun(a, b):
    '''a cannot be < 0'''
    return a + b

Это имеет три явных преимущества. Во-первых, это кратко, на самом деле нет никакого дополнительного кода, делающего что-либо, не имеющее отношения к тому, что вы на самом деле пытаетесь выполнить. Во-вторых, он помещает информацию именно туда, где она находится, в help(myfun), где ожидается, что pythonistas будет искать примечания по использованию. Наконец, действительно ли неположительное значение для a является ошибкой? Хотя вы можете так думать, если что-то определенно не сломается, если a равно нулю (здесь это, вероятно, не будет), тогда, возможно, пропустить это и вызвать ошибку в потоке вызовов, что мудрее. в конце концов, если a + b содержит ошибку, это вызывает исключение, которое передается в стек вызовов, и поведение остается почти таким же.

0 голосов
/ 29 октября 2009

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

Если вы используете много переменных, которые должны иметь одинаковые свойства, почему бы не создать подкласс любого используемого вами типа и добавить эту проверку в сам класс? Затем, когда вы используете свой новый класс, вы знаете, что у вас никогда нет недопустимого значения, и вам не нужно проверять его повсеместно.

...