Python argparse需要选项,具体取决于定义的标志

时间:2017-03-31 07:08:27

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

我有一个小的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函数会引发异常并退出脚本。

如果定义了-f,如果定义-b并且需要-a,如何配置argparse使-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

输出:

sizes = UK.groupby(['College/University']).size().sort_values(ascending=True)
sizes.plot(kind='barh',figsize=(8,8),title='UK Uni')

for y, x in enumerate(sizes['size']):
    plt.annotate(str(x), xy=(x, y), va='center')

2 个答案:

答案 0 :(得分:3)

您可以使用ay / be as子命令定义subparser,或者为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:])

答案 1 :(得分:1)

您的问题在于argparse如何处理默认值。即使-f是唯一的参数,您也会遇到此行为。如果默认值是字符串值,则如果未看到Action,则将对其进行“评估”。

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

在解析开始时,默认值被放入args命名空间。在解析过程中,它会跟踪是否已经看到Actions。在解析结束时,它会检查尚未看到的Actions的Namespace值。如果它们与默认值(通常情况)匹配并且是字符串,则它会通过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为True时才有用,为什么不将它们合并?

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

使用?,这有三种方式。 (docs on const)

  • 没有-aargs.a = default
  • -aargs.a = const
  • -a afile , 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在默认情况下会被阻塞,它也会运行。