当Python argparse中存在命名冲突时,如何从命令行获取两个不同对象的参数
我有两个类,A和B,每个类都定义了自己的参数解析器(使用argparse) 现在我想向A添加功能,以便它调用类B。我正在使用组合(即A有一个对象B的实例)来完成此操作 我在这里询问了如何组合两个arg parse对象,以便argparseA现在在问题中包含argparseB中的参数 我的问题如下:A和B都有同名的参数。但是-我需要用户为输入两个不同的值(即argpaseA.val1需要获取argparseA.val1和argParseB.val1) (显而易见的解决方案是在argparseA或argpaseB中重命名val1,但是已有50多个脚本继承了类A,50多个脚本继承了类B,因此我希望对A和B的更改尽可能少。) 我考虑向argpaseA添加一个名为val2的新的、命名不同的参数,然后将其作为val1传递给argparseB 我的问题是-从argparseA到argparseB的这种转换或参数的正确方式是什么当Python argparse中存在命名冲突时,如何从命令行获取两个不同对象的参数,python,python-2.7,oop,argparse,Python,Python 2.7,Oop,Argparse,我有两个类,A和B,每个类都定义了自己的参数解析器(使用argparse) 现在我想向A添加功能,以便它调用类B。我正在使用组合(即A有一个对象B的实例)来完成此操作 我在这里询问了如何组合两个arg parse对象,以便argparseA现在在问题中包含argparseB中的参数 我的问题如下:A和B都有同名的参数。但是-我需要用户为输入两个不同的值(即argpaseA.val1需要获取argparseA.val1和argParseB.val1) (显而易见的解决方案是在argparseA或a
或者有更好的设计方法吗?我猜你是在尝试我父母的建议,并说明可能发生的情况。但即使你采用了另一种方法,这也可能会有所帮助
import argparse
parserA=argparse.ArgumentParser()
a1=parserA.add_argument('-f','--foo','--bar')
print(a1)
print()
parserB=argparse.ArgumentParser()
b1=parserB.add_argument('-g','--goo','--bar')
print(b1)
b1.dest='bar' # can change attributes like dest after creation
print()
# parser with parents; not the conflict_handler
parserC=argparse.ArgumentParser(conflict_handler='resolve',
parents=[parserA, parserB])
print(parserC._actions) # the actions (arguments) of C
print()
parserA.print_help()
print()
parserC.print_help() # uses the C._actions
产生
1445:~/mypy$ python3 stack38071986.py
_StoreAction(option_strings=['-f', '--foo', '--bar'], dest='foo',
nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)
_StoreAction(option_strings=['-g', '--goo', '--bar'], dest='goo',
nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)
[_StoreAction(option_strings=['-f', '--foo'], dest='foo', nargs=None,
const=None, default=None, type=None, choices=None, help=None, metavar=None),
_HelpAction(option_strings=['-h', '--help'], dest='help', nargs=0,
const=None, default='==SUPPRESS==', type=None, choices=None, help='show this help message and exit', metavar=None),
_StoreAction(option_strings=['-g', '--goo', '--bar'], dest='bar',
nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)]
usage: stack38071986.py [-f FOO]
optional arguments:
help show this help message and exit
-f FOO, --foo FOO
usage: stack38071986.py [-f FOO] [-h] [-g BAR]
optional arguments:
-f FOO, --foo FOO
-h, --help show this help message and exit
-g BAR, --goo BAR, --bar BAR
1027:~/mypy$ python3 stack38071986.py --var1 1 --var 3
['--var']
Action --var: ['--var1'] var1
Action -g: ['-g', '--goo'] goo
Action --foo: ['-f', '--foo'] foo
Action -h: ['-h', '--help'] help
Action --goo: ['-g', '--goo'] goo
Action --help: ['-h', '--help'] help
Action -f: ['-f', '--foo'] foo
_StoreAction(option_strings=['--var1'], dest='var1', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)
Action -g: ['-g', '--goo'] goo
Action --var1: ['--var1'] var1
Action --foo: ['-f', '--foo'] foo
Action -h: ['-h', '--help'] help
Action --goo: ['-g', '--goo'] goo
Action --help: ['-h', '--help'] help
Action -f: ['-f', '--foo'] foo
usage: stack38071986.py [-f FOO] [--var1 VAR1]
optional arguments:
help show this help message and exit
-f FOO, --foo FOO
--var1 VAR1
usage: stack38071986.py [-f FOO] [--var1 VAR1] [-h] [-g GOO] [--var VAR]
optional arguments:
-f FOO, --foo FOO
--var1 VAR1
-h, --help show this help message and exit
-g GOO, --goo GOO
--var VAR
Namespace(foo=None, goo=None, var='3', var1='1')
正常的store
行为是将值存储在args
命名空间中的dest
属性处,setattr(namespace,action.dest,value)
dest
default是第一个长选项字符串(减去--),但可以设置为参数(对于选项),也可以在创建后设置
当新操作的一个或多个选项字符串(标志)与现有操作冲突时,将调用冲突\u处理程序
。默认处理程序会引发错误,但这里我使用resolve
处理程序。它试图删除足够多的现有参数来解决冲突
所有3个解析器都创建一个-h
(帮助)操作<parserC
中的code>resolve
删除除一个之外的所有内容。请注意,parserA
帮助中缺少[-h]
parserB
中定义的--bar
与parserA
中的相同字符串冲突resolve
已将其从A的定义中删除,但仍保留-f
和-foo
parserC
的创建搞乱了其他解析器;因此,我建议在这次运行中只使用parserC.parse_args()
我们可以编写一个不同的冲突处理程序方法。resolve
one有一些粗糙的边缘,不经常使用
我正在使用一些没有文档记录的功能。有些人认为这是不安全的。但是,如果你想要不寻常的行为,你必须接受一些风险。另外,argparse
文档并不是关于其行为的最终决定,并且比代码本身更容易更改。在进行更改时,开发人员几乎对向后冲突心存疑虑
==================
下面是定制resolve
冲突处理程序的一个尝试
import argparse
def pp(adict):
for k in adict:
v=adict[k]
print('Action %10s:'%k,v.option_strings, v.dest)
def new_resolve(self, action, conflicting_actions):
rename_dict={'--var':'--var1'}
for option_string, action in conflicting_actions:
new_string = rename_dict.get(option_string, None)
if new_string:
# rename rather than replace
print(action.option_strings)
action.option_strings = [new_string]
action.dest = new_string[2:]
pp(self._option_string_actions)
a1=self._option_string_actions.pop(option_string, None)
print(a1)
self._option_string_actions[new_string] = a1
pp(self._option_string_actions)
else:
# regular remove action
action.option_strings.remove(option_string)
self._option_string_actions.pop(option_string, None)
# if the option now has no option string, remove it from the
# container holding it
if not action.option_strings:
action.container._remove_action(action)
argparse._ActionsContainer._handle_conflict_resolve=new_resolve
parserA=argparse.ArgumentParser()
a1=parserA.add_argument('-f','--foo')
a1=parserA.add_argument('--var')
parserB=argparse.ArgumentParser()
b1=parserB.add_argument('-g','--goo')
b1=parserB.add_argument('--var')
parserC=argparse.ArgumentParser(conflict_handler='resolve',
parents=[parserA, parserB],
add_help=False)
parserA.print_help()
print()
parserC.print_help()
print(parserC.parse_args())
产生
1445:~/mypy$ python3 stack38071986.py
_StoreAction(option_strings=['-f', '--foo', '--bar'], dest='foo',
nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)
_StoreAction(option_strings=['-g', '--goo', '--bar'], dest='goo',
nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)
[_StoreAction(option_strings=['-f', '--foo'], dest='foo', nargs=None,
const=None, default=None, type=None, choices=None, help=None, metavar=None),
_HelpAction(option_strings=['-h', '--help'], dest='help', nargs=0,
const=None, default='==SUPPRESS==', type=None, choices=None, help='show this help message and exit', metavar=None),
_StoreAction(option_strings=['-g', '--goo', '--bar'], dest='bar',
nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)]
usage: stack38071986.py [-f FOO]
optional arguments:
help show this help message and exit
-f FOO, --foo FOO
usage: stack38071986.py [-f FOO] [-h] [-g BAR]
optional arguments:
-f FOO, --foo FOO
-h, --help show this help message and exit
-g BAR, --goo BAR, --bar BAR
1027:~/mypy$ python3 stack38071986.py --var1 1 --var 3
['--var']
Action --var: ['--var1'] var1
Action -g: ['-g', '--goo'] goo
Action --foo: ['-f', '--foo'] foo
Action -h: ['-h', '--help'] help
Action --goo: ['-g', '--goo'] goo
Action --help: ['-h', '--help'] help
Action -f: ['-f', '--foo'] foo
_StoreAction(option_strings=['--var1'], dest='var1', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)
Action -g: ['-g', '--goo'] goo
Action --var1: ['--var1'] var1
Action --foo: ['-f', '--foo'] foo
Action -h: ['-h', '--help'] help
Action --goo: ['-g', '--goo'] goo
Action --help: ['-h', '--help'] help
Action -f: ['-f', '--foo'] foo
usage: stack38071986.py [-f FOO] [--var1 VAR1]
optional arguments:
help show this help message and exit
-f FOO, --foo FOO
--var1 VAR1
usage: stack38071986.py [-f FOO] [--var1 VAR1] [-h] [-g GOO] [--var VAR]
optional arguments:
-f FOO, --foo FOO
--var1 VAR1
-h, --help show this help message and exit
-g GOO, --goo GOO
--var VAR
Namespace(foo=None, goo=None, var='3', var1='1')
冲突处理程序函数是在一个超类中定义的,所以我不能只是子类ArgumentParser
来添加一个新的。添加自定义Action
和FormatHandler
类很容易,但这并不容易。我猜没有人尝试过这种定制
因此,我的难题是编写resolve
方法的修改,并动态链接它。不干净,但足以进行测试
此处理程序知道现有操作的标识(Action
参数),但不知道新操作的标识(即来自ParserA的--var
,但不知道来自ParserB的--var
)。因此,我正在修改现有的“名称”。目前,该方法必须通过其rename\u dict
知道要替换的选项字符串和新名称
这样做之后,我想编写一个自定义版本的parents
机制可能会更容易。一个可以用作:
parserC = argparse.ArgumentParser()
parserC.add_argument(...) # C's own arguments
copy_arguments(parserC, [parserA, parserB], rename_dict={...})
==========================
我更喜欢这一点——一种定制的家长
机制,它允许我指定一个跳过列表
和替换目录
。(我可以删除关于解决方案的内容)
和一个样本运行
2245:~/mypy$ python3 stack38071986_1.py --varA 1 --varB 3
skipping help
skipping help
usage: stack38071986_1.py [-h] [-f FOO] [--varA VARA] [-g GOO] [--varB VARB]
optional arguments:
-h, --help show this help message and exit
-f FOO, --foo FOO
--varA VARA
-g GOO, --goo GOO
--varB VARB
Namespace(foo=None, goo=None, varA='1', varB='3')
=============================
如果我加上
print('parserC actions')
for action in parserC._actions:
print(action.option_strings, action.dest)
我得到这个打印件
parserC actions
['-h', '--help'] help
['-f', '--foo'] foo
['--varA'] varA
['-g', '--goo'] goo
['--varB'] varB
\u actions
是解析器的操作(参数)列表。它是“隐藏”的,所以请谨慎使用,但我不认为这样的基本属性会发生任何变化。您可以修改这些操作的许多属性,例如dest
例如,如果我重命名了一些dest
:
for action in parserC._actions:
if action.dest.startswith('var'):
action.dest = action.dest+'_C'
print(parserC.parse_args())
名称空间如下所示
Namespace(foo=None, goo=None, varA_C=None, varB_C='one')
正确的做法是做对用户最有意义的事情。首先给我们一个小的具体例子。我在相关问题中提出了各种备选方案。你在用哪一种?hpaulj-我本来打算用你的“父母”解决方案,但后来我发现我仍然有一个矛盾的论点,这就是为什么我问这个问题。我很难给出一个具体的例子,因为它是公司内部代码。给定objectA和objectB,每个都有自己的参数解析器:对于arg arg1和arg2,我希望objectA调用objectB,但使用单独的参数。用户必须能够写入:objectA.py--arg1--arg2--arg1'--arg2',objectA才能向objectB发送--arg1'和arg2'。我不关心外部调用的参数是什么,这些参数可能会改变,但我希望尽量减少objectA和objectB所需的代码更改(因为我们的代码库中有十几个这样的遗留代码类),我喜欢编写自定义冲突处理程序