问题描述
(问题的简化形式。)我正在编写一个涉及一些Python组件的API。 这些可能是功能,但具体来说,它们是对象。 我希望能够从命令行解析各种组件的选项。
from argparse import ArgumentParser
class Foo(object):
def __init__(self, foo_options):
"""do stuff with options"""
"""..."""
class Bar(object):
def __init__(sef, bar_options):
"""..."""
def foo_parser():
"""(could also be a Foo method)"""
p = ArgumentParser()
p.add_argument('--option1')
#...
return p
def bar_parser(): "..."
但现在我希望能够构建更大的组件:
def larger_component(options):
f1 = Foo(options.foo1)
f2 = Foo(options.foo2)
b = Bar(options.bar)
# ... do stuff with these pieces
精细。 但是如何编写适当的解析器? 我们可能希望这样的事情:
def larger_parser(): # probably need to take some prefix/ns arguments
# general options to be overridden by p1, p2
# (this could be done automagically or by hand in `larger_component`):
p = foo_parser(prefix=None, namespace='foo')
p1 = foo_parser(prefix='first-foo-', namespace='foo1')
p2 = foo_parser(prefix='second-foo-', namespace='foo2')
b = bar_parser()
# (you wouldn't actually specify the prefix/namespace twice: )
return combine_parsers([(p1, namespace='foo1', prefix='first-foo-'),
(p2,...),p,b])
larger_component(larger_parser().parse_args())
# CLI should accept --foo1-option1, --foo2-option1, --option1 (*)
如果你忘了我们想要前缀(以便能够添加多个相同类型的解析器)并且可能是命名空间(这样我们可以构建树状结构的命名空间来反映结构的结构),它看起来有点像argparse
的parents
节点特征组件)。
当然,我们希望greater_component和greater_parser可以以相同的方式组合,并且传递给某个组件的名称空间对象应始终具有相同的内部形状/命名结构。
问题似乎是argparse
API基本上是在改变你的解析器,但查询它们更加困难 - 如果你直接将数据类型转换为解析器,你可以只是走这些对象。
如果用户编写了一堆函数来手动为解析器添加参数,我设法破解了一些add_argument
,但是每个add_argument
调用必须接受一个前缀,整个事情变得非常难以理解并且可能是不可组合的。
(您可以以复制内部数据结构的某些部分为代价来对此进行抽象...)。
我还尝试将parser
和group
对象子类化...
你可以想象这可能是使用更多代数的CLI解析API,但我不认为重写argparse
是一个很好的解决方案。
有没有一种已知/直接的方法来做到这一点?
1楼
一些可能有助于构建更大解析器的想法:
parser = argparse.ArgumentParser(...)
arg1 = parser.add_argument('--foo',...)
现在arg1
是对add_argument
创建的Action
对象的add_argument
。
我建议在交互式shell中执行此操作并查看其属性。
或至少打印其repr
。
您还可以尝试修改属性。
解析器“知道”关于参数的大多数内容都包含在这些actions
。
从某种意义上说,解析器是一个“包含”一堆“动作”的对象。
再看看:
parser._actions
这是解析器的主要操作列表,其中包括默认帮助以及您添加的帮助。
parents
机制将Action
引用从父级复制到子级。
请注意,它不会复制Action对象。
它还会重新创建参数组 - 但这些组仅用于对帮助行进行分组。
它们与解析无关。
args1, extras = parser.parse_known_args(argv, namespace)
在处理多个解析器时非常有用。 有了它,每个解析器都可以处理它所知道的参数,并将其余的解析器传递给其他人。 尝试了解该方法的输入和输出。
我们在早期的SO问题中谈到了复合Namespace
对象。
默认的argparse.Namespace
类是一个带有repr
方法的简单对象类。
解析器只使用hasattr
, getattr
和setattr
,尝试尽可能不具体。
您可以构建更精细的命名空间类。
您还可以自定义Action
类。
这是大多数值插入命名空间的地方(尽管默认值在其他地方设置)。
IPython
使用argparse
,既用于主要调用, argparse
内部用于magic
命令。
它从config
文件构造了许多参数。
因此,可以使用默认配置,自定义配置或在最后时刻通过命令行参数设置许多值。
2楼
您可以使用组合操作的概念来实现所需的功能。 您可以根据需要构建修改命名空间,dest等的操作,然后使用以下命令组合它们:
def compose_actions(*actions):
"""Compose many argparse actions into one callable action.
Args:
*actions: The actions to compose.
Returns:
argparse.Action: Composed action.
"""
class ComposableAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
for action in actions:
action(option_string, self.dest).__call__(parser,
namespace,
values,
option_string)
return ComposableAction
参见示例: :