Python argparse 中存在命名冲突时如何从命令行获取 2 个不同对象的参数
How to get arguments for 2 different objects from the command line when there is a naming conflict in Python argparse
我有两个 classes,A 和 B,每个都有自己定义的参数解析器(使用 argparse)
我现在想向 A 添加功能,以便它调用 class B。我正在使用组合来执行此操作(即 A 具有对象 B 的实例)
我在这里询问如何组合两个 arg 解析对象,以便 argparseA 现在将在问题 中包含 argparseB 中的参数
我的问题如下:A 和 B 都有同名参数。但是-我需要用户输入两个不同的值(即 argpaseA.val1 需要获取值 argparseA.val1 和 argParseB.val1)
(显而易见的解决方案是在 argparseA 或 argpaseB 中重命名 val1,但是已经有超过 50 个脚本继承了 class A,还有 50 个脚本继承了 class B,所以我想要对 A 和 B 的更改尽可能少。)
我想向 argpaseA 添加一个新的且命名不同的参数,称为 val2,然后可以将其作为 val1 传递给 argparseB。
我的问题是 - 进行这种从 argparseA 到 argparseB 的转换或参数的正确方法是什么?
或者有更好的设计方法吗?
我猜你正在尝试我的 parents
建议,并说明可能发生的情况。但即使您采用了另一种方法,这也可能有所帮助。
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
正常的 store
行为是将 value
存储在 args
命名空间 setattr(namespace, action.dest, value)
的 dest
属性中。 dest
默认是第一个长选项字符串(减去 --),但可以设置为参数(对于可选),或在创建后设置。
当新操作的一个或多个选项字符串(标志)与现有操作冲突时,将引发 conflict_handler
。默认处理程序会引发错误,但在这里我使用 resolve
处理程序。它试图删除足够的现有参数来解决冲突。
所有 3 个解析器都创建了一个 -h
(帮助)操作。 parserC
中的 resolve
删除除一个以外的所有内容。请注意,parserA
帮助中缺少 [-h]
。
parserB
中定义的 --bar
与 parserA
中的相同字符串冲突。 resolve
已将其从 A 的定义中删除,但 -f
和 --foo
保留。
parserC
的创建扰乱了其他解析器;所以我建议在 运行.
中只使用 parserC.parse_args()
我们可以写一个不同的 conflict_handler
方法。 resolve
有一些粗糙的边缘,不经常使用。
我正在使用一些未记录的功能。有些人认为这是不安全的。但如果你想要不寻常的行为,你就必须接受一些风险。此外,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())
产生
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')
冲突处理函数定义在一个超级class中,所以我不能只用subclass ArgumentParser
来添加一个新的。添加自定义 Action
和 FormatHandler
class 很容易,但这并不容易。我的猜测是没有人尝试过这种定制。
所以我的笨拙是编写 resolve
方法的修改,并且 link 它在运行中。不干净,但足以进行测试。
此处理程序知道现有操作(action
参数)的身份,但不知道新操作的身份(即来自 ParserA 的 --var
,但不知道来自 ParserB 的 --var
) .所以我正在修改那个现有的 'name' 。现在,此方法必须通过其 rename_dict
知道要替换的选项字符串和新名称。
完成此操作后,我认为编写 parents
机制的自定义版本可能会更容易。一个可以用作:
parserC = argparse.ArgumentParser()
parserC.add_argument(...) # C's own arguments
copy_arguments(parserC, [parserA, parserB], rename_dict={...})
============================
我更喜欢这个 - 自定义 parents
机制,让我可以指定 skip_list
和 replace_dict
。 (我可能会删除关于解决方案)。
import argparse
def add_actions(parser, other, skip_list=None, rename_dict=None):
# adapted from _add_container_actions (used for parents)
# copy (by reference) selected actions from other to parser
# can skip actions (to avoid use of conflict_handler)
# can rename other actions (again to avoid conflict)
if skip_list is None:
skip_list = ['-h','--help']
if rename_dict is None:
rename_dict = {}
# group handling as before
# collect groups by titles
title_group_map = {}
for group in parser._action_groups:
if group.title in title_group_map:
msg = _('cannot merge actions - two groups are named %r')
raise ValueError(msg % (group.title))
title_group_map[group.title] = group
# map each action to its group
group_map = {}
for group in other._action_groups:
# if a group with the title exists, use that, otherwise
# create a new group matching the other's group
if group.title not in title_group_map:
title_group_map[group.title] = parser.add_argument_group(
title=group.title,
description=group.description,
conflict_handler=group.conflict_handler)
# map the actions to their new group
for action in group._group_actions:
group_map[action] = title_group_map[group.title]
# add other's mutually exclusive groups
# NOTE: if add_mutually_exclusive_group ever gains title= and
# description= then this code will need to be expanded as above
for group in other._mutually_exclusive_groups:
mutex_group = parser.add_mutually_exclusive_group(
required=group.required)
# map the actions to their new mutex group
for action in group._group_actions:
group_map[action] = mutex_group
# add all actions to this other or their group
# addition with skip and rename
for action in other._actions:
option_strings = action.option_strings
if any([s for s in option_strings if s in skip_list]):
print('skipping ', action.dest)
continue
else:
sl = [s for s in option_strings if s in rename_dict]
if len(sl):
mod = rename_dict[sl[0]]
action.dest = action.dest+mod
action.option_strings = [option_strings[0]+mod]
group_map.get(action, parser)._add_action(action)
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()
# parserC.add_argument('baz')
add_actions(parserC, parserA, rename_dict={'--var':'A'})
add_actions(parserC, parserB, rename_dict={'--var':'B'})
parserC.print_help()
print(parserC.parse_args())
和一个样本 运行
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
_actions
是解析器的动作(参数)列表。它是 'hidden',因此请谨慎使用,但我预计不会像这样对基本 属性 进行任何更改。您可以修改这些操作的许多属性,例如 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')
我有两个 classes,A 和 B,每个都有自己定义的参数解析器(使用 argparse) 我现在想向 A 添加功能,以便它调用 class B。我正在使用组合来执行此操作(即 A 具有对象 B 的实例)
我在这里询问如何组合两个 arg 解析对象,以便 argparseA 现在将在问题
(显而易见的解决方案是在 argparseA 或 argpaseB 中重命名 val1,但是已经有超过 50 个脚本继承了 class A,还有 50 个脚本继承了 class B,所以我想要对 A 和 B 的更改尽可能少。)
我想向 argpaseA 添加一个新的且命名不同的参数,称为 val2,然后可以将其作为 val1 传递给 argparseB。
我的问题是 - 进行这种从 argparseA 到 argparseB 的转换或参数的正确方法是什么?
或者有更好的设计方法吗?
我猜你正在尝试我的 parents
建议,并说明可能发生的情况。但即使您采用了另一种方法,这也可能有所帮助。
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
正常的 store
行为是将 value
存储在 args
命名空间 setattr(namespace, action.dest, value)
的 dest
属性中。 dest
默认是第一个长选项字符串(减去 --),但可以设置为参数(对于可选),或在创建后设置。
当新操作的一个或多个选项字符串(标志)与现有操作冲突时,将引发 conflict_handler
。默认处理程序会引发错误,但在这里我使用 resolve
处理程序。它试图删除足够的现有参数来解决冲突。
所有 3 个解析器都创建了一个 -h
(帮助)操作。 parserC
中的 resolve
删除除一个以外的所有内容。请注意,parserA
帮助中缺少 [-h]
。
parserB
中定义的 --bar
与 parserA
中的相同字符串冲突。 resolve
已将其从 A 的定义中删除,但 -f
和 --foo
保留。
parserC
的创建扰乱了其他解析器;所以我建议在 运行.
parserC.parse_args()
我们可以写一个不同的 conflict_handler
方法。 resolve
有一些粗糙的边缘,不经常使用。
我正在使用一些未记录的功能。有些人认为这是不安全的。但如果你想要不寻常的行为,你就必须接受一些风险。此外,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())
产生
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')
冲突处理函数定义在一个超级class中,所以我不能只用subclass ArgumentParser
来添加一个新的。添加自定义 Action
和 FormatHandler
class 很容易,但这并不容易。我的猜测是没有人尝试过这种定制。
所以我的笨拙是编写 resolve
方法的修改,并且 link 它在运行中。不干净,但足以进行测试。
此处理程序知道现有操作(action
参数)的身份,但不知道新操作的身份(即来自 ParserA 的 --var
,但不知道来自 ParserB 的 --var
) .所以我正在修改那个现有的 'name' 。现在,此方法必须通过其 rename_dict
知道要替换的选项字符串和新名称。
完成此操作后,我认为编写 parents
机制的自定义版本可能会更容易。一个可以用作:
parserC = argparse.ArgumentParser()
parserC.add_argument(...) # C's own arguments
copy_arguments(parserC, [parserA, parserB], rename_dict={...})
============================
我更喜欢这个 - 自定义 parents
机制,让我可以指定 skip_list
和 replace_dict
。 (我可能会删除关于解决方案)。
import argparse
def add_actions(parser, other, skip_list=None, rename_dict=None):
# adapted from _add_container_actions (used for parents)
# copy (by reference) selected actions from other to parser
# can skip actions (to avoid use of conflict_handler)
# can rename other actions (again to avoid conflict)
if skip_list is None:
skip_list = ['-h','--help']
if rename_dict is None:
rename_dict = {}
# group handling as before
# collect groups by titles
title_group_map = {}
for group in parser._action_groups:
if group.title in title_group_map:
msg = _('cannot merge actions - two groups are named %r')
raise ValueError(msg % (group.title))
title_group_map[group.title] = group
# map each action to its group
group_map = {}
for group in other._action_groups:
# if a group with the title exists, use that, otherwise
# create a new group matching the other's group
if group.title not in title_group_map:
title_group_map[group.title] = parser.add_argument_group(
title=group.title,
description=group.description,
conflict_handler=group.conflict_handler)
# map the actions to their new group
for action in group._group_actions:
group_map[action] = title_group_map[group.title]
# add other's mutually exclusive groups
# NOTE: if add_mutually_exclusive_group ever gains title= and
# description= then this code will need to be expanded as above
for group in other._mutually_exclusive_groups:
mutex_group = parser.add_mutually_exclusive_group(
required=group.required)
# map the actions to their new mutex group
for action in group._group_actions:
group_map[action] = mutex_group
# add all actions to this other or their group
# addition with skip and rename
for action in other._actions:
option_strings = action.option_strings
if any([s for s in option_strings if s in skip_list]):
print('skipping ', action.dest)
continue
else:
sl = [s for s in option_strings if s in rename_dict]
if len(sl):
mod = rename_dict[sl[0]]
action.dest = action.dest+mod
action.option_strings = [option_strings[0]+mod]
group_map.get(action, parser)._add_action(action)
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()
# parserC.add_argument('baz')
add_actions(parserC, parserA, rename_dict={'--var':'A'})
add_actions(parserC, parserB, rename_dict={'--var':'B'})
parserC.print_help()
print(parserC.parse_args())
和一个样本 运行
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
_actions
是解析器的动作(参数)列表。它是 'hidden',因此请谨慎使用,但我预计不会像这样对基本 属性 进行任何更改。您可以修改这些操作的许多属性,例如 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')