Spec-Zone.ru › Python 3.12

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

автор:

Tshepang Mbambo

Это руководство — несложное введение в 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()

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

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

options:
  -h, --help  show this help message and exit
$ python prog.py --verbose
usage: prog.py [-h]
prog.py: error: unrecognized arguments: --verbose
$ python 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)

Запустим код:

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

positional arguments:
  echo

options:
  -h, --help  show this help message and exit
$ python 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)

Теперь получим:

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

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

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

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

$ python prog.py 4
16
$ python 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")

Результат:

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

options:
  -h, --help            show this help message and exit
  --verbosity VERBOSITY
                        increase output verbosity
$ python 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")

Результат:

$ python prog.py --verbose
verbosity turned on
$ python prog.py --verbose 1
usage: prog.py [-h] [--verbose]
prog.py: error: unrecognized arguments: 1
$ python 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.
  • Если указать значение, программа выдаст ошибку, как и положено флагу.
  • Обратите внимание на изменившийся справочный текст.

Короткие параметры

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

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")

Вот что получится:

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

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

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

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

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

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)

Теперь результат:

$ python prog.py
usage: prog.py [-h] [-v] square
prog.py: error: the following arguments are required: square
$ python prog.py 4
16
$ python prog.py 4 --verbose
the square of 4 equals 16
$ python 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)

Результат:

$ python prog.py 4
16
$ python prog.py 4 -v
usage: prog.py [-h] [-v VERBOSITY] square
prog.py: error: argument -v/--verbosity: expected one argument
$ python prog.py 4 -v 1
4^2 == 16
$ python prog.py 4 -v 2
the square of 4 equals 16
$ python 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)

Результат:

$ python 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)
$ python 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», которое подсчитывает количество вхождений определённых параметров.

$ python prog.py 4
16
$ python prog.py 4 -v
4^2 == 16
$ python prog.py 4 -vv
the square of 4 equals 16
$ python prog.py 4 --verbosity --verbosity
the square of 4 equals 16
$ python prog.py 4 -v 1
usage: prog.py [-h] [-v] square
prog.py: error: unrecognized arguments: 1
$ python 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
$ python 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)

Получим следующий результат:

$ python prog.py 4 -vvv
the square of 4 equals 16
$ python prog.py 4 -vvvv
the square of 4 equals 16
$ python 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).

Результат:

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

Результат:

$ python prog.py
usage: prog.py [-h] [-v] x y
prog.py: error: the following arguments are required: x, y
$ python 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
$ python 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)

Результат:

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

Указание неоднозначных аргументов

Если непонятно, является ли аргумент позиционным или предназначен для параметра, можно использовать --, чтобы сообщить методу parse_args(), что всё после него является позиционным аргументом:

>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('-n', nargs='+')
>>> parser.add_argument('args', nargs='*')

>>> # ambiguous, so parse_args assumes it's an option
>>> parser.parse_args(['-f'])
usage: PROG [-h] [-n N [N ...]] [args ...]
PROG: error: unrecognized arguments: -f

>>> parser.parse_args(['--', '-f'])
Namespace(args=['-f'], n=None)

>>> # ambiguous, so the -n option greedily accepts arguments
>>> parser.parse_args(['-n', '1', '2', '3'])
Namespace(args=[], n=['1', '2', '3'])

>>> parser.parse_args(['-n', '1', '--', '2', '3'])
Namespace(args=['2', '3'], n=['1'])

Конфликтующие параметры

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

Теперь программа стала проще, а ради демонстрации мы отказались от части возможностей. Вот результат:

$ python prog.py 4 2
4^2 == 16
$ python prog.py 4 2 -q
16
$ python prog.py 4 2 -v
4 to the power 2 equals 16
$ python 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
$ python 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, но не оба одновременно:

$ 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

Как перевести вывод argparse

Вывод модуля 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.

Пользовательские преобразователи типов

Модуль argparse позволяет задавать пользовательские преобразователи типов для аргументов командной строки. С их помощью можно изменять введённые пользователем данные до сохранения в argparse.Namespace. Это может быть полезно, если перед использованием данных в программе нужно предварительно их обработать.

Для пользовательского преобразователя типа подойдёт любой вызываемый объект, который принимает один строковый аргумент (значение аргумента) и возвращает преобразованное значение. Однако если нужно обрабатывать более сложные случаи, вместо этого можно использовать пользовательский класс действия с параметром action.

Например, предположим, что нужно обрабатывать аргументы с разными префиксами по-разному:

import argparse

parser = argparse.ArgumentParser(prefix_chars='-+')

parser.add_argument('-a', metavar='<value>', action='append',
                    type=lambda x: ('-', x))
parser.add_argument('+a', metavar='<value>', action='append',
                    type=lambda x: ('+', x))

args = parser.parse_args()
print(args)

Результат:

$ python prog.py -a value1 +a value2
Namespace(a=[('-', 'value1'), ('+', 'value2')])

В этом примере мы:

  • Создали парсер с пользовательскими символами префиксов с помощью параметра prefix_chars.
  • Определили два аргумента — -a и +a, — для которых с помощью параметра type создали пользовательские преобразователи типов, сохраняющие значение в кортеже вместе с префиксом.

Без пользовательских преобразователей типов аргументы -a и +a считались бы одним и тем же аргументом, что было бы нежелательно. Пользовательские преобразователи типов позволили нам различать эти аргументы.

Заключение

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

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

Spec-Zone.ru

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