我有一个小的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')
答案 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
版本中,无论是否使用默认值,都会对其进行评估。对于类似int
或float
类型的问题,但对于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
)
-a
,args.a = default
-a
,args.a = const
,
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
在默认情况下会被阻塞,它也会运行。