为什么这个argparse代码在Python 2和3之间表现不同?

17

使用argparse的子解析器时,以下代码在Python 3上失败,但在Python 2中按预期运行。阅读文档后,我仍然无法确定原因。

#!/usr/bin/env python
from __future__ import print_function
from argparse import ArgumentParser


def action(args):
    print(args)

if __name__ == '__main__':
    std = ArgumentParser(add_help=False)
    std.add_argument('standard')

    ap = ArgumentParser()
    sp = ap.add_subparsers()

    cmd = sp.add_parser('subcommand', parents=[std], description='Do subcommand')
    cmd.add_argument('arg')
    cmd.set_defaults(do=action)

    args = ap.parse_args()
    args.do(args)

Python 2.7.6 的输出结果为:

me@computer$ python test.py 
usage: test.py [-h] {subcommand} ...
test.py: error: too few arguments

在Python 3.3.5中,我得到:

me@computer$ python3 test.py 
Traceback (most recent call last):
  File "test.py", line 21, in <module>
    args.do(args)
AttributeError: 'Namespace' object has no attribute 'do'

请注意,如果您使用子解析器:args = cmd.parse_args(),它将起作用。 - alecxe
这段代码在我的Python 2.7.4上似乎会出现相同的错误。很可能是你运行了错误的文件版本或其他原因。它不应该工作。请再仔细尝试一遍。 - bosnjak
当我在 Python 2.7.6 解释器中输入您的代码时,我在 args = ap.parse_args() 行处得到了相同的错误。 - Two-Bit Alchemist
1个回答

26
最新版的argparse更改了对必需参数的测试方式,导致子命令失效。它们不再是“必需”的。http://bugs.python.org/issue9253#msg186387 当你看到test.py: error: too few arguments时,表明你没有给它一个“subcommand”参数。在3.3.5中,它会通过该步骤并返回args
通过这个更改,3.3.5应该与早期版本的行为相同:
ap = ArgumentParser()
sp = ap.add_subparsers(dest='parser')  # dest needed for error message
sp.required = True   # force 'required' testing
注意:必须同时设置destrequired。其中dest用于在错误消息中为该参数命名。

此错误:

AttributeError: 'Namespace' object has no attribute 'do'

产生这个问题的原因是cmd子解析器没有运行,也没有将其参数(默认或非默认)放入命名空间中。通过定义另一个子解析器并查看生成的args,您可以看到这种影响。


1
谢谢,这解决了问题。如果有疑问,这个解决方案是与Python 2.7向后兼容的。 - Wedgwood
1
Python 3.8.2。我尝试设置所需属性,但却得到了一个回溯而不是友好的帮助信息。File "/usr/lib/python3.8/argparse.py", line 2035, in _parse_known_args', '.join(required_actions)) TypeError: 序列项0:期望str实例,但发现了NoneType。 - tobixen
@tobixen,另请参阅https://dev59.com/-mAg5IYBdhLWcg3wpMSz。您设置了“dest”吗? - hpaulj
不,我没有尝试dest(嗯,在上面的答案中明确写着需要dest,奇怪我没看到)。我尝试了设置required=True,但没有成功。最终采用了一个解决方法 - https://github.com/tobixen/calendar-cli/commit/03f9e707eb1c74cc71ef30dad4b0fe3f70359fd0 - 也许我做错了,但是...它有效。 - tobixen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接