如何创建在整数旁边显示选项并允许使用整数输入选择选项的命令行提示符



如何使用单击编写命令行提示符,如下所示:

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

总之,如何创建一个命令提示符来显示用户选项并允许他们输入整数以选择与这些选项对应的整数?

另外,如果从命令行调用函数时使用--name Bob,如何使命令提示符可选?

使用继承自click.Option的自定义类,您可以截获选项处理并显示所需的菜单,然后验证响应,如下所示:

自定义类

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)

使用自定义类:

若要使用自定义类,请将 cls 参数传递给@click.option()修饰器,如下所示:

@click.option('--name', cls=EnumMenuPromptFromDict, prompt=titles)

其中prompt是选择的字典,例如:

titles = OrderedDict((
    ('Karen', 'CEO'),
    ('Bob', 'CFO'),
    ('Jo', 'COO'),
    ('Steve', 'CIO')
))

这是如何工作的?

这是有效的,因为单击是一个设计良好的OO框架。@click.option()修饰器通常实例化click.Option对象,但允许使用 cls 参数覆盖此行为。因此,从我们自己的类中继承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([])

好吧,我不是询问参数问题的合适人选(sys.argv 将是一个很好的起点(,但我可以帮助您解决第一个问题:

choice = int (input ("[1] Karen n[2] Bob n[3] Jo n[4] Steven")) - 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)

最新更新