为什么调用 argparse.parse_args() 或 .parse_args(sys.argv) 时会有所不同

Why is there a difference when calling argparse.parse_args() or .parse_args(sys.argv)

我在 python 代码中创建了以下参数解析器。

parser = argparse.ArgumentParser()
parser.add_argument('projectPath')
parser.add_argument('-project')
parser.add_argument('-release')
parser.add_argument('--test', default=False, action='store_true')
args = parser.parse_args()

我正在按以下方式执行我的程序。

myProgram.py /path/to/file -project super --test

如果我将上面的 sysntax 与

一起使用,它工作正常
args = parser.parse_args()

但是,如果我将 sys.argv 作为输入

args = parser.parse_args(sys.argv)

解析器突然对参数的顺序挑剔,我收到无法识别的参数错误。

usage: fbu.py [-h] [-project PROJECT] [-release RELEASE] [--test] projectPath
fbu.py: error: unrecognized arguments: /path/to/file

正如我从错误中看到的那样,还使用了 -h 参数。路径参数必须在最后,并且错误在最后一个示例中有意义。 但是为什么它不关心第一个例子中的顺序?

编辑:我正在使用 python 版本 3.4.3

sys.argv 包含脚本名称作为第一项,即 myProgram.py。该论点取代了 projectPath。现在有一个额外的位置参数 /path/to/file,它无法与任何参数匹配,因此出现错误。

不带参数调用 parse_args ArgumentParser 足够聪明,可以忽略脚本名称进行解析。但是当显式传递一个参数数组时,它不能这样做并且会解析所有内容。

正如您在 the source code 中看到的那样 parse_known_args(由 parse_args 调用):

if args is None:
    # args default to the system args
    args = _sys.argv[1:]

当您没有明确提供参数时,Python 会从 .argv 中删除第一项(这是脚本的名称)。如果你手动传递参数,你必须自己做:

parser.parse_args(sys.argv[1:])

这在文档中没有明确涵盖,但请注意 this section 在手动调用 parse_args 时不包含脚本名称:

Beyond sys.argv

Sometimes it may be useful to have an ArgumentParser parse arguments other than those of sys.argv. This can be accomplished by passing a list of strings to parse_args(). This is useful for testing at the interactive prompt:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument(
...     'integers', metavar='int', type=int, choices=xrange(10),
...  nargs='+', help='an integer in the range 0..9')
>>> parser.add_argument(
...     '--sum', dest='accumulate', action='store_const', const=sum,
...   default=max, help='sum the integers (default: find the max)')
>>> parser.parse_args(['1', '2', '3', '4'])
Namespace(accumulate=<built-in function max>, integers=[1, 2, 3, 4])
>>> parser.parse_args('1 2 3 4 --sum'.split())
Namespace(accumulate=<built-in function sum>, integers=[1, 2, 3, 4])

手动传递参数的优势在于它可以更轻松地测试解析功能,因为您可以传递适当参数的列表而不是尝试修补 sys.argv.