Самоучитель по Argparse¶
- автор:
Тшепанг Мбамбо
Это руководство призвано стать легким введением в 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"
. Это означает, что, если опция указана, присваивайте значениеTrue
args.verbose
. Если опция не указана, то присваивается значение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
Мы вернули позиционный аргумент, отсюда и жалоба.
Обратите внимание, что порядок не имеет значения.
Как насчет того, чтобы вернуть этой программе возможность иметь несколько значений 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,
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
Обратите внимание, что изменения отражаются как в сообщении об ошибке, так и в строке справки.
Теперь давайте воспользуемся другим подходом к игре со значением verbosity, который довольно распространен. Он также соответствует тому, как исполняемый файл CPython обрабатывает свой собственный аргумент verbosity (проверьте вывод 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
, чтобы сделать его сравнимым с другими значениями int. Помните, что по умолчанию, если необязательный аргумент не указан, он получает значение None
, а его нельзя сравнивать со значением int (отсюда исключение 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
Обратите внимание, что до сих пор мы использовали уровень verbosity для изменения отображаемого текста. В следующем примере вместо этого используется уровень сложности для отображения больше текста:
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
предлагает гораздо больше, чем показано здесь. Документация по нему довольно подробна и полна примеров. Пройдя через этот учебник, вы легко разберетесь в них, не чувствуя себя перегруженным.