Spec-Zone.ru › Python 3.10

Руководство по Argparse

author

Tshepang Lekhonkhobe

Данное руководство представляет собой введение в argparse, рекомендуемый модуль для парсинга командной строки в стандартной библиотеке Python.

Примечание

Существуют два других модуля, выполняющих ту же задачу, а именно getopt (аналог getopt() из языка C) и устаревший optparse. Обратите также внимание, что argparse основан на optparse, и поэтому очень похож по использованию.

Основные понятия

Давайте продемонстрируем функциональность, которую мы будем изучать в этом вводном руководстве, используя команду ls:

$ ls
cpython  devguide  prog.py  pypy  rm-unused-function.patch
$ ls pypy
ctypes_configure  demo  dotviewer  include  lib_pypy  lib-python ...
$ ls -l
total 20
drwxr-xr-x 19 wena wena 4096 Feb 18 18:51 cpython
drwxr-xr-x  4 wena wena 4096 Feb  8 12:04 devguide
-rwxr-xr-x  1 wena wena  535 Feb 19 00:05 prog.py
drwxr-xr-x 14 wena wena 4096 Feb  7 00:59 pypy
-rw-r--r--  1 wena wena  741 Feb 18 01:01 rm-unused-function.patch
$ ls --help
Usage: ls [OPTION]... [FILE]...
List information about the FILEs (the current directory by default).
Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.
...

Несколько понятий, которые мы можем узнать из этих четырёх команд:

  • Команда ls полезна, когда запускается без каких-либо опций. По умолчанию она отображает содержимое текущей директории.
  • Если нам нужно больше, чем по умолчанию, мы даём ей больше информации. В данном случае мы хотим отобразить другую директорию, pypy. Мы указали то, что известно как позиционный аргумент. Такое название дано потому, что программа должна понимать, что делать со значением, исходя только из того, где оно появляется в командной строке. Этот концепт более актуален для команд, подобных cp, основное использование которой cp SRC DEST. Первая позиция — что копируется, а вторая позиция — куда копируется.
  • Теперь предположим, что мы хотим изменить поведение программы. В нашем примере мы отображаем больше информации для каждого файла вместо простого отображения имён файлов. В этом случае -l известно как необязательный аргумент.
  • Это фрагмент справки. Это очень полезно, так как вы можете столкнуться с программой, которую никогда не использовали, и понять, как она работает, просто прочитав её справку.

Основы

Давайте начнём с очень простого примера, который (почти) ничего не делает:

import argparse
parser = argparse.ArgumentParser()
parser.parse_args()

Ниже приведен результат выполнения кода:

$ python3 prog.py
$ python3 prog.py --help
usage: prog.py [-h]

options:
  -h, --help  show this help message and exit
$ python3 prog.py --verbose
usage: prog.py [-h]
prog.py: error: unrecognized arguments: --verbose
$ python3 prog.py foo
usage: prog.py [-h]
prog.py: error: unrecognized arguments: foo

Вот что происходит:

  • Запуск скрипта без опций не приводит к отображению чего-либо в stdout. Не очень полезно.
  • Второй пример демонстрирует полезность модуля argparse. Мы сделали почти ничего, но уже получили красивое сообщение справки.
  • Опция --help, которую также можно сократить до -h, является единственной опцией, которую мы получаем бесплатно (т.е. не нужно указывать её). Указание чего-либо ещё приводит к ошибке. Но даже тогда мы получаем полезное сообщение об использовании, тоже бесплатно.

Вводные позиционные аргументы

Пример:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo")
args = parser.parse_args()
print(args.echo)

И выполнение кода:

$ python3 prog.py
usage: prog.py [-h] echo
prog.py: error: the following arguments are required: echo
$ python3 prog.py --help
usage: prog.py [-h] echo

positional arguments:
  echo

options:
  -h, --help  show this help message and exit
$ python3 prog.py foo
foo

Вот что происходит:

  • Мы добавили метод add_argument(), который используется для указания опций командной строки, которые программа готова принять. В данном случае я назвал его echo, чтобы он соответствовал его функции.
  • Теперь для вызова нашей программы необходимо указать опцию.
  • Метод parse_args() фактически возвращает данные из указанных опций, в данном случае echo.
  • Переменная — это некоторая «магия», которую argparse выполняет бесплатно (т.е. не нужно указывать, в какой переменной это значение хранится). Вы также заметите, что его имя соответствует строковому аргументу, переданному методу, echo.

Однако, хотя отображение справки выглядит красиво, оно пока не так полезно, как могло бы быть. Например, мы видим, что echo является позиционным аргументом, но мы не знаем, что он делает, кроме как по догадкам или по чтению исходного кода. Итак, давайте сделаем его немного более полезным:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo", help="echo the string you use here")
args = parser.parse_args()
print(args.echo)

И мы получим:

$ python3 prog.py -h
usage: prog.py [-h] echo

positional arguments:
  echo        echo the string you use here

options:
  -h, --help  show this help message and exit

А теперь как насчёт чего-то ещё более полезного:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", help="display a square of a given number")
args = parser.parse_args()
print(args.square**2)

Результат выполнения кода:

$ python3 prog.py 4
Traceback (most recent call last):
  File "prog.py", line 5, in <module>
    print(args.square**2)
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'

Это не сработало. Это потому, что argparse обрабатывает предоставленные нами опции как строки, если мы не укажем иначе. Итак, давайте скажем argparse, чтобы он обрабатывал этот ввод как целое число:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", help="display a square of a given number",
                    type=int)
args = parser.parse_args()
print(args.square**2)

Результат выполнения кода:

$ python3 prog.py 4
16
$ python3 prog.py four
usage: prog.py [-h] square
prog.py: error: argument square: invalid int value: 'four'

Всё прошло хорошо. Программа теперь даже дружелюбно завершается при некорректном вводе, прежде чем продолжить.

Вводные необязательные аргументы

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

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbosity", help="increase output verbosity")
args = parser.parse_args()
if args.verbosity:
    print("verbosity turned on")

И вывод:

$ python3 prog.py --verbosity 1
verbosity turned on
$ python3 prog.py
$ python3 prog.py --help
usage: prog.py [-h] [--verbosity VERBOSITY]

options:
  -h, --help            show this help message and exit
  --verbosity VERBOSITY
                        increase output verbosity
$ python3 prog.py --verbosity
usage: prog.py [-h] [--verbosity VERBOSITY]
prog.py: error: argument --verbosity: expected one argument

Вот что происходит:

  • Программа написана так, чтобы отображать что-то, когда --verbosity указано, и ничего, когда нет.
  • Чтобы показать, что опция действительно необязательна, нет ошибки при запуске программы без неё. Обратите внимание, что по умолчанию, если необязательный аргумент не используется, соответствующая переменная, в данном случае args.verbosity, получает значение None, что является причиной неудачи проверки истинности в инструкции if.
  • Сообщение справки немного отличается.
  • При использовании опции --verbosity, необходимо также указать некоторое значение, любое значение.

Приведённый выше пример принимает произвольные целые значения для --verbosity, но для нашей простой программы полезны только два значения, True или False. Давайте изменим код соответственно:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbose", help="increase output verbosity",
                    action="store_true")
args = parser.parse_args()
if args.verbose:
    print("verbosity turned on")

И вывод:

$ python3 prog.py --verbose
verbosity turned on
$ python3 prog.py --verbose 1
usage: prog.py [-h] [--verbose]
prog.py: error: unrecognized arguments: 1
$ python3 prog.py --help
usage: prog.py [-h] [--verbose]

options:
  -h, --help  show this help message and exit
  --verbose   increase output verbosity

Вот что происходит:

  • Опция теперь больше похожа на флаг, чем на что-то, что требует значения. Мы даже изменили имя опции, чтобы отразить эту идею. Обратите внимание, что мы теперь используем новое ключевое слово action, и задаём ему значение "store_true". Это означает, что, если опция указана, присвоить значение True переменной args.verbose. Если опция не указана, значение False.
  • Происходит жалованиe, когда вы указываете значение, в истинном духе того, что представляют собой флаги.
  • Обратите внимание на разницу в тексте справки.

Короткие опции

Если вы знакомы с использованием командной строки, вы заметите, что я ещё не затрагивал тему коротких версий опций. Это довольно просто:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbose", help="increase output verbosity",
                    action="store_true")
args = parser.parse_args()
if args.verbose:
    print("verbosity turned on")

И вот:

$ python3 prog.py -v
verbosity turned on
$ python3 prog.py --help
usage: prog.py [-h] [-v]

options:
  -h, --help     show this help message and exit
  -v, --verbose  increase output verbosity

Обратите внимание, что новая возможность также отражена в тексте справки.

END_OF_DOCUMENT_MARKER

Сочетание позиционных и необязательных аргументов

Наша программа продолжает усложняться:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
                    help="display a square of a given number")
parser.add_argument("-v", "--verbose", action="store_true",
                    help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbose:
    print(f"the square of {args.square} equals {answer}")
else:
    print(answer)

И теперь вывод:

$ python3 prog.py
usage: prog.py [-h] [-v] square
prog.py: error: the following arguments are required: square
$ python3 prog.py 4
16
$ python3 prog.py 4 --verbose
the square of 4 equals 16
$ python3 prog.py --verbose 4
the square of 4 equals 16
  • Мы вернули позиционный аргумент, отсюда и жалоба.
  • Обратите внимание, что порядок не имеет значения.

Как насчет того, чтобы вернуть нашей программе возможность иметь несколько значений уровня подробности и фактически использовать их:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
                    help="display a square of a given number")
parser.add_argument("-v", "--verbosity", type=int,
                    help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
    print(f"the square of {args.square} equals {answer}")
elif args.verbosity == 1:
    print(f"{args.square}^2 == {answer}")
else:
    print(answer)

И вывод:

$ python3 prog.py 4
16
$ python3 prog.py 4 -v
usage: prog.py [-h] [-v VERBOSITY] square
prog.py: error: argument -v/--verbosity: expected one argument
$ python3 prog.py 4 -v 1
4^2 == 16
$ python3 prog.py 4 -v 2
the square of 4 equals 16
$ python3 prog.py 4 -v 3
16

Все выглядит хорошо, кроме последнего, который раскрывает ошибку в нашей программе. Исправим её, ограничив значения, которые может принимать опция --verbosity:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
                    help="display a square of a given number")
parser.add_argument("-v", "--verbosity", type=int, choices=[0, 1, 2],
                    help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
    print(f"the square of {args.square} equals {answer}")
elif args.verbosity == 1:
    print(f"{args.square}^2 == {answer}")
else:
    print(answer)

И вывод:

$ python3 prog.py 4 -v 3
usage: prog.py [-h] [-v {0,1,2}] square
prog.py: error: argument -v/--verbosity: invalid choice: 3 (choose from 0, 1, 2)
$ python3 prog.py 4 -h
usage: prog.py [-h] [-v {0,1,2}] square

positional arguments:
  square                display a square of a given number

options:
  -h, --help            show this help message and exit
  -v {0,1,2}, --verbosity {0,1,2}
                        increase output verbosity

Обратите внимание, что изменение отражается как в сообщении об ошибке, так и в строке справки.

Теперь давайте попробуем другой подход к работе с уровнем подробности, который довольно распространён. Он также соответствует тому, как исполняемый файл CPython обрабатывает свой собственный аргумент уровня подробности (проверьте вывод python --help) :

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
                    help="display the square of a given number")
parser.add_argument("-v", "--verbosity", action="count",
                    help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
    print(f"the square of {args.square} equals {answer}")
elif args.verbosity == 1:
    print(f"{args.square}^2 == {answer}")
else:
    print(answer)

Мы добавили другое действие «count» для подсчёта числа появлений определённых опций.

$ python3 prog.py 4
16
$ python3 prog.py 4 -v
4^2 == 16
$ python3 prog.py 4 -vv
the square of 4 equals 16
$ python3 prog.py 4 --verbosity --verbosity
the square of 4 equals 16
$ python3 prog.py 4 -v 1
usage: prog.py [-h] [-v] square
prog.py: error: unrecognized arguments: 1
$ python3 prog.py 4 -h
usage: prog.py [-h] [-v] square

positional arguments:
  square           display a square of a given number

options:
  -h, --help       show this help message and exit
  -v, --verbosity  increase output verbosity
$ python3 prog.py 4 -vvv
16
  • Да, теперь это больше флаг (похожий на action="store_true") в предыдущей версии нашего скрипта. Это должно объяснить жалобу.
  • Он также ведет себя подобно действию «store_true».
  • Теперь демонстрация того, что даёт действие «count». Вы, вероятно, видели подобное использование ранее.
  • Если вы не укажете флаг -v, этот флаг будет считаться имеющим значение None.
  • Как и ожидалось, указав полную форму флага, мы должны получить тот же вывод.
  • К сожалению, наш вывод справки не очень информативен о новых возможностях нашего скрипта, но это всегда можно исправить, улучшив документацию для нашего скрипта (например, с помощью ключевого слова help).
  • Этот последний вывод показывает ошибку в нашей программе.

Давайте исправим:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
                    help="display a square of a given number")
parser.add_argument("-v", "--verbosity", action="count",
                    help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2

# bugfix: replace == with >=
if args.verbosity >= 2:
    print(f"the square of {args.square} equals {answer}")
elif args.verbosity >= 1:
    print(f"{args.square}^2 == {answer}")
else:
    print(answer)

И вот что это даёт:

$ python3 prog.py 4 -vvv
the square of 4 equals 16
$ python3 prog.py 4 -vvvv
the square of 4 equals 16
$ python3 prog.py 4
Traceback (most recent call last):
  File "prog.py", line 11, in <module>
    if args.verbosity >= 2:
TypeError: '>=' not supported between instances of 'NoneType' and 'int'
  • Первый вывод прошел хорошо и исправил ошибку, которая была у нас раньше. То есть, мы хотим, чтобы любое значение ≥ 2 было максимально подробным.
  • Третий вывод не очень хороший.

Давайте исправим эту ошибку:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
                    help="display a square of a given number")
parser.add_argument("-v", "--verbosity", action="count", default=0,
                    help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity >= 2:
    print(f"the square of {args.square} equals {answer}")
elif args.verbosity >= 1:
    print(f"{args.square}^2 == {answer}")
else:
    print(answer)

Мы только что добавили ещё один ключевой параметр, default. Мы установили его в значение 0, чтобы сделать его сопоставимым с другими целочисленными значениями. Помните, что по умолчанию, если необязательный аргумент не указан, он получает значение None, которое нельзя сравнить со значением типа int (отсюда и исключение TypeError).

И:

$ python3 prog.py 4
16

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

Более продвинутые примеры

Что если мы захотим расширить нашу программу, чтобы выполнять другие операции, а не только возведение в квадрат:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
parser.add_argument("-v", "--verbosity", action="count", default=0)
args = parser.parse_args()
answer = args.x**args.y
if args.verbosity >= 2:
    print(f"{args.x} to the power {args.y} equals {answer}")
elif args.verbosity >= 1:
    print(f"{args.x}^{args.y} == {answer}")
else:
    print(answer)

Вывод:

$ python3 prog.py
usage: prog.py [-h] [-v] x y
prog.py: error: the following arguments are required: x, y
$ python3 prog.py -h
usage: prog.py [-h] [-v] x y

positional arguments:
  x                the base
  y                the exponent

options:
  -h, --help       show this help message and exit
  -v, --verbosity
$ python3 prog.py 4 2 -v
4^2 == 16

Обратите внимание, что до сих пор мы использовали уровень подробности, чтобы изменить текст, который отображается. В следующем примере уровень подробности используется для отображения больше текста:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
parser.add_argument("-v", "--verbosity", action="count", default=0)
args = parser.parse_args()
answer = args.x**args.y
if args.verbosity >= 2:
    print(f"Running '{__file__}'")
if args.verbosity >= 1:
    print(f"{args.x}^{args.y} == ", end="")
print(answer)

Вывод:

$ python3 prog.py 4 2
16
$ python3 prog.py 4 2 -v
4^2 == 16
$ python3 prog.py 4 2 -vv
Running 'prog.py'
4^2 == 16

Конфликтующие опции

До сих пор мы работали с двумя методами экземпляра argparse.ArgumentParser. Давайте представим третий, add_mutually_exclusive_group(). Он позволяет указывать опции, которые конфликтуют друг с другом. Изменим остальную часть программы, чтобы новая функциональность имела больший смысл: введём опцию --quiet, которая будет противоположной опции --verbose:

import argparse

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
args = parser.parse_args()
answer = args.x**args.y

if args.quiet:
    print(answer)
elif args.verbose:
    print(f"{args.x} to the power {args.y} equals {answer}")
else:
    print(f"{args.x}^{args.y} == {answer}")

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

$ python3 prog.py 4 2
4^2 == 16
$ python3 prog.py 4 2 -q
16
$ python3 prog.py 4 2 -v
4 to the power 2 equals 16
$ python3 prog.py 4 2 -vq
usage: prog.py [-h] [-v | -q] x y
prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose
$ python3 prog.py 4 2 -v --quiet
usage: prog.py [-h] [-v | -q] x y
prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose

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

Перед завершением вы, вероятно, захотите рассказать своим пользователям о главной цели вашей программы, на случай, если они этого не знают:

import argparse

parser = argparse.ArgumentParser(description="calculate X to the power of Y")
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
args = parser.parse_args()
answer = args.x**args.y

if args.quiet:
    print(answer)
elif args.verbose:
    print(f"{args.x} to the power {args.y} equals {answer}")
else:
    print(f"{args.x}^{args.y} == {answer}")

Обратите внимание на небольшое различие в тексте использования. Обратите внимание на [-v | -q], который сообщает нам, что мы можем использовать -v или -q, но не оба одновременно:

$ python3 prog.py --help
usage: prog.py [-h] [-v | -q] x y

calculate X to the power of Y

positional arguments:
  x              the base
  y              the exponent

options:
  -h, --help     show this help message and exit
  -v, --verbose
  -q, --quiet

Заключение

Модуль argparse предлагает намного больше, чем показано здесь. Его документация довольно подробна и исчерпывающа, и полна примеров. Пройдя этот учебник, вы легко сможете усвоить их, не чувствуя себя подавленным.

© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.10/howto/argparse.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API