Подтвердить что ты не робот

Argparse: как я могу разрешить нескольким значениям переопределять значение по умолчанию

Это немного связано с темой, рассмотренной в вопросе о том, что нужно задать аргумент несколько раз.

Я хотел бы указать параметр несколько раз, например:

 tool --foo 1 --foo 2 --foo 3

И также вот так:

 tool a b c

Я также хотел бы поддержать оба одновременно:

 tool a b c --foo 1 --foo2 --foo 3

Это отлично работает:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('foo', nargs='*', action='append')
parser.add_argument('--foo', nargs='*', dest='foo', action='append')

Список результатов можно легко сгладить:

args = parser.parse_args('a b c --foo 1 --foo 2 --foo 3'.split())
args.foo = [el for elements in args.foo for el in elements]

дает:

>>> args
Namespace(foo=['a', 'b', 'c', '1', '2', '3'])

Как добавить значение по умолчанию таким образом, что значение по умолчанию не используется, как только один аргумент указан пользователем?

Если вы добавляете только default=[['spam']] к одному из вызовов add_argument(), значение по умолчанию всегда является частью результата. Я не могу заставить argparse удалить его сам по себе, как только пользователь сам предоставит аргумент.

Я надеюсь, что существует решение с тем, что argparse уже обеспечивает себя.

4b9b3361

Ответ 1

Я думаю, что это немного более чистое изменение на другом ответе (опираясь на атрибут self.default пользовательских действий):

import argparse
import sys

class Extender(argparse.Action):
    def __call__(self,parser,namespace,values,option_strings=None):
        #Need None here incase `argparse.SUPPRESS` was supplied for `dest`
        dest = getattr(namespace,self.dest,None) 
        #print dest,self.default,values,option_strings
        if(not hasattr(dest,'extend') or dest == self.default):
            dest = []
            setattr(namespace,self.dest,dest)
            #if default isn't set to None, this method might be called
            # with the default as `values` for other arguements which
            # share this destination.
            parser.set_defaults(**{self.dest:None}) 

        try:
            dest.extend(values)
        except ValueError:
            dest.append(values)

        #another option:
        #if not isinstance(values,basestring):
        #    dest.extend(values)
        #else:
        #    dest.append(values) #It a string.  Oops.

def new_parser(args):
    parser = argparse.ArgumentParser()
    parser.add_argument('foo', nargs='*',action=Extender)
    parser.add_argument('--foo', nargs='*', dest='foo', action=Extender)
    parser.set_defaults(foo = [['spam']])
    return parser.parse_args(args.split())

tests = {'a b c --foo 1 --foo 2 --foo 3':['a','b','c','1','2','3'],
         '':[['spam']],
         'a b c --foo 1 2 3':['a','b','c','1','2','3'],
         '--foo 1':['1'],
         'a':['a']}

for s,r in tests.items():
    print ( "parsing: {0}".format(s) )
    args = new_parser(s)
    if(args.foo != r):
        print ("ERROR")
        print (args.foo)
        print (r)
        sys.exit(1)
    print ( args )
    print ('*'*80)

Также обратите внимание, что я использовал parser.set_defaults(...) для установки значения по умолчанию для атрибута foo.