Содержание страницы
Руководство по Argparse¶Argparse Tutorial
- автор:
Tshepang Mbambo
Это руководство задумано как мягкое введение в argparse, рекомендуемый модуль для разбора командной строки в стандартной библиотеке Python.
Примечание
В стандартную библиотеку входят ещё два модуля, напрямую связанных с обработкой параметров командной строки: модуль optparse более низкого уровня (для настройки приложения может потребоваться больше кода, но он позволяет запрашивать поведение, не поддерживаемое argparse) и модуль очень низкого уровня getopt (который служит аналогом семейства функций getopt() для программ на C).
Хотя ни один из этих модулей напрямую не рассматривается в этом руководстве, многие ключевые концепции argparse изначально появились в optparse, поэтому некоторые аспекты этого руководства будут полезны и пользователям 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()
Ниже приведён результат выполнения кода:
$ 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, – единственная опция, которую мы получаем бесплатно (т.е. её не нужно указывать). Всё остальное приводит к ошибке. Но даже в этом случае мы получаем полезное сообщение об использовании – тоже бесплатно.
Знакомство с позиционными аргументами¶Introducing Positional arguments
Пример:
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'
Всё прошло хорошо. Программа теперь даже корректно завершается при неверном вводе перед продолжением.
Знакомство с необязательными аргументами¶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")
И вывод:
$ 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.Программа выдаёт ошибку, если указано значение, в полном соответствии с тем, что такое флаги на самом деле.
Обратите внимание на другой текст справки.
Короткие опции¶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")
И вот:
$ 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
Обратите внимание, что новая возможность также отражена в справочном тексте.
Сочетание позиционных и необязательных аргументов¶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)
А теперь вывод:
$ 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, --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
# исправление ошибки: заменить == на >=
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 очень мощный, и мы рассмотрим его чуть подробнее, прежде чем завершить этот учебник.
Немного более продвинутый материал¶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)
Вывод:
$ 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
Указание неоднозначных аргументов¶Specifying ambiguous arguments
Когда есть неоднозначность в определении того, является ли аргумент позиционным или относится к другому аргументу, можно использовать --, чтобы указать parse_args(), что всё после него – позиционные аргументы:
>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('-n', nargs='+')
>>> parser.add_argument('args', nargs='*')
>>> # неоднозначно, поэтому parse_args считает это опцией
>>> 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)
>>> # неоднозначно, поэтому опция -n жадно принимает аргументы
>>> 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'])
Конфликтующие опции¶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}")
Наша программа теперь проще, и ради демонстрации мы потеряли часть функциональности. В любом случае, вот вывод:
$ 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¶How 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.
Пользовательские преобразователи типов¶Custom type converters
Модуль 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 как один и тот же аргумент, что было бы нежелательно. Используя пользовательские
преобразователи типов, мы смогли различать эти два аргумента.
Заключение¶Conclusion
Модуль argparse предлагает гораздо больше, чем показано здесь.
Его документация весьма подробна и обстоятельна, полна примеров.
Пройдя этот учебник, вы легко сможете её усвоить,
не чувствуя перегрузки.