编写干净,灵活且易于维护的用户输入提示

时间:2016-02-11 23:38:03

标签: python python-3.x input prompt

我经常要求用户提供输入。我总是只是根据需要写出我的提示"在我的主要执行脚本中。这有点难看,因为我经常要求跨多个脚本输入相同类型的输入,我的代码只是复制/粘贴提示循环。这就是我过去做过的事情:

while True:
    username = input("Enter New Username: ")
    if ldap.search(username):
        print "   [!] Username already taken."
    if not validator.validate_username(username):
        print "   [!] Invalid Username."
    else:
        break

我想创建一些可以被称为的内容:

username = prompt(prompt="Enter New Username: ",
                  default=None,
                  rules=["user_does_not_exist",
                         "valid_username"])

然后提示函数如下:

def prompt(prompt, default, rules):
    while True:
        retval = input(prompt)
        if default and retval == "":
            break
            return default
        if not rule_match(retval, rules):
            continue
        break
        return retval

def rule_match(value, rules):
    if "user_does_not_exist" in rules:
        if not user.user_exists(value):
            return False

    if "valid_username" in rules:
        if not validator.username(value):
            return False

    if "y_n_or_yes_no" in rules:
        if "ignore_case" in rules:
            if value.lower() not in ["y", "yes", "n", "no"]:
                return False
        else:
            if value not in ["y", "yes", "n", "no"]:
                return False

    return True

我考虑的另一种方法是制作一个Prompt类,这样可以让结果更加灵活。例如,如果我想转换" y"或" n"无论是对还是对,上述都不起作用。

create_another = Prompt(prompt="Create another user? (y/n): ,"
                       default=False,
                       rules=["y_n_or_yes_no",
                              "ignore_case"]).prompt().convert_to_bool()

我考虑的另一个选择就是制作个性化的提示并命名它们,每个提示都与我原来的代码类似。这实际上并没有改变任何东西。它只是用于从主执行代码中获取这些循环,这使得主执行代码更容易浏览:

username = prompt("get_new_username")

def prompt(prompt_name):
    if prompt_name == "get_new_username":
        while True:
            username = input("Enter New Username: ")
            if ldap.search(username):
                print "   [!] Username already taken."
            if not validator.validate_username(username):
                print "   [!] Invalid Username."
            else:
                break
                return username

    if prompt_name == "y_n_yes_no_ignore_case":
        # do prompt
    if prompt_name == "y_n_yes_no":
        # do prompt
    if prompt_name == "y_n":
        # do prompt
    if prompt_name == "y_n_ignore_case":
        # do prompt
    if prompt_name == "yes_no":
        # do prompt 
    if prompt_name == "yes_no_ignore_case":
        # do prompt 

我意识到,接受一个被接受的" y / n"这可能只是一个好主意。所有程序的格式,我会的。这只是为了表明,在我需要一个非常相似但略有不同的提示的情况下,它会导致大量的复制/粘贴代码(根本没有这种方法的灵活性)。

编写干净,灵活且易于维护的用户提示有什么好方法?

(我已经看到了这个:Asking the user for input until they give a valid response以及其他一些回复。我的问题不是如何获取输入并验证它,而是关于如何制作灵活的输入系统可以在多个程序中重复使用。)

3 个答案:

答案 0 :(得分:0)

我曾经为类似的东西写了一个函数。解释在doc-string中:

def xory(question = "", setx = ["yes"], sety = ["no"], setz = [], strict = False):
    """xory([question][, setx][, sety][, setz][, strict]) -> string

    Asks question.  If the answer is equal to one of the elements in setx,
    returns True.  If the answer is equal to one of the elements in sety,
    returns False.  If the answer is equal to one of the elements in setz,
    returns the element in setz that answer is equal to.  If the answer is
    not in any of the sets, reasks the question.  Strict controls whether
    the answer is case-sensitive.  If show is True, an indication of the
    acceptable answers will be displayed next to the prompt."""

    if isinstance(setx, str):
        setx = [setx]
    if isinstance(sety, str):
        sety = [sety]
    if isinstance(setz, str):
        setz = [setz]
    if (setx[0])[0] != (sety[0])[0]:
        setx = [(setx[0])[0]] + setx
        sety = [(sety[0])[0]] + sety
    question = question.strip(" ") + " "
    while True:
        if show:
            shows = "[%s/%s] " % (setx[0], sety[0])
        else:
            shows = ""
        user_input = raw_input(question + shows)
        for y in [setx, sety, setz]:
            for x in y:
                if (user_input == x) or ((not strict) and (user_input.lower() == x.lower())):
                    if y is setx:
                        return True
                    elif y is sety:
                        return False
                    else:
                        return x
        question = ""
        show = True

示例:

>>> response = xory("1 or 0?", ["1", "one", "uno"], ["0", "zero", "null"], ["quit", "exit"])
1 or 0? x
[1/0] eante
[1/0] uno
>>> print(response)
True
>>> response = xory("Is that so?")
Is that so? Who knows?
[y/n] no
>>> print(response)
False
>>> response = xory("Will you do it?", setz=["quit", "exit", "restart"])
Will you do it? hm
[y/n] quit
>>> print(response)
quit

答案 1 :(得分:0)

我建议写一个包含许多非常明确定义的构建块的库,每个构建块尽可能小且重量轻,没有太多关于你如何去做的假设将各个部分放在一起。

也就是说,我包含一个执行循环的函数,但是我没有传递一组规则,而是允许传递一个函数,或者返回一个值(如果如果输入不可用,则给出有效输入并以任何必要的方式转换后输出ValueError。其他构建块将实现某些检查或转换(例如将'y''n'解析为布尔值)。

通过这种方式,您可以完全由用户以适合用例的方式组装内容。

# library:

def prompt(prompt, default, postprocess):
    value = input('{} ({}): '.format(prompt, default)) or default
    try:
        return postprocess(value)
    except ValueError:
        continue

def check_lower(value):
    if not value.islower():
        raise ValueError()

def to_bool(value):
    return value in 'yes'

# using the library:

def postprocess(value):
    check_lower(value)
    return to_bool(value)

prompt('Really?', 'n', postprocess)

答案 2 :(得分:0)

我将这样创建一个提示函数:

def prompt(prompt, default=None, rules=[]):
    while True:
        response = input(prompt)
        if response:
            valid = [rule(response) for rule in rules]
            if not(False in valid):
                return response
            else:
                print('Invalid input')
        else:
            return default

然后您可以创建其他验证功能,例如

def filterValidEmail(string):
    if '@' in string:
        if '.' in string.split('@')[1]:
            return True
        else:
            return False
    else:
        return False

并像这样调用这些函数:

prompt('What is your email? ', rules=[filterValidEmail])

您还可以对此进行调整,以便告诉用户哪些验证失败或不允许空白输入。