替换argparse中的参数

6

我有一个基类,无法修改(代码可能存在其他错误,请忽略这些错误)

class BaseClass(object):
    def __init__(self):
        self.parser = argparse.ArgumentParser()
        self.parser.add_argument("arg1", choices=("a", "b"))

我想要的是以下方式覆盖arg1
class DerivedClass(BaseClass):
    def __init__(self):
        BaseClass.__init__(self)
        self.parser.add_argument("arg1", choices=("a", "c", "d"))

这段代码有什么问题吗?它在我这里可以运行。你的参数只是被覆盖了。 - MrE
@MrE $ python subclass.py c 用法:subclass.py [-h] {a,b} {a,b,c} subclass.py:错误:参数arg1:无效的选择:'c'(可从'a'、'b'中选择) - anthony sottile
这段代码定义了2个位置参数,每个参数都有不同的选择集。 - hpaulj
2
谈论在马逃走之后关闭谷仓的事情!投票关闭的人有没有读答案? - hpaulj
2
我怀疑这点。我甚至认为他们没有读懂问题。一开始就不是调试帮助。 - max_max_mir
2个回答

10

如果您无法修改基类实现,则可以进入parser属性并修改您关心的特定操作。这是一个示例代码草图:

from base import BaseClass


def _modify_choices(parser, dest, choices):
    for action in parser._actions:
        if action.dest == dest:
            action.choices = choices
            return
    else:
        raise AssertionError('argument {} not found'.format(dest))


class MySubClass(BaseClass):
    def __init__(self):
        super(MySubClass, self).__init__()
        _modify_choices(self.parser, 'arg1', ('a', 'b', 'c'))


def main():
    inst = MySubClass()
    inst.parser.parse_args()


if __name__ == '__main__':
    exit(main())

使用方法:

$ python subclass.py d
usage: subclass.py [-h] {a,b,c}
subclass.py: error: argument arg1: invalid choice: 'd' (choose from 'a', 'b', 'c')

请注意,这里涉及私有实现细节(尤其是ArgumentParser._actions),但除此之外使用公共接口。

我可能会使用def _append_choiceaction.choices = action.choices + appended_choices,并在其中加入('c',)。这样做是为了防止基类发生更改时覆盖这些更改,但这是一个权衡。无论哪种方式,我们都背离了从BaseClass继承的原则。 - TemporalWolf

2

add_argument创建一个Action对象,用于编码add_argument调用中给出的参数。这些参数可以在创建后进行读取和/或修改。

class BaseClass(object)
    def __init__(self):
        self.parser = argparse.ArgumentParser()
        self.arg = self.parser.add_argument("arg1", choices=("a", "b"))
        # self.arg is now the Action object defined by this add_argument method

class DerivedClass(BaseClass):
    def __init__(self):
        BaseClass.__init__(self)
        # modify the choices attribute of self.arg
        self.arg.choices = ("a","b","c")

那个 Action 对象也可以在 parser._actions 列表中找到,但我更喜欢在我的代码中保存一个引用。

(这段代码没有经过测试,可能会有一两个错误。)


class BaseClass(object):
    def __init__(self):
        self.parser = argparse.ArgumentParser(prog="BASE")
        self.parser.add_argument("arg1", choices=("a", "b"))
        self.parser.add_argument('-f','--foo')

class DerivedClass(BaseClass):
    def __init__(self):
        BaseClass.__init__(self)
        self.parser.prog = "DERIVED"
        print(self.parser._actions)

在创建DerivedClass时,显示此列表:

[_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=[], dest='arg1', nargs=None, const=None, default=None, type=None, choices=('a', 'b'), 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)]

"所以添加:"

    idx = [a.dest for a in self.parser._actions].index('arg1')
    self.parser._actions[idx].choices = ("a","b","c")

p1 = BaseClass()
p1.parser.print_help()

p2 = DerivedClass()
p2.parser.print_help()

产生两种用途:

usage: BASE [-h] [-f FOO] {a,b}

positional arguments:
  {a,b}

optional arguments:
  -h, --help         show this help message and exit
  -f FOO, --foo FOO

并且

usage: DERIVED [-h] [-f FOO] {a,b,c}

positional arguments:
  {a,b,c}

optional arguments:
  -h, --help         show this help message and exit
  -f FOO, --foo FOO

2
我有一个基类,我无法修改。我不确定这如何满足该约束条件。 - TemporalWolf
1
如果您无法调整基类,则可以在“_actions”列表中查找操作。 - hpaulj
我该如何从_actions列表中获取正确的动作?我不能通过索引来获取它,因为它可能会改变(基类作者可能会添加更多)。 - max_max_mir
@max_max_mir,如果你无法选择一个,比如最后一个,那么按照其他答案中给出的dest进行搜索可能是正确的方法。 - hpaulj
@max_max_mir,你可以按照相同的方式搜索任何属性,包括“choices”。 - hpaulj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接