gpt4 book ai didi

python - 当 Python argparse 中存在命名冲突时,如何从命令行获取 2 个不同对象的参数

转载 作者:行者123 更新时间:2023-11-30 22:53:49 32 4
gpt4 key购买 nike

我有两个类 A 和 B,每个类都定义了自己的参数解析器(使用 argparse)
我现在想向 A 添加功能,以便它调用 B 类。我正在使用组合来执行此操作(即 A 具有对象 B 的实例)

我在这里询问了如何组合两个 arg 解析对象,以便 argparseA 现在将在问题 Can two Python argparse objects be combined? 中包含 argparseB 中的参数。
我的问题如下: A 和 B 都有同名的参数。但是-我需要用户输入两个不同的值(即 argpaseA.val1 需要获取值 argparseA.val1 和 argParseB.val1)

(显而易见的解决方案是在 argparseA 或 argpaseB 中重命名 val1,但是已经有 50 多个脚本继承了 A 类,还有 50 个脚本继承了 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行为是存储 valuedest args 中的属性命名空间, setattr(namespace, action.dest, value) . dest默认是第一个长选项字符串(减去 --),但可以设置为参数(用于选项),或者在创建之后。

当新操作的一个或多个选项字符串(标志)与现有操作冲突时, conflict_handler被唤起。默认处理程序会引发错误,但这里我使用 resolve处理程序。它试图删除足够多的现有论点来解决冲突。

所有 3 个解析器创建一个 -h (帮助)行动。 resolveparserC删除除一个以外的所有内容。请注意 [-h] parserA 中缺少帮助。
--bar定义于 parserBparserA 中的相同字符串冲突. 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')

冲突处理函数是在一个父类(super class)中定义的,所以我不能只是子类 ArgumentParser添加一个新的。添加自定义很容易 ActionFormatHandler类,但这并不容易。我的猜测是没有人尝试过这种定制。

所以我的kludge是写一个修改 resolve方法,并将其链接到 - 运行中。不干净,但足以进行测试。

这个处理程序知道现有 Action 的身份( action 参数),但不知道新 Action 的身份(即来自 ParserA 的 --var,但不知道来自 ParserB 的 --var)。因此,我正在修改现有名称的“名称”。现在这个方法必须知道,通过它的 rename_dict要替换的选项字符串和新名称。

完成此操作后,我认为编写 parents 的自定义版本可能会更容易。机制。一种可以用作:
parserC = argparse.ArgumentParser()
parserC.add_argument(...) # C's own arguments
copy_arguments(parserC, [parserA, parserB], rename_dict={...})

===========================

我更喜欢这个 - 自定义 parents机制,让我指定 skip_listreplace_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())

和 sample 运行
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是解析器的 Action (参数)列表。它是“隐藏的”,因此请谨慎使用,但我预计不会像这样的基本属性发生任何变化。您可以修改这些操作的许多属性,例如 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')

关于python - 当 Python argparse 中存在命名冲突时,如何从命令行获取 2 个不同对象的参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38071986/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com