最Pythonic的输入验证方法

9

在Python中,最“正确”、最符合Python习惯的方法是什么来进行用户输入验证呢?

我一直使用以下方法:

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

这个句子看起来很清晰易懂,但我不禁想知道是否有一些内置函数或其他东西可以代替它。


1
能否请您展示更多的代码? - thefourtheye
我认为你用try~except做的不错,虽然有其他完成相同任务的方法。我没有听说过“Pythonic”的方式...这种任务在所有其他语言中都会出现。 - GoodGJ
1
请发布 some_test 函数。 - inspectorG4dget
some_test函数不应该是相关的。在我现在处理的一个特定情况中,some_test如下所示:with smtplib.SMTP_SSL(host=host, port=port) as server: server.login(user, password)如果登录不成功,它会引发异常。然而,我是在一般情况下询问。 - henrebotha
@LukasGraf:当验证失败时,我想做的是不断要求用户输入,直到他遵守为止,哈哈。 - henrebotha
显示剩余3条评论
3个回答

11

我喜欢装饰器来将检查与其余输入处理分开。

#!/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指令下方表示),并返回其“替代品”。 在我们的例子中,此替代品在循环中调用原始函数并捕获执行期间发生的任何异常。 如果没有异常发生,它只会返回原始函数的结果。


这也非常好,更进一步帮助泛化。Python装饰器是一种非常强大的工具,可以让您以一种不错的方式组合函数。 - James Mills
@Alfe:非常感谢您的回答。然而,我完全不知道装饰器是如何工作的,尽管我努力地在Google上搜索,所以我很抱歉我并不真正理解您的回答。:( - henrebotha
2
我为代码添加了一个简短的解释,但当然,在这里解释装饰器是超出范围的。关于这个主题在Stackoverflow上有很多相关问题。 - Alfe
谢谢。我现在有点理解了。 - henrebotha

3

处理“用户输入”的验证最符合Python的方式是捕获适当的异常。

示例:

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))

在IT技术中,一个好的实践是让异常在其发生的位置上抛出并上浮,而不是隐藏它们,以便你可以清楚地看到在Python Traceback中出了什么问题。

在这种情况下,验证用户输入时,使用Python的Duck Typing并捕获错误。即:如果它像一只鸭子一样行走,那么它肯定是一只鸭子。 (如果它行为像一个整数,那么它一定是一个整数)。


换句话说,就是我正在做的事情。 :) 谢谢James - henrebotha
1
没错 :) 我只是想展开讨论一下“Pythonic方式”以及其原因 :) - James Mills

0
有点复杂,但可能很有趣:
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)

确实很有趣,但对于常规任务来说非常复杂,我认为。 - henrebotha
1
有点可怕的是我必须遵循以下步骤才能找到导致断言错误的函数:将traceback对象转换为字符串,使用正则表达式在该字符串中搜索,将组传递给globals以查找函数对象,获取函数的文档!我没有成功地直接从traceback对象中提取函数或其名称。-但基本思想可能很有趣:可以简单地添加条件函数而不更改except块。 - eyquem
@henrebotha 我得到了一个强大的简化。 - eyquem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接