Проверка логических аргументов в python - PullRequest
1 голос
/ 20 января 2011

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

# size
if isinstance(size, str):
    if size in range(4):
        self.data[uid]['size'] = int(size)
    else:
        warnings.warn("ID %s: illegal size %s" % (uid, size))
        self.data[uid]['size'] = None
elif size == None:                                
    self.data[uid]['size'] = None
else:
    warnings.warn("ID %s: illegal size %s" % (uid, str(size)))
    self.data[uid]['size'] = None

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

Спасибо

Ответы [ 4 ]

1 голос
/ 20 января 2011

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

Я использую formencode для таких вещей, какэтот.Похоже, что он предназначен только для анализа HTML-форм, но он с радостью проанализирует и проверит все, что вы ему передадите.Вы определяете классы схемы, которые проверяют все входные данные одновременно.

1 голос
/ 20 января 2011

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

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

ParseError: while parsing 'number = foo': ValueError: invalid literal for int() with base 10: 'foo'

Код выглядит примерно так:

try:
    parse(input)
catch Exception, e:
    raise ParseError("while parsing %r: %r" %(input, e)), None, sys.exc_info()[2]

Третий аргумент raise будет использовать исходную трассировку, поэтому трассировка стека будет указывать на строку, которая фактически вызвала ошибку (например, size = int(value)), а не на вызов raise.

1 голос
/ 20 января 2011

Я бы переписал это:

# size
if isinstance(size, str):
    if size in range(4):
        self.data[uid]['size'] = int(size)
    else:
        warnings.warn("ID %s: illegal size %s" % (uid, size))
        self.data[uid]['size'] = None
elif size == None:                                
    self.data[uid]['size'] = None
else:
    warnings.warn("ID %s: illegal size %s" % (uid, str(size)))
    self.data[uid]['size'] = None

как это:

if size in ["0", "1", "2", "3"]: # alternative: if size in map(str, range(4)):
    self.data[uid]['size'] = int(size)
else:
    if size != None:
        warnings.warn("ID %s: illegal size %s" % (uid, size))
    self.data[uid]['size'] = None    

Что мне искренне не нравится, так это использование isinstance(size, str) (явная проверка типов обычно не одобряется в Python, поскольку она легко ломает ducktyping ).

По этой причине вы не сможете легко найти библиотеку в Python для автоматизации проверки типов: это противоречит основным целям языка.

0 голосов
/ 20 января 2011

Если ваш проект - библиотека, а ваш «пользователь» - другой разработчик, не делайте этого вообще. Самое большее, замените ваши тесты утверждением:

assert 0 <= int(size) <= 4, "size must be between 0 and 4"

Таким образом, когда пользователь вводит неверные данные, они сразу же узнают об этом - и тогда они несут ответственность за правильность ввода.


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

Мое предложение должно состоять в том, чтобы кодировать ваши служебные функции, как указано выше, и выполнять проверку отдельно (т. Е. Уровень пользовательского интерфейса). Очень редко, когда служебная / библиотечная функция проверяет и переопределяет (даже с предупреждением) значение: она должна препятствовать дальнейшему продвижению вызывающего / пользователя до тех пор, пока он не исправит свою сторону вещей.

...