Самый Pythonic способ проверки ввода

Каков самый «правильный» Pythonic способ проверки пользовательского ввода в Python?

Я использовал следующее:

while True:
    stuff = input("Please enter foo: ")
    try:
        some_test(stuff)
        print("Thanks.")
        break
    except SomeException:
        print("Invalid input.")

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


person henrebotha    schedule 12.12.2013    source источник
comment
Не могли бы вы показать больше кода?   -  person thefourtheye    schedule 12.12.2013
comment
Извините, я случайно отправил, не закончив печатать!   -  person henrebotha    schedule 12.12.2013
comment
Я думаю, что то, что вы сделали (используя try~except), неплохо, хотя есть и другие способы для той же задачи. Я не слышал о пути Pythonic. Такая задача возникает на всех других языках.   -  person GoodGJ    schedule 12.12.2013
comment
Опубликуйте функцию some_test, пожалуйста   -  person inspectorG4dget    schedule 12.12.2013
comment
Полностью зависит от того, что вы подразумеваете под проверкой ввода (номер кредитной карты, IP-адрес, int, float?), и что вы хотите делать, когда происходит сбой проверки.   -  person Lukas Graf    schedule 12.12.2013
comment
Функция some_test не должна иметь значения. В одной конкретной ситуации, с которой я сейчас имею дело, some_test выглядит следующим образом: with smtplib.SMTP_SSL(host=host, port=port) as server: server.login(user, password) вызывает исключение, если вход не удался. Впрочем, я спрашиваю в целом.   -  person henrebotha    schedule 12.12.2013
comment
@LukasGraf: Что я хочу сделать в случае сбоя проверки, так это неоднократно запрашивать у пользователя ввод, пока он не подчинится, лол.   -  person henrebotha    schedule 12.12.2013
comment
@InbarRose: Спасибо за ссылку, мне очень нравится ваш ответ.   -  person henrebotha    schedule 12.12.2013


Ответы (3)


Мне нравятся декораторы, чтобы отделить проверку от остальной обработки ввода.

#!/usr/bin/env python

def repeatOnError(*exceptions):
  def checking(function):
    def checked(*args, **kwargs):
      while True:
        try:
          result = function(*args, **kwargs)
        except exceptions as problem:
          print "There was a problem with the input:"
          print problem.__class__.__name__
          print problem
          print "Please repeat!"
        else: 
          return result
    return checked
  return checking

@repeatOnError(ValueError)
def getNumberOfIterations():
  return int(raw_input("Please enter the number of iterations: "))

iterationCounter = getNumberOfIterations()
print "You have chosen", iterationCounter, "iterations."

РЕДАКТИРОВАТЬ:

Декоратор — это более или менее оболочка для существующей функции (или метода). Он берет существующую функцию (обозначенную ниже ее директивы @decorator) и возвращает для нее "замену". Эта замена в нашем случае вызывает исходную функцию в цикле и перехватывает любое возникающее при этом исключение. Если исключения не происходит, он просто возвращает результат исходной функции.

person Alfe    schedule 12.12.2013
comment
Это тоже неплохо, делает шаг вперед и помогает немного обобщить. Декораторы Python — это очень мощный инструмент, позволяющий красиво компоновать функции. - person James Mills; 12.12.2013
comment
@Alfe: Большое спасибо за ваш ответ. Однако я совершенно не понимаю, как работают декораторы, несмотря на пылкое гугление, поэтому, боюсь, я не очень понимаю ваш ответ. :( - person henrebotha; 12.12.2013
comment
Я добавил небольшое пояснение к коду, но, конечно, было бы нецелесообразно объяснять здесь декораторы. На Stackoverflow есть много вопросов по этой теме. - person Alfe; 12.12.2013
comment
Спасибо. Я как бы понимаю это немного лучше сейчас. - person henrebotha; 12.12.2013

Самый Pythonic способ выполнить такую ​​проверку «User INput» — перехватить соответствующее исключение.

Пример:

def get_user_input():
    while True:
        try:
            return int(input("Please enter a number: "))
        except ValueError:
            print("Invalid input. Please try again!")

n = get_user_input()
print("Thanks! You entered: {0:d}".format(n))

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

В этом случае Проверка пользовательского ввода — используйте Duck Typing в Python и перехватите ошибку. то есть: если он действует как воздуховод, это должна быть утка. (Если он действует как int, он должен быть int).

person James Mills    schedule 12.12.2013
comment
Другими словами, именно то, что я делаю. :) Спасибо, Джеймс. - person henrebotha; 12.12.2013
comment
Точно :) Я просто подумал, что стоит расширить это и рассказать о пути Pythonic и почему :) - person James Mills; 12.12.2013

Немного сложно, но может быть интересно:

import re
from sys import exc_info,excepthook
from traceback import format_exc

def condition1(stuff):
    '''
    stuff must be the string of an integer'''
    try:
        i = int(stuff)
        return True
    except:
        return False

def condition2(stuff):
    '''
    stuff is the string of an integer
    but the integer must be in the range(10,30)'''
    return int(stuff) in xrange(10,30)

regx = re.compile('assert *\( *([_a-z\d]+)')
                  
while True:
    try:
        stuff = raw_input("Please enter foo: ")
        assert(condition1(stuff))
        assert (  condition2(stuff))
        print("Thanks.")
        break
    except AssertionError:
        tbs = format_exc(exc_info()[0])
        funky = globals()[regx.search(tbs).group(1)]
        excepthook(exc_info()[0], funky.func_doc, None)

результат

Please enter foo: g
AssertionError: 
    stuff must be the string of an integer
Please enter foo: 170
AssertionError: 
    stuff is the string of an integer
    but the integer must be in the range(10,30)
Please enter foo: 15
Thanks.

.

РЕДАКТИРОВАТЬ

Я нашел способ упростить:

from sys import excepthook

def condition1(stuff):
    '''
    stuff must be the string of an integer'''
    try:
        int(stuff)
        return True
    except:
        return False

def another2(stuff):
    '''
    stuff is the string of an integer
    but the integer must be in the range(10,30)'''
    return int(stuff) in xrange(10,30)

tup = (condition1,another2)

while True:
    try:
        stuff = raw_input("Please enter foo: ")
        for condition in tup:
            assert(condition(stuff))
        print("Thanks.")
        break
    except AssertionError:
        excepthook('AssertionError', condition.func_doc, None)
person eyquem    schedule 12.12.2013
comment
Действительно интересно, но очень сложно для рутинных задач, я думаю. - person henrebotha; 12.12.2013
comment
Что несколько ужасно, так это способ, которым я должен следовать, чтобы найти функцию, результат которой вызвал ошибку Assertion: объект трассировки, преобразованный в строку, поиск регулярных выражений в этой строке, передача группы в glabals, чтобы найти объект функции, получение документа функция ! Мне не удалось извлечь функцию или ее имя непосредственно из объекта трассировки. - Но основная идея может быть интересной: условные функции можно добавлять просто, не меняя блок exclude. - person eyquem; 12.12.2013
comment
@henrebotha У меня сильное упрощение - person eyquem; 12.12.2013