Документация Python неофициальный перевод
Содержание страницы

Руководство по ArgparseArgparse Tutorial

автор:

Tshepang Lekhonkhobe

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

Примечание

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

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

Давайте покажем, какие возможности мы будем изучать в этом вводном руководстве, на примере команды 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 в этом случае называется необязательным аргументом.

  • Это фрагмент справки. Она очень полезна: вы можете встретить незнакомую программу и понять, как она работает, просто прочитав её справку.

ОсновыThe basics

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

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, – единственная опция, которую мы получаем бесплатно (т.е. её не нужно указывать). Всё остальное приводит к ошибке. Но даже в этом случае мы получаем полезное сообщение об использовании – тоже бесплатно.

Знакомство с позиционными аргументамиIntroducing Positional arguments

Пример:

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'

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

Знакомство с необязательными аргументамиIntroducing Optional arguments

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

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". Это означает, что если опция указана, то args.verbose присваивается значение True. Если не указана – то False.

  • Программа выдаёт ошибку, если указано значение, в полном соответствии с тем, что такое флаги на самом деле.

  • Обратите внимание на другой текст справки.

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

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

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

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

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

Наша программа становится всё сложнее:

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

# исправление ошибки: заменить == на >=
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, и такое значение нельзя сравнить с целым числом (отсюда исключение TypeError).

И:

$ python3 prog.py 4
16

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

Немного более продвинутый материалGetting a little more advanced

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

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

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

До сих пор мы работали с двумя методами экземпляра 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

Как перевести вывод argparseHow to translate the argparse output

Вывод модуля argparse, такой как текст справки и сообщения об ошибках, можно сделать переводимым с помощью модуля gettext. Это позволяет приложениям легко локализовать сообщения, создаваемые argparse. См. также Интернационализация ваших программ и модулей.

Например, в этом выводе argparse:

$ python 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

Строки usage:, positional arguments:, options: и show this help message and exit – все переводимы.

Чтобы перевести эти строки, их сначала нужно извлечь в файл .po. Например, с помощью Babel выполните следующую команду:

$ pybabel extract -o messages.po /usr/lib/python3.12/argparse.py

Эта команда извлекает все переводимые строки из модуля argparse и выводит их в файл с именем messages.po. Команда предполагает, что ваша установка Python находится в /usr/lib.

Узнать расположение модуля argparse в вашей системе можно с помощью следующего скрипта:

import argparse
print(argparse.__file__)

После того как сообщения в файле .po будут переведены и переводы установлены с помощью gettext, argparse сможет отображать переведённые сообщения.

Чтобы перевести собственные строки в выводе argparse, используйте gettext.

ЗаключениеConclusion

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