如何创建在整数旁边显示选项并允许使用整数输入选择选项的命令行提示符?
How to create a command line prompt that displays options next to integers and allows choosing an option using integer input?
如何使用click编写命令行提示符,如下所示:
choose name:
[1] Karen
[2] Bob
[3] Jo
[4] Steve
并调用如下函数:
def print_function(name):
if name == 'Karen':
print('CEO')
elif name == 'Bob':
print('CFO')
elif name == 'Jo':
print('COO')
elif name == 'Steve':
print('CIO')
else:
raise ValueError('Wrong name')
总而言之,如何创建一个命令提示符来显示用户选项并允许他们输入与这些选项对应的 select 整数?
此外,如果从命令行调用函数时使用 --name Bob
,如何使命令提示符可选?
好吧,我不是询问参数问题的合适人选(sys.argv 将是一个很好的起点),但我可以帮助您解决第一个问题:
choice = int (input ("[1] Karen \n[2] Bob \n[3] Jo \n[4] Steve\n")) - 1
positions = ["CEO", "CFO", "COO", "CIO"]
print (positions [choice])
这基本上利用了有序列表(并从输入中减去 1,因为列表从 0 开始)
您可以使用argparse
手动构建它。
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--name")
args = parser.parse_args()
def print_function(name):
if name == 'Karen':
print('CEO')
elif name == 'Bob':
print('CFO')
elif name == 'Jo':
print('COO')
elif name == 'Steve':
print('CIO')
else:
raise ValueError('Wrong name')
if args.name:
print_function(args.name)
else:
name = input("""
choose name:
[1] Karen
[2] Bob
[3] Jo
[4] Steve
""".strip())
print_function(name)
用字典很容易做到这一点
names = {
1: 'Karen',
2: 'Bob',
3: 'Jo',
4: 'Steve'
}
使用 string.format() 生成提示并迭代字典的项目:
str_items = ['[{}] {}\n'.format(val, name) for val, name in names.items()]
prompt = ''.join(str_items)
val = input('Choose name:\n' + prompt))
然后直接将用户输入转换成int,然后在字典中查找值
name = names[int(val)]
print('You chose: ' + name)
然后为标题再做一次
titles = {
'Karen': 'CEO',
'Bob': 'CFO',
'Jo': 'COO',
'Steve': 'CIO'
}
print(titles[name])
最好只有 一个 结构来保存数据(姓名和职位)而不是两个。只是为了在人们更改职位或加入或离开时的可维护性,那么您只需更新一件事而不是两件事(姓名列表 + 职务列表,或姓名字典和标题字典)。
一种方法是使用元组列表:
people = [("Karen", "CEO"), ("Bob", "CFO"), ("Jo", "COO"), ("Steve", "CIO")]
然后通过名字获取一个人很简单:
def get_person_by_name(name):
for person in people:
if person[0] == name:
return person
除了--name
,你没有说你需要接受多少其他命令行参数,但如果只是这个,那么你可以这样做:
if len(sys.argv) == 3 and sys.argv[1] == "--name":
person_name = sys.argv[2]
person = get_person_by_name(person_name)
if not person:
print("Person '%s' not found" % person_name)
然后如果你还没有找到人(没有给定参数,或者给定的名字没有找到),那么你可以使用input
:
def ask_for_person():
'''Ask which person the user wants'''
prompt = "\n".join(["[%d] %s" % (num+1, person[0]) for num, person in enumerate(people)])
while True:
try:
person_num = int(input("choose name:\n" + prompt + "\n? "))
if person_num >=1 and person_num <= len(people):
return people[person_num - 1]
except ValueError:
pass
print("try again!")
最后你可以打印你的输出:
if not person:
person = ask_for_person()
print("'%s' is the '%s'" % person)
使用继承自 click.Option
的自定义 class,您可以拦截选项处理并显示所需的菜单,然后验证响应,如:
自定义Class
import click
class EnumMenuPromptFromDict(click.Option):
def __init__(self, *args, **kwargs):
super(EnumMenuPromptFromDict, self).__init__(*args, **kwargs)
if 'prompt' not in kwargs:
raise TypeError(
"'prompt' keyword required for '{}' option".format(
args[0][0]))
self.choices_dict = self.prompt
self.prompt_menu = '\n'.join('[{}] {}'.format(i + 1, name)
for i, name in enumerate(self.prompt))
self.prompt = 'Choose from,\n{}\n{}'.format(
self.prompt_menu, self.name)
def prompt_for_value(self, ctx):
"""Get entered value and then validate"""
while True:
value = super(EnumMenuPromptFromDict, self).prompt_for_value(ctx)
try:
choice = int(value)
if choice > 0:
return list(self.choices_dict)[choice - 1]
except (ValueError, IndexError):
if value in self.choices_dict:
return value
click.echo('Error: {} is not a valid choice'.format(value))
def full_process_value(self, ctx, value):
"""Convert the entered value to the value from the choices dict"""
value = super(EnumMenuPromptFromDict, self).full_process_value(
ctx, value)
try:
return self.choices_dict[value]
except (KeyError, IndexError):
raise click.UsageError(
"'{}' is not a valid choice".format(value), ctx)
使用自定义 Class:
要使用自定义 class,请将 cls
参数传递给 @click.option()
装饰器,例如:
@click.option('--name', cls=EnumMenuPromptFromDict, prompt=titles)
其中 prompt
是一个选项的字典,例如:
titles = OrderedDict((
('Karen', 'CEO'),
('Bob', 'CFO'),
('Jo', 'COO'),
('Steve', 'CIO')
))
这是如何工作的?
之所以可行,是因为 click 是一个设计良好的 OO 框架。 @click.option()
装饰器通常实例化一个 click.Option
object 但允许使用 cls
参数来覆盖此行为。因此,在我们自己的 class 中继承 click.Option
并覆盖所需的方法是一件相对容易的事情。
在这种情况下,我们超越 click.Option.prompt_for_value()
以拦截命令处理并允许输入菜单编号或值。我们还越过 click.Option.full_process_value()
将名称转换为标题。
测试代码:
from collections import OrderedDict
titles = OrderedDict((
('Karen', 'CEO'),
('Bob', 'CFO'),
('Jo', 'COO'),
('Steve', 'CIO')
))
@click.command()
@click.option('--name', cls=EnumMenuPromptFromDict, prompt=titles)
def cli(name):
"""The Great CLI APP"""
click.echo('a title: %s' % name)
if __name__ == "__main__":
print('Click Version: {}'.format(click.__version__))
print('Python Version: {}'.format(sys.version))
cli([])
如何使用click编写命令行提示符,如下所示:
choose name:
[1] Karen
[2] Bob
[3] Jo
[4] Steve
并调用如下函数:
def print_function(name):
if name == 'Karen':
print('CEO')
elif name == 'Bob':
print('CFO')
elif name == 'Jo':
print('COO')
elif name == 'Steve':
print('CIO')
else:
raise ValueError('Wrong name')
总而言之,如何创建一个命令提示符来显示用户选项并允许他们输入与这些选项对应的 select 整数?
此外,如果从命令行调用函数时使用 --name Bob
,如何使命令提示符可选?
好吧,我不是询问参数问题的合适人选(sys.argv 将是一个很好的起点),但我可以帮助您解决第一个问题:
choice = int (input ("[1] Karen \n[2] Bob \n[3] Jo \n[4] Steve\n")) - 1
positions = ["CEO", "CFO", "COO", "CIO"]
print (positions [choice])
这基本上利用了有序列表(并从输入中减去 1,因为列表从 0 开始)
您可以使用argparse
手动构建它。
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--name")
args = parser.parse_args()
def print_function(name):
if name == 'Karen':
print('CEO')
elif name == 'Bob':
print('CFO')
elif name == 'Jo':
print('COO')
elif name == 'Steve':
print('CIO')
else:
raise ValueError('Wrong name')
if args.name:
print_function(args.name)
else:
name = input("""
choose name:
[1] Karen
[2] Bob
[3] Jo
[4] Steve
""".strip())
print_function(name)
用字典很容易做到这一点
names = {
1: 'Karen',
2: 'Bob',
3: 'Jo',
4: 'Steve'
}
使用 string.format() 生成提示并迭代字典的项目:
str_items = ['[{}] {}\n'.format(val, name) for val, name in names.items()]
prompt = ''.join(str_items)
val = input('Choose name:\n' + prompt))
然后直接将用户输入转换成int,然后在字典中查找值
name = names[int(val)]
print('You chose: ' + name)
然后为标题再做一次
titles = {
'Karen': 'CEO',
'Bob': 'CFO',
'Jo': 'COO',
'Steve': 'CIO'
}
print(titles[name])
最好只有 一个 结构来保存数据(姓名和职位)而不是两个。只是为了在人们更改职位或加入或离开时的可维护性,那么您只需更新一件事而不是两件事(姓名列表 + 职务列表,或姓名字典和标题字典)。
一种方法是使用元组列表:
people = [("Karen", "CEO"), ("Bob", "CFO"), ("Jo", "COO"), ("Steve", "CIO")]
然后通过名字获取一个人很简单:
def get_person_by_name(name):
for person in people:
if person[0] == name:
return person
除了--name
,你没有说你需要接受多少其他命令行参数,但如果只是这个,那么你可以这样做:
if len(sys.argv) == 3 and sys.argv[1] == "--name":
person_name = sys.argv[2]
person = get_person_by_name(person_name)
if not person:
print("Person '%s' not found" % person_name)
然后如果你还没有找到人(没有给定参数,或者给定的名字没有找到),那么你可以使用input
:
def ask_for_person():
'''Ask which person the user wants'''
prompt = "\n".join(["[%d] %s" % (num+1, person[0]) for num, person in enumerate(people)])
while True:
try:
person_num = int(input("choose name:\n" + prompt + "\n? "))
if person_num >=1 and person_num <= len(people):
return people[person_num - 1]
except ValueError:
pass
print("try again!")
最后你可以打印你的输出:
if not person:
person = ask_for_person()
print("'%s' is the '%s'" % person)
使用继承自 click.Option
的自定义 class,您可以拦截选项处理并显示所需的菜单,然后验证响应,如:
自定义Class
import click
class EnumMenuPromptFromDict(click.Option):
def __init__(self, *args, **kwargs):
super(EnumMenuPromptFromDict, self).__init__(*args, **kwargs)
if 'prompt' not in kwargs:
raise TypeError(
"'prompt' keyword required for '{}' option".format(
args[0][0]))
self.choices_dict = self.prompt
self.prompt_menu = '\n'.join('[{}] {}'.format(i + 1, name)
for i, name in enumerate(self.prompt))
self.prompt = 'Choose from,\n{}\n{}'.format(
self.prompt_menu, self.name)
def prompt_for_value(self, ctx):
"""Get entered value and then validate"""
while True:
value = super(EnumMenuPromptFromDict, self).prompt_for_value(ctx)
try:
choice = int(value)
if choice > 0:
return list(self.choices_dict)[choice - 1]
except (ValueError, IndexError):
if value in self.choices_dict:
return value
click.echo('Error: {} is not a valid choice'.format(value))
def full_process_value(self, ctx, value):
"""Convert the entered value to the value from the choices dict"""
value = super(EnumMenuPromptFromDict, self).full_process_value(
ctx, value)
try:
return self.choices_dict[value]
except (KeyError, IndexError):
raise click.UsageError(
"'{}' is not a valid choice".format(value), ctx)
使用自定义 Class:
要使用自定义 class,请将 cls
参数传递给 @click.option()
装饰器,例如:
@click.option('--name', cls=EnumMenuPromptFromDict, prompt=titles)
其中 prompt
是一个选项的字典,例如:
titles = OrderedDict((
('Karen', 'CEO'),
('Bob', 'CFO'),
('Jo', 'COO'),
('Steve', 'CIO')
))
这是如何工作的?
之所以可行,是因为 click 是一个设计良好的 OO 框架。 @click.option()
装饰器通常实例化一个 click.Option
object 但允许使用 cls
参数来覆盖此行为。因此,在我们自己的 class 中继承 click.Option
并覆盖所需的方法是一件相对容易的事情。
在这种情况下,我们超越 click.Option.prompt_for_value()
以拦截命令处理并允许输入菜单编号或值。我们还越过 click.Option.full_process_value()
将名称转换为标题。
测试代码:
from collections import OrderedDict
titles = OrderedDict((
('Karen', 'CEO'),
('Bob', 'CFO'),
('Jo', 'COO'),
('Steve', 'CIO')
))
@click.command()
@click.option('--name', cls=EnumMenuPromptFromDict, prompt=titles)
def cli(name):
"""The Great CLI APP"""
click.echo('a title: %s' % name)
if __name__ == "__main__":
print('Click Version: {}'.format(click.__version__))
print('Python Version: {}'.format(sys.version))
cli([])