【问题标题】:Pythonic way for adding parameters to argparse based on another parameter基于另一个参数向 argparse 添加参数的 Pythonic 方式
【发布时间】:2020-03-23 23:58:54
【问题描述】:

在主函数中,我有一个验证可选输入的解析器:

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--platform',required=True)
    parser.add_argument('--foo')
    parser.add_argument('--bar')
    parser.add_argument('--baz')
    parser.parse_args()

上面的 sn-p 是一个示例,仅当我提供 --platform 和 --foo,--bar 时才有效或 --baz.

这段代码被不同的组件使用,我们称它们为组件A、B和C。

组件A实际上只指定了--foo和--bar:

python script.py --platform A --foo first_example --bar first_example

组件B实际上只指定了--bar和--baz:

python script.py --platform B --bar second_example --baz second_exmaple

组件C其实只指定--baz:

python script.py --platform C --baz third_example

随着我引入更多提供不同参数的组件,我必须添加到解析器的参数数量增加。以上只是一个例子,我目前正在处理大约 20 个参数(将来可能会更多)。

我一直在考虑创建一个配置文件 (.yaml),在其中定义每个组件需要哪些参数:

# parameters.yaml
A:
  - foo
  - bar

B:
  - bar
  - baz

C:
  - baz

我想简化主函数以查看 --platform 参数,并根据作为参数传递的平台,读取配置并向解析器添加其他参数。

这是我尝试过的:

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--platform',required=True)
    
    # Read from .yaml file
    with open('parameters.yaml') as parameter_file:
        parameters = yaml.safe_load(parameter_file)
    
    for argument in parameters[sys.argv[sys.argv.index('--platform') + 1]]:
        parser.add_argument(
           '--' + argument
    )

    parser.parse_args()

调用函数:

python script.py --platform C --baz third_example

上面的代码有效,但我正在寻找其他 Pythonic 解决方案,因为我是 Python 的初学者。一世 不太喜欢查看 sys.argv 来确定指定的 --platform 是什么。有没有更好的办法解决这个问题?

【问题讨论】:

  • 你应该看看argparse' subparsers。
  • 有一些关于你想要的行为的问题。 1)您希望每个平台都需要一组特定的可选参数吗? 2)如果你有其他参数为None(如果它们被错误地调用,在命令行中没有错误)可以吗?
  • @Fomalhaut 1) 每个平台的可选参数集在配置 .yaml 文件中定义。每个平台都有自己的一组可选参数,只有在调用函数时指定了某个平台时才应将其添加到解析器中。 2)如果对于某个平台应该有一个错误,例如。在我的示例中,C 指定了 foo。

标签: python argparse


【解决方案1】:

使用断言。

如果要禁止某些参数,可以添加assert。例子:

parser = argparse.ArgumentParser()
parser.add_argument('--platform',required=True)
parser.add_argument('--foo')
parser.add_argument('--bar')
parser.add_argument('--baz')
args = parser.parse_args()

if args.platform == 'A':
    assert args.baz is None, "no argument baz for the platform A"
...

使用 del

排除不需要的参数,用del:

parser = argparse.ArgumentParser()
parser.add_argument('--platform',required=True)
parser.add_argument('--foo')
parser.add_argument('--bar')
parser.add_argument('--baz')
args = parser.parse_args()

if args.platform == 'A':
    del args.baz
...

我不建议您在同一个脚本中使用 argparse 和 sys.argv,因为在您的情况下它们的目的相同。最好选择其中之一。

argparge 在您的脚本知道值之前解析您的参数。因此,您不能根据其中一个参数的值来更改参数列表。因此,如果您想保留argparse,则必须使用.add_argument 添加整个参数集,并在获取值时在下面做一些额外的事情。

【讨论】:

  • 感谢您的回答,但在我看来,您的提议需要将所有参数添加到解析器中?这就是我要避免的。 “因此,如果您想保留 argparse,则必须使用 .add_argument 添加整个参数集,并在获取值时在下面做一些额外的事情。” 所以您认为我要完成的工作无法实现?
  • 如果你想在指定错误参数的情况下出现错误,请使用我的assert方式。就够了,不是吗?
  • 也许你误解了我的问题。我想删除所有参数的添加,并且只有 --platform 参数。根据 --platform 参数的值,从 .yaml 文件中读取值,然后将参数动态添加到解析器中。
  • “所以你认为我想要完成的事情无法实现” - 实际上是的。因为在argparse 中,参数名称的集合是在解析命令行之前指定的,而值在之后。
  • 我接受了另一个答案,因为它更接近我想要实现的目标。虽然我必须为每个对象创建一个解析器,但我可以根据配置文件动态填充要使用的参数。感谢您的帮助。
【解决方案2】:

使用子解析器作为子命令:

import argparse

def run_command(parser, args):
    if args.command == 'A':
        print(args)
    elif args.command == 'B':
        print(args)
    elif args.command == 'C':
        print(args)

parser = argparse.ArgumentParser(
    prog='PROG', 
    epilog="See '<command> --help' to read about a specific sub-command."
)
subparsers = parser.add_subparsers(dest='command', help='Sub-commands')

A_parser = subparsers.add_parser('A', help='Platform A')
A_parser.add_argument("--foo")
A_parser.add_argument('--bar')
A_parser.set_defaults(func=run_command)

B_parser = subparsers.add_parser('B', help='Platform B')
B_parser.add_argument('--bar')
B_parser.add_argument('--baz')
B_parser.set_defaults(func=run_command)

C_parser = subparsers.add_parser('C', help='Platform C')
C_parser.add_argument('--baz')
C_parser.set_defaults(func=run_command)

args = parser.parse_args()
if args.command is not None:
    args.func(parser, args)
else:
    parser.print_help()

这会生成:

~ python args.py -h
usage: PROG [-h] {A,B,C} ...

positional arguments:
  {A,B,C}     Sub-commands
    A         Platform A
    B         Platform B
    C         Platform C

optional arguments:
  -h, --help  show this help message and exit

See '<command> --help' to read about a specific sub-command.

和

~ python args.py B -h
usage: PROG B [-h] [--bar BAR] [--baz BAZ]

optional arguments:
  -h, --help  show this help message and exit
  --bar BAR
  --baz BAZ

【讨论】:

  • 谢谢,这样我就必须为每个组件创建一个解析器。我试过以这种方式实现它,当我运行解析器时:args = parser.parse_args(['A']) 每个参数都是无。我该如何解决?
  • 您需要为参数设置默认值,例如A_parser.add_argument("--foo", default=12) 或为参数赋值:args = parser.parse_args(['A', '--foo', 'abc', '--bar', '12'])
  • 谢谢 Alex,我已经使用了您的解决方案,效果很好。
猜你喜欢
  • 2019-02-04
  • 2017-12-13
  • 2013-09-13
  • 1970-01-01
  • 2012-12-14
  • 2013-05-20
  • 2011-11-21
  • 2013-06-20
  • 1970-01-01
相关资源
最近更新 更多