关于python:大多数Pythonic方式进行输入验证

Most Pythonic way to do input validation

本问题已经有最佳答案,请猛点这里访问。

在Python中,用户输入验证最"正确"的方法是什么?

我一直在使用以下功能:

1
2
3
4
5
6
7
8
while True:
    stuff = input("Please enter foo:")
    try:
        some_test(stuff)
        print("Thanks.")
        break
    except SomeException:
        print("Invalid input.")

我想这是一个很好的、可读性很强的函数,但是我不禁想知道是否没有内置函数或者我应该使用的函数。


我喜欢装饰师将检查与输入处理的其余部分分开。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/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或多或少是现有函数(或方法)的包装器。它接受现有的函数(在它的@decorator指令下面表示)并返回一个"替换"函数。在我们的例子中,这个替换调用循环中的原始函数,并捕获执行此操作时发生的任何异常。如果没有异常发生,它只返回原始函数的结果。


对"用户输入"进行这种验证的最方法是捕获适当的异常。

例子:

1
2
3
4
5
6
7
8
9
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回溯中发生了什么问题,这也是一个很好的实践。

在本例中,验证用户输入——使用python的duck类型并捕获错误。如果它像管道一样,那一定是鸭子。(如果它的行为类似于int,则必须是int)。


有点复杂,但可能很有趣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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)

结果

1
2
3
4
5
6
7
8
9
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.

.

编辑

我找到了一种简化方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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)