提问人:CptSupermrkt 提问时间:2/12/2016 最后编辑:CommunityCptSupermrkt 更新时间:11/20/2019 访问量:762
编写干净、灵活且易于维护的用户输入提示
Writing clean, flexible, and easy to maintain user input prompts
问:
我经常负责向用户征求意见。我总是在我的主要执行脚本中“根据需要”编写我的提示。这有点丑陋,因为我经常在多个脚本中要求相同类型的输入,所以我的大量代码只是复制/粘贴的提示循环。以下是我过去所做的:
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”转换为 True 或 False,则上述方法不起作用。
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”格式可能是一个好主意,我会的。这只是为了表明,在我需要非常相似但略有不同的提示的情况下,它会导致大量复制/粘贴代码(此方法根本没有灵活性)。
编写干净、灵活且易于维护的用户提示的好方法是什么?
(我见过这个:要求用户输入,直到他们给出有效的响应和其他一些响应。我的问题不是关于如何获取输入并验证它,而是关于如何制作一个可以在多个程序中重复使用的灵活输入系统)。
答:
我曾经为类似的东西写过一个函数。解释在文档字符串中:
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
我建议编写一个包含许多定义非常明确的构建块的库,每个构建块都尽可能小巧轻便,不要对如何将这些组件组合在一起进行太多假设。
也就是说,我将包含一个执行循环的函数,但不是传入一组规则,而是只允许传入一个函数,该函数要么返回一个值(如果给定了有效的输入,并且在以任何必要的方式转换它之后)或在输入不可用时引发一个值。其他构建块将实现某些检查或转换(例如布尔值的解析和转换为布尔值)。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)
我会创建一个提示函数,如下所示:
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])
您还可以对此进行调整,以便可以告诉用户他们失败了什么验证或不允许空白输入。
评论