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

当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

我有两个类,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的这种转换或参数的正确方式是什么


或者有更好的设计方法吗?

我猜你是在尝试我父母的建议,并说明可能发生的情况。但即使你采用了另一种方法,这也可能会有所帮助

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所需的代码更改(因为我们的代码库中有十几个这样的遗留代码类),我喜欢编写自定义冲突处理程序