Вопрос:

Есть ли способ принять выбор аргумента в виде одной строки

python argparse

67 просмотра

4 ответа

18 Репутация автора

Я хотел бы обработать строку выбора для данного аргумента, а не указывать аргумент несколько раз или использовать пробелы.

У меня есть скрипт, который отображает метаданные видео файла. Большую часть времени мне нужен только список файлов, но иногда мне нужно видеть продолжительность файлов, размер, битрейт и т. Д.

Первоначально у меня был аргумент для каждого куска метаданных. -l для длины или продолжительности, -d для даты создания, -m для измененной даты, -b для битрейта, -r для разрешения, -c для аудиоканалов, -s размер, -e для всего и т. д. указать некоторые или все или ни одного и получить именно ту информацию, которая была мне нужна, но список аргументов начал становиться в целом неуправляемым, и когда я добавил метаданные для отображения и дополнительные функции, у меня закончились логические назначения букв и мне пришлось поменяться именами аргументов и я хотел лучшего пути.

Я хотел упростить выражение метаданных и подумал, что нужно объединить некоторые аргументы в один и сократить длину заключительной команды и вводимых данных.

В частности, я пытаюсь включить это:

script.py -d -t -l -s -b -r -f -c -v -a

в это:

script.py -m dtlsbrfcva

Вот моя текущая функция:

def get_arguments():
    parser = argparse.ArgumentParser(description=DESCRIPTION)
    parser.add_argument('-m', action='append', nargs='+', choices=['d','t','l','s','b','r','f','c','v','a','e'],help='Display metadata for each file. Choices: (d)ate, (t)ime, (l)ength, (s)ize, (b)itrate, (r)esolution, (f)ramerate, (c)hannels, (v)ideo codec, (a)spect ratio, (e)verything')
    parser.add_argument('files', nargs='*')
    args = parser.parse_args()

    if len(args.files) == 0:
        args.files="."

    return args

Использование выбора казалось подходящим способом, но когда я использую «append» для действия и «+» для nargs, мне нужно либо повторно указать аргумент

script.py -md -mt -ml -ms -mb -mr -mf -mc -mv -ma

что хуже ... или использовать пробелы

script.py -m d t l s b r f c v a

что несколько лучше, я думаю?

Но я получаю информативную помощь:

 -m {d,t,l,s,b,r,f,c,v,a,e} [{d,t,l,s,b,r,f,c,v,a,e} ...]
                        Display metadata for each file. Choices: 
                        (d)ate, (t)ime, (l)ength, (s)ize, (b)itrate, 
                        (r)esolution, (f)ramerate, (c)hannels, (v)ideo codec, 
                        (a)spect ratio, (e)verything

Теперь будет ясно, если я использую

add_argument('-m', action="store", help='Display metadata for each file. Choices: (d)ate, (t)ime, (l)ength, (s)ize, (b)itrate, (r)esolution, (f)ramerate, (c)hannels, (v)ideo codec, (a)spect ratio, (e)verything')

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

 -m M          Display metadata for each file. Choices: (d)ate, (t)ime,
                (l)ength, (s)ize, (b)itrate, (r)esolution, (f)ramerate,
                (c)hannels, (v)ideo codec, (a)spect ratio, (e)verything

Я также изучил подпарасеры, но из прочитанного мною чтения я лишь расширил бы свою проблему до другого уровня кода и сделал бы вывод справки менее полезным. Я рад, чтобы исправить в этом отношении.

В идеале я надеюсь придерживаться argparse вариантов программных преимуществ, в том числе ошибок неправильных опций и красиво отформатированной справки, но я открыт для других методов. Любое руководство с благодарностью.

Автор: mg. Источник Размещён: 11.08.2019 05:52

Ответы (4)


0 плюса

13744 Репутация автора

Вполне приемлемо передавать отдельные параметры, собранные вместе. Таким образом:

script.py -d -t -l -s -b -r -f -c -v -a

уже может быть эквивалентно вызван как:

script.py -dtlsbrfcva

которая, вероятно, решает ваши проблемы без каких-либо изменений.

Автор: donkopotamus Размещён: 11.08.2019 06:32

0 плюса

5341 Репутация автора

Решение

Это может принять, -mвозможно, сопровождаемый единственной строкой, составленной из букв, соответствующих выборам.

import argparse

MCHOICES = 'dtlsbrfcvae'
def msplit(marg):
    mlist = list(marg)
    for ch in mlist:
        if ch not in MCHOICES:
            raise argparse.ArgumentTypeError(f"{ch} is not a valid choice")
    return mlist


parser = argparse.ArgumentParser(description="<put description here>")
parser.add_argument('-m', type=msplit, nargs='?', const=[], default=[], help='Display metadata for each file...')

# some examples:
args = parser.parse_args("-m dtlsv".split())
print(args)
args = parser.parse_args("-m".split()) # const=... is used in this case (bare -m)
print(args)
args = parser.parse_args("".split()) # default=... is used in this case (no -m at all)
print(args)

ОБНОВИТЬ:

При установке значения по умолчанию строки обрабатываются так, как если бы они были аргументами. Нестроки присваиваются напрямую, они не обрабатываются mlist. Например, чтобы сделать "-m e" выбором метаданных по умолчанию, используйте либо default='e'или default=['e']const=...также). (спасибо @hpaulj за его комментарий)


Эта альтернатива делает строку с выбором обязательной:

parser.add_argument('-m', type=msplit, default=[], help='Display metadata for each file...')
Автор: VPfB Размещён: 11.08.2019 07:32

0 плюса

18 Репутация автора

Если кому-то было интересно, моя (вероятно) излишне сложная реализация приведена ниже.

С точки зрения поведения, мой список опций находится под контролем, так что я могу добавлять или удалять элементы без изменения какого-либо кода argparse или строк справки. Он строит необходимые строки для справочной информации и генерирует список для сравнения с помощью dict.

Затем он сравнивает входную строку со списком доступных параметров и создает новый список для итерации и выполнения функций в соответствии с определенными правилами.

-m предполагает -me, который становится -mdtlsbrfcvae и преобразуется в список.
Независимо от того, что еще указано, если 'e' находится в строке вообще, установите список равным всем metadata_choices.

-М с чем-либо еще анализируется надлежащим образом, выдавая ошибки по мере необходимости.

metadata_options={'d':'date',
                  't':'time',
                  'l':'length',
                  's':'size',
                  'b':'bitrate',
                  'r':'resolution',
                  'f':'framerate',
                  'c':'channels',
                  'v':'video codec',
                  'a':'aspect ratio',
                  'e':'everything'}

def metadata_option_extractor():
    metadata_choices=[]
    metadata_options_string=''
    punct=''
    last=len(metadata_options)
    count=0
    for choice,description in metadata_options.items():
        metadata_choices.append(choice)
        word=list(description)
        word.insert(0, '(')
        word.insert(2, ')')
        description = "".join(word)
        count += 1
        if count < last:
            punct=','
        else:
            punct='.'
        metadata_options_string += description + punct + ' '
    return metadata_choices,metadata_options_string

metadata_choices,metadata_options_string=metadata_option_extractor()

def msplit(mlist):
    new_mlist=[]
    for ch in mlist:
        if ch not in metadata_choices:
            raise argparse.ArgumentTypeError(f"invalid choice: '{ch}' in '{mlist}' (choose from "+str(metadata_choices)+")")

        else:
            new_mlist.append(ch)

    if 'e' in new_mlist:
        new_mlist = metadata_choices

    return new_mlist

def get_arguments():
    parser = argparse.ArgumentParser(description=DESCRIPTION)
    parser.add_argument('-g', action='store', default='d', nargs=1, choices=['d', 'l', 't', 'b'], help='Group by various criteria. (d)ate, (t)ime, (l)ength, (b)itrate. Default: Date')
    parser.add_argument('-m', type=msplit, nargs='?', const='e', help='Display metadata for each file. When given without any options it is taken to mean all options. Choices: '+metadata_options_string)
    parser.add_argument('-n', nargs='*', action='store', help='Go back only n many groups (typically days). A second argument may be added to limit the number of grouped results.', type=int)
    parser.add_argument('-r', action='store_true', help='Recurse subdirectories.')
    parser.add_argument('-s', action='store', help='Search for "Word(s)", separated by commas, in filenames.', type=str)
    parser.add_argument('-a', action='store_true', help='Switch search method from OR to AND. Requires -s')
    parser.add_argument('-x', action='store_true', help='Export filenames in quotes as a single line for use with another program.')
    parser.add_argument('-c', action='store_true', help='Count number of videos by group rather than list the videos.')

    parser.add_argument('files', nargs='*')
    args = parser.parse_args()

    # Default behaviour
    if len(args.files) == 0:
        args.files="."

    return args
Автор: mg. Размещён: 12.08.2019 03:56

0 плюса

434 Репутация автора

Вы можете вызывать каждый parser.add_argumentв цикле, и он будет работать просто отлично.

Примерно так будет работать

METADATA_FIELDS = {
    'b': 'bitrate',
    # ... put the rest here
}

# Some other code, probably

parser = argparse.ArgumentParser(
    # ... blah
)

for flag, name in METADATA_FIELDS.items():
    parser.add_argument(
        '-' + flag, '--' + name,
        dest='fields',
        action='append_const',
        const=name,
        help="Show {}".format(name)
    )

parser.add_argument(
    '-e', '--everything',
    dest='fields',
    action='store_const',
    const=list(METADATA_FIELDS.values())
)

args = parser.parse_args()

Это позволяет вам вырезать -m и иметь длинные имена для каждого поля. Вы сможете комбинировать короткие флаги, и порядок будет сохранен.

Автор: Beefster Размещён: 12.08.2019 04:15
Вопросы из категории :
32x32