【问题标题】:Python argparse requiring option, depending on the defined flagsPython argparse 需要选项,取决于定义的标志
【发布时间】:2017-08-25 08:43:28
【问题描述】:

我有一个小的 python 脚本,它使用argparse 让用户定义选项。它使用两个用于不同模式的标志和一个参数来让用户定义文件。请参阅下面的简化示例:

#!/usr/bin/python3

import argparse
from shutil import copyfile

def check_file(f):
    # Mock function: checks if file exists, else "argparse.ArgumentTypeError("file not found")"
    return f

def main():
    aFile = "/tmp/afile.txt"

    parser = argparse.ArgumentParser(description="An example",formatter_class=argparse.RawTextHelpFormatter)
    parser.add_argument("-f", "--file", help="A file, used with method A.", default=aFile, type=check_file)
    parser.add_argument("-a", "--ay", help="Method A, requires file.", action='store_true')
    parser.add_argument("-b", "--be", help="Method B, no file required.", action='store_true')

    args = parser.parse_args()
    f = args.file
    a = args.ay
    b = args.be

    if a:
        copyfile(f, f+".a")
    elif b:
        print("Method B")

if __name__ == "__main__":
    main()

方法 A 需要该文件。

方法 B 没有。

如果我使用方法 A 运行脚本,我将使用默认文件或使用-f/--file 定义的文件。该脚本检查文件是否存在并且一切正常。

现在,如果我使用方法 B 运行脚本,它不应该需要该文件,但会检查默认选项,如果它不存在,则 argparse 函数会引发异常并退出脚本。

如果定义了-b 并要求它,如果定义了-a,我如何配置argparse 以使-f 可选?

编辑: 我刚刚意识到让-f-b 互斥就足够了。但是,如果我只运行-b,则无论如何都会执行check_file。有没有办法防止这种情况发生?

#!/usr/bin/python3

import argparse
from shutil import copyfile

def check_file(f):
    # Mock function: checks if file exists, else "argparse.ArgumentTypeError("file not found")"
    print("chk file")
    return f

def main():
    aFile = "/tmp/afile.txt"

    parser = argparse.ArgumentParser(description="An example",formatter_class=argparse.RawTextHelpFormatter)
    group = parser.add_mutually_exclusive_group(required=True)

    group.add_argument("-f", "--file", help="A file, used with method A.", default=aFile, type=check_file)
    parser.add_argument("-a", "--ay", help="Method A, requires file.", action='store_true')
    group.add_argument("-b", "--be", help="Method B, no file required.", action='store_true')

    args = parser.parse_args()
    f = args.file
    a = args.ay
    b = args.be

    if a:
        print("File: "+str(f))
    elif b:
        print("Method B")
        print("file: "+str(f))

if __name__ == "__main__":
    main()

输出:

chk file
Method B
file: /tmp/afile.txt

【问题讨论】:

  • 你可能想看看 [stackoverflow.com/questions/4466197/… 问题。
  • 4466197 对于这种情况来说是一个糟糕的答案。 “组”不是为嵌套而设计的,它们也不能解决这里的“默认”评估问题。

标签: python arguments parameter-passing argparse optional-parameters


【解决方案1】:

您可以使用 ay/be 作为子命令定义子解析器,或者为 a 声明第二个解析器实例。比如:

parser = argparse.ArgumentParser(
    description="An example",
    formatter_class=argparse.RawTextHelpFormatter
)
# ensure either option -a or -b only
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("-a", "--ay", help="Method A, requires file.",
                   action='store_true')
group.add_argument("-b", "--be", help="Method B, no file required.",
                   action='store_true')
# define a parser for option -a
parser_a = argparse.ArgumentParser()
parser_a.add_argument("-f", "--file", help="A file, used with method A.",
                      type=check_file, required=True)
parser_a.add_argument("-a", "--ay", help="Method A, requires file.",
                      action='store_true')

# first parse - get either -a/-b
args = parser.parse_known_args(sys.argv[1:])
# if -a, use the second parser to ensure -f is in argument
# note parse_known_args return tuple, the first one is the populated namespace
if args[0].ay:
    args = parser_a.parse_args(sys.argv[1:])

【讨论】:

    【解决方案2】:

    您的问题在于argparse 如何处理默认值。即使-f 是唯一的参数,你也会得到这种行为。如果默认值是一个字符串值,如果没有看到 Action,它将被“评估”。

    parser.add_argument("-f", "--file", help="A file, used with method A.", default=aFile, type=check_file)
    

    在解析开始时,默认值被放入args 命名空间。在解析过程中,它会跟踪是否看到了动作。在解析结束时,它会检查尚未看到的操作的命名空间值。如果它们匹配默认值(通常情况下)并且是字符串,则通过type 函数传递默认值。

    在您的-f 情况下,默认值可能是文件名、字符串。因此,如果用户不提供替代方案,它将被“评估”。在早期的argparse 版本中,无论是否使用默认值都会被评估。对于像 intfloat 这样的类型,这不是问题,但对于 FileType,它可能会导致不需要的文件打开/创建。

    解决办法?

    • 写入check_file,以便它优雅地处理aFile
    • 确保aFile 有效,以便check_file 运行时不会出错。这是通常的情况。
    • 使用非字符串默认值,例如一个已经打开的文件。
    • 使用默认的默认None,解析后添加默认值。

      if args.file is None: args.file = aFile

    将此与-a-b 操作相结合,您必须决定是否:

    • 如果-a,是否需要-f 值?如果没有提供-f,那么default是什么。

    • 如果-b-f 是否具有默认值或用户是否提供此参数是否重要?你能忽略它吗?

    如果-f 仅在-a 为真时有用,为什么不将它们结合起来?

    parser.add_argument('-a', nargs='?', default=None, const='valid_file', type=check_file)
    

    使用?,这可以通过 3 种方式进行。 (docs on const)

    • 没有-a, args.a = default
    • -a, args.a = const
    • -a 文件,args.a = afile

    这个行为的一个更简单的例子

    In [956]: p = argparse.ArgumentParser()
    In [957]: p.add_argument('-f',type=int, default='astring')
    ...
    In [958]: p.parse_args('-f 1'.split())
    Out[958]: Namespace(f=1)
    In [959]: p.parse_args(''.split())
    usage: ipython3 [-h] [-f F]
    ipython3: error: argument -f: invalid int value: 'astring'
    

    字符串默认值通过int 传递,导致错误。如果我将默认设置为类似列表default=[1,2,3] 之类的其他内容,即使int 会因默认设置而窒息,它也会运行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-18
      • 2013-01-13
      • 2014-12-01
      • 2012-07-15
      • 1970-01-01
      • 1970-01-01
      • 2013-04-12
      • 2014-07-09
      相关资源
      最近更新 更多