我正在尝试编写一个派生自Python argparse ArgumentParser类的解析器类。以下代码的大纲在命令行上运行正常,但是生成错误,我在模块的上下文中难以理解。
代码(剥离一点以删除不重要的东西)如下:
class SansParser(argparse.ArgumentParser):
"""Argument parser for preparing a SansModel fit or calculation
"""
def __init__(self):
"""Initialisation method for the parser class"""
argparse.ArgumentParser.__init__(self)
# Subparsers for the two commands 'calc' and 'fit'
self.subparsers = self.add_subparsers()
self.fit_parser = self.subparsers.add_parser('fit', help="Fit a dataset")
self.fit_parser.add_argument('-d', '-data', '-dataset', type = str,
dest = 'dataset',
help = "The dataset to fit in SasXML format")
self.fit_parser.set_defaults(func=fit)
self.calc_parser = self.subparsers.add_parser('calc', prog='test')
self.calc_parser.set_defaults(func=calculate)
我可以将其作为一个脚本运行相当于它的罚款。如果我从shell运行它或导入python命令行并尝试实例化我得到的类:
$ python sansmodel.py
Traceback (most recent call last):
File "sansmodel.py", line 57, in <module>
parser = SansParser()
File "sansmodel.py", line 41, in __init__
self.fit_parser = self.subparsers.add_parser('fit', help="Fit a dataset")
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/argparse.py",
line 1064, in add_parser
parser = self._parser_class(**kwargs)
TypeError: __init__() got an unexpected keyword argument 'prog'
据我所知,在第1064行argparse本身的代码显式创建了'prog'关键字,这是预期的行为,所以我对它出乎意料的地方感到困惑。我猜我在某种程度上对范围有所反对?
答案 0 :(得分:4)
除非你要覆盖一些argparse.ArgumentParser
行为,否则我建议创建一个解析器对象,并将参数和子分析符添加到该对象。
那就是说,问题在于添加新的解析器时,__init__
实现覆盖的SansParser
方法不接受与原始ArgumentParser
相同的参数。 }。
问题的解决方法应该是这个:
self.subparsers._parser_class = argparse.ArgumentParser
这样,当调用add_parser
时,不会创建新的SansParser
(由于无限递归而失败),而是会创建一个新的ArgumentParser
。
答案 1 :(得分:1)
我同意@ jcollado建议简单地向ArgumentParser
对象添加参数,而不是子类。
但是,如果您进行子类化,我建议您更改__init__
方法的签名,而不是更改self.subparsers._parser_class
的值。
class SansParser(argparse.ArgumentParser):
"""Argument parser for preparing a SansModel fit or calculation
"""
def __init__(self, *args, **kwargs):
"""Initialisation method for the parser class"""
if 'my_keyword' in kwargs:
# Do what needs to be done with kwargs['my_keyword']
del kwargs['my_keyword'] # Since ArgumentParser won't recognize it
argparse.ArgumentParser.__init__(self, *args, **kwargs)
这样,您的子类可以像ArgumentParser
一样工作,除非您覆盖其行为。
答案 2 :(得分:0)
我今天下午遇到了同样的错误,并在寻找解决方案时找到了你的问题。
如果您已阅读ArgumentParser __init__()
,您可以看到它接受了一系列参数,包括正确的&#39;编译:
class ArgumentParser(_AttributeHolder, _ActionsContainer):
"""SOME DOC STRING..."""
def __init__(self,
prog=None,
usage=None,
description=None,
epilog=None,
parents=[],
formatter_class=HelpFormatter,
prefix_chars='-',
fromfile_prefix_chars=None,
argument_default=None,
conflict_handler='error',
add_help=True,
allow_abbrev=True):
...... # SOME IMPLEMENTATION
我认为事实是:自定义解析器类重写了__init__()
方法,并且不接受任何参数。但其他方法没有修改。这使得方法&#39;行为冲突。在创建子解析器时,add_parser()使用包括&#39; prog&#39;在内的参数调用解析器的__init__()
。对于ArgumentParser,没关系,但对于覆盖__init__()
的自定义解析器,显然会失败。
当然@ jcollado的建议工作正常,但似乎它也取消了子解析器行为的定制。
我对这个问题的解决方案,有点难看,但效果也不错。覆盖ArgumentParser的__init__()
时,只需保留每个参数及其默认值。像这样:
class MyParser(argparse.ArgumentParser):
def __init__(
self,
prog=None,
usage=None,
description=None,
epilog=None,
parents=[],
formatter_class=argparse.HelpFormatter,
prefix_chars='-',
fromfile_prefix_chars=None,
argument_default=None,
conflict_handler='error',
add_help=True,
allow_abbrev=True
# and your custom arguments here
):
super(MyParser, self).__init__(prog=prog, usage=usage, description=description, epilog=epilog,
parents=parents, formatter_class=formatter_class,
prefix_chars=prefix_chars, fromfile_prefix_chars=fromfile_prefix_chars,
argument_default=argument_default, conflict_handler=conflict_handler,
add_help=add_help, allow_abbrev=allow_abbrev
)
# and your custom actions here