Изучаем Python. Программирование игр, визуализация данных, веб-приложения. Мэтиз Эрик

До загрузки: 30 сек.



Благодарим, что скачиваете у нас :)

Если, что - то:

  • Поделится ссылкой:
  • Документ найден в свободном доступе.
  • Загрузка документа - бесплатна.
  • Если нарушены ваши права, свяжитесь с нами.
Формат: pdf
Найдено: 03.09.2020
Добавлено: 30.09.2020
Размер: 4.91 Мб

ББК 32.973.2-018.1
УДК 004.43М54 Мэтиз Эрик
М54 Изучаем Python. Программирование игр, визуализация данных, веб-прило- жения. — СПб.: Питер, 2017. — 496 с.: ил. — (Серия «Библиотека программиста»).
ISBN 978-5-496-02305-4
Книга «Изучаем Python» — это ускоренный курс, который позволит вам сэкономить время
и сразу начать писать работоспособные программы (игры, визуализации данных, веб-приложения
и многое другое). Хотите стать программистом? В первой части книги вам предстоит узнать о базо-
вых принципах программирования, познакомиться со списками, словарями, классами и циклами, вы
научитесь создавать программы и тестировать код. Во второй части книги вы начнете использовать
знания на практике, работая над тремя крупными проектами: создадите собственную «стрелялку»
с нарастающей сложностью уровней, займетесь работой с большими наборами данных и освоите их
визуализацию, и, наконец, создадите полноценное веб-приложение на базе Django, гарантирующее
конфиденциальность пользовательской информации. Если вы решились разобраться в том, что такое
программирование, не нужно ждать. Ключ на старт и вперед!
12+ (В соответствии с Федеральным законом от 29 декабря 2010 г. № 436-ФЗ.)
ББК 32.973.2-018.1
УДК 004.43
Права на издание получены по соглашению с No Starch Press Все права защищены. Никакая часть данной книги
не может быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских прав.
ISBN 978-1593276034 англ. © 2016 by Eric Matthes
ISBN 978-5-496-02305-4 © Перевод на русский язык ООО Издательство «Питер», 2017 © Издание на русском языке, оформление
ООО Издательство «Питер», 2017
© Серия «Вы и ваш ребенок», 2017

Оглавление Об авторе ��������������������������������������������������� � 10
Благодарности ������������������������������������������������ 12
Введение �������������������������������������������������� �� 13
Часть I. Основы ............................................... 16
Глава 1. Начало работы ..................................... 17
Подготовка среды программирования ������������������������������ 17
Решение проблем с установкой ����������������������������������� 28
Запуск программ Python в терминале ������������������������������ 29
Итоги �������������������������������������������������� ���� 31
Глава 2. Переменные и простые типы данных .................. 32
Что происходит при запуске hello_world�py �������������������������� 32
Переменные ������������������������������������������������� 32
Строки �������������������������������������������������� ��� 35
Числа ��������������������������������������������������� ��� 41
Комментарии ������������������������������������������������ 44
Философия Python �������������������������������������������� 45
Итоги �������������������������������������������������� ���� 47
Глава 3. Списки ............................................ 48
Что такое список? ��������������������������������������������� 48
Индексы начинаются с 0, а не с 1 ��������������������������������� 49
Упорядочение списка ������������������������������������������ 56
Ошибки индексирования при работе со списками ��������������������� 59
Итоги �������������������������������������������������� ���� 61
Глава 4. Работа со списками ................................. 62
Перебор всего списка ������������������������������������������ 62

6 Оглавление
Создание числовых списков ������������������������������������� 69
Работа с частью списка ����������������������������������������� 73
Кортежи ��������������������������������������������������� � 77
Стиль программирования ��������������������������������������� 79
Итоги �������������������������������������������������� ���� 82
Глава 5. Команды if ......................................... 83
Проверка условий ��������������������������������������������� 84
Команды if �������������������������������������������������� 89
Использование команд if со списками ������������������������������ 96
Оформление команд if ����������������������������������������� 99
Итоги �������������������������������������������������� ��� 100
Глава 6. Словари .......................................... 101
Простой словарь ��������������������������������������������� 101
Работа со словарями ������������������������������������������ 102
Перебор словаря ��������������������������������������������� 107
Вложение �������������������������������������������������� 113
Итоги �������������������������������������������������� ��� 119
Глава 7. Ввод данных и циклы while .......................... 120
Как работает функция input() ����������������������������������� 120
Циклы while ������������������������������������������������ 124
Использование цикла while со списками и словарями ����������������� 130
Итоги �������������������������������������������������� ��� 134
Глава 8. Функции .......................................... 135
Определение функции ���������������������������������������� 135
Передача аргументов ����������������������������������������� 137
Возвращаемое значение ��������������������������������������� 142
Передача списка ��������������������������������������������� 147
Хранение функций в модулях ����������������������������������� 154
Стилевое оформление функций ��������������������������������� 158
Итоги �������������������������������������������������� ��� 159
Глава 9. Классы ........................................... 160
Создание и использование класса �������������������������������� 161
Работа с классами и экземплярами ������������������������������� 165
Наследование ����������������������������������������������� 170
Импортирование классов �������������������������������������� 176
Импортирование нескольких классов из модуля ��������������������� 179
Стандартная библиотека Python ��������������������������������� 182
Оформление классов ����������������������������������������� 184
Итоги �������������������������������������������������� ��� 184

Оглавление 7
Глава 10. Файлы и исключения .............................. 185
Чтение из файла ��������������������������������������������� 185
Запись в файл ����������������������������������������������� 193
Исключения ������������������������������������������������ 195
Сохранение данных ������������������������������������������ 204
Итоги �������������������������������������������������� ��� 209
Глава 11. Тестирование .................................... 210
Тестирование функции ���������������������������������������� 210
Тестирование класса ������������������������������������������ 216
Итоги �������������������������������������������������� ��� 222
Часть II. Проекты ............................................. 224
Программирование игры на языке Python �������������������������� 224
Визуализация данных ����������������������������������������� 224
Веб-приложения ��������������������������������������������� 224
Глава 12. Стреляющий корабль .............................. 226
Планирование проекта ���������������������������������������� 226
Установка Pygame �������������������������������������������� 227
Создание проекта игры ���������������������������������������� 231
Добавление изображения корабля ������������������������������� 235
Рефакторинг: модуль game_functions ����������������������������� 238
Управление кораблем ����������������������������������������� 240
В двух словах ����������������������������������������������� 247
Стрельба �������������������������������������������������� 248
Итоги �������������������������������������������������� ��� 255
Глава 13. Осторожно, пришельцы! ........................... 256
Анализ проекта ���������������������������������������������� 256
Создание пришельца ����������������������������������������� 257
Построение флота ������������������������������������������� 260
Перемещение флота ������������������������������������������ 266
Уничтожение пришельцев �������������������������������������� 270
Завершение игры �������������������������������������������� 274
Определение исполняемых частей игры ��������������������������� 280
Итоги �������������������������������������������������� ��� 280
Глава 14. Ведение счета .................................... 281
Добавление кнопки Play ��������������������������������������� 281
Повышение сложности ���������������������������������������� 288
Подсчет очков ���������������������������������������������� 291
Итоги �������������������������������������������������� ��� 307

8 Оглавление
Глава 15. Генерирование данных ............................ 309
Установка matplotlib ������������������������������������������ 309
Построение простого графика ����������������������������������� 311
Случайное блуждание ����������������������������������������� 319
Моделирование бросков кубиков в Pygal ��������������������������� 327
Итоги �������������������������������������������������� ��� 335
Глава 16. Загрузка данных .................................. 336
Формат CSV ������������������������������������������������ 336
Формат JSON ����������������������������������������������� 348
Итоги �������������������������������������������������� ��� 361
Глава 17. Работа с API ...................................... 362
Использование API веб-приложений ������������������������������ 362
Итоги �������������������������������������������������� ��� 378
Глава 18. Знакомство с Django ............................... 380
Подготовка к созданию проекта ��������������������������������� 380
Начало работы над приложением �������������������������������� 385
Построение других страниц ������������������������������������� 398
Итоги �������������������������������������������������� ��� 407
Глава 19. Учетные записи пользователей ..................... 408
Редактирование данных ��������������������������������������� 408
Создание учетных записей пользователей �������������������������� 419
Редактирование данных ��������������������������������������� 428
Итоги �������������������������������������������������� ��� 435
Глава 20. Оформление и развертывание приложения ........... 437
Оформление приложения Learning Log ���������������������������� 437
Развертывание Learning Log ������������������������������������ 448
Итоги �������������������������������������������������� ��� 465
Приложение А. Установка Python ............................ 466
Python в системе Linux ���������������������������������������� 466
Python в OS X ����������������������������������������������� 467
Python в Windows �������������������������������������������� 468
Ключевые слова и встроенные функции Python ���������������������� 469
Приложение Б. Текстовые редакторы ........................ 471
Geany ��������������������������������������������������� �� 471
Sublime Text ������������������������������������������������ 474
IDLE ��������������������������������������������������� ��� 476
Emacs и vim ������������������������������������������������ 477

Оглавление 9
Приложение В. Помощь и поддержка ......................... 478
Первые шаги ����������������������������������������������� 478
Поиск в Интернете ������������������������������������������� 479
IRC (Internet Relay Chat) ��������������������������������������� 481
Приложение Г. Git и контроль версий ......................... 483
Установка Git ����������������������������������������������� 483
Создание проекта �������������������������������������������� 484
Игнорирование файлов ���������������������������������������� 484
Инициализация репозитория ������������������������������������ 485
Проверка статуса �������������������������������������������� 485
Добавление файлов в репозиторий ������������������������������� 486
Закрепление ������������������������������������������������ 486
Просмотр журнала ������������������������������������������� 487
Второе закрепление ������������������������������������������ 487
Отмена изменений ������������������������������������������� 488
Извлечение предыдущих закреплений ����������������������������� 489
Удаление репозитория ���������������������������������������� 490
Послесловие .............................................. 492

Об авторе
Эрик Мэтиз (Eric Matthes), преподаватель физики и математики, живет на Аляске
и ведет курс th начального уровня Эрик пишет программы с пяти лет, а в на
стоящее время занимается разработкой продуктов, которые исправляют недочеты
в системе образования и помогают использовать возможности программных про
дуктов с открытым кодом в системе образования В свободное время занимается альпинизмом и проводит время с семьей О научном рецензенте
Кеннет Лав (eeth e) — преподаватель и программист th с многолетним
стажем Он выступал с докладами и лекциями на конференциях, занимался про
фессиональной подготовкой, работал внештатным программистом th и a,
а в настоящее время ведет занятия в компании дистанционного образования Кен
нет также является одним из создателей пакета django-braces, предоставляющего
удобные примеси (iis) для представлений на базе классов a Желающие могут читать его сообщения в Твиттере ( @kennethlove)

Моему отцу, который никогда не жалел
времени, чтобы ответить на мои вопросы
по программированию, и Эверу, который
только начинает задавать мне свои
вопросы.

Благодарности
Эта книга никогда бы не появилась на свет без великолепных, чрезвычайно про
фессиональных сотрудников издательства N Билл Поллок (i
c) предложил мне написать вводный учебник, и я глубоко благодарен ему
за это Тайлер Ортман (er rta) помог привести в порядок мои идеи на ран
ней стадии подготовки чернового варианта Лиз Чедвик (i haic) и Лесли
Шен (esie he) предоставили бесценные отзывы на исходные варианты каждой
главы, а Энн Мэри Уокер (e Marie aer) помогла прояснить многие части
книги Райли Хоффман (ie a) отвечал на все вопросы, которые возникали
у меня в процессе построения полной книги, и терпеливо превращал мою работу в прекрасный завершенный продукт
Также хочу поблагодарить Кеннета Лава (eeth e), научного рецензента
книги Я познакомился с Кеннетом на конференции , и его энтузиазм в от
ношении языка и сообщества th с тех пор неизменно оставался для меня
источником профессионального вдохновения Кеннет вышел за рамки простой
проверки фактов он следил за тем, чтобы книга помогала начинающим програм
мистам сформировать основательное понимание языка th и программирова
ния в целом Вместе с тем ответственность за все оставшиеся нето чности лежит
исключительно на мне
Я хочу поблагодарить своего отца, который познакомил меня с программированием
в раннем возрасте и не побоялся, что я сломаю его оборудование Также хочу ска
зать спасибо своей жене Эрин за поддержку и помощь во время работы над книгой и своему сыну Эверу, чья любознательность постоянно служит мне примером

Введение
У каждого программиста найдется своя история о том, как он написал свою пер
вую программу Я начал изучать программирование еще в детстве, когда мой отец
работал на  , одну из ведущих компаний современной
эры вычислительной техники Я написал свою первую программу на компьютере,
который был собран моим отцом из набора комплектующих в подвале дома Ком
пьютер представлял собой системную плату (без корпуса), подключенную к кла
виатуре, а в качестве монитора использовалась простейшая электронн о лучевая
трубка Моей первой программой стала игра по отгадыванию чисел, которая выглядела примерно так
Я загадал число! Попробуйте отгадать мое число: 25
Слишком мало! Следующая попытка: 50
Слишком много! Следующая попытка: 42
Верно! Хотите сыграть снова? (да/нет) нет
Спасибо за игру!
Никогда не забуду, как доволен я был, когда моя семья играла в написанную мной игру и все работало точно так, как я задумал
Мои ранние переживания имели далеко идущие последствия Очень приятно по
строить нечто, предназначенное для конкретной цели нечто, успешно решающее
свою задачу Программы, которые я пишу сейчас, намного серьезнее моих детских
попыток, но чувство удовлетворения, которое я ощущаю от вида работающей про
граммы, остается практически тем же Для кого написана эта книга?
Цель этой книги — как можно быстрее ввести читателя в курс дела, чтобы тот
начал писать на th работоспособные программы (игры, визуализации
данных и вебприложения), и одновременно заложить основу в области про
граммирования, которая пригодится ему на протяжении всей жизни Книга
написана для людей любого возраста, которые прежде никогда не программи
ровали на th или вообще никогда не программировали Если вы хотите
быстро изучить азы программирования, чтобы сосредоточиться на интересных
проектах, а также проверить свое понимание новых концепций на содержа
тельных задачах — эта книга для вас Книга также прекрасно подходит для
преподавателей, желающих предложить вводный курс программирования, основанный на проектах

14 Введение
Чему эта книга вас научит?
Цель книги — сделать вас хорошим программистом вообще и хорошим программи
стом th в частности Процесс обучения будет эффективным, и вы приобретете
много полезных навыков, так как я представлю основательное введение в общие
концепции программирования После того как вы перевернете последнюю стра
ницу, вы будете готовы к знакомству с более серьезными возможностями th, а изучение вашего следующего языка программирования тоже упростится
В первой части книги будут представлены базовые концепции программирования,
которые необходимо знать для написания программ th Эти концепции ничем
не отличаются от тех, которые рассматриваются в начале изучения почти любого
языка программирования Вы познакомитесь с разными видами данных и возмож
ностями хранения данных в списках и словарях Вы научитесь создавать коллекции
данных и эффективно работать с этими коллекциями В частности, циклы while
и if позволяют выполнять определенные фрагменты кода, если некоторое условие
истинно, и выполнять другие фрагменты в противном случае — эти конструкции очень сильно помогают при автоматизации процессов
Вы научитесь получать входные данные от пользователя, чтобы ваши программы
стали интерактивными, и выполнять их до тех пор, пока пользователь остается ак
тивным Также вы узнаете, как написать функции для многократного выполнения
некоторых частей ваших программ, чтобы вы один раз программировали некоторое
действие, а потом могли использовать его столько раз, сколько потребуется За
тем эта концепция будет распространена на более сложное поведение с классами,
что позволит даже относительно простым программам реагировать на множество
разнообразных ситуаций Вы научитесь писать программы, корректно обрабаты
вающие многие типичные ошибки После знакомства с базовыми концепциями
мы напишем несколько коротких программ для решения конкретных задач На
конец, вы сделаете первые шаги на пути к программированию среднего уровня вы
научитесь писать тесты для своего кода, чтобы вы могли продолжать разработку
программ, не беспокоясь о возможном внесении ошибок Вся информация части подготовит вас к более сложным и масштабным проектам
В части знания, полученные в части , будут применены для построения трех про
ектов Вы можете взяться за любые из этих проектов в том порядке, который лучше
подходит для вас В первом проекте (главы –) будет создана игра«стрелялка»
в стиле классического хита ace ƒaers, состоящая из многих уровней с нараста
ющей сложностью После завершения этого проекта вы будете знать многое из того, что необходимо знать для разработки собственных ěигр
Второй проект (главы –) познакомит вас с визуализацией данных Чтобы
разобраться в огромных объемах доступной информации, специалисты по ана
лизу данных применяют различные средства визуализации Вы будете работать
с наборами данных, генерируемыми в программах наборами данных, загружен
ными из сетевых источников и наборами данных, которые загруж аются вашей
программой автоматически После завершения этого проекта вы сможете писать
программы, обрабатывающие большие наборы данных и строящие визуальные представления сохраненной информации

Введение 15
В третьем проекте (главы –) будет построено небольшое вебприл ожение
eari Этот проект позволяет вести журнал новых идей и концепций, кото
рые вы узнали в ходе изучения конкретной темы Пользователь приложения смо
жет вести разные журналы по разным темам, создавать учетные записи и начинать
новые журналы Вы также узнаете, как развернуть свой проект в Интернете, чтобы любой желающий мог работать с ним откуда угодно Почему именно Python?
Каждый год я задумываюсь над тем, продолжать ли мне работать на th или же
перейти на другой язык — вероятно, более новый в мире программирования И все
же я продолжаю работать на th по многим причинам Язык th невероятно
эффективен ваши программы делают больше, чем многие другие языки, в мень
шем объеме кода Синтаксис th также позволяет писать «чистый» код Ваш
код будет легко читаться, у вас будет меньше проблем с отладкой и расширением программ по сравнению с другими языками
th используется для разных целей для создания игр, построения веб
приложений, решений бизнесзадач и разработки внутренних инструментов для
всевозможных интересных проектов th также широко применяется в научной области для теоретических исследований и решения прикладных задач
Впрочем, одной из самых важных причин для использования th для меня
остается сообщество th, состоящее из невероятно разных и благожелательных
людей Сообщество играет исключительно важную роль в программировании, по
тому что программирование не является сугубо индивидуальным делом Многим
из нас, даже самым опытным программистам, приходится обращаться за советом
к коллегам, которые уже решали похожие задачи Существование дружного, добро
желательного сообщества помогает решать задачи, и сообщество th готово
прийти на помощь людям, у которых th является первым языком програм
мирования th — замечательный язык, давайте же браться за дело От издательства
Ваши замечания, предложения, вопросы отправляйте по адресу электронной почты
comp@piter�com (издательство «Питер», компьютерная редакция)
Мы будем рады узнать ваше мнение
На вебсайте издательства http://www�piter�com вы найдете подробную информацию
о наших книгах

Часть I �
Основы
В части I этой книги представлены базовые концепции, необходимые для написания
программ на языке Python� Многие из этих концепций встречаются во всех языках про-
граммирования, поэтому они пригодятся вам на протяжении всей карьеры программиста�
В главе 1 вы установите Python на свой компьютер и запустите свою первую программу,
которая выводит на экран сообщение
Hello world! � В главе 2 вы научитесь хранить ин-
формацию в переменных, работать с текстовыми и числовыми данными�
В главах 3 и 4 вы познакомитесь со списками� Списки позволяют хранить любой объем
информации в одной переменной, что повышает эффективность работы с данными� Вы
сможете работать с сотнями, тысячами и даже миллионами значений всего в нескольких строках кода�
В главе 5 будут представлены команды if� С их помощью вы сможете написать код,
который делает что-то одно, если некоторое условие истинно, и что-то другое, если условие не выполняется�
Глава 6 показывает, как использовать словари Python, связывающие разные виды ин-
формации� Словари, как и списки, могут содержать столько информации, сколько вы захотите в них поместить�
В главе 7 вы научитесь получать данные от пользователей, чтобы ваши программы стали
интерактивными� Также в этой главе описаны циклы while, многократно выполняющие
блоки кода, пока некоторое условие остается истинным�
В главе 8 вы займетесь написанием функций — именованных блоков кода, которые ре- шают конкретную задачу и запускаются тогда, когда потребуется�
В главе 9 представлены классы, предназначенные для моделирования реальных объ -
ектов: собак, кошек, людей, машин, ракет и т� д� С их помощью вы сможете представить в своем коде любые сущности, реальные или абстрактные�
Глава 10 научит вас работать с файлами и обрабатывать ошибки, чтобы ваши программы
не завершались аварийно� Вы узнаете, как сохранить данные перед закрытием програм -
мы и снова загрузить их при запуске программы� В этой главе рассматриваются исклю-
чения Python; с их помощью вы сможете предвидеть возможные ошибки и организовать их корректную обработку в программах�
В главе 11 вы научитесь писать тесты для кода� Тесты проверяют, что ваша программа
работает так, как предполагается� В результате вы сможете расширять свои программы,
не беспокоясь о возможном внесении новых ошибок� Тестирование — один из первых навыков, отличающих новичка от программиста среднего уровня�

1 Начало работы
В этой главе вы запустите свою первую программу на языке th, hello_world�
py Сначала вы проверите, установлен ли th на вашем компьютере, и если
нет — установите его Также будет установлен текстовый редактор для подготовки
программ th Текстовые редакторы распознают код th и выделяют син
таксические конструкции во время работы, упрощая понимание структуры кода разработчиком Подготовка среды программирования
Поддержка th слегка отличается в разных операционных системах, поэтому
вы должны учитывать некоторые аспекты В этой главе представлены две основные
версии th, используемые в наше время, и описаны основные действия по настройке th в вашей системе Python 2 и Python 3
Сейчас доступны две версии th th и более новая версия th ҆ Каж
дый язык программирования развивается с появлением новых идей и технологий,
и разработчики th неустанно трудятся над тем, чтобы сделать язык более
мощным и гибким Многие изменения имеют второстепенный характер и мало
заметны на первый взгляд, но в отдельных случаях код, написанный на th ,
некорректно работает в системах с установленной поддержкой th ҆ В книге
я буду указывать на существенные различия между th и th , так что вы
сможете следовать приведенным инструкциям независимо от используемой версии
Если в вашей системе установлены обе версии или вы еще не установили th,
используйте th ҆ Если в вашей системе установлена только версия th
и вы предпочитаете с ходу взяться за написание кода, не желая возиться с установ
кой, начните с th Ć Но чем скорее вы перейдете на th , тем лучше — все же полезнее использовать самую новую версию Выполнение фрагментов кода Python
В поставку th входит интерпретатор, который выполняется в терминальном
окне и позволяет опробовать фрагменты кода th без сохранения и запуска всей программы

18 Глава 1 • Начало работы
В этой книге встречаются фрагменты следующего вида  >>> print("Hello Python interpreter!")
Hello Python interpreter!
Жирным шрифтом выделен текст, который вы вводите и выполняете нажатием
клавиши Eter Большинство примеров в книге представляет собой небольшие са
мостоятельные программы, которые запускаются из редактора, потому что именно
так вы будете писать б ульшую часть своего кода Но в некоторых случаях базовые
концепции будут проиллюстрированы серией фрагментов в терминальном сеансе
th для более эффективной демонстрации отдельных концепций Каждый раз,
когда в листинге встречаются три угловые скобки , это означает, что перед вами
вывод терминального сеанса Вскоре мы опробуем возможность программирования в интерпретаторе для вашей системы
Hello World!
В мире программирования издавна принято начинать освоение нового языка с про
граммы, выводящей на экран сообщение   — считается, что это приносит
удачу На языке th программа e r состоит всего из одной строки
print("Hello world!")
Даже такая простая программа выполняет вполне конкретную функцию Если
она запускается в вашей системе, то и любая программа, которую вы напишете
на th, тоже должна запускаться нормально О том, как написать эту программу для вашей конкретной системы, мы поговорим чуть ниже Python в разных операционных системах
th является кроссплатформенным языком программирован ия это означа
ет, что он работает во всех основных операционных системах Любая программа
на языке th, написанная вами, должна выполняться на любом современном
компьютере с установленной поддержкой th Впрочем, способы настройки th для разных операционных систем слегка отличаются
В этом разделе вы узнаете, как подготовить th к работе и запустить программу
  в вашей системе Сначала вы проверите, установлена ли поддержка
th в вашей системе, и если нет — установите ее Затем вы установите про
стой текстовый редактор и сохраните пустой файл th с именем hello_world�
py Наконец, вы запустите программу   и устраните любые неполадки
Этот процесс будет описан для всех операционных систем, так что в итоге в вашем
распоряжении появится простая и удобная среда программирования на th
Python в системе Linux
Системы семейства iԡ ориентированы на программистов, поэтому поддержка
th уже установлена на большинстве компьютеров iԡ Люди, которые

Подготовка среды программирования 19
занимаются разработкой и сопровождением iԡ, ожидают, что в какойто мо
мент вы займетесь программированием, и всячески способствуют этому По этой
причине для перехода к программированию вам почти ничего не придется уста
навливать, а количество необходимых настроек будет минимальным Проверка версии Python
Откройте терминальное окно, запустив приложение Terminal в вашей системе
(в ԃt нажмите клавиши tr״tש) Чтобы проверить, установлена ли под
держка th в вашей системе, введите команду python (со строчной буквы )
На экране появится информация о том, какая версия th у вас установлена, и приглашение >>>, в котором можно вводить команды th
$ python
Python 2.7.6 (default, Mar 22 2014, 22:59:38) [GCC 4.8.2] on linux2Type "help", "copyright", "credits" or "license" for more information. >>>
Этот вывод сообщает, что th Ćˆ в настоящее время является версией th
по умолчанию, установленной на данном компьютере Нажмите trכ или вве
дите exit() , чтобы выйти из приглашения th и вернуться к п риглашению
терминала
Чтобы проверить наличие th , возможно, вам придется указать эту версию
итак, даже при том, что в качестве версии по умолчанию в выходных данных указан th Ć, попробуйте ввести команду python3
$ python3
Python 3.5.0 (default, Sep 17 2015, 13:05:18) [GCC 4.8.4] on linuxType "help", "copyright", "credits" or "license" for more information.>>>
Из выходных данных видно, что в системе также установлена версия th , так
что вы сможете использовать любую из этих версий Каждый раз, когда вы встреча
ете команду python в этой книге, вводите вместо нее команду python3 В большин
стве дистрибутивов iԡ поддержка th уже установлена, но, если по какойто
причине в вашей системе ее нет или ваша система была укомплектована th , а вы хотите установить th , обращайтесь к приложению А Установка текстового редактора
ea — простой и удобный текстовый редактор он легко устанавливается, по
зволяет запускать практически любые программы прямо из редактора (вместо
терминала) и использует цветовое выделение синтаксиса, а код выполняется
в терминальном окне В приложении Б приведена информация о других текстовых
редакторах, но я рекомендую использовать ea, если только у вас нет веских причин для работы в другом редакторе В большинстве систем iԡ установка ea выполняется одной строкой
$ sudo apt-get install geany

20 Глава 1 • Начало работы
Если команда не работает, обращайтесь к инструкциям по адресу .

Запуск программы Hello World
Чтобы запустить свою первую программу, откройте ea Нажмите клавишу
er (она также часто называется клавишей is) и найдите ea в вашей
системе Создайте ярлык, перетащив значок на панель задач или рабочий стол
Создайте папку для своих проектов и присвойте ей имя python_work (В именах
файлов и папок лучше использовать буквы нижнего регистра и символы по д
черкивания, потому что это соответствует соглашениям об именах th)
Вернитесь к ea и сохраните пустой файл th ( FileSave As ) с именем
hello_world�py в папке python_work Расширение �py сообщает ea, что файл со
держит программу th Оно также подсказывает ea, как следует запускать программу и как правильно выделить элементы синтаксиса
После того как файл будет сохранен, введите следующую строку
print("Hello Python world!")
Если в системе установлено несколько версий th, проследите за тем, что
бы в ea была настроена правильная версия Откройте окно BuildSet Build
Commands В окне приведены команды Compile и Execute , рядом с каждой из которых
располагается команда ea предполагает, что правильной командой в каждом
случае является python, но, если в системе должна использоваться команда python3,
настройку необходимо изменить
Если команда python3 работала в терминальном сеансе, измените команды Compile
и Execute так, чтобы в ea использовался интерпретатор th ҆ Команда
Compile должна выглядеть так
python3 -m py_compile "%f"
Команда должна быть введена точно в таком виде без малейших изменений Про следите за правильностью регистра символов и расстановки пробелов Команда Execute должна выглядеть так
python3 "%f"
И снова тщательно проверьте пробелы и регистр символов На рис Æ показано,
как эти команды должны выглядеть в меню конфигурации ea
Теперь выполните программу hello_world�py выберите команду меню BuildExecute ,
щелкните на кнопке Execute (с шестеренками) или нажмите клавишу ʆ
На экране появляется терминальное окно со следующим выводом
Hello Python world! ------------------ (program exited with code: 0) Press return to continue
Если вы не увидели это сообщение, проверьте каждый символ во введенной строке
Может, вы случайно набрали print с прописной буквы Пропустили одну или обе

Подготовка среды программирования 21
Рис. 1.1. Настройка Geany для использования Python 3 в Linux
кавычки или круглые скобки В языках программирования используется предель
но конкретный синтаксис, и при малейшем его нарушении произойдет ошибка
Если программа так и не заработала, обращайтесь к разделу «Решение проблем с установкой» на с ͆ Запуск Python в терминальном сеансе
Для выполнения фрагментов кода th можно открыть терминальное окно и вве
сти команду python или python3 , как мы поступили при проверке версии Сделайте
то же самое, но на этот раз введите в терминальном сеансе следующую строку
>>> print("Hello Python interpreter!")
Hello Python interpreter! >>>
Сообщение выводится прямо в текущем терминальном окне Вспомните, что интер
претатор th закрывается комбинацией клавиш trכ или командой exit()
Python в системе OS X
В большинстве систем поддержка th уже установлена Даже если вы
уверены в том, что th устанавливать не нужно, вам придется установить текстовый редактор и убедиться в том, что он правильно настроен

22 Глава 1 • Начало работы
Проверка наличия Python
Откройте терминальное окно (команда ApplicationsUtilities Terminal ) Также
можно нажать aпробел, ввести terminal и нажать Eter Чтобы прове
рить, установлена ли поддержка th в вашей системе, введите команду python
(со строчной буквы ) На экране появится информация о том, какая версия
th у вас установлена, и приглашение >>>, в котором можно вводить команды
th
$ python
Python 2.7.5 (default, Mar 9 2014, 22:15:05) [GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin
Type "help", "copyright", "credits", or "license" for more information.>>>
Этот вывод сообщает, что th Ćˆ в настоящее время является версией th
по умолчанию, установленной на данном компьютере Нажмите trכ или вве
дите exit() , чтобы выйти из приглашения th и вернуться к п риглашению
терминала
Чтобы проверить наличие th , попробуйте ввести команду python3 На экра
не может появиться сообщение об ошибке, но, если из вывода следует, что версия
th в вашей системе установлена, вы сможете использовать ее без необхо
димости установки Если команда python3 работает в вашей системе, каждый раз,
когда вы встречаете команду python в этой книге, вводите вместо нее команду
python3 Если по какойто причине в вашей системе нет th или ваша система
была укомплектована th , а вы хотите установить th , обращайтесь к приложению А Запуск Python в терминальном сеансе
Для выполнения фрагментов кода th можно открыть терминальное окно и вве
сти команду python или python3 , как мы поступили при проверке версии Сделайте
то же самое, но на этот раз введите в терминальном сеансе следующую строку
>>> print("Hello Python interpreter!")
Hello Python interpreter! >>>
Сообщение выводится прямо в текущем терминальном окне Вспомните, что интер
претатор th закрывается комбинацией клавиш trכ или командой exit()
Установка текстового редактора
֧ie et — простой и удобный текстовый редактор он легко устанавливается
в , позволяет запускать практически любые программы прямо из редактора
(вместо терминала) и использует цветовое выделение синтаксиса, а код выполня
ется в терминальном окне, встроенном в окно ֧ie et В приложении Б при
ведена информация о других текстовых редакторах, но я рекомендую использовать
֧ie et, если только у вас нет веских причин для работы в другом редакторе

Подготовка среды программирования 23
Программу установки ֧ie et можно загрузить по адресу .
 Щелкните на ссылке загрузки и найдите программу установки для ܆
Политика лицензирования ֧ie et более чем либеральна вы можете бес
платно пользоваться редактором сколь угодно долго, но автор требует приобре
сти лицензию, если программа вам понравилась и вы собираетесь использовать
ее в будущем После того как программа установки будет загружена, откройте ее и перетащите значок ֧ie et в папку Applications
Настройка Sublime Text для Python 3
Если для запуска терминального сеанса th вместо python используется другая
команда, вам придется настроить ֧ie et, чтобы программа знала, где найти
правильную версию th в вашей системе Чтобы узнать полный путь к интерпретатору th, введите следующую команду
$ type -a python3
python3 is /usr/local/bin/python3
Теперь откройте ֧ie et и выберите команду ToolsBuild System New Build
System Команда открывает новый конфигурационный файл Удалите его текущее
содержимое и введите следующий код
{ "cmd": ["/usr/local/bin/python3", "-u", "$file"], }
Этот код приказывает ֧ie et использовать команду python3 вашей си
стемы для запуска текущего открытого файла Проследите за тем, чтобы в коде
использовался путь, полученный при выполнении команды type -a python3
на предыдущем шаге Сохраните файл с именем Python3�sublime-build в каталоге
по умолчанию, который ֧ie et открывает при выполнении команды Save
Запуск программы Hello World
Чтобы запустить свою первую программу, запустите ֧ie et — откройте папку
Applications и сделайте двойной щелчок на значке ֧ie et Также можно нажать
aпробел и ввести sublime text в открывшейся панели поиска
Создайте для своих проектов папку с именем python_work (В именах файлов
и папок лучше использовать буквы нижнего регистра и символы подчеркивания,
потому что это соответствует соглашениям об именах th) Сохраните пустой
файл th ( FileSave As ) с именем hello_world�py в папке python_work Расшире
ние �py сообщает ֧ie et, что файл содержит программу th Оно также
подсказывает ֧ie et, как следует запускать программу и как правильно вы
делить элементы синтаксиса После того как файл будет сохранен, введите следующую строку
print("Hello Python world!")
Если команда python работает в вашей системе, программу можно запустит ь
командой меню ToolsBuild или комбинацией клавиш trצ Если вы настроили

24 Глава 1 • Начало работы
֧ie et на использование другой команды вместо python, выберите команду
меню ToolsBuild System , а затем Python 3 Тем самым вы назначаете th вер
сией th по умолчанию, и в дальнейшем программы можно будет запускать командой ToolsBuild или комбинацией клавиш aצ
Терминальное окно должно отображаться в нижней части окна ֧ie et со следующим текстом
Hello Python world! [Finished in 0.1s]
Если вы не увидели это сообщение, проверьте каждый символ во введенной строке
Может, вы случайно набрали print с прописной буквы Пропустили одну или обе
кавычки или круглые скобки В языках программирования используется предель
но конкретный синтаксис, и при малейшем его нарушении произойдет ошибка
Если программа так и не заработала, обращайтесь к разделу «Решение проблем с установкой» на с ͆ Python в системе Windows
is далеко не всегда включает поддержку th Скорее всего, вам придется
загрузить и установить th, а затем загрузить и установить текстовый редактор Установка Python
Для начала проверьте, установлена ли поддержка th в вашей системе
Откройте окно командной строки введите command в меню Пуск или щелкните
на рабочем столе с нажатой клавишей hit и выбери те команду Open command
window here Введите в окне командной строки команду python в нижнем регистре
Рис. 1.2. Не забудьте установить флажок Add Python to PATH

Подготовка среды программирования 25
Если на экране появится приглашение >>>, значит, в системе установлена под
держка th Впрочем, скорее всего вместо приглашения появится сообщение об ошибке, в котором говорится, что команда python не опознана системой
В таком случае загрузите программу установки th для is Откройте
страницу . Вы увидите на ней две кнопки для загрузки
th и для загрузки th Ć Щелкните на кнопке th , которая запускает
автоматическую загрузку правильного установочного пакета для вашей системы По
сле того как загрузка файла будет завершена, запустите программу установки Не за
будьте установить флажок Add Python to PAT H — это упростит правильную настройку
системы На рис Æ изображено окно мастера установки с активным флажком Запуск терминального сеанса
Настроить текстовый редактор будет несложно, если вы сначала подготовите си
стему к запуску th в терминальном сеансе Откройте окно командной строки
и введите команду python в нижнем регистре Если на экране появится приглашение
th ( >>>), значит, система is обнаружила установленную версию th
C:\> python
Python 3.5.0 (v3.5.0:374f501f4567, Sep 13 2015, 22:15:05) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.>>>
Если команда сработала, переходите к следующему разделу «Запуск th в тер
минальном сеансе» Однако вывод может выглядеть и так
C:\> python
'python' is not recognized as an internal or external command, operable program or batch file.
В этом случае необходимо сообщить is, как найти свежеустановленную
версию th Команда python в вашей системе обычно хранится на диске
запустите Проводник is и откройте диск Найдите папку, имя которой
начинается с Python, откройте ее и найдите файл python (в нижнем регистре) На
пример, на моем компьютере существует папка Python35, в которой находится
файл с именем python, поэтому путь к команде python в вашей системе имеет вид
C:\Python35\python Если найти файл не удалось, введите строку python в поле поиска
в Проводнике is — система поиска покажет, где именно хранится команда
python в вашей системе
Когда вы решите, что знаете путь к команде, проверьте его введите этот путь в тер
минальном окне Откройте окно командной строки и введите только что найденный полный путь
C:\> C:\Python35\python
Python 3.5.0 (v3.5.0:374f501f4567, Sep 13 2015, 22:15:05) [MSC

26 Глава 1 • Начало работы
v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.>>>
Если команда успешно работает, то вы знаете, как запустить th в вашей си стеме Запуск Python в терминальном сеансе
Введите в терминальном сеансе следующую строку и убедитесь в том, что на экране появился вывод   
>>> print("Hello Python world!")
Hello Python interpreter! >>>
Каждый раз, когда вы захотите выполнить фрагмент кода th, откройте окно
командной строки и запустите терминальный сеанс th Чтобы закрыть терми
нальный сеанс, нажмите tr или введите команду exit()
Установка текстового редактора
ea — простой и удобный текстовый редактор он легко устанавливается, по
зволяет запускать практически любые программы прямо из редактора (вместо
терминала) и использует цветовое выделение синтаксиса, а код выполняется
в терминальном окне В приложении Б приведена информация о других текстовых
редакторах, но я рекомендую использовать ea, если только у вас нет веских причин для работы в другом редакторе
Программу установки ea для is можно загрузить по адресу . Щелкните в строке Releases меню Download и найдите пакет
geany-1�25_setup�exe (или чтонибудь в этом роде) Запустите программу и под
твердите все значения по умолчанию
Чтобы запустить свою первую программу, откройте ea нажмите клавишу
is и найдите ea в вашей системе Создайте ярлык, перетащив значок
на панель задач или рабочий стол Создайте папку для своих проектов и при
свойте ей имя python_work (В именах файлов и папок лучше использовать буквы
нижнего регистра и символы подчеркивания, потому что это соответствует согла
шениям об именах th) Вернитесь к ea и сохраните пустой файл th ( File Save As ) с именем hello_world�py в папке python_work Расширение �py сообщает
ea, что файл содержит программу th Оно также подсказывает ea, как следует запускать программу и как правильно выделить элементы синтаксиса После того как файл будет сохранен, введите следующую строку
print("Hello Python world!")
Если команда python успешно сработала в вашей системе, то дополнительная на
стройка ea не нужна пропустите следующий раздел и переходите к разделу
«Запуск программы e r» на с ͆ Если для запуска интерпретатора th

Подготовка среды программирования 27
пришлось вводить полный путь вида C:\Python35\python, выполните инструкции по
настройке ea для вашей системы, приведенные в следующем разделе
Настройка Geany
Чтобы настроить ea для работы с th, откройте окно BuildSet Build
Commands В окне приведены команды Compile и Execute , рядом с каждой из которых
располагается команда Команды Compile и Execute начинаются с команды python,
записанной символами нижнего регистра, но ea не знает, где в вашей системе
находится исполняемый файл python К команде нужно добавить путь, который вы
ввели в окне командной строки
Добавьте в начало команд Compile и Execute диск и путь к папке, в которой находится
файл Команда Compile должна выглядеть примерно так
C:\Python35\python -m py_compile "%f"
Возможно, в вашей системе путь будет выглядеть немного иначе проследите за правильностью регистра символов и расстановки пробелов
Рис. 1.3. Настройка Geany для использования Python 3 в Windows
Команда Execute должна выглядеть примерно так
C:\Python35\python "%f"

28 Глава 1 • Начало работы
И снова внимательно проверьте пробелы и регистр символов На рис Æ показано, как эти команды должны выглядеть в меню конфигурации ea Завершив настройку команд, нажмите кнопку OK
Запуск программы Hello World
Все должно быть готово для успешного выполнения программы Запустите про грамму hello_world�py выберите команду меню BuildExecute , щелкните на кнопке
Execute (с шестеренками) или нажмите клавишу ʆ На экране появляется терми
нальное окно со следующим выводом
Hello Python world! ------------------ (program exited with code: 0) Press return to continue
Если вы не увидели это сообщение, проверьте каждый символ во введенной строке
Может, вы случайно набрали print с прописной буквы Пропустили одну или обе
кавычки или круглые скобки В языках программирования используется предель
но конкретный синтаксис, и при малейшем его нарушении произойдет ошибка
Если программа так и не заработала, возможно, следующий раздел поможет вам в этом Решение проблем с установкой
Хочется надеяться, что вы успешно настроили среду разработки на своем компью
тере Но если вам так и не удалось запустить программу hello_world�py, возможно,
вам помогут следующие полезные советы

‰ Если программа содержит серьезную ошибку, th выводит данные
трас
сировки th анализирует содержимое файла и пытается составить отчет
о проблеме Возможно, трассировка подскажет, что именно мешает выполнению программы

‰ Отойдите от компьютера, отдохните и попробуйте снова Помните, что син

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

‰ Начните заново Вероятно, ничего переустанавливать не придется, но хотя бы попробуйте удалить файл
hello_world�py и создать его «с нуля»

‰ Попросите когонибудь повторить действия, описанные в этой главе, на вашем
(или на другом) компьютере Внимательно понаблюдайте за происходящим Возможно, вы упустили какуюнибудь мелочь, которую заметят другие

‰ Найдите специалиста, хорошо знающего th, и попросите его помочь
Вполне может оказаться, что такой специалист есть среди ваших знакомых

‰ Инструкции по настройке среды программирования, приведенные в этой главе,
также доступны по адресу
.. Воз
можно, сетевая версия будет для вас более удобной

Запуск программ Python в терминале 29

‰ Обратитесь за помощью в Интернет В приложении В перечислены некоторые
ресурсы (форумы, чаты и т д), где вы сможете проконсультироваться у людей, уже сталкивавшихся с вашей проблемой
Не стесняйтесь обращаться к опытным программистам Любой программист
в какойто момент своей жизни заходил в тупик многие программисты охотно по
могут вам правильно настроить вашу систему Если вы сможете четко объяснить,
что вы хотите сделать, что уже пытались и какие результаты получили, скорее
всего, ктонибудь вам поможет Как упоминалось во введении, сообщество th доброжелательно относится к новичкам
th должен нормально работать на любом современном компьютере, и если
у вас все же возникли проблемы — обращайтесь за помощью На первых порах
проблемы могут быть весьма неприятными, но с ними стоит разобраться Когда
программа hello_world�py заработает, вы сможете приступить к изучению th,
а ваша работа станет намного более интересной и принесет больше удовольствия Запуск программ Python в терминале
Большинство программ, написанных вами в текстовом редакторе, будут запускать
ся прямо из редактора Тем не менее иногда бывает полезно запускать программы
из терминала — например, если вы хотите просто выполнить готовую программу, не открывая ее для редактирования
Это можно сделать в любой системе с установленной поддержкой th необ
ходимо лишь знать путь к каталогу, в котором хранится файл программы Приве
денные ниже примеры предполагают, что вы сохранили файл hello_world�py в папке
python_work на рабочем столе
В Linux и OS X
Запуск программы th в терминальном сеансе в системах iԡ и осу
ществляется одинаково Команда cd (hae irectr) используется для пере
мещения по файловой системе в терминальном сеансе Команда ls (it) выводит
список всех нескрытых файлов в текущем каталоге Откройте новое терминальное окно и введите следующие команды для запуска программы hello_world�py
 ~$ cd Desktop/python_work/
 ~/Desktop/python_work$ ls
hello_world.py
 ~/Desktop/python_work$ python hello_world.py
Hello Python world!
Команда cd используется для перехода к папке python_work, находящейся
в папке Desktop  Затем команда ls проверяет, что файл hello_world�py действи
тельно находится в этой папке  Далее файл запускается командой python
hello_world�py 
Как видите, все просто По сути вы просто используете команду python (или
python3 ) для запуска программ th

30 Глава 1 • Начало работы
В Windows Команда cd (hae irectr) используется для перемещения по файловой систе
ме в окне командной строки Команда dir (¸ectr) выводит список всех файлов
в текущем каталоге
Откройте новое терминальное окно и введите следующие команды для запуска программы hello_world�py
 C:\> cd Desktop\python_work
 C:\Desktop\python_work> dir
hello_world.py
 C:\Desktop\python_work> python hello_world.py
Hello Python world!
Команда cd используется для перехода к папке python_work, находящейся в папке
Desktop  Затем команда dir проверяет, что файл hello_world�py действительно на
ходится в этой папке  Далее файл запускается командой python hello_world�py 
Если вы еще не настроили свою систему для использования пр остой команды
python , возможно, вам придется использовать более длинную версию этой команды
C:\$ cd Desktop\python_work
C:\Desktop\python_work$ dir
hello_world.py C:\Desktop\python_work$ C:\Python35\python hello_world.py
Hello Python world!
В основном ваши программы будут нормально запускать ся прямо из редактора
Со временем ваша работа станет более сложной, и может оказаться, что какието программы придется запускать в режиме терминала
УПРАЖНЕНИЯ
Упражнения этой главы в основном направлены на самостоятельный поиск информации�
Начиная с главы 2, упражнения будут ориентированы на решение задач по изложенному материалу�
1-1� Python�org: изучите домашнюю страницу Python (http://python�org/) и найдите темы,
которые вас заинтересуют� Со временем вы лучше узнаете Python, и другие разделы этого сайта покажутся вам более полезными�
1-2� Опечатки в Hello World: откройте только что созданный файл hello_world�py� Сделайте
где-нибудь намеренную опечатку и снова запустите программу� Удастся ли вам сделать
опечатку, которая приводит к ошибке? Поймете ли вы смысл сообщения об ошибке? Удаст-
ся ли вам сделать опечатку, которая не приводит к ошибке? Как вы думаете, почему на этот
раз выполнение обходится без ошибки?
1-3� Бесконечное мастерство: если бы вы были программистом с неограниченными воз-
можностями, за какой проект вы бы взялись? Вы сейчас учитесь программировать� Если
у вас имеется ясное представление о конечной цели, вы сможете немедленно применить
свои новые навыки на практике; попробуйте набросать общие описания тех программ, над
которыми вам хотелось бы поработать� Заведите «блокнот идей», к которому вы сможете
обращаться каждый раз, когда вы собираетесь начать новый проект� Выделите пару минут и составьте описания трех программ, которые вам хотелось бы создать�

Итоги 31
Итоги
В этой главе вы познакомились с языком th и установили поддержку th
в своей системе, если это было необходимо Также вы установили текстовый
редактор, упрощающий работу над кодом th Вы научились выполнять фраг
менты кода th в терминальном сеансе и запустили свою первую настоящую программу hello_world�py Скорее всего, попутно вы коечто узнали о поиске и ис
правлении ошибок
В следующей главе рассматриваются структуры данных, с которыми вы будете
работать в программах th Кроме того, вы научитесь пользоваться перемен
ными th

2
Переменные и простые типы данных
В этой главе представлены разные виды данных, с которыми вы будете работать
в своих программах th Вы также научитесь хранить данные в переменных и использовать эти переменные в программах
Что происходит при запуске hello_world�py
Давайте повнимательнее разберемся с тем, что же делает th при запуске
hello_world�py Оказывается, даже для такой простой программы th проделы
вает достаточно серьезную работу
hello_world.py print("Hello Python world!")
При выполнении этого кода выводится следующий текст Hello Python world!
Суффикс �py в имени файла hello_world�py указывает, что файл является програм
мой th Редактор запускает файл в интерпретаторе th, который читает
программу и определяет, что означает каждое слово в программе Например, когда
интерпретатор обнаруживает слово print, он выводит на экран текст, заключенный
в скобки
Во время написания программы редактор выделяет цветом разные части про
граммы Например, он понимает, что print является именем функции, и выводит
это слово синим шрифтом С другой стороны, “e th r” не является
кодом th, поэтому этот текст выделяется оранжевым цветом Этот механизм,
называемый цветовым выделением синтаксиса , очень вам пригодится, когда вы
возьметесь за самостоятельное программирование Переменные
Попробуем использовать переменную в программе hello_world�py Добавьте новую
строку в начало файла и измените вторую строку
message = "Hello Python world!" print(message)

Переменные 33
Запустите программу и посмотрите, что получится Программа выводит уже зна комый результат Hello Python world!
В программу добавилась переменная с именем message В каждой переменной
хранится значение, то есть данные, связанные с переменной В данном случае зна
чением является текст “e th r”
Добавление переменной немного усложняет задачу интерпретатора th Во вре
мя обработки первой строки он связывает текст “e th r” с переменной message А когда интерпретатор доберется до второй строки, он выводит на экран
значение, связанное с именем message
Давайте немного расширим эту программу hello_world�py, чтобы она выводила
второе сообщение Добавьте в hello_world�py пустую строку, а после нее еще две
строки кода
message = "Hello Python world!" print(message)
message = "Hello Python Crash Course world!"print(message)
Теперь при выполнении hello_world�py на экране должны появляться две строки
Hello Python world!Hello Python Crash Course world!
Вы можете в любой момент изменить значение переменной в своей программе th всегда отслеживает его текущее состояние Выбор имен и использование переменных
При работе с переменными в языке th необходимо соблюдать некоторые
правила и рекомендации Нарушение правил приведет к ошибке рекомендации
всего лишь помогают писать более понятный и удобочитаемый код Работая с пере
менными, помните о следующем

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

вания Они могут начинаться с буквы или символа подчеркивания, но не с циф
ры Например, переменной можно присвоить имя message_1, но не 1_message

‰ Пробелы в именах переменных запрещены, а для разделения слов в именах
переменных используются символы подчеркивания Например, имя
greeting_
message допустимо, а имя greeting message вызовет ошибку

‰ Не используйте имена функций и ключевые слова th в качестве имен
переменных иначе говоря, не используйте слова, которые зарезервированы
в th для конкретной цели, например слово
print (см раздел «Ключевые
слова и встроенные функции th», с )

‰ Имена переменных должны быть короткими, но содержательными Напри

мер, имя name лучше n, имя student_name лучше s_n, а имя name_length лучше
length_of_persons_name

34 Глава 2 • Переменные и простые типы данных

‰ Будьте внимательны при использовании строчной буквы
l и прописной буквы O,
потому что они похожи на цифры и Ά
Вероятно, вы не сразу научитесь создавать хорошие имена переменных, особенно
когда ваши программы станут более сложными и интересными Но когда вы нач
нете писать свои программы и читать код, написанный другими разработчиками, ваши имена переменных станут более содержательными ПРИМЕЧАНИЕ
Пока ограничьтесь именами переменных, записанными в нижнем регистре� Использование симво- лов верхнего регистра не приведет к ошибке, и все же пока лучше обойтись без них� Предотвращение ошибок в именах при использовании переменных
Каждый программист совершает ошибки, а большинство программистов ошибается
ежедневно И хотя даже опытный программист не застрахован от ошибок, он знает,
как эффективно реагировать на них Рассмотрим типичную ошибку, которую вы довольно часто будете совершать на первых порах, и выясним, как ее исправить
Для начала напишем код с намеренно внесенной ошибкой Введите следу
ющий фрагмент (неправильно написанное слово выделено жирным
шрифтом)
message = "Hello Python Crash Course reader!" print( mesage)
Когда в программе происходит ошибка, интерпретатор th всеми силами ста
рается помочь вам в поиске причины Если программа не выполняется нормально,
интерпретатор предоставляет данные трассировки — информацию о том, в каком
месте кода находился интерпретатор при возникновении проблем Ниже приведен
пример трассировки, которую выдает th после случайной опечатки в имени переменной
Traceback (most recent call last):
 File "hello_world.py", line 2, in
 print(mesage)
 NameError: name 'mesage' is not defined
Строка  сообщает, что ошибка произошла в строке файла hello_world�py Ин
терпретатор выводит номер строки, чтобы вам было проще найти ошибку , и со
общает тип обнаруженной ошибки  В данном случае была обнаружена ошибка
в имени переменная с указанным именем ( mesage) не определена Другими слова
ми, th не распознает имя переменной Обычно такие ошибки возникают в том
случае, если вы забыли присвоить значение переменной перед ее использованием или ошиблись при вводе имени
Конечно, в данном примере в имени переменной во второй строке пропущена бук
ва s Интерпретатор th не проверяет код на наличие опечаток, но следит за тем,

Строки 35
чтобы имена переменных записывались одинаково Например, вот что происходит, если имя message будет неправильно записано еще в одном месте кода
mesage = "Hello Python Crash Course reader!" print(mesage)
На этот раз программа выполняется успешно Hello Python Crash Course reader!
Компьютеры не отличаются гибкостью, но орфография их совершенно не волнует
Как следствие, вам не нужно следить за тем, чтобы в именах переменных идеально соблюдались правила орфографии английского языка
Многие ошибки программирования сводятся к простым опечаткам – случайной за
мене одного символа в одной строке программы Если вы потратили много времени
на поиск одной из таких ошибок, знайте, что вы не одиноки Многие опытные и та
лантливые программисты тратят долгие часы на поиск подобных мелких ошибок
Нечто подобное будет часто происходить в ходе вашей работы – поэтому просто посмейтесь и идите дальше ПРИМЕЧАНИЕ
Как лучше всего освоить новые концепции программирования? Попытайтесь использовать их
в своей программе� Если в ходе работы над упражнением вы зайдете в тупик, попробуйте на какое-
то время заняться чем-нибудь другим� Если это не поможет, перечитайте соответствующую часть этой главы� Если и это не помогло, обращайтесь к рекомендациям из приложения В�
УПРАЖНЕНИЯ
Напишите отдельную программу для выполнения каждого из следующих упражнений� Со-
храните каждую программу в файле, имя которого подчиняется стандартным правилам
Python по использованию строчных букв и символов подчеркивания – например, simple_
message�py и simple_messages�py�
2-1� Простое сообщение: сохраните текстовое сообщение в переменной и выведите его на экран�
2-2� Простые сообщения: сохраните сообщение в переменной и выведите это сообщение� Затем замените значение переменной другим сообщением и выведите новое сообщение�
Строки
Так как многие программы определяют и собирают некие данные, а затем делают
с ними чтото полезное, желательно выделить основные разновидности данных
Начнем со строковых данных На первый взгляд строки достаточно просты, но с ними можно работать многими разными способами
Строка представляет собой простую последовательность символов Любая по
следовательность символов, заключенная в кавычки, в th считается строкой
при этом строки могут быть заключены как в одиночные, так и в двойные кавычки
"This is a string." 'This is also a string.'

36 Глава 2 • Переменные и простые типы данных
Это правило позволяет использовать внутренние кавычки и апострофы в строках 'I told my friend, "Python is my favorite language!"' "The language 'Python' is named after Monty Python, not the snake.""One of Python's strengths is its diverse and supportive community."
Рассмотрим некоторые типичные операции со строками Изменение регистра символов в строках
Одна из простейших операций, выполняемых со строками, – изменение регистра
символов Взгляните на следующий фрагмент кода и попробуйте определить, что в нем происходит
name.py name = "ada lovelace" print(name.title())
Сохраните файл с именем name�py и запустите его Вывод программы должен вы
глядеть так
Ada Lovelace
В этом примере в переменной name сохраняется строка, состоящая из букв нижнего
регистра "ada lovelace" За именем переменной в команде print() следует вызов
метода title() Метод представляет собой действие, которое th выполняет
с данными Точка ( .) после name в конструкции name.title() приказывает th
применить метод title() к переменной name За именем метода всегда следует пара
круглых скобок, потому что методам для выполнения их работы часто требуется
дополнительная информация Эта информация указывается в скобках Функции
title() дополнительная информация не нужна, поэтому в круглых скобках ничего
нет
Метод title() преобразует первый символ каждого слова в строке к верхнему
регистру, тогда как все остальные символы выводятся в нижнем регистре Напри
мер, данная возможность может быть полезна, если в вашей программе входные значения Ada, ADA и ada должны рассматриваться как одно и то же имя, и все они
должны отображаться в виде Ada
Для работы с регистром также существуют другие полезные методы Например, все символы строки можно преобразовать к верхнему или нижнему регистру
name = "Ada Lovelace" print(name.upper())print(name.lower())
Программа выводит следующий результат ADA LOVELACEada lovelace
Метод lower() особенно полезен для хранения данных Нередко программист
не может рассчитывать на то, что пользователи введут все данные с точным со

Строки 37
блюдением регистра, поэтому строки перед сохранением преобразуются к нижнему
регистру Затем, когда потребуется вывести информацию, используется регистр, наиболее подходящий для каждой строки Конкатенация
Также часто возникает необходимость в объединении строк Представьте, что имя
и фамилия хранятся в разных переменных и вы хотите объединить их для вывода полного имени first_name = "ada" last_name = "lovelace"
 full_name = first_name + " " + last_name
print(full_name)
Для объединения строк в th используется знак «плюс» ( +) В приве
денном примере полное имя строится объединением
first_name , пробел
и
last_name 
ada lovelace
Такой способ объединения строк называется конкатенацией Вы можете исполь
зовать конкатенацию для построения сложных сообщений с информацией, храня
щейся в переменных Рассмотрим пример
first_name = "ada" last_name = "lovelace"full_name = first_name + " " + last_name
 print("Hello, " + full_name.title() + "!")
Полное имя используется в точке  для вывода приветственного сообщения,
а метод title() обеспечивает правильное форматирование имени Этот фрагмент
возвращает простое, хорошо отформатированное сообщение
Hello, Ada Lovelace!
Конкатенацией также можно воспользоваться для построения сообщения, которое затем сохраняется в переменной
first_name = "ada" last_name = "lovelace"full_name = first_name + " " + last_name
 message = "Hello, " + full_name.title() + "!"
 print(message)
Этот код также выводит сообщение “e, a eace”, но сохранение тек
ста сообщения в переменной  существенно упрощает завершающую команду
печати 

38 Глава 2 • Переменные и простые типы данных
Табуляции и разрывы строк
В программировании термином « пропуск» (hitesace) называются такие непе
чатаемые символы, как пробелы, табуляции и символы конца строки Пропуски структурируют текст, чтобы пользователю было удобнее читать его
Для включения в текст позиции табуляции используется комбинация символов \t , как в точке 
>>> print("Python")
Python
 >>> print("\tPython")
Python
Разрывы строк добавляются с помощью комбинации символов \n
>>> print("Languages:\nPython\nC\nJavaScript")
Languages: PythonCJavaScript
Табуляции и разрывы строк могут сочетаться в тексте Скажем, последователь
ность "\n\t" приказывает th начать текст с новой строки, в начале которой
располагается табуляция
Следующий пример демонстрирует вывод одного сообщения с разбиением
на четыре строки
>>> print("Languages:\n\tPython\n\tC\n\tJavaScript")
Languages: Python C JavaScript
Разрывы строк и табуляции часто встречаются в двух следующих главах, когда наши программы начнут выводить относительно длинный текст Удаление пропусков
Лишние пропуски могут вызвать путаницу в программах Для программиста стро
ки 'python' и 'python ' внешне неотличимы, но для программы это совершенно
разные строки th видит лишний пробел в 'python ' и считает, что он действи
тельно важен — до тех пор, пока вы не сообщите о противоположном
Обращайте внимание на пропуски, потому что в программах часто приходится
сравнивать строки, чтобы проверить на совпадение их содержимое Типичный при
мер — проверка имен пользователей при входе на сайт Л ишние пропуски могут
создавать путаницу и в более простых ситуациях К счастью, th позволяет легко удалить лишние пропуски из данных, введенных пользователем
th может искать лишние пропуски у левого и правого края строки Чтобы
убедиться в том, что у правого края (в конце) строки нет пропусков, вызовите метод rstrip()

Строки 39
 >>> favorite_language = 'python '
 >>> favorite_language
'python '
 >>> favorite_language.rstrip()
'python'
 >>> favorite_language
'python '
Значение, хранящееся в переменной favorite_language в точке , содержит лиш
ние пропуски в конце строки Когда вы приказываете th вывести это значе
ние в терминальном сеансе, вы видите пробел в конце значения  Когда метод
rstrip() работает с переменной favorite_language в точке , этот лишний пробел
удаляется Впрочем, удаление лишь временное если снова запросить значение
favorite_language , мы видим, что строка не отличается от исходной, включая
лишний пропуск 
Чтобы навсегда исключить пропуск из строки, следует записать усеченное значение обратно в переменную
>>> favorite_language = 'python '
 >>> favorite_language = favorite_language.rstrip()
>>> favorite_language
'python'
Сначала пропуски удаляются в конце строки, а потом значение записывается в ис
ходную переменную  Операция изменения значения переменной с последующим
его сохранением в исходной переменной часто выполняется в программировании
Так значение переменной может изменяться в ходе выполнения программы или в ответ на действия пользователя
Пропуски также можно удалить у левого края (в начале) строки при помощи ме тода lstrip() , а метод strip() удаляет пропуски с обоих концов
 >>> favorite_language = ' python '
 >>> favorite_language.rstrip()
' python'
 >>> favorite_language.lstrip()
'python '
 >>> favorite_language.strip()
'python'
В этом примере исходное значение содержит пропуски в начале и в конце  Затем
пропуски удаляются у правого края , у левого края  и с обоих концов строки 
Поэкспериментируйте с функциями удаления пропусков, это поможет вам осво
иться с работой со строками На практике эти функции чаще всего применяются для «очистки» пользовательского ввода перед его сохранением в программе Предотвращение синтаксических ошибок в строках
Синтаксические ошибки встречаются в программах более или менее регулярно
Синтаксическая ошибка происходит тогда, когда th не распознает часть вашей
программы как действительный код th Например, если заключить апостроф

40 Глава 2 • Переменные и простые типы данных
в одиночные кавычки, случится ошибка Это происходит изза того, что th
интерпретирует все символы от первой одиночной кавычки до апострофа как
строку После этого th пытается интерпретировать остаток текста строки как код th, что порождает ошибки
Разберемся, как же правильно использовать одиночные или двойные кавычки Со
храните следующую программу в файле apostrophe�py и запустите ее
apostrophe.py message = "One of Python's strengths is its diverse community." print(message)
Апостроф находится в строке, заключенной в двойные кавычки, так что у интер
претатора th не возникает проблем с правильной интерпретацией следующей строки One of Python's strengths is its diverse community.
Однако при использовании одиночных кавычек th не сможет определить, где должна заканчиваться строка message = 'One of Python's strengths is its diverse community.'
print(message)
Программа выводит следующий результат File "apostrophe.py", line 1 message = 'One of Python's strengths is its diverse community.' ^ 
SyntaxError: invalid syntax
Из выходных данных видно, что ошибка происходит в позиции  сразу же после
второй одиночной кавычки Эта синтаксическая ошибка указывает, что интерпрета
тор не распознает какуюто конструкцию как действит ельный код th Ошибки
могут возникать по разным причинам я буду выделять наиболее распространенные источники по мере того, как они будут встречаться нам
Синтаксические ошибки будут часто досаждать вам, пока вы учитесь писать пра
вильный код th Кроме того, ошибки этой категории также являются наиболее
расплывчатыми и неконкретными, поэтому их особенно трудно находить и ис
правлять Если вы зайдете в тупик изза особенно коварной ошибки, обращайтесь к рекомендациям в приложении В ПРИМЕЧАНИЕ
Функция цветового выделения синтаксиса ускоряет выявление некоторых синтаксических ошибок
прямо во время написания программы� Если вы увидите, что код Python выделяется как обычный
текст (или обычный текст выделяется как код Python), скорее всего, в вашем файле где-то про-пущена кавычка� Вывод в Python 2 В th команда print имеет немного иной синтаксис

Числа 41
>>> python2.7
>>> print "Hello Python 2.7 world!"
Hello Python 2.7 world!
В th выводимый текст не обязательно заключать в круглые скобки Фор
мально в th print является функцией, поэтому круглые скобки обязательны
Некоторые команды print в th содержат круглые скобки, однако их пове
дение несколько отличается от того, что вы видите в th ҆ Когда вы смотрите
на код, написанный на th , с большой вероятностью в одних командах print
будут присутствовать круглые скобки, а в других круглых скобок не будет
УПРАЖНЕНИЯ
Сохраните код каждого из следующих упражнений в отдельном файле с именем name_
cases�py� Если у вас возникнут проблемы, сделайте перерыв или обратитесь к рекоменда-циям в приложении В�
2-3� Личное сообщение: сохраните имя пользователя в переменной и выведите сообщение,
предназначенное для конкретного человека� Сообщение должно быть простым, например:
“Hello Eric, would you like to learn some Python today?”
2-4� Регистр символов в именах: сохраните имя пользователя в переменной и выведите его
в нижнем регистре, в верхнем регистре и с капитализацией начальных букв каждого слова�
2-5� Знаменитая цитата: найдите известное высказывание, которое вам понравилось� Вы-
ведите текст цитаты с именем автора� Результат должен выглядеть примерно так (включая кавычки): Albert Einstein once said, "A person who never made a mistake never tried anything new."
2-6� Знаменитая цитата 2: повторите упражнение 2-5, но на этот раз сохраните имя автора
цитаты в переменной famous_person� Затем составьте сообщение и сохраните его в новой переменной с именем message� Выведите свое сообщение�
2-7� Удаление пропусков: сохраните имя пользователя в переменной� Добавьте в начале
и в конце имени несколько пропусков� Проследите за тем, чтобы каждая служебная после-
довательность , “\t” и “\n”, встречалась по крайней мере один раз�
Выведите имя, чтобы были видны пропуски в начале и конце строки� Затем выведите его снова с использованием каждой из функций удаления пропусков: lstrip(), rstrip() и strip()�
Числа
Числа очень часто применяются в программировании для ведения счета в играх,
представления данных в визуализациях, хранения информации в вебприложениях
и т д В th числовые данные делятся на несколько категорий по способу их
использования Для начала посмотрим, как th работает с целыми числами, потому что с ними возникает меньше всего проблем Целые числа
В th с целыми числами можно выполнять операции сложения ( +), вычита
ния ( -), умножения ( *) и деления( /)

42 Глава 2 • Переменные и простые типы данных
>>> 2 + 3
5 >>> 3 - 2
1 >>> 2 * 3
6 >>> 3 / 2
1.5
В терминальном сеансе th просто возвращает результат операции Для пред
ставления операции возведения в степень в th используется сдвоенный знак умножения >>> 3 ** 2
9 >>> 3 ** 3
27>>> 10 ** 6
1000000
В th также существует определенный порядок операций, что позволяет ис
пользовать несколько операций в одном выражении Круглые скобки используются
для изменения порядка операций, чтобы выражение могло вычисляться в нужном порядке Пример
>>> 2 + 3*4
14 >>> (2 + 3) * 4
20
Пробелы в этих примерах не влияют на то, как th вычисляет выражения они просто помогают быстрее найти приоритетные операции при чтении кода Вещественные числа
В th числа, имеющие дробную часть, называются вещественными (или «чис
лами с плавающей точкой») Обычно разработчик может просто пользоваться
дробными значениями, не особенно задумываясь об их поведении Просто введите нужные числа, а th скорее всего сделает именно то, что вы от него хотите
>>> 0.1 + 0.1
0.2 >>> 0.2 + 0.2
0.4 >>> 2 * 0.1
0.2 >>> 2 * 0.2
0.4
Однако в некоторых ситуациях вдруг оказывается, что результат содержит неожи
данно большое количество разрядов в дробной части
>>> 0.2 + 0.1
0.30000000000000004

Числа 43
>>> 3 * 0.1
0.30000000000000004
Нечто подобное может произойти в любом языке для беспокойства нет причин
th пытается подобрать как можно более точное представление результата, что
иногда бывает нелегко изза особенностей внутреннего представления чисел в ком
пьютерах Пока просто не обращайте внимания на «лишние» разряды вы узнаете,
как поступать в подобных ситуациях, когда эта проблема станет актуальной для вас в проектах части † Предотвращение ошибок типов с использованием функции str()
Часто значение переменной должно использоваться вну три сообщения Допустим,
вы хотите поздравить пользователя с днем рождения И вы написали для этого следующий код
birthday.py age = 23 message = "Happy " + age + "rd Birthday!"
print(message)
Казалось бы, программа должна вывести простое приветствие
Но, если запустить ее, появляется сообщение об ошибке
Traceback (most recent call last): File "birthday.py", line 2, in message = "Happy " + age + "rd Birthday!"
 TypeError: Can't convert 'int' object to str implicitly
На этот раз произошла ошибка типа Это означает, что th не понимает, какую
информацию вы используете В данном примере th видит, что в точке  ис
пользуется переменная с целочисленным значением ( int), но не знает, как следует
интерпретировать это значение Дело в том, что переменная может представлять
как число , так и пару отдельных символов и ҆ При таком использовании целых
чисел в строках необходимо явно указать, что целое число должно использоваться
как строка из символов Для этого переменная передается функции str(), преоб
разующей нестроковые значения к строковому виду
age = 23message = "Happy " + str(age) + "rd Birthday!"
print(message)
Теперь th понимает, что вы хотите преобразовать числовое значение
в строку и вывести символы и в составе поздравления Ожидаемый результат выводится без всяких ошибок Happy 23rd Birthday!
В большинстве случаев работа с числами в th проходит достаточно тривиаль
но Если вы получаете неожиданные результаты, проверьте, правильно ли th интерпретирует числовые данные – как числовое значение или как строку

44 Глава 2 • Переменные и простые типы данных
Целые числа в Python 2 При делении целых чисел th возвращает несколько иной результат
>>> python2.7
>>> 3 / 2
1
Вместо Æ th возвращает Æ Результатом деления целых чисел в th
становится целое число с потерей остатка Обратите внимание результат не округляется, просто остаток от деления пропадает
Чтобы избежать этого поведения в th , проследите за тем, чтобы хотя бы
одно из двух чисел было вещественным В этом случае результат также будет вещественным >>> 3 / 2
1 >>> 3.0 / 2
1.5 >>> 3 / 2.0
1.5 >>> 3.0 / 2.0
1.5
Такое поведение при делении часто приводит к недоразумениям, когда люди, при
выкшие работать с th , начинают использовать th , или наоборот Если
вы используете или пишете код, в котором смешиваются целые и вещественные числа, будьте внимательны
УПРАЖНЕНИЯ
2-8� Число 8: напишите операции сложения, вычитания, умножения и деления, результатом
которых является число 8� Не забудьте заключить операции в команды print, чтобы прове-
рить результат� Вы должны написать четыре строки кода, которые выглядят примерно так: print(5 + 3)
Результатом должны быть четыре строки, в каждой из которых выводится число 8�
2-9� Любимое число: сохраните свое любимое число в переменной� Затем при помощи пе- ременной создайте сообщение для вывода этого числа� Выведите это сообщение�
Комментарии
Комментарии чрезвычайно полезны в любом языке программирования До сих
пор ваши программы состояли только из кода th По мере роста объема
и сложности кода в программы следует добавлять комментарии, описывающие
общий подход к решаемой задаче, — своего рода заметки, написанные на по
нятном языке
Как создаются комментарии?
В языке th признаком комментария является символ «решетка» ( #) Интер
претатор th игнорирует все символы, следующие в коде после # до конца
строки Пример

Философия Python 45
comment.py # Say hello to everyone. print("Hello Python people!")
th игнорирует первую строку и выполняет вторую Hello Python people!
Какие комментарии следует писать?
Комментарии пишутся прежде всего для того, чтобы объяснить, что должен делать
ваш код и как он работает В ходе работы над проектом вы понимаете, как работают
все его компоненты Но, если вернуться к проекту спустя некоторое время, скорее
всего, некоторые подробности будут забыты Конечно, всегда можно изучить код
и разобраться в том, как должны работать его части, но хорошие комментарии с до
ступным изложением общих принципов работы кода сэкономят немало времени
Если вы хотите стать профессиональным программистом или участвовать в со
вместной работе с другими программистами, научитесь писать осмысленные
комментарии В наши дни почти все программы разрабатываются коллективно
в группах — либо группами работников одной компании, либо группами энтузиа
стов, совместно работающих над проектом с открытым кодом Опытные програм
мисты ожидают увидеть комментарии в коде, поэтому лучше привыкайте добавлять
содержательные комментарии прямо сейчас Написание простых, лаконичных
комментариев – одна из самых полезных привычек, необходимых начинающему программисту
Принимая решение о том, нужно ли писать комментарий или нет, спросите себя,
пришлось ли вам перебрать несколько вариантов в поисках разумного решения для
некоторой задачи если ответ будет положительным, напишите комментарий по по
воду вашего решения Удалить лишние комментарии позднее намного проще, чем
возвращаться и добавлять комментарии в программу С этого момента я буду ис
пользовать комментарии в примерах для пояснения смысла некоторых частей кода
УПРАЖНЕНИЯ
2-10� Добавление комментариев: выберите две программы из написанных вами и добавьте
в каждую хотя бы один комментарий� Если вы не найдете, что написать в комментариях,
потому что программы были слишком просты, добавьте свое имя и текущую дату в начало кода� Затем добавьте одно предложение с описанием того, что делает программа�
Философия Python
Долгое время язык программирования er был краеугольным камнем интернет
программирования На первых порах функционирование многих интерактивных
сайтов было основано на сценариях er В то время сообщество er руководство
валось девизом «Это можно сделать несколькими способами» Какоето время
разработчикам нравился такой подход, потому что гибкость, присущая языку,
позволяла решать многие задачи разными способами Подобный подход был до

46 Глава 2 • Переменные и простые типы данных
пустим при работе над собственными проектами, но со временем стало ясно, что
чрезмерная гибкость усложняет долгосрочное сопровождение крупных проектов
Было слишком трудно, утомительно и долго разбираться в коде и пытаться понять, что же думал другой разработчик при решении сложной задачи
Опытные программисты th рекомендуют избегать лишних сложностей и при
менять простые решения там, где это возможно Философия сообщества th
выражена в очерке Тима Питерса «he e th» Чтобы просмотреть этот
краткий набор принципов написания хорошего кода t h, достаточно ввести
команду import this в интерпретаторе Я не стану воспроизводить все принципы,
но приведу несколько строк, чтобы вы поняли, почему они важны для вас как для начинающего программиста th
>>> import this
The Zen of Python, by Tim Peters

‰ Красивое лучше, чем уродливое
Программисты th считают, что код может быть красивым и элегантным
В программировании люди занимаются решением задач Программисты всегда
ценили хорошо спроектированные, эффективные и даже красивые решения
Со временем вы больше узнаете о th, начнете писать больше кода, и когда
нибудь ваш коллега посмотрит на экран вашего компьютера и скажет «Ого, какой красивый код»

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

‰ Сложное лучше, чем запутанное
Реальность создает свои сложности иногда простое решение задачи невозможно В таком случае используйте самое простое решение, которое работает

‰ Удобочитаемость имеет значение
Даже если ваш код сложен, он должен нормально читаться Работая над проектом,
требующим написания сложного кода, постарайтесь написать содержательные комментарии для этого кода

‰ Должен существовать один — и желательно только один — очевидный способ сделать это
Если предложить двум программистам th решить одну и ту же задачу, они
должны выработать похожие решения Это не значит, что в программировании
нет места для творчества Наоборот Но б ульшая часть работы программиста за
ключается в применении небольших, стандартных решений для простых ситуаций
в контексте большого, более творческого проекта Внутренняя организация ваших
программ должна выглядеть логично с точки зрения других программистов th

‰ Сейчас лучше, чем никогда
Вы можете потратить весь остаток жизни на изучение всех тонкостей th
и программирования в целом, но тогда вы никогда не закончите ни один проект

Итоги 47
Не пытайтесь написать идеальный код напишите код, который работает, а потом
решите, стоит ли доработать его для текущего проекта или же перейти на чтото другое
Когда вы перейдете к следующей главе и займетесь изучением более сложных
тем, постарайтесь не забывать об этой философии простоты и ясности Опытные
программисты будут с большим уважением относиться к вашему коду, охотнее делиться своим мнением и сотрудничать с вами в интересных проектах
УПРАЖНЕНИЯ
2-11� Дзен Python: введите команду import this в терминальном сеансе Python и просмотри- те другие принципы�
Итоги
В этой главе вы научились работать с переменными Вы узнали, как использовать
содержательные имена переменных и как исправлять ошибки в именах и синтак
сические ошибки в случае их возникновения Вы узнали, что такое строки и как
выводить их в нижнемверхнем регистре и с капитализацией всех слов Мы рас
смотрели способы аккуратного оформления вывода с применением пропусков,
а также удаления лишних пропусков из разных частей строки Вы начали работать
с целыми и вещественными числами и узнали о некоторых неожида нностях, встре
чающихся при работе с числовыми данными Вы научились писать содержательные
комментарии, которые упрощают написание кода для вас и его чтение для других
разработчиков В завершение главы была представлена философия максимальной простоты кода
В главе рассматривается хранение наборов данных в переменных, называемых списками Вы узнаете, как перебрать содержимое списка и обработать хранящуюся
в нем информацию

3 Списки
В этой и следующей главах вы узнаете, что собой представляют списки и как
начать работать с элементами списка Списки позволяют хранить в одном месте
взаимосвязанные данные, сколько бы их ни было — несколько элементов или
несколько миллионов элементов Работа со списками относится к числу самых
выдающихся возможностей th, доступных для начинающего программиста
Операции со списками связывают воедино многие важные концепции в про
граммировании Что такое список?
Список — это набор элементов, следующих в определенном порядке Вы можете
создать список для хранения букв алфавита, цифр от до или имен всех членов
вашей семьи В список можно поместить любую информацию, причем данные
в списке даже не обязаны быть както связаны друг с другом Так как список обыч
но содержит более одного элемента, рекомендуется присваивать спискам имена во множественном числе letters, digits , names и т д
В языке th список обозначается квадратными скобками ( []), а отдельные
элементы списка разделяются запятыми Простой пример списка с названиями моделей велосипедов
bicycles.py bicycles = ['trek', 'cannondale', 'redline', 'specialized'] print(bicycles)
Если вы прикажете th вывести список, то на экране появится перечисление элементов списка в квадратных скобках ['trek', 'cannondale', 'redline', 'specialized']
Конечно, вашим пользователям такое представление не подойдет разберемся, как обратиться к отдельным элементам в списке Обращение к элементам списка
Списки представляют собой упорядоченные наборы данных, поэтому для обраще
ния к любому элементу списка следует сообщить th позицию ( индекс) нужного

Индексы начинаются с 0, а не с 1 49
элемента Чтобы обратиться к элементу в списке, укажите имя списка, за которым следует индекс элемента в квадратных скобках
Например, название первого велосипеда в списке bicycles выводится следующим
образом
bicycles = ['trek', 'cannondale', 'redline', 'specialized']  print(bicycles[0])
Синтаксис обращения к элементу показан в точке  Когда мы запрашиваем один
элемент из списка, th возвращает только этот элемент без квадратных скобок или кавычек trek
Именно такой результат должны увидеть пользователи — чистый, аккуратно отформатированный вывод
Также можно использовать строковые методы из главы с любым элементом
списка Например, элемент 'trek' можно более аккуратно отформатировать
при помощи метода title()
bicycles = ['trek', 'cannondale', 'redline', 'specialized']print(bicycles[0].title())
Этот пример выдает такой же результат, как и предыдущий, только название 'Trek'
выводится с прописной буквы Индексы начинаются с 0, а не с 1
th считает, что первый элемент списка находится в позиции , а не в позиции Æ
Этот принцип встречается в большинстве языков программирования и объясняется
особенностями низкоуровневой реализации операций со списками Если вы полу
чаете неожиданные результаты, определите, не допустили ли вы простую ошибку «смещения на »
Второму элементу списка соответствует индекс Æ В этой простой схеме индекс
любого элемента вычисляется уменьшением на его позиции в списке Напри
мер, чтобы обратиться к четвертому элементу списка, следует запросить элемент с индексом ҆ В следующем примере выводятся названия велосипедов с индексами и ҽ
bicycles = ['trek', 'cannondale', 'redline', 'specialized']print(bicycles[1]) print(bicycles[3])
При этом выводится второй и четвертый элементы списка cannondalespecialized
В th также существует специальный синтаксис для обращения к последнему
элементу списка Если запросить элемент с индексом –, th всегда возвращает
последний элемент в списке

50 Глава 3 • Списки
bicycles = ['trek', 'cannondale', 'redline', 'specialized']print(bicycles[-1])
Фрагмент вернет значение 'specialized' Этот синтаксис весьма полезен, потому
что при работе со списками часто требуется обратиться к после дним элементам,
не зная точное количество элементов в списке Синта ксис также распространяется
на другие отрицательные значения индексов Индекс – возвращает второй эле
мент от конца списка, индекс – — третий элемент от конца и т д Использование отдельных элементов из списка
Отдельные значения из списка используются так же, как и любые другие пере
менные Например, вы можете воспользоваться конкате нацией для построения
сообщения, содержащего значение из списка
Попробуем извлечь название первого велосипеда из списка и составить сообщение, включающее это значение
bicycles = ['trek', 'cannondale', 'redline', 'specialized']  message = "My first bicycle was a " + bicycles[0].title() + "."
print(message)
В точке  программа строит сообщение, содержащее значение из bicycles[0] ,
и сохраняет его в переменной message Так создается простое предложение с упо
минанием первого велосипеда из списка
My first bicycle was a Trek. УПРАЖНЕНИЯ
Попробуйте написать несколько коротких программ, чтобы получить предварительное
представление о списках Python� Возможно, для упражнений из каждой главы стоит создать отдельную папку, чтобы избежать путаницы�
3-1� Имена: сохраните имена нескольких своих друзей в списке с именем names� Выведите имя каждого друга, обратившись к каждому элементу списка (по одному за раз)�
3-2� Сообщения: начните со списка, использованного в упражнении 3-1, но вместо вывода
имени каждого человека выведите сообщение� Основной текст всех сообщений должен быть одинаковым, но каждое сообщение должно включать имя адресата�
3-3� Собственный список: выберите свой любимый вид транспорта (например, мотоциклы
или машины) и создайте список с примерами� Используйте свой список для вывода утверж- дений об элементах типа: «Я хотел бы купить мотоцикл Honda»�
Изменение, добавление и удаление элементов
Обычно вы будете создавать динамические списки это означает, что во время вы
полнения программы в созданном вами списке будут добавляться и удаляться
элементы Например, вы можете создать игру, в котор ой игрок должен стрелять
по кораблям космических захватчиков Исходный набор кораблей сохраняется
в списке каждый раз, когда вы сбиваете корабль, он удаляется из списка Каждый

Индексы начинаются с 0, а не с 1 51
раз, когда на экране появляется новый враг, он включается в список Длина списка кораблей будет уменьшаться и увеличиваться по ходу игры Изменение элементов в списке
Синтаксис изменения элемента напоминает синтаксис обращения к элементу
списка Чтобы изменить элемент, укажите имя списка и индекс изменяемого эле
мента в квадратных скобках далее задайте новое значение, которое должно быть присвоено элементу
Допустим, имеется список мотоциклов, и первым элементом списка хранится строка 'honda' Как изменить значение первого элемента
motorcycles.py
 motorcycles = ['honda', 'yamaha', 'suzuki']
print(motorcycles)
 motorcycles[0] = 'ducati'
print(motorcycles)
В точке  определяется исходный список, в котором первый элемент содержит
строку 'honda' В точке  значение первого элемента заменяется строкой 'ducati'
Из вывода видно, что первый элемент действительно изменился, а ос тальные эле
менты списка сохранили прежние значения
['honda', 'yamaha', 'suzuki'] ['ducati', 'yamaha', 'suzuki']
Изменить можно значение любого элемента в списке, не только первого Добавление элементов в список
Новые элементы могут добавляться в списки по разным причинам — например,
для появления на экране новых космических кораблей, включения новых данных
в визуализацию или добавления новых зарегистрированных пользователей на по
строенный вами сайт th предоставляет несколько способов добавления новых данных в существующие списки Присоединение элементов в конец списка
Простейший способ добавления новых элементов в список — присоединение эле
мента в конец списка Используя список из предыдущего примера, добавим новый элемент 'ducati'
motorcycles = ['honda', 'yamaha', 'suzuki'] print(motorcycles)
 motorcycles.append('ducati')
print(motorcycles)

52 Глава 3 • Списки
Метод append() в точке  присоединяет строку 'ducati' в конец списка, другие
элементы в списке при этом остаются неизменными
['honda', 'yamaha', 'suzuki'] ['honda', 'yamaha', 'suzuki', 'ducati']
Метод append() упрощает динамическое построение списков Например, вы може
те начать с пустого списка и добавлять в него элементы серией команд append()
В следующем примере в пустой список добавляются элементы 'honda', 'yamaha'
и 'suzuki'
motorcycles = [] motorcycles.append('honda') motorcycles.append('yamaha') motorcycles.append('suzuki') print(motorcycles)
Полученный список выглядит точно так же, как и спис ки из предыдущих примеров
['honda', 'yamaha', 'suzuki']
Такой способ построения списков встречается очень часто, потому что данные,
которые пользователь захочет сохранить в программе, нередко становятся из
вестными только после запуска программы Чтобы пользователь мог управлять
содержимым списка, начните с определения пустого списка, а затем присоединяйте к нему каждое новое значение Вставка элементов в список
Метод insert() позволяет добавить новый элемент в произвольную по зицию спи
ска Для этого следует указать индекс и значение нового элемента
motorcycles = ['honda', 'yamaha', 'suzuki']
 motorcycles.insert(0, 'ducati')
print(motorcycles)
В этом примере в точке  значение 'ducati' вставляется в начало списка Ме
тод insert() выделяет свободное место в позиции и сохраняет в нем значение
'ducati' Все остальные значения списка при этом сдвигаются на одну позицию
вправо
['ducati', 'honda', 'yamaha', 'suzuki']
Удаление элементов из списка
Нередко возникает необходимость в удалении одного или нескольких элементов
из списка Например, когда игрок сбивает корабль пришельца, этот корабль нужно
удалить из списка активных врагов Или когда пользо ватель решает удалить свою
учетную запись в созданном вами вебприложении, этот пользователь должен быть

Индексы начинаются с 0, а не с 1 53
убран из списка активных пользователей Элементы удаляются из списка по по зиции или по значению Удаление элемента с использованием команды del
Если вам известна позиция элемента, который должен быть удален из списка, вос
пользуйтесь командой del
motorcycles = ['honda', 'yamaha', 'suzuki'] print(motorcycles)
 del motorcycles[0]
print(motorcycles)
В точке  вызов del удаляет первый элемент, 'honda', из списка motorcycles
['honda', 'yamaha', 'suzuki']['yamaha', 'suzuki']
Команда del позволяет удалить элемент из любой позиции списка, если вам изве
стен его индекс Например, вот как из списка удаляется второй элемент 'yamaha'
motorcycles = ['honda', 'yamaha', 'suzuki'] print(motorcycles)
del motorcycles[1] print(motorcycles)
Второй элемент исчез из списка ['honda', 'yamaha', 'suzuki']['honda', 'suzuki']
В обоих примерах значение, удаленное из списка после и спользования команды
del , становится недоступным
Удаление элемента с использованием метода pop()
Иногда значение, удаляемое из списка, должно както использоваться Допустим,
вы хотите получить координаты и только что сбитого корабля пришельцев, что
бы изобразить взрыв в этой позиции В вебприложении пользователь, удаленный
из списка активных участников, может быть добавлен в список неактивных и т д
Метод pop() удаляет последний элемент из списка, но позволяет работать с ним
после удаления Удалим мотоцикл из списка  motorcycles = ['honda', 'yamaha', 'suzuki']
print(motorcycles)
 popped_motorcycle = motorcycles.pop()
 print(motorcycles)
 print(popped_motorcycle)
Сначала в точке  определяется и выводится содержимое списка motorcycles
В точке  значение извлекается из списка и сохраняется в переменной с именем

54 Глава 3 • Списки
popped_motorcycle Вывод измененного списка в точке  показывает, что значе
ние было удалено из списка Затем мы выводим извлеченное значение в точке ,
демонстрируя, что удаленное из списка значение остается доступным в программе
Из вывода видно, что значение 'suzuki', удаленное в конце списка, теперь хранится
в переменной popped_motorcycle
['honda', 'yamaha', 'suzuki'] ['honda', 'yamaha']suzuki
Для чего может понадобиться метод pop() Представьте, что мотоциклы в списке
хранятся в хронологическом порядке в соответствии с датой их покупки В таком
случае команда pop() может использоваться для вывода сообщения о последнем
купленном мотоцикле
motorcycles = ['honda', 'yamaha', 'suzuki'] last_owned = motorcycles.pop() print("The last motorcycle I owned was a " + last_owned.title() + ".")
Программа выводит простое сообщение The last motorcycle I owned was a Suzuki.
Извлечение элементов из произвольной позиции списка Вызов pop() может использоваться для удаления элемента в произвольной позиции
списка для этого следует указать индекс удаляемого элемента в круглых скобках
motorcycles = ['honda', 'yamaha', 'suzuki']
 first_owned = motorcycles.pop(0)
 print('The first motorcycle I owned was a ' + first_owned.title() +
'.')
Сначала первый элемент извлекается из списка в точке , а затем в точке  вы
водится сообщение об этом мотоцикле Программа выводит простое сообщение о первом мотоцикле
The first motorcycle I owned was a Honda.
Помните, что после каждого вызова pop() элемент, с которым вы работаете, уже
не находится в списке
Если вы не уверены в том, какой из двух способов выбрать — команду del или ме
тод pop() , — то простое правило поможет вам определиться Если вы собираетесь
просто удалить элемент из списка, никак не используя его, выбирайте команду del
если же вы намерены использовать элемент после удаления из списка, выбирайте метод pop()
Удаление элементов по значению
Иногда позиция удаляемого элемента неизвестна Если вы знаете только значение элемента, используйте метод remove()

Индексы начинаются с 0, а не с 1 55
Допустим, из списка нужно удалить значение 'ducati'
motorcycles = ['honda', 'yamaha', 'suzuki', 'ducati'] print(motorcycles)
 motorcycles.remove('ducati')
print(motorcycles)
Код в точке  приказывает th определить, в какой позиции списка находится
значение 'ducati', и удалить этот элемент
['honda', 'yamaha', 'suzuki', 'ducati'] ['honda', 'yamaha', 'suzuki']
Метод remove() также может использоваться для работы со значением, которое
удаляется из списка Следующая программа удаляет значение 'ducati' и выводит
причину удаления 
motorcycles = ['honda', 'yamaha', 'suzuki', 'ducati'] print(motorcycles)
 too_expensive = 'ducati'
 motorcycles.remove(too_expensive)
print(motorcycles)
 print("\nA " + too_expensive.title() + " is too expensive for me.")
После определения списка в точке  значение 'ducati' сохраняется в перемен
ной с именем too_expensive в точке  Затем эта переменная сообщает th,
какое значение должно быть удалено из списка  В точке  значение 'ducati'
было удалено из списка, но продолжает храниться в переменной too_expensive,
что позволяет вывести сообщение с причиной удаления 'ducati' из списка мо
тоциклов
['honda', 'yamaha', 'suzuki', 'ducati'] ['honda', 'yamaha', 'suzuki'] A Ducati is too expensive for me.
ПРИМЕЧАНИЕ
Метод remove() удаляет только первое вхождение заданного значения� Если существует вероят-
ность того, что значение встречается в списке более одного раза, используйте цикл для опреде-
ления того, были ли удалены все вхождения данного значения� О том, как это делать, рассказано
в главе 7�
УПРАЖНЕНИЯ
Следующие упражнения немного сложнее упражнений из главы 2, но они предоставляют возможность попрактиковаться в выполнении всех описанных операций со списками�
3-4� Список гостей: если бы вы могли пригласить кого угодно (из живых или умерших)
на обед, то кого бы вы пригласили? Создайте список, включающий минимум трех людей,
которых вам хотелось бы пригласить на обед� Затем используйте этот список для вывода пригласительного сообщения каждому участнику�

56 Глава 3 • Списки
3-5� Изменение списка гостей: вы только что узнали, что один из гостей прийти не сможет,
поэтому вам придется разослать новые приглашения� Отсутствующего гостя нужно заме-нить кем-то другим�
• Начните с программы из упражнения 3-4� Добавьте в конец программы команду print для вывода имени гостя, который прийти не сможет�
• Измените список и замените имя гостя, который прийти не сможет, именем нового приглашенного�
• Выведите новый набор сообщений с приглашениями – по одному для каждого участ- ника, входящего в список�
3-6� Больше гостей: вы решили купить обеденный стол большего размера� Дополнительные места позволяют пригласить на обед еще трех гостей� • Начните с программы из упражнения 3-4 или 3-5� Добавьте в конец программы коман- ду print, которая выводит сообщение о расширении списка гостей�
• Добавьте вызов insert() для добавления одного гостя в начало списка�
• Добавьте вызов insert() для добавления одного гостя в середину списка�
• Добавьте вызов append() для добавления одного гостя в конец списка�
• Выведите новый набор сообщений с приглашениями – по одному для каждого участ- ника, входящего в список�
3-7� Сокращение списка гостей: только что выяснилось, что новый обеденный стол привез- ти вовремя не успеют, и места хватит только для двух гостей� • Начните с программы из упражнения 3-6� Добавьте команду для вывода сообщения о том, что на обед приглашаются всего два гостя�
• Используйте метод pop() для последовательного удаления гостей из списка до тех пор, пока в списке не останутся только два человека� Каждый раз, когда из списка
удаляется очередное имя, выведите для этого человека сообщение о том, что вы со-жалеете об отмене приглашения�
• Выведите сообщение для каждого из двух человек, остающихся в списке� Сообщение должно подтверждать, что более раннее приглашение остается в силе�
• Используйте команду del для удаления двух последних имен, чтобы список остался пустым� Выведите список, чтобы убедиться в том, что в конце работы программы список действительно не содержит ни одного элемента�
Упорядочение списка
Нередко список создается в непредсказуемом порядке, потому что порядок полу
чения данных от пользователя не всегда находится под вашим контролем И хотя
во многих случаях такое положение дел неизбежно, часто требуется вывести
информацию в определенном порядке В одних случаях требуется сохранить
исходный порядок элементов в списке, в других исходный порядок должен быть
изменен th предоставляет в распоряжение программиста несколько разных способов упорядочения списка в зависимости от ситуации Постоянная сортировка списка методом sort() Метод sort() позволяет относительно легко отсортировать список Предположим,
имеется список машин, и вы хотите переупорядочить эти элементы по алфавиту

Упорядочение списка 57
Чтобы упростить задачу, предположим, что все значения в списке состоят из сим волов нижнего регистра cars.py cars = ['bmw', 'audi', 'toyota', 'subaru']
 cars.sort()
print(cars)
Метод sort() в точке  осуществляет постоянное изменение порядка элементов
в списке Названия машин располагаются в алфавитном порядке, и вернуться к ис
ходному порядку уже не удастся
['audi', 'bmw', 'subaru', 'toyota']
Список также можно отсортировать в обратном алфавитном порядке для этого
методу sort() следует передать аргумент reverse=True В следующем примере
список сортируется в порядке, обратном алфавитному
cars = ['bmw', 'audi', 'toyota', 'subaru']cars.sort(reverse=True) print(cars)
И снова порядок элементов изменяется на постоянной основе ['toyota', 'subaru', 'bmw', 'audi']
Временная сортировка списка функцией sorted()
Чтобы сохранить исходный порядок элементов списка, но временно представить их
в отсортированном порядке, можно воспользоваться функцией sorted() Функция
sorted() позволяет представить список в определенном порядке, но не изменяет
фактического порядка элементов в списке Попробуем применить эту функцию к списку машин
cars = ['bmw', 'audi', 'toyota', 'subaru']
 print("Here is the original list:")
print(cars)
 print("\nHere is the sorted list:")
print(sorted(cars))
 print("\nHere is the original list again:")
print(cars)
Сначала список выводится в исходном порядке , а затем в алфавитном  После
того как список будет выведен в новом порядке, в точке , мы убеждаемся в том,
что список все еще хранится в исходном порядке
Here is the original list: ['bmw', 'audi', 'toyota', 'subaru'] Here is the sorted list:

58 Глава 3 • Списки
['audi', 'bmw', 'subaru', 'toyota']
 Here is the original list again:
['bmw', 'audi', 'toyota', 'subaru']
Обратите внимание после вызова функции sorted() список продолжает хранить
ся в исходном порядке  Функции sorted() также можно передать аргумент
reverse=True , чтобы список был представлен в порядке, обратном алфавитному
ПРИМЕЧАНИЕ
Если не все значения записаны в нижнем регистре, алфавитная сортировка списка немного услож-
няется� При определении порядка сортировки появляются разные способы интерпретации пропис-
ных букв, и точное определение порядка уже не столь важно (во всяком случае, чтобы отвлекаться
на него сейчас)� Впрочем, большинство способов сортировки напрямую следует из того, о чем вы узнали в этом разделе� Вывод списка в обратном порядке
Чтобы переставить элементы списка в обратном порядке, используйте метод reverse() Скажем, если список машин первоначально хранился в хронологиче
ском порядке даты приобретения, элементы можно легко переупорядочить в об
ратном хронологическом порядке
cars = ['bmw', 'audi', 'toyota', 'subaru']print(cars) cars.reverse() print(cars)
Обратите внимание метод reverse() не сортирует элементы в обратном алфавит
ном порядке, а просто переходит к обратному порядку списка
['bmw', 'audi', 'toyota', 'subaru'] ['subaru', 'toyota', 'audi', 'bmw']
Метод reverse() осуществляет постоянное изменение порядка элементов, но вы
можете легко вернуться к исходному порядку, снова применив reverse() к об
ратному списку Определение длины списка
Вы можете быстро определить длину списка с помощью функции len() Список
в нашем примере состоит из четырех элементов, поэтому его длина равна ƽ
>>> cars = ['bmw', 'audi', 'toyota', 'subaru']
>>> len(cars)
4
Метод len() может пригодиться для определения количества пришельцев, кото
рых необходимо сбить в игре объема данных, которыми необходимо управлять
в визуализации количества зарегистрированных пользователей на вебсайте и т д

Ошибки индексирования при работе со списками 59
ПРИМЕЧАНИЕ
Python подсчитывает элементы списка, начиная с 1, поэтому при определении длины списка оши- бок «смещения на 1» уже быть не должно�
УПРАЖНЕНИЯ 3-8� Повидать мир: вспомните хотя бы пять стран, в которых вам хотелось бы побывать�• Сохраните названия стран в списке� Проследите за тем, чтобы список не хранился в алфавитном порядке�
• Выведите список в исходном порядке� Не беспокойтесь об оформлении списка, просто выведите его как обычный список Python�
• Используйте функцию sorted() для вывода списка в алфавитном порядке без измене- ния списка�
• Снова выведите список, чтобы показать, что он по-прежнему хранится в исходном порядке�
• Используйте функцию sorted() для вывода списка в обратном алфавитном порядке без изменения порядка исходного списка�
• Снова выведите список, чтобы показать, что исходный порядок не изменился�
• Измените порядок элементов вызовом reverse()� Выведите список, чтобы показать, что элементы следуют в другом порядке�
• Измените порядок элементов повторным вызовом reverse()� Выведите список, чтобы показать, что список вернулся к исходному порядку�
• Отсортируйте список в алфавитном порядке вызовом sort()� Выведите список, чтобы показать, что элементы следуют в другом порядке�
• Вызовите sort() для перестановки элементов списка в обратном алфавитном порядке� Выведите список, чтобы показать, что порядок элементов изменился�
3-9� Количество гостей: в одной из программ из упражнений с 3-4 по 3-7 используйте len() для вывода сообщения с количеством людей, приглашенных на обед�
3-10� Все функции: придумайте информацию, которую можно было бы хранить в списке�
Например, создайте список гор, рек, стран, городов, языков… словом, чего угодно� Напи-
шите программу, которая создает список элементов, а затем вызывает каждую функцию, упоминавшуюся в этой главе, хотя бы один раз�
Ошибки индексирования при работе со списками
Когда программист только начинает работать со списками, он часто допускает одну
характерную ошибку Допустим, имеется список с тремя элементами, и программа запрашивает четвертый элемент motorcycles = ['honda', 'yamaha', 'suzuki'] print(motorcycles[3])
В этом случае происходит ошибка индексирования
Traceback (most recent call last): File "motorcycles.py", line 3, in print(motorcycles[3])IndexError: list index out of range

60 Глава 3 • Списки
th пытается вернуть элемент с индексом ҆ Однако при поиске по списку
ни один элемент motorcycles не обладает индексом ҆ Изза смещения индексов
на эта ошибка весьма распространена Люди думают, что третьим элементом
является элемент с индексом , потому что они начинают отсчет с Æ Но для
th третьим является элемент с индексом , потому что индексирование начинается с Ά
Ошибка индексирования означает, что th не может понять, какой индекс за
прашивается в программе Если в вашей программе происходит ошибка индексиро
вания, попробуйте уменьшить запрашиваемый индекс на Æ Затем снова запустите программу и проверьте правильность результатов
Помните, что для обращения к последнему элементу в списке использ уется
индекс –Æ Этот способ работает всегда, даже если размер списка изменился
с момента последнего обращения к нему
motorcycles = ['honda', 'yamaha', 'suzuki']print(motorcycles[-1])
Индекс – всегда возвращает последний элемент списка, в данном случае значение 'suzuki'
'suzuki'
Этот синтаксис порождает ошибку только в одном случае — при попытке получить последний элемент пустого списка motorcycles = [] print(motorcycles[-1])
В списке motorcycles нет ни одного элемента, поэтому th снова выдает ошибку
индексирования
Traceback (most recent call last): File "motorcyles.py", line 3, in print(motorcycles[-1]) IndexError: list index out of range
ПРИМЕЧАНИЕ
Если в вашей программе произошла ошибка индексирования и вы не знаете, как с ней справиться,
попробуйте вывести список или хотя бы его длину� Возможно, ваш список выглядит совсем не так,
как вы думаете, особенно если его содержимое динамически определялось программой� Фактиче-
ское состояние списка или точное количество элементов в нем поможет вам выявить логические ошибки такого рода� УПРАЖНЕНИЯ
3-11� Намеренная ошибка: если ни в одной из предшествующих программ вы еще не стал-
кивались с ошибками индексирования, попробуйте создать такую ошибку искусственно�
Измените индекс в одной из программ, чтобы вызвать ошибку индексирования� Не забудьте исправить ошибку перед тем, как закрыть программу�

Итоги 61
Итоги
В этой главе вы узнали, что представляют собой списки и как работать с отдельны
ми элементами в списках Вы научились определять списки, добавлять и удалять
элементы, выполнять сортировку (постоянную или временную для отображения)
Также вы узнали, как определить длину списка и как избежать ошибок индексирования при работе со списком
В главе рассматриваются приемы более эффективной работы со сп исками Пере
бор всех элементов списка всего в нескольких строках кода, даже если список со держит тысячи или миллионы элементов, сокращает объем программы

4 Работа со списками
В главе вы научились создавать простые списки и работать с отдельными элемен
тами списков В этой главе вы узнаете, как перебрать весь список всего в несколь
ких строках кода (независимо от длины списка) Механизм перебора позволяет
выполнить одно действие или набор действий с каждым элементом в списке С его
помощью вы сможете эффективно работать со списками любой длины, даже со
стоящими из тысяч и миллионов элементов Перебор всего списка
Типичная задача из области программирования — перебрать все элементы списка
и выполнить с каждым элементом одну и ту же операцию Например, в компью
терной игре все экранные объекты могут смещаться на одинаковую величину,
или в списке чисел к каждому элементу может применяться одна и та же стат и
стическая операция А может быть, вам нужно вывести все заголовки из списка
статей на сайте В ситуациях, требующих применения одного действия к каждому
элементу списка, можно воспользоваться циклами for
Допустим, имеется список с именами фокусников, и вы хотите вывести каждое
имя из списка Конечно, можно обратиться к каждому элементу по отдельности,
но такой подход создает ряд проблем Вопервых, для очень длинных списков все
сведется к однообразным повторениям Вовторых, при любом изменении длины
списка в программу придется вносить изменения Цикл for решает обе проблемы
th будет следить за всеми техническими деталями в своей внутренней реали
зации В следующем примере цикл for используется для вывода имен фокусников
magicians.py
 magicians = ['alice', 'david', 'carolina']
 for magician in magicians:
 print(magician)
Все начинается с определения списка , как и в главе ҆ В точке  определяется
цикл for Эта строка приказывает th взять очередное имя из списка и сохра
нить его в переменной magician В точке  выводится имя, только что сохранен
ное в переменной magician Затем строки  и  повторяются для каждого имени

Перебор всего списка 63
в списке Этот код можно описать так «Для каждого фокусника в списке вывести его имя» Результат представляет собой простой перечень имен из списка alice david carolina
Подробнее о циклах
Концепция циклов очень важна, потому что она представляет один из основных
способов автоматизации повторяющихся задач компьютером Например, в простом
цикле, использованном в magicians�py, th сначала читает первую строку цикла
for magician in magicians:
Эта строка означает, что нужно взять первое значение из списка magicians и со
хранить его в переменной magician Первое значение в списке — 'alice' Затем
th читает следующую строку
print(magician)
th выводит текущее значение magician, которое все еще равно 'alice' Так
как в списке еще остались другие значения, th возвращается к первой строке цикла
for magician in magicians:
th берет следующее значение из списка — 'david' — и сохраняет его в magician
Затем выполняется строка
print(magician)
th снова выводит текущее значение magician теперь это строка 'david' Весь
цикл повторяется еще раз с последним значением в списке, 'carolina' Так как
других значений в списке не осталось, th переходит к следующей строке в про
грамме В данном случае после цикла for ничего нет, поэтому программа просто
завершается
Помните, что все действия повторяются по одному разу для каждого элемента
в списке независимо от их количества Если список содержит миллион элементов,
th повторит эти действия миллион раз — обычно это происходит очень быстро
Также помните, что при написании собственных циклов for временной переменной
для текущего значения из списка можно присвоить любое имя Однако на практи
ке рекомендуется выбирать осмысленное имя, описывающее отдельный элемент списка Несколько примеров
for cat in cats: for dog in dogs:for item in list_of_items:
Выполнение этого правила поможет вам проследить за тем, какие действия выпол
няются с каждым элементом в цикле for В зависимости от того, какое число ис

64 Глава 4 • Работа со списками
пользуется — одиночное и множественное, вы сможете понять, работает ли данная часть кода с отдельным элементом из списка или со всем списком
Более сложные действия в циклах for
В цикле for с каждым элементом списка может выполняться практически любое
действие Дополним предыдущий пример, чтобы программа выводила для каждого фокусника отдельное сообщение
magicians = ['alice', 'david', 'carolina'] for magician in magicians:
 print(magician.title() + ", that was a great trick!")
Единственное отличие этого кода от предыдущего заключается в том, что в точке 
для каждого фокусника строится сообщение с его именем При первом проходе
цикла переменная magician содержит значение 'alice', поэтому th начинает
первое сообщение с имени 'Alice' При втором проходе сообщение будет начи
наться с имени 'David', а при третьем — с имени 'Carolina'
Alice, that was a great trick! David, that was a great trick! Carolina, that was a great trick!
Тело цикла for может содержать сколько угодно строк кода Каждая строка с на
чальным отступом после строки for magician in magicians считается находящейся
в цикле и выполняется по одному разу для каждого значения в списке Таким об
разом, с каждым значением в списке можно выполнить любые операции на ваше усмотрение Включим в сообщение для каждого фокусника вторую строку
magicians = ['alice', 'david', 'carolina'] for magician in magicians: print(magician.title() + ", that was a great trick!")
 print("I can't wait to see your next trick, " + magician.
title() + ".\n")
Так как обе команды print снабжены отступами, каждая строка будет выполнена
по одному разу для каждого фокусника в списке Символ новой строки ( "\n")
во второй команде rit  вставляет пустую строку после каждого прохода цик
ла В результате будет создан набор сообщений, аккуратно сгруппированных для каждого фокусника в списке
Alice, that was a great trick! I can't wait to see your next trick, Alice. David, that was a great trick! I can't wait to see your next trick, David. Carolina, that was a great trick!
I can't wait to see your next trick, Carolina.

Перебор всего списка 65
Тело цикла for может содержать сколько угодно строк кода На прак тике часто
требуется выполнить в цикле for несколько разных операций для каждого эле
мента списка
Выполнение действий после цикла for
Что происходит после завершения цикла for Обычно программа выводит сводную
информацию или переходит к другим операциям
Каждая строка кода после цикла for, не имеющая отступа, выполняется без по
вторения Допустим, вы хотите вывести сообщение для всей группы фокусников
и поблагодарить их за превосходное представление Чтобы вывести общее сообще
ние после всех отдельных сообщений, разместите его после цикла for без отступа
magicians = ['alice', 'david', 'carolina'] for magician in magicians: print(magician.title() + ", that was a great trick!") print("I can't wait to see your next trick, " + magician.title() + ".\n")

 print("Thank you, everyone. That was a great magic show!")
Первые две команды print повторяются по одному разу для каждого фокусника
в списке, как было показано ранее Но поскольку строка  отступа не имеет, это
сообщение выводится только один раз
Alice, that was a great trick! I can't wait to see your next trick, Alice. David, that was a great trick! I can't wait to see your next trick, David. Carolina, that was a great trick! I can't wait to see your next trick, Carolina. Thank you, everyone. That was a great magic show!
При обработке данных в циклах for завершающее сообщение позволяет подвести
итог операции, выполненной со всем набором данных Например, цикл for может
инициализировать игру, перебирая список персонажей и изображая каждого пер
сонажа на экране После цикла выполняется блок без отступа, который выводит кнопку Начать игру после того, как все персонажи появятся на экране
Предотвращение ошибок с отступами
В th связь одной строки кода с предшествующей строкой обозначается от
ступами В приведенных примерах строки, выводившие сообщения для отдельных
фокусников, были частью цикла, потому что они были снабжены отступами При
менение отступов в th сильно упрощает чтение кода Фактически отступы
заставляют разработчика писать аккуратно отформатированный код с четкой
визуальной структурой В более длинных программах th могут встречаться

66 Глава 4 • Работа со списками
блоки кода с отступами нескольких разных уровней Эт и уровни способствуют
пониманию общей структуры программы
Когда разработчики только начинают писать код, работа которого зависит
от правильности отступов, в их коде нередко встречаются распространенные
ошибки Например, иногда они расставляют отступы в коде, в котором эти отступы
не нужны, или наоборот — забывают ставить отступы в блоках, где это необходимо
Несколько примеров помогут вам избежать подобных ошибок в б удущем и успеш
но исправлять их, когда они встретятся в ваших программах Итак, рассмотрим несколько типичных ошибок при использовании отступов Пропущенный отступ
Строка после команды for в цикле всегда должна снабжаться отступом Если вы
забудете поставить отступ, th напомнит вам об этом
magicians.py magicians = ['alice', 'david', 'carolina'] for magician in magicians:
 print(magician)
Команда print в точке  должна иметь отступ, но здесь его нет Когда th ожи
дает увидеть блок с отступом, но не находит его, появляется сообщение с указанием номера строки
File "magicians.py", line 3 print(magician) ^ IndentationError: expected an indented block
Обычно для устранения подобных ошибок достаточно поставить отступ в строке (или строках), следующей непосредственно после команды for
Пропущенные отступы в других строках
Иногда цикл выполняется без ошибок, но не выдает ожидаемых результатов Такое
часто происходит, когда вы пытаетесь выполнить несколько операций в цикле, но забываете снабдить отступом некоторые из строк
Например, вот что происходит, если вы забудете снабдить отступом вторую строку в цикле magicians = ['alice', 'david', 'carolina'] for magician in magicians: print(magician.title() + ", that was a great trick!")
 print("I can't wait to see your next trick, " + magician.title() +
".\n") Команда print в точке  должна быть снабжена отступом, но, поскольку th
находит хотя бы одну строку с отступом после команды for, сообщение об ошибке
не выдается В результате первая команда print будет выполнена для каждого

Перебор всего списка 67
элемента в списке, потому что в ней есть отступ Вторая команда print отступа
не имеет, поэтому она будет выполнена только один раз после завершения цикла
Так как последним значением magician является строка 'carolina', второе сообще
ние будет выведено только с этим именем
Alice, that was a great trick! David, that was a great trick! Carolina, that was a great trick! I can't wait to see your next trick, Carolina.
Это пример логической ошибки Код имеет действительный синтаксис, но он не
приводит к желаемому результату, потому что проблема кроется в его логике
Если некоторое действие должно повторяться для каждого элемента в списке, но
выполняется только один раз, проверьте, не нужно ли добавить отступы в строке или нескольких строках кода Лишние отступы
Если вы случайно поставите отступ в строке, в которой он не нужен, th со
общит об этом
hello_world.py message = "Hello Python world!"
 print(message)
Отступ команды print в точке  не нужен, потому что эта строка не подчинена
предшествующей th сообщает об ошибке
File "hello_world.py", line 2 print(message) ^IndentationError: unexpected indent
Чтобы избежать непредвиденных ошибок с отступами, используйте их только там,
где для этого существуют конкретные причины В тех программах, которые вы
пишете на этой стадии изучения th, отступы нужны только в строках действий, повторяемых для каждого элемента в цикле for
Лишние отступы после цикла
Если вы случайно снабдите отступом код, который должен выполняться после за
вершения цикла, то этот код будет выполнен для кажд ого элемента Иногда th
выводит сообщение об ошибке, но часто дело ограничивается простой логической ошибкой
Например, что произойдет, если случайно снабдить отступом строку с выводом завершающего приветствия для группы фокусников
magicians = ['alice', 'david', 'carolina'] for magician in magicians: print(magician.title() + ", that was a great trick!")

68 Глава 4 • Работа со списками
print("I can't wait to see your next trick, " + magician.title() + ".\n")
 print("Thank you everyone, that was a great magic show!")
Так как строка  имеет отступ, сообщение будет продублировано для каждого
фокусника в списке 
Alice, that was a great trick! I can't wait to see your next trick, Alice.
 Thank you everyone, that was a great magic show!
David, that was a great trick! I can't wait to see your next trick, David.
 Thank you everyone, that was a great magic show!
Carolina, that was a great trick! I can't wait to see your next trick, Carolina.
 Thank you everyone, that was a great magic show!
Это еще один пример логической ошибки, наподобие описанной в разделе «Про
пущенные отступы в других строках» на с ؆ th не знает, что вы пытаетесь
сделать в своем коде, поэтому он просто выполняет весь код, не нарушающий правил
синтаксиса Если действие, которое должно выполняться один раз, выполняется
многократно, проверьте, нет ли лишнего отступа в соответствующей строке кода
Пропущенное двоеточие
Двоеточие в конце команды for сообщает th, что следующая строка является
началом цикла
magicians = ['alice', 'david', 'carolina']
 for magician in magicians
print(magician)
Если вы случайно забудете поставить двоеточие, как в примере , произойдет
синтаксическая ошибка, так как полученная команда нарушает правила языка
И хотя такие ошибки легко исправляются, найти их бывает достаточно трудно
Вы не поверите, сколько времени тратят программисты на поиск подобных «одно
символьных» ошибок Поиск таких ошибок усложняется еще и тем, что человек склонен видеть то, что он ожидает увидеть
УПРАЖНЕНИЯ
4-1� Пицца: вспомните по крайней мере три ваши любимые разновидности пиццы� Сохрани-
те их в списке и используйте цикл for для вывода всех названий�• Измените цикл for так, чтобы вместо простого названия пиццы выводилось сообще- ние, включающее это название� Таким образом, для каждого элемента должна выво-
диться строка с простым текстом вида «I like pepperoni pizza»�
• Добавьте в конец программы (после цикла for) строку с завершающим сообщением� Таким образом, вывод должен состоять из трех (и более) строк с названиями пиццы
и дополнительного сообщения, скажем, «I really love pizza!»�

Создание числовых списков 69
4-2� Животные: создайте список из трех (и более) животных, обладающих общей характе-
ристикой� Используйте цикл for для вывода названий всех животных�
• Измените программу так, чтобы вместо простого названия выводилось сообщение, включающее это название, например «A dog would make a great pet»�
• Добавьте в конец программы строку с описанием общего свойства� Например, можно вывести сообщение «Any of these animals would make a great pet!»�
Создание числовых списков
Необходимость хранения наборов чисел возникает в программах по многим при
чинам Например, в компьютерной игре могут храниться координаты каждого
персонажа на экране, таблицы рекордов и т д В программах визуализации данных
пользователь почти всегда работает с наборами чисел температурой, расстоянием, численностью населения, широтойдолготой и другими числовыми данными
Списки идеально подходят для хранения наборов чисел, а th предоставляет
специальные средства для эффективной работы с числовыми списками Достаточно
один раз понять, как эффективно пользоваться этими средствами, и ваш код будет хорошо работать даже в том случае, если список содержит миллионы элементов
Функция range() Функция range() упрощает построение числовых последовательностей Например,
с ее помощью можно легко вывести серию чисел
numbers.py for value in range(1,5): print(value)
И хотя на первый взгляд может показаться, что он должен вывести числа от до , на самом деле число не выводится 1 234
В этом примере range() выводит только числа от до Ɔ Перед вами еще одно про
явление «смещения на », часто встречающегося в языках программирования При
выполнении функции range() th начинает отсчет от первого переданного зна
чения и прекращает его при достижении второго Так как на втором значении про
исходит остановка, конец интервала ( в данном случае) не встречается в выводе Чтобы вывести числа от до , используйте вызов range(1,6)
for value in range(1,6): print(value)
На этот раз вывод начинается с и завершается ʽ

70 Глава 4 • Работа со списками
1 2345
Если ваша программа при использовании range() выводит не тот результат,
на который вы рассчитывали, попробуйте увеличить конечное значение на Æ
Использование range() для создания числового списка
Если вы хотите создать числовой список, преобразуйте результаты range() в спи
сок при помощи функции list() Если заключить вызов range() в list() , то ре
зультат будет представлять собой список с числовыми элементами
В примере из предыдущего раздела числовая последовательность просто выводи
лась на экран Тот же набор чисел можно преобразовать в список вызовом list()
numbers = list(range(1,6)) print(numbers)
Результат [1, 2, 3, 4, 5]
Функция range() также может генерировать числовые последовательности, про
пуская числа в заданном диапазоне Например, построение списка четных чисел от до происходит так
even_numbers.py even_numbers = list(range(2,11,2)) print(even_numbers)
В этом примере функция range() начинает со значения , а затем увеличи
вает его на Ć Приращение последовательно применя ется до тех пор, пока
не будет достигнуто или пройдено конечное значение , после чего выводится
результат
[2, 4, 6, 8, 10]
С помощью функции range() можно создать практически любой диапазон чисел
Например, как бы вы создали список квадратов всех целых чисел от до В язы
ке th операция возведения в степень обозначается двумя звездочками ( **)
Один из возможных вариантов выглядит так
squares.py
 squares = []
 for value in range(1,11):
 square = value**2
 squares.append(square)
 print(squares)

Создание числовых списков 71
Сначала в точке  создается пустой список с именем squares В точке  вы при
казываете th перебрать все значения от до при помо щи функции range()
В цикле текущее значение возводится во вторую степень, а результат сохра няется
в переменной square в точке  В точке  каждое новое значение square присо
единяется к списку squares Наконец, после завершения цикла список квадратов
выводится в точке 
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Чтобы сделать код более компактным, можно опустить временную переменную square и присоединять каждое новое значение прямо к списку
squares = [] for value in range(1,11):
 squares.append(value**2)
print(squares)
Конструкция  выполняет ту же работу, что и строки  и  в squares�py Каждое
значение в цикле возводится во вторую степень, а затем немедленно присоединя ется к списку квадратов
При создании более сложных списков можно использовать любой из двух подхо
дов Иногда использование временной переменной упрощает чтение кода в других
случаях оно приводит лишь к напрасному удлинению кода Сначала сосредо
точьтесь на написании четкого и понятного кода, который делает именно то, что
нужно, — и только потом переходите к анализу кода и поиску более эффективных
решений Простая статистика с числовыми списками
Некоторые функции th предназначены для работы с числовыми списками
Например, вы можете легко узнать минимум, максимум и сумму числового списка
>>> digits = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
>>> min(digits)
0 >>> max(digits)
9>>> sum(digits)
45
ПРИМЕЧАНИЕ
В примерах этой главы используются короткие списки чисел, но это делается только для того, что-
бы данные помещались на странице� Примеры также будут работать и в том случае, если список содержит миллионы чисел� Генераторы списков
Описанный выше способ генерирования списка squares состоял из трех или
четырех строк кода Генератор списка (ist crehesi) позволяет сгенериро

72 Глава 4 • Работа со списками
вать тот же список всего в одной строке Генератор списка объединяет цикл for
и создание новых элементов в одну строку и автоматически присоединяет к списку
все новые элементы Учебники не всегда рассказывают о генераторах списка на
чинающим программистам, но я привожу этот материал, потому что вы с большой
вероятностью встретите эту конструкцию, как только начнете просматривать код других разработчиков
В следующем примере список квадратов, знакомый вам по предыдущим примерам, строится с использованием генератора списка
squares.py squares = [value**2 for value in range(1,11)] print(squares)
Чтобы использовать этот синтаксис, начните с содержательного имени списка, например squares Затем откройте квадратные скобки и определите выражение
для значений, которые должны быть сохранены в новом списке В данном приме
ре это выражение value**2, которое возводит значение во вторую степень Затем
напишите цикл for для генерирования чисел, которые должны передаваться вы
ражению, и закройте квадратные скобки Цикл for в данном примере — for value
in range(1,11) — передает значения с до выражению value**2 Обратите
внимание на отсутствие двоеточия в конце команды for
Результатом будет уже знакомый вам список квадратов
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Чтобы успешно писать собственные генераторы списков, необходим определенный
опыт Тем не менее, как только вы освоитесь с созданием обычных списков, вы
оцените возможности генераторов Когда после очередного трехчетырехстрочного блока вам это надоест, подумайте о написании собственных генераторов списков
УПРАЖНЕНИЯ
4-3� Считаем до 20: используйте цикл for для вывода чисел от 1 до 20 включительно�
4-4� Миллион: создайте список чисел от 1 до 1 000 000, затем воспользуйтесь циклом for
для вывода чисел� (Если вывод занимает слишком много времени, остановите его нажатием
Ctrl+C или закройте окно вывода�)
4-5� Суммирование миллиона чисел: создайте список чисел от 1 до 1 000 000, затем вос-
пользуйтесь функциями min() и max() и убедитесь в том, что список действительно начи-
нается с 1 и заканчивается 1 000 000� Вызовите функцию sum() и посмотрите, насколько быстро Python сможет просуммировать миллион чисел�
4-6� Нечетные числа: воспользуйтесь третьим аргументом функции range() для создания
списка нечетных чисел от 1 до 20� Выведите все числа в цикле for�
4-7� Тройки: создайте список чисел, кратных 3, в диапазоне от 3 до 30� Выведите все числа
своего списка в цикле for�
4-8� Кубы: результат возведения числа в третью степень называется кубом� Например,
куб 2 записывается в языке Python в виде 2**3� Создайте список первых 10 кубов (то есть
кубов всех целых чисел от 1 до 10) и выведите значения всех кубов в цикле for�
4-9� Генератор кубов: используйте конструкцию генератора списка для создания списка первых 10 кубов�

Работа с частью списка 73
Работа с частью списка
В главе вы узнали, как обращаться к отдельным элементам списка, а в этой главе
мы занимались перебором всех элементов списка Также можно работать с конкрет
ным подмножеством элементов списка в th такие подмножества называются срезами (sices)
Создание среза
Чтобы создать срез списка, следует задать индексы первого и последнего элементов,
с которыми вы намереваетесь работать Как и в случае с функцией range(), th
останавливается на элементе, предшествующем второму индексу Скажем, чтобы
вывести первые три элемента списка, запросите индексы с по , и вы получите элементы , и Ć В следующем примере используется список игроков команды
players.py players = ['charles', 'martina', 'michael', 'florence', 'eli']
 print(players[0:3])
В точке  выводится срез списка, включающий только первых трех игроков Вывод
сохраняет структуру списка, но включает только первых трех игроков
['charles', 'martina', 'michael']
Подмножество может включать любую часть списка Например, чтобы ограничить
ся вторым, третьим и четвертым элементами списка, срез начинается с индекса и заканчивается на индексе ƽ
players = ['charles', 'martina', 'michael', 'florence', 'eli'] print(players[1:4])
На этот раз срез начинается с элемента 'martina' и заканчивается элементом
'florence'
['martina', 'michael', 'florence']
Если первый индекс среза не указан, то th автоматически начинает срез
от начала списка
players = ['charles', 'martina', 'michael', 'florence', 'eli'] print(players[:4])
Без начального индекса th берет элементы от начала списка ['charles', 'martina', 'michael', 'florence']
Аналогичный синтаксис работает и для срезов, включающих конец списка Напри
мер, если вам нужны все элементы с третьего до последнего, начните с индекса и не указывайте второй индекс
players = ['charles', 'martina', 'michael', 'florence', 'eli']
print(players[2:])

74 Глава 4 • Работа со списками
th возвращает все элементы с третьего до конца списка ['michael', 'florence', 'eli']
Этот синтаксис позволяет вывести все элементы от любой позиции до конца спи
ска независимо от его длины Вспомните, что отрицательный индекс возвращает
элемент, находящийся на заданном расстоянии от конца списка следовательно, вы
можете получить любой срез от конца списка Например, чтобы отобрать последних трех игроков, используйте срез players[-3:]
players = ['charles', 'martina', 'michael', 'florence', 'eli'] print(players[-3:])
Программа выводит имена трех последних игроков, причем продолжает работать даже при изменении размера списка Перебор содержимого среза
Если вы хотите перебрать элементы, входящие в подмножество элементов, исполь
зуйте срез в цикле for В следующем примере программа перебирает первых трех
игроков и выводит их имена
players = ['charles', 'martina', 'michael', 'florence', 'eli'] print("Here are the first three players on my team:")
 for player in players[:3]:
print(player.title())
Вместо того чтобы перебирать весь список игроков , th ограничивается
первыми тремя именами
Here are the first three players on my team: Charles Martina Michael
Срезы приносят огромную пользу во многих ситуациях Например, при создании
компьютерной игры итоговый счет игрока может добавляться в список после
окончания текущей партии После этого программа может получить три лучших ре
зультата игрока, отсортировав список по уменьшению и получив срез, включающий
только три элемента При работе с данными срезы могут использоваться для об
работки данных блоками заданного размера Или при построении вебприложения
срезы могут использоваться для постраничного вывода информации так, чтобы на каждой странице выводился соответствующий объем информации Копирование списка
Часто разработчик берет существующий список и создает на его основе совершенно
новый Посмотрим, как работает копирование списков, и рассмотрим одну ситуа
цию, в которой копирование списка может принести пользу

Работа с частью списка 75
Чтобы скопировать список, создайте срез, включающий весь исходный список
без указания первого и второго индекса ( [:]) Эта конструкция создает срез,
который начинается с первого элемента и завершается последним в результате создается копия всего списка
Представьте, что вы создали список своих любимых блюд и теперь хотите создать
отдельный список блюд, которые нравятся вашему другу Пока вашему другу
нравятся все блюда из нашего списка, поэтому вы можете создать другой список простым копированием нашего
foods.py
 my_foods = ['pizza', 'falafel', 'carrot cake']
 friend_foods = my_foods[:]
print("My favorite foods are:") print(my_foods) print("\nMy friend's favorite foods are:") print(friend_foods)
В точке  создается список блюд с именем my_foods В точке  создается дру
гой список с именем friend_foods Чтобы создать копию my_foods, программа
запрашивает срез my_foods без указания индексов, и мы сохраняем копию
в friend_foods
При выводе обоих списков становится видно, что они содержат одинаковые данные
My favorite foods are: ['pizza', 'falafel', 'carrot cake'] My friend's favorite foods are: ['pizza', 'falafel', 'carrot cake']
Чтобы доказать, что речь в действительности идет о двух разных списках, добавим новое блюдо в каждый список
my_foods = ['pizza', 'falafel', 'carrot cake']
 friend_foods = my_foods[:]
 my_foods.append('cannoli')
 friend_foods.append('ice cream')
print("My favorite foods are:") print(my_foods)print("\nMy friend's favorite foods are:")print(friend_foods)
В точке  исходные элементы my_foods копируются в новый список friend_foods,
как было сделано в предыдущем примере Затем в  каждый список добавляется
новый элемент 'cannoli' в my_foods , и 'ice cream' в friend_foods После этого
вывод двух списков наглядно показывает, что каждое блюдо находится в соответ ствующем списке

76 Глава 4 • Работа со списками
My favorite foods are:
 ['pizza', 'falafel', 'carrot cake', 'cannoli']
My friend's favorite foods are:
 ['pizza', 'falafel', 'carrot cake', 'ice cream']
Вывод в точке  показывает, что элемент 'cannoli' находится в списке my_foods,
а элемент 'ice cream' в этот список не входит В точке  видно, что 'ice cream'
входит в список friend_foods, а элемент 'cannoli' в этот список не входит Если
бы эти два списка просто совпадали, то их содержимое уже не различалось бы
Например, вот что происходит при попытке копирования списка без использо
вания среза
my_foods = ['pizza', 'falafel', 'carrot cake']
# This doesn't work:
 friend_foods = my_foods
my_foods.append('cannoli') friend_foods.append('ice cream') print("My favorite foods are:") print(my_foods) print("\nMy friend's favorite foods are:") print(friend_foods)
Вместо того чтобы сохранять копию my_foods в friend_foods в точке , мы зада
ем friend_foods равным my_foods Этот синтаксис в действительности сообщает
th, что новая переменная friend_foods должна быть связана со списком, уже
хранящимся в my_foods, поэтому теперь обе переменные связаны с одним списком
В результате при добавлении элемента 'cannoli' в my_foods этот элемент также
появляется в friend_foods Аналогичным образом элемент 'ice cream' появляется
в обоих списках, хотя на первый взгляд он был добавлен только в friend_foods
Вывод показывает, что оба списка содержат одинаковы е элементы, а это совсем не
то, что требовалось
My favorite foods are: ['pizza', 'falafel', 'carrot cake', 'cannoli', 'ice cream'] My friend's favorite foods are: ['pizza', 'falafel', 'carrot cake', 'cannoli', 'ice cream']
ПРИМЕЧАНИЕ
Если какие-то подробности в этом примере кажутся непонятными, не огорчайтесь� В двух словах,
если при работе с копией списка происходит что-то непредвиденное, убедитесь в том, что список копируется с использованием среза, как это делается в нашем первом примере�
УПРАЖНЕНИЯ
4-10� Срезы: добавьте в конец одной из программ, написанных в этой главе, фрагмент,
который делает следующее�

Кортежи 77
• Выводит сообщение «The first three items in the list are:», а затем использует срез для
вывода первых трех элементов из списка�
• Выводит сообщение «Three items from the middle of the list are:», а затем использует срез для вывода первых трех элементов из середины списка�
• Выводит сообщение «The last three items in the list are:», а затем использует срез для вывода последних трех элементов из списка�
4-11� Моя пицца, твоя пицца: начните с программы из упражнения 4-1� Создайте копию
списка с видами пиццы, присвойте ему имя friend_pizzas� Затем сделайте следующее� • Добавьте новую пиццу в исходный список�
• Добавьте другую пиццу в список friend_pizzas�
• Докажите, что в программе существуют два разных списка� Выведите сообщение «My favorite pizzas are:», а затем первый список в цикле for� Выведите сообщение «My
friend’s favorite pizzas are:», а затем второй список в цикле for� Убедитесь в том, что каждая новая пицца находится в соответствующем списке�
4-12� Больше циклов: во всех версиях foods�py из этого раздела мы избегали использования
цикла for при выводе для экономии места� Выберите версию foods�py и напишите два цикла
for для вывода каждого списка�
Кортежи
Списки хорошо подходят для хранения наборов элементов, которые могут из
меняться на протяжении жизненного цикла программы Например, возможность
модификации списков жизненно необходима при работе со списками пользовате
лей сайта или списками персонажей игры Однако в некоторых ситуациях требу
ется создать список элементов, который не может изменяться Кортежи (tɧes)
предоставляют именно такую возможность В языке th значения, которые
не могут изменяться, называются неизменяемыми (ita֧e), а неизменяемый
список называется кортежем
Определение кортежа
Кортеж выглядит как список, не считая того, что вместо квадратных скобок исполь
зуются круглые скобки После определения кортежа вы можете обращаться к его от
дельным элементам по индексам точно так же, как это делается при работе со списком
Допустим, имеется прямоугольник, который в программе всегда должен иметь
строго определенные размеры Чтобы гарантировать неизменность размеров, мож
но объединить размеры в кортеж
dimensions.py
 dimensions = (200, 50)
 print(dimensions[0])
print(dimensions[1])
В точке  определяется кортеж dimensions, при этом вместо квадратных скобок
используются круглые В точке  каждый элемент кортежа выводится по отдельно
сти с использованием того же синтаксиса, который использовался для обращения к элементу списка

78 Глава 4 • Работа со списками
200 50
Посмотрим, что произойдет при попытке изменения одного из элементов в кортеже dimensions
dimensions = (200, 50)
 dimensions[0] = 250
Код в точке  пытается изменить первое значение, но th возвращает ошибку
типа По сути, так как мы пытаемся изменить кортеж, а эта операция недопустима
для объектов этого типа, th сообщает о невозможности присваивания нового значения элементу в кортеже
Traceback (most recent call last): File "dimensions.py", line 3, in dimensions[0] = 250TypeError: 'tuple' object does not support item assignment
И это хорошо, потому что мы хотим, чтобы th сообщал о попытке изменения размеров прямоугольника в программе, выдавая сообщение об ошибке Перебор всех значений в кортеже
Для перебора всех значений в кортеже используется цикл for, как и при работе
со списками
dimensions = (200, 50)for dimension in dimensions: print(dimension)
th возвращает все элементы кортежа по аналогии с тем, как это делается со списком 200 50
Замена кортежа
Элементы кортежа не могут изменяться, но вы можете присвоить новое значение
переменной, в которой хранится кортеж Таким образом, для изменения размеров прямоугольника следует переопределить весь кортеж 
dimensions = (200, 50)print("Original dimensions:") for dimension in dimensions: print(dimension)
 dimensions = (400, 100)
 print("\nModified dimensions:")
for dimension in dimensions:
print(dimension)

Стиль программирования 79
Блок в точке  определяет исходный кортеж и выводит исходные размеры
В точке  в переменной dimensions сохраняется новый кортеж, после чего в точке
 выводятся новые размеры На этот раз th не выдает сообщений об ош ибках,
потому что замена значения переменной является допустимой операцией
Original dimensions: 200 50 Modified dimensions: 400 100
По сравнению со списками структуры данных кортежей относительно просты
Используйте их для хранения наборов значений, которые не должны изменяться на протяжении жизненного цикла программы
УПРАЖНЕНИЯ
4-13� Шведский стол: меню «шведского стола» в ресторане состоит всего из пяти пунктов� Придумайте пять простых блюд и сохраните их в кортеже�• Используйте цикл for для вывода всех блюд, предлагаемых рестораном�
• Попробуйте изменить один из элементов и убедитесь в том, что Python отказывается вносить изменения�
• Ресторан изменяет меню, заменяя два элемента другими блюдами� Добавьте блок кода, который заменяет кортеж, и используйте цикл for для вывода каждого элемента обновленного меню�
Стиль программирования
Итак, вы постепенно начинаете писать более длинные программы, и вам стоит
познакомиться с некоторыми рекомендациями по стилевому оформлению кода
Не жалейте времени на то, чтобы ваш код читался как можно проще Понятный код
помогает следить за тем, что делает ваша программа, и упрощает изучение вашего кода другими разработчиками
Программисты th выработали ряд соглашений по стилю, чтобы весь код
имел хотя бы отдаленно похожую структуру Научившись писать «чистый»
код th, вы сможете понять общую структуру кода th, написанного
любым другим программистом, соблюдающим те же рекомендации Если вы
рассчитываете когданибудь стать профессиональным программистом, привы
кайте соблюдать эти рекомендации уже сейчас, чтобы выработать полезную привычку
Рекомендации по стилю
Когда ктонибудь хочет внести изменения в язык th, он пишет документ E
(th Ehaceet rɂsa) Одним из самых старых E является документ
E с рекомендациями по стилевому оформлению кода E имеет довольно

80 Глава 4 • Работа со списками
большую длину, но б ульшая часть документа посвящена более сложным про
граммным структурам, нежели те, которые встречались вам до настоящего момента
Руководство по стилю th было написано с учетом того факта, что код читается
чаще, чем пишется Вы пишете свой код один раз, а потом начинаете читать его,
когда переходите к отладке При расширении функциональности программы вы
снова тратите время на чтение своего кода А когда вашим кодом начинают пользоваться другие программисты, они тоже читают его
Выбирая между написанием кода, который проще пишется, и кодом, который
проще читается, программисты th почти всегда рекомендуют второй вариант
Следующие советы помогут вам с самого начала писать чистый, понятный код Отступы
E рекомендует обозначать уровень отступа четырьмя пробелами Использо
вание четырех пробелов упрощает чтение программы и при этом оставляет достаточно места для нескольких уровней отступов в каждой строке
В программах форматирования текста для создания отступов часто используются
табуляции вместо пробелов Такой способ хорошо работает в текстовых про
цессорах, но интерпретатор th приходит в замешательство, когда табуляции
смешиваются с пробелами В каждом текстовом редакторе имеется параметр кон
фигурации, который заменяет нажатие клавиши табуляции заданным количеством
пробелов Конечно, клавиша табуляции удобна, но вы должны проследить за тем, чтобы редактор вставлял в документ пробелы вместо табуляций
Смешение табуляций и пробелов в файле может создать проблемы, сильно за
трудняющие диагностику Если вы думаете, что в программе табуляции смешались
с пробелами, помните, что в большинстве редакторов существует возможность преобразования всех табуляций в пробелы Длина строк
Многие программисты th рекомендуют ограничивать длину строк сим
волами Исторически эта рекомендация появилась изза того, что в большинстве
компьютеров в одной строке терминального окна помещалось всего символов
В настоящее время на экранах помещаются куда более длинные строки, но для
применения стандартной длины строки в символов существуют и другие при
чины Профессиональные программисты часто открывают на одном экране сразу
несколько файлов стандартная длина строки позволяет видеть все строки в двух
или трех файлах, открытых на экране одновременно E также рекомендует
ограничивать комментарии символами на строку, потому что некоторые
служебные программы, автоматически генерирующие док ументацию в больших
проектах, добавляют символы форматирования в начале каждой строки коммен
тария
Рекомендации E по выбору длины строки не являются незыблемыми, и неко
торые программисты предпочитают ограничение в символов Пока вы учитесь,

Стиль программирования 81
длина строки в коде не так важна, но учтите, что при совместной работе в группах
почти всегда соблюдаются рекомендации E ͆ В большинстве редакторов можно
установить визуальный ориентир (обычно вертикальную линию на экране), по
казывающий, где проходит граница ПРИМЕЧАНИЕ
В приложении Б показано, как настроить текстовый редактор, чтобы он всегда вставлял четыре
пробела при нажатии клавиши табуляции и отображал вертикальную линию для соблюдения огра-ничения длины строки 79 символами� Пустые строки
Пустые строки применяются для визуальной группировки частей программы Ис
пользуйте пустые строки для структурирования файлов, но не злоупотребляйте
ими Примеры, приведенные в книге, помогут вам выработать нужный баланс
Например, если в программе пять строк кода создают список, а затем следующие
три строки чтото делают с этим списком, два фрагмента уместно разделить пустой
строкой Тем не менее между ними не стоит вставлять три или четыре пустые строки
Пустые строки не влияют на работу кода, но отражаются на его удобочитаемости
Интерпретатор th использует горизонтальные отступы для интерпретации смысла кода, но игнорирует вертикальные интервалы Другие рекомендации
E содержит много других рекомендаций по стилю, но эти рекомендации
в основном относятся к программам более сложным, чем те, которые вы пишете
на данный момент По мере изучения более сложных возможностей th я буду приводить соответствующие фрагменты рекомендаций E ͆
УПРАЖНЕНИЯ
4-14� Просмотрите исходное руководство по стилю PEP 8 по адресу https://python�org/dev/
peps/pep-0008/� Пока вы будете пользоваться им относительно редко, но просмотреть его будет интересно�
4-15� Анализ кода: выберите три программы, написанные в этой главе, и измените каждую в соответствии с рекомендациями PEP 8�• Используйте четыре пробела для каждого уровня отступов� Настройте текстовый редактор так, чтобы он вставлял четыре пробела при каждом нажатии клавиши
табуляции, если это не было сделано ранее (за инструкциями обращайтесь к при -
ложению Б)�
• Используйте менее 80 символов в каждой строке� Настройте редактор так, чтобы он отображал вертикальную черту в позиции 80-го символа�
• Не злоупотребляйте пустыми строками в файлах программ�

82 Глава 4 • Работа со списками
Итоги
В этой главе вы научились эффективно работать с элементами списка Вы узнали,
как работать со списком в цикле for, как th использует отступы для опреде
ления структуры программы и как избежать некоторых типичных ошибок при
использовании отступов Вы научились создавать простые числовые списки, а так
же изучили некоторые операции с числовыми списками Вы узнали, как создать
срез списка для работы с подмножеством элементов и как правильно копировать
списки с использованием среза Глава завершается описанием кортежей, до опре
деленной степени защищающих наборы значений, которые не должны изменяться,
и рекомендациями по стилевому оформлению вашего кода (сложность которого со временем только возрастает) для упрощения его чтения
В главе мы займемся обработкой различных условий с использованием команд if
Вы научитесь группировать относительно сложные наборы проверок для обработ
ки именно той ситуации или информации, которая вам нужна Также в этой главе
будет рассматриваться использование команд if при переборе элементов списка
для выполнения действий с элементами, выбранными по некоторому условию

5 Команды if
Программисту часто приходится проверять наборы условий и принимать решения
в зависимости от этих условий Команда if в языке th позволяет проверить
текущее состояние программы и выбрать дальнейшие действия в зависимости от результатов проверки
В этой главе вы научитесь писать условные проверки для любых интересующих
вас условий Мы начнем с простых команд if, а затем перейдем к более сложным
сериям команд if для проверки комбинированных условий Затем эта концепция
будет применена к спискам вы узнаете, как написать цикл, который выполняет
с большинством элементов списка одну операцию, и о том, что для некоторых элементов с конкретными значениями применяется особая обработка Простой пример
Следующий короткий пример показывает, как правильно организовать обработ
ку специальных ситуаций с использованием if Допустим, у вас имеется список
машин, и вы хотите вывести название каждой машины Названия большинства
машин должны записываться с капитализацией (первая буква в верхнем регистре,
остальные в нижнем) С другой стороны, значение 'bmw' должно записываться
в верхнем регистре Следующий код перебирает список названий машин и ищет
в нем значение 'bmw' Для всех элементов, содержащих значение 'bmw', значение
выводится в верхнем регистре
cars.py cars = ['audi', 'bmw', 'subaru', 'toyota'] for car in cars:
 if car == 'bmw':
print(car.upper()) else: print(car.title())
Цикл в этом примере  сначала проверяет, содержит ли car значение 'bmw' Если
проверка дает положительный результат, то значение выводится в верхнем ре
гистре Если car содержит все что угодно, кроме 'bmw', то при выводе значения
применяется капитализация
AudiBMW

84 Глава 5 • Команды if
Subaru Toyota
В этом примере объединяются несколько концепций, о которых вы узнаете в этой
главе Для начала рассмотрим основные конструкции, применяемые для проверки условий в программах Проверка условий
В каждой команде if центральное место занимает выражение, результатом которо
го является логическая истина ( True) или логическая ложь (ase) это выражение
называется условием В зависимости от результата проверки th решае т, должен
ли выполняться код в команде if Если результат условия равен True, то th
выполняет код, следующий за командой if
Проверка равенства
Во многих условиях текущее значение переменной сравнивается с конкретным
значением, интересующим вас Простейшее условие проверяет, равно ли значение переменной конкретной величине  >>> car = 'bmw'
 >>> car == 'bmw'
True
В строке  переменной car присваивается значение 'bmw' операция выполняется
одним знаком =, как вы уже неоднократно видели Строка  проверяет, равно ли
значение car строке 'bmw' для проверки используется двойной знак равенства ( ==)
Этот оператор возвращает True, если значения слева и справа от оператора равны
если же значения не совпадают, оператор возвращает False В нашем примере
значения совпадают, поэтому th возвращает True
Если car принимает любое другое значение вместо 'bmw', проверка возвращает
False
 >>> car = 'audi'
 >>> car == 'bmw'
False
Одиночный знак равенства выполняет операцию код  можно прочитать в форме
«Присвоить car значение 'audi'» С другой стороны, двойной знак равенства, как
в строке , задает вопрос «Значение car равно 'bmw'» Такое применение знаков
равенства встречается во многих языках программирования Проверка равенства без учета регистра
В языке th проверка равенства выполняется с учетом регистра Например, два
значения с разным регистром символов равными не считаются

Проверка условий 85
>>> car = 'Audi'
>>> car == 'audi'
False
Если регистр символов важен, такое поведение принос ит пользу Но если проверка
должна выполняться на уровне символов без учета регистра, преобразуйте значение переменной к нижнему регистру перед выполнением сравнения
>>> car = 'Audi'
>>> car.lower() == 'audi'
True
Условие возвращает True независимо от регистра символов 'Audi', потому что
проверка теперь выполняется без учета регистра Функция lower() не изменяет
значения, которое изначально хранилось в car, так что сравнение не отражается
на исходной переменной  >>> car = 'Audi'
 >>> car.lower() == 'audi'
True
 >>> car
'Audi'
В точке  строка 'Audi' сохраняется в переменной car В точке  значение car
приводится к нижнему регистру и сравнивается со значением строки 'audi', также
записанным в нижнем регистре Две строки совпадают, поэтому th возвращает True Вывод в точке  показывает, что значение, хранящееся в car, не изменилось
в результате проверки
Вебсайты устанавливают определенные правила для данных, вводимых пользова
телями подобным образом Например, сайт может использовать проверку условия,
чтобы убедиться в том, что имя каждого пользователя уникально (а не совпадает
с именем другого пользователя, отличаясь от него только регистром символов)
Когда ктото указывает новое имя пользователя, это имя преобразуется к нижнему
регистру и сравнивается с версиями всех существующих имен в нижнем регистре
Во время такой проверки имя 'John' будет отклонено, если в системе уже исполь
зуется любая разновидность 'john'
Проверка неравенства
Если вы хотите проверить, что два значения различны, используйте комбинацию
из восклицательного знака и знака равенства ( !=) Восклицательный знак пред
ставляет отрицание, как и во многих языках программирования
Для знакомства с оператором неравенства мы воспользуемся другой командой if
В переменной хранится заказанное дополнение к пицце если клиент не заказал анчоусы (achies), программа выводит сообщение
toppings.py requested_topping = 'mushrooms'
 if requested_topping != 'anchovies':
print("Hold the anchovies!")

86 Глава 5 • Команды if
Строка  сравнивает значение requested_topping со значением 'anchovies' Если
эти два значения не равны, th возвращает True и выполняет код после команды
if Если два значения равны, th возвращает False и не выполняет код после
команды if Так как значение requested_topping отлично от 'anchovies' , команда
print будет выполнена
Hold the anchovies!
В большинстве условных выражений, которые вы будете использовать в програм
мах, будет проверяться равенство, но иногда проверка неравенства оказывается более эффективной Сравнения чисел
Проверка числовых значений достаточно прямолинейна Например, следующий код проверяет, что переменная age равна ͽ
>>> age = 18
>>> age == 18
True
Также можно проверить условие неравенства двух чисел Например, следующий
код выводит сообщение, если значение переменной answer отлично от ожидаемого
magic_ number.py answer = 17
 if answer != 42:
print("That is not the correct answer. Please try again!")
Условие  выполняется, потому что значение answer () не равно Ć Так как
условие истинно, блок с отступом выполняется That is not the correct answer. Please try again!
В условные команды также можно включать всевозможные математические срав
нения меньше, меньше или равно, больше, больше или равно
>>> age = 19
>>> age < 21
True >>> age <= 21
True>>> age > 21
False>>> age >= 21
False
Все эти математические сравнения могут использовать ся в условиях if, что повы
шает точность формулировки интересующих вас условий Проверка нескольких условий
Иногда требуется проверить несколько условий одновр еменно Например, в не
которых случаях для выполнения действия бывает нужн о, чтобы истинными были

Проверка условий 87
сразу два условия в других случаях достаточно, чтобы истинным было хотя бы
одно из двух условий Ключевые слова and и or помогут вам в подобных ситуациях
Использование and для проверки нескольких условий
Чтобы проверить, что два условия истинны одновременно, объедините их ключевым
словом and если оба условия истинны, то и все выражение тоже истинно Если хотя
бы одно (или оба) условия ложны, то и результат всего выражения равен False
Например, чтобы убедиться в том, что каждому из двух людей больше года, ис пользуйте следующую проверку  >>> age_0 = 22
>>> age_1 = 18
 >>> age_0 >= 21 and age_1 >= 21
False
 >>> age_1 = 22
>>> age_0 >= 21 and age_1 >= 21
True
В точке  определяются две переменные, age_0 и age_1 В точке  программа про
веряет, что оба значения равны и более Левое условие выполняется, а правое
нет, поэтому все условное выражение дает результат False В точке  переменной
age_1 присваивается значение Ć Теперь значение age_1 больше ü обе проверки
проходят, а все условное выражение дает истинный результат
Чтобы код лучше читался, отдельные условия можно заклю чить в круглые скобки,
но это не обязательно С круглыми скобками проверка может выглядеть так
(age_0 >= 21) and (age_1 >= 21)
Использование or для проверки нескольких условий
Ключевое слово or тоже позволяет проверить несколько условий, но результат
общей проверки является истинным в том случае, когда истинно хотя бы одно
или оба условия Ложный результат достигается только в том случае, если оба от дельных условия ложны
Вернемся к примеру с возрастом, но на этот раз проверим, что хотя бы одна из двух переменных больше ý  >>> age_0 = 22
>>> age_1 = 18
 >>> age_0 >= 21 or age_1 >= 21
True
 >>> age_0 = 18
>>> age_0 >= 21 or age_1 >= 21
False
Как и в предыдущем случае, в точке  определяются две переменные Так как ус
ловие для age_0 в точке  истинно, все выражение также дает истинный результат
Затем значение age_0 уменьшается до ͆ При проверке  оба условия оказываются
ложными, и общий результат всего выражения тоже ложен

88 Глава 5 • Команды if
Проверка вхождения значений в список
Иногда бывает важно проверить, содержит ли список некоторое значение, пре
жде чем выполнять действие Например, перед завершением регистрации нового
пользователя на сайте можно проверить, существует ли его имя в списке имен
действующих пользователей, или в картографическом проекте определить, входит ли передаваемое место в список известных мест на карте
Чтобы узнать, присутствует ли заданное значение в списке, воспользуйтесь клю
чевым словом in Допустим, вы пишете программу для пиццерии Вы создали
список дополнений к пицце, заказанных клиентом, и хотите проверить, входят ли некоторые дополнения в этот список
>>> requested_toppings = ['mushrooms', 'onions', 'pineapple']
 >>> 'mushrooms' in requested_toppings
True
 >>> 'pepperoni' in requested_toppings
False
В точках  и  ключевое слово in приказывает th проверить, входят ли зна
чения 'mushrooms' и 'pepperoni' в список requested_toppings Это весьма полезно,
потому что вы можете создать список значений, критичных для вашей программы, а затем легко проверить, присутствует ли проверяемое значение в списке Проверка отсутствия значения в списке
В других случаях программа должна убедиться в том, что значение не входит
в список Для этого используется ключевое слово not Для примера рассмотрим
список пользователей, которым запрещено писать комментарии на форуме Прежде
чем разрешить пользователю отправку комментария, можно проверить, не был ли пользователь включен в «черный список»
banned_users.py banned_users = ['andrew', 'carolina', 'david'] user = 'marie'
 if user not in banned_users:
print(user.title() + ", you can post a response if you wish.")
Строка  достаточно четко читается если пользователь не входит в «черный
список» banned_users , то th возвращает True и выполняет строку с отступом
Пользователь 'marie' в этот список не входит, поэтому программа выводит соот
ветствующее сообщение
Marie, you can post a response if you wish.
Логические выражения
В процессе изучения программирования вы рано или поздно услышите термин
«логическое выражение» По сути это всего лишь другое название для проверки

Команды if 89
условия Результат логического выражения равен True или False , как и результат
условного выражения после его вычисления
Логические выражения часто используются для проверки некоторых условий —
например, запущена ли компьютерная игра или разрешено ли пользователю редак
тирование некоторой информации на сайте
game_active = True can_edit = False
Логические выражения предоставляют эффективные средства для контроля со
стояния программы или определенного условия, играющего важную роль в вашей
программе
УПРАЖНЕНИЯ
5-1� Проверка условий: напишите последовательность условий� Выведите описание каждой
проверки и ваш прогноз относительно ее результата� Код должен выглядеть примерно так: car = 'subaru' print("Is car == 'subaru'? I predict True.")print(car == 'subaru')print("\nIs car == 'audi'? I predict False.")print(car == 'audi')• Внимательно просмотрите результаты� Убедитесь в том, что вы понимаете, почему результат каждой строки равен True или False�
• Создайте как минимум 10 условий� Не менее 5 должны давать результат True, а не менее 5 других — результат False�
5-2� Больше условий: количество условий не ограничивается 10� Попробуйте написать дру-
гие условия и включить их в conditional_tests�py� Программа должна выдавать по крайней мере один истинный и один ложный результат для следующих видов проверок� • Проверка равенства и неравенства строк�
• Проверки с использованием функции lower()�
• Числовые проверки равенства и неравенства, условий «больше», «меньше», «больше или равно», «меньше или равно»�
• Проверки с ключевым словом and и or�
• Проверка вхождения элемента в список�
• Проверка отсутствия элемента в списке�
Команды if
Когда вы поймете, как работают проверки условий, можно переходить к написанию
команд if Существуют несколько разновидностей команд if, и выбор варианта
зависит от количества проверяемых условий Примеры команд if уже встречались
вам при обсуждении проверки условий, но сейчас эта тема будет рассмотрена более подробно

90 Глава 5 • Команды if
Простые команды if Простейшая форма команды if состоит из одного условия и одного действия
if условие :
действие
В первой строке размещается условие, а в блоке с отступом — п рактически
любое действие Если условие истинно, то th выполняет код в блоке после команды if, а если ложно, этот код игнорируется
Допустим, имеется переменная, представляющая возраст человека Следующий код проверяет, что этот возраст достаточен для голосования
voting.py age = 19
 if age >= 18:
 print("You are old enough to vote!")
В точке  th проверяет, что значение переменной age больше или равно ͆
В таком случае выполняется команда print  в строке с отступом
You are old enough to vote!
Отступы в командах if играют ту же роль, что и в циклах for Если условие истин
но, то все строки с отступом после команды if выполняются, а если ложно — весь
блок с отступом игнорируется
Блок команды if может содержать сколько угодно строк Добавим еще одну строку
для вывода дополнительного сообщения в том случае, если возраст достаточен для голосования
age = 19 if age >= 18:print("You are old enough to vote!")
print("Have you registered to vote yet?")
Условие выполняется, а обе команды print снабжены отступом, поэтому выводятся
оба сообщения
You are old enough to vote! Have you registered to vote yet?
Если значение age меньше , программа ничего не выводит
Команды if-else
Часто в программе необходимо выполнить одно действие в том случае, если ус
ловие истинно, и другое действие, если оно ложно С синтаксисом ifelse это
возможно Блок ifelse в целом похож на команду if, но секция else определяет
действие или набор действий, выполняемых при неудачной проверке

Команды if 91
В следующем примере выводится то же сообщение, которое выводилось ранее, если
возраст достаточен для голосования, но на этот раз при любом другом возрасте выводится другое сообщение age = 17

if age >= 18: print("You are old enough to vote!") print("Have you registered to vote yet?")
 else:
print("Sorry, you are too young to vote.") print("Please register to vote as soon as you turn 18!")
Если условие  истинно, то выполняется первый блок с командами print Если же
условие ложно, выполняется блок else в точке  Так как значение age на этот раз
меньше , условие оказывается ложным, и выполняется код в блоке else
Sorry, you are too young to vote.Please register to vote as soon as you turn 18!
Этот код работает, потому что существуют всего две возможные ситуации воз
раст либо достаточен для голосования, либо недостаточен Структура ifelse
хорошо подходит для тех ситуаций, в которых th всегда выполняет только
одно из двух возможных действий В подобных простых цепочках ifelse всегда
выполняется одно из двух возможных действий
Цепочки if-elif-else
Нередко в программе требуется проверять более двух возможных ситуаций для
таких ситуаций в th предусмотрен синтаксис ifelif else th выполняет
только один блок в цепочке ifelif else Все условия проверяются по порядку
до тех пор, пока одно из них не даст истинный результат Далее выполняется код, следующий за этим условием, а все остальные проверки th пропускает
Во многих реальных ситуациях существуют более двух возможных результатов
Представьте себе парк аттракционов, который взимает разную плату за вход для разных возрастных групп

‰ Для посетителей младше лет вход бесплатный

‰ Для посетителей от до лет билет стоит ʆ

‰ Для посетителей от лет и старше билет стоит Ά
Как использовать команду if для определения платы за вход Следующий код
определяет, к какой возрастной категории относится посетитель, и выводит со
общение со стоимостью билета
amusement_park.py age = 12
 if age < 4:
print("Your admission cost is $0.")
 elif age < 18:
print("Your admission cost is $5.")

92 Глава 5 • Команды if
 else:
print("Your admission cost is $10.")
Условие if в точке  проверяет, что возраст посетителя меньше лет Если условие
истинно, то программа выводит соответствующее сообщение, и th пропускает
остальные проверки Строка elif в точке  в действительности является еще одной
проверкой if, которая выполняется только в том случае, если предыдущая проверка
завершилась неудачей В этом месте цепочки известно, что возраст посетителя не
меньше лет, потому что первое условие было ложным Если посетителю меньше
лет, программа выводит соответствующее сообщение, и th пропускает блок else Если ложны оба условия — if и elif , то th выполняет код в блоке else
в точке 
В данном примере условие  дает ложный результат, поэтому его блок не выпол
няется Однако второе условие оказывается истинным ( меньше ), поэтому код будет выполнен Вывод состоит из одного сообщения с ценой билета Your admission cost is $5.
При любом значении возраста больше первые два условия ложны В таких ситуациях блок else будет выполнен, и цена билета составит Ά
Вместо того чтобы выводить сообщение с ценой билета в блоках ifelif else ,
лучше использовать другое, более компактное решение присвоить цену в цепочке if elif else , а затем добавить одну команду print после выполнения цепочки
age = 12 if age < 4:
 price = 0 elif age < 18:  price = 5 else:  price = 10
 print("Your admission cost is $" + str(price) + ".")
Строки ,  и  присваивают значение price в зависимости от значения age,
как и в предыдущем примере После присваивания цены в цепочке ifelif else
отдельная команда print без отступа  использует это значение для вывода со
общения с ценой билета
Этот пример выводит тот же результат, что и предыдущий, но цепочка ifelif else
имеет более четкую специализацию Вместо того чтобы определять цену и выво
дить сообщения, она просто определяет цену билета Кроме повышения эффектив
ности, у этого кода есть дополнительное преимущество лучшая изменяемость
Чтобы изменить текст выходного сообщения, достаточно будет отредактировать всего одну команду print — вместо трех разных команд
Серии блоков elif
Код может содержать сколько угодно блоков elif Например, если парк аттракци
онов введет особую скидку для пожилых посетителей, вы можете добавить в свой

Команды if 93
код еще одну проверку для определения того, распространяется ли скидка на те
кущего посетителя Допустим, посетители с возрастом и выше платят половину от обычной цены билета, или ʽ
age = 12 if age < 4: price = 0elif age < 18:price = 5
 elif age < 65:
price = 10
 else:
price = 5
print("Your admission cost is $" + str(price) + ".")
Б ульшая часть кода осталась неизменной Второй блок elif в точке  теперь
убеждается в том, что посетителю меньше лет, прежде чем назначить ему пол
ную цену билета Ά Обратите внимание значение, присвоенное в блоке else  ,
должно быть заменено на , потому что до этого блока доходят только посетители с возрастом и выше Отсутствие блока else
th не требует, чтобы цепочка ifelif непременно завершалась блоком else
Иногда блок else удобен в других случаях бывает нагляднее использовать допол
нительную секцию elif для обработки конкретного условия
age = 12 if age < 4: price = 0elif age < 18:price = 5elif age < 65:price = 10
 elif age >= 65: price = 5 print("Your admission cost is $" + str(price) + ".")
Блок elif в точке  назначает цену , если возраст посетителя равен и выше
смысл такого кода более понятен, чем у обобщенного блока else С таким измене
нием выполнение каждого блока возможно только при истинности конкретного условия Блок else «универсален» он обрабатывает все условия, не подходящие ни под одну
конкретную проверку if или elif, причем в эту категорию иногда могут попасть
недействительные или даже вредоносные данные Если у вас имеется завершающее
конкретное условие, лучше используйте завершающий блок elif и опустите блок
else В этом случае вы можете быть уверены в том, что ваш код будет выполняться
только в правильных условиях

94 Глава 5 • Команды if
Проверка нескольких условий Цепочки ifelif else эффективны, но они подходят только в том случае, если ис
тинным должно быть только одно условие Как только th находит выполня
ющееся условие, все остальные проверки пропускаются Такое поведение доста
точно эффективно, потому что оно позволяет проверить одно конкретное условие
Однако иногда бывает важно проверить все условия, представляющие интерес
В таких случаях следует применять серии простых команд if без блоков elif или
else Такое решение уместно, когда истинными могут быть сразу несколько усло
вий и вы хотите отреагировать на все истинные условия
Вернемся к примеру с пиццей Если ктото закажет пиццу с двумя дополнениями, программа должна обработать оба дополнения
toppings.py
 requested_toppings = ['mushrooms', 'extra cheese']
 if 'mushrooms' in requested_toppings:
print("Adding mushrooms.")
 if 'pepperoni' in requested_toppings:
print("Adding pepperoni.")
 if 'extra cheese' in requested_toppings:
print("Adding extra cheese.") print("\nFinished making your pizza!")
Обработка начинается в точке  со списка, содержащего заказанные дополнения
Команды if в точке  и  проверяют, включает ли заказ конкретные дополне
ния — грибы и пепперони, и если включает — выводят подтве рждающее сообщение
Проверка в точке  реализована простой командой if, а не elif или else, поэтому
условие будет проверяться независимо от того, было ли предыдущее условие ис
тинным или ложным Код в точке  проверяет, была ли заказана дополнительная
порция сыра, независимо от результата первых двух проверок Эти три независимых условия проверяются при каждом выполнении программы
Так как в этом коде проверяются все возможные варианты дополнений, в заказ будут включены два дополнения из трех
Adding mushrooms. Adding extra cheese. Finished making your pizza!
Если бы в программе использовался блок ifelif else , код работал бы неправиль
но, потому что он прерывал работу после обнаружения первого истинного условия Вот как это выглядело бы
requested_toppings = ['mushrooms', 'extra cheese'] if 'mushrooms' in requested_toppings: print("Adding mushrooms.")
elif 'pepperoni' in requested_toppings:print("Adding pepperoni.")

Команды if 95
elif 'extra cheese' in requested_toppings:print("Adding extra cheese.") print("\nFinished making your pizza!")
Первое же проверяемое условие (для 'mushrooms') оказывается истинным Од
нако значения 'extra cheese' и 'pepperoni' после этого не проверяются, потому
что в цепочках ifelif else после обнаружения первого истинного условия все
остальные условия пропускаются В результате в пицц у будет включено только
первое из заказанных дополнений
Adding mushrooms. Finished making your pizza!
Итак, если вы хотите, чтобы в программе выполнялся только один блок кода, — ис
пользуйте цепочку ifelif else Если же выполняться должны несколько блоков,
используйте серию независимых команд if
УПРАЖНЕНИЯ
5-3� Цвета 1: представьте, что в вашей компьютерной игре только что был подбит корабль
пришельцев� Создайте переменную с именем alien_color и присвойте ей значение ‘green’,
‘yellow’ или ‘red’� • Напишите команду if для проверки того, что переменная содержит значение ‘green’� Если условие истинно, выведите сообщение о том, что игрок только что заработал 5 очков�
• Напишите одну версию программы, в которой условие if выполняется, и другую вер- сию, в которой оно не выполняется� (Во второй версии никакое сообщение выводить-
ся не должно�)
5-4� Цвета 2: выберите цвет, как это было сделано в упражнении 5-3, и напишите цепочку
if-else� • Напишите команду if для проверки того, что переменная содержит значение ‘green’� Если условие истинно, выведите сообщение о том, что игрок только что заработал 5 очков�
• Если переменная содержит любое другое значение, выведите сообщение о том, что игрок только что заработал 10 очков�
• Напишите одну версию программы, в которой выполняется блок if, и другую версию, в которой выполняется блок else�
5-5� Цвета 3: преобразуйте цепочку if-else из упражнения 5-4 в цепочку if-elif-else�
• Если переменная содержит значение 'green’, выведите сообщение о том, что игрок только что заработал 5 очков�
• Если переменная содержит значение 'yellow’, выведите сообщение о том, что игрок только что заработал 10 очков�
• Если переменная содержит значение 'red’, выведите сообщение о том, что игрок толь- ко что заработал 15 очков�
• Напишите три версии программы и проследите за тем, чтобы для каждого цвета при- шельца выводилось соответствующее сообщение�
5-6� Периоды жизни: напишите цепочку if-elif-else для определения периода жизни челове-ка� Присвойте значение переменной age, а затем выведите сообщение� • Если значение меньше 2 — младенец�

96 Глава 5 • Команды if
• Если значение больше или равно 2, но меньше 4 — малыш�
• Если значение больше или равно 4, но меньше 13 — ребенок�
• Если значение больше или равно 13, но меньше 20 — подросток�
• Если значение больше или равно 20, но меньше 65 — взрослый�
• Если значение больше или равно 65 — пожилой человек�
5-7� Любимый фрукт: составьте список своих любимых фруктов� Напишите серию независи- мых команд if для проверки того, присутствуют ли некоторые фрукты в списке� • Создайте список трех своих любимых фруктов и назовите его favorite_fruits�
• Напишите пять команд if� Каждая команда должна проверять, входит ли определен- ный тип фрукта в список� Если фрукт входит в список, блок if должен выводить со-
общение вида «You really like bananas!»�
Использование команд if со списками
Объединение команд if со списками открывает ряд интересных возможностей На
пример, вы можете отслеживать специальные значения, для которых необходима
особая обработка по сравнению с другими значениями в списке, или эффективно
управлять изменяющимися условиями — например, наличием некоторых блюд
в ресторане Также объединение команд if со списками помогает продемонстри
ровать, что ваш код корректно работает во всех возможных ситуациях Проверка специальных значений
Эта глава началась с простого примера, показывающего, как обрабатывать осо
бые значения (такие, как 'bmw'), которые должны выводиться в другом формате
по сравнению с другими значениями в списке Теперь, когда вы лучше разбираетесь
в проверках условий и командах if, давайте повнимательнее рассмотрим процесс
поиска и обработки особых значений в списке
Вернемся к примеру с пиццерией Программа выводит сообщение каждый раз, ког
да пицца снабжается дополнением в процессе приготовления Код этого действия
можно записать чрезвычайно эффективно нужно создать список дополнений, зака
занных клиентом, и использовать цикл для перебора всех заказанных дополнений
toppings.py requested_toppings = ['mushrooms', 'green peppers', 'extra cheese'] for requested_topping in requested_toppings: print("Adding " + requested_topping + ".") print("\nFinished making your pizza!")
Вывод достаточно тривиален, поэтому код сводится к простому циклу for
Adding mushrooms. Adding green peppers.Adding extra cheese. Finished making your pizza!

Использование команд if со списками 97
А если в пиццерии вдруг кончится зеленый перец Команда if в цикле for может
правильно обработать эту ситуацию
requested_toppings = ['mushrooms', 'green peppers', 'extra cheese'] for requested_topping in requested_toppings:
 if requested_topping == 'green peppers':
print("Sorry, we are out of green peppers right now.")
 else:
print("Adding " + requested_topping + ".") print("\nFinished making your pizza!")
На этот раз программа проверяет каждый заказанный элемент перед добавлением
его к пицце В точке  программа проверяет, заказал ли клиент зеленый перец,
и если заказал — выводит сообщение о том, что этого дополнения нет Блок else
в точке  гарантирует, что все другие дополнения будут включены в заказ
Из выходных данных видно, что все заказанные дополнения обрабатываются правильно
Adding mushrooms. Sorry, we are out of green peppers right now.Adding extra cheese. Finished making your pizza!
Проверка наличия элементов в списке
Для всех списков, с которыми мы работали до сих пор, действовало одно про
стое предположение мы считали, что в каждом списке есть хотя бы один эле
мент Скоро мы предоставим пользователю возможность вводить информацию,
хранящуюся в списке, поэтому мы уже не можем предполагать, что при каждом
выполнении цикла в списке есть хотя бы один элемент В такой ситуации перед
выполнением цикла for будет полезно проверить, есть ли в списке хотя бы один
элемент
Проверим, есть ли элементы в списке заказанных дополнений, перед изготовлением
пиццы Если список пуст, программа предлагает пользователю подтвердить, что он
хочет базовую пиццу без дополнений Если список не пуст, пицца готовится так же, как в предыдущих примерах  requested_toppings = []
 if requested_toppings:
for requested_topping in requested_toppings: print("Adding " + requested_topping + ".")print("\nFinished making your pizza!")
 else:
print("Are you sure you want a plain pizza?")
На этот раз мы начинаем с пустого списка заказанных дополнений в точке 
Вместо того чтобы сразу переходить к циклу for, программа выполняет проверку

98 Глава 5 • Команды if
в точке  Когда имя списка используется в условии if, th возвращает True,
если список содержит хотя бы один элемент если список пуст, возвращается значе
ние False Если requested_toppings проходит проверку условия, выполняется тот
же цикл for, который мы использовали в предыдущем примере Если же условие
ложно, то программа выводит сообщение, которое предлагает клиенту подтвердить, действительно ли он хочет получить базовую пиццу без дополнений 
В данном примере список пуст, поэтому выводится сообщение
Are you sure you want a plain pizza?
Если в списке есть хотя бы один элемент, в выходные данные включается каждое заказанное дополнение Множественные списки
Посетители способны заказать что угодно, особенно когда речь заходит о дополне
ниях к пицце Что если клиент захочет положить на пиццу картофель фри Списки
и команды if позволят вам убедиться в том, что входные данные имеют смысл,
прежде чем обрабатывать их
Давайте проверим наличие нестандартных дополнений перед тем, как готовить
пиццу В следующем примере определяются два списка Первый список содержит
перечень доступных дополнений, а второй — список дополнений, заказанных кли
ентом На этот раз каждый элемент из requested_toppings проверяется по списку
доступных дополнений перед добавлением в пиццу  available_toppings = ['mushrooms', 'olives', 'green peppers',
'pepperoni', 'pineapple', 'extra cheese']
 requested_toppings = ['mushrooms', 'french fries', 'extra cheese']
 for requested_topping in requested_toppings:
 if requested_topping in available_toppings:
print("Adding " + requested_topping + ".")
 else:
print("Sorry, we don't have " + requested_topping + ".") print("\nFinished making your pizza!")
В точке  определяется список доступных дополнений к пицце Стоит заметить,
что если в пиццерии используется постоянный ассортимент дополнений, этот спи
сок можно реализовать в виде кортежа В точке  создается список дополнений,
заказанных клиентом Обратите внимание на необычный заказ 'french fries'
В точке  программа перебирает список заказанных дополнений Внутри цикла
программа сначала проверяет, что каждое заказанное дополнение присутствует
в списке доступных дополнений  Если дополнение доступно, оно добавляется
в пиццу Если заказанное дополнение не входит в список, выполняется блок else 
Блок else выводит сообщение о том, что дополнение недоступно
С этим синтаксисом программа выдает четкий, содержательный вывод

Оформление команд if 99
Adding mushrooms. Sorry, we don't have french fries.Adding extra cheese. Finished making your pizza!
Всего в нескольких строках кода нам удалось эффективно решить впол не реальную
проблему
УПРАЖНЕНИЯ
5-8� Hello Admin: создайте список из пяти и более имен пользователей, включающий имя
‘admin’� Представьте, что вы пишете код, который выводит приветственное сообщение для
каждого пользователя после его входа на сайт� Переберите элементы списка и выведите сообщение для каждого пользователя�• Для пользователя с именем 'admin’ выведите особое сообщение — например: «Hello admin, would you like to see a status report?»
• В остальных случаях выводите универсальное приветствие — например: «Hello Eric, thank you for logging in again»�
5-9� Без пользователей: добавьте в hello_admin�py команду if, которая проверит, что список пользователей не пуст�
• Если список пуст, выведите сообщение: «We need to find some users!»
• Удалите из списка все имена пользователей и убедитесь в том, что программа выво- дит правильное сообщение�
5-10� Проверка имен пользователей: выполните следующие действия для создания про- граммы, моделирующей проверку уникальности имен пользователей� • Создайте список current_users, содержащий пять и более имен пользователей�
• Создайте другой список new_users, содержащий пять и более имен пользователей� Убедитесь в том, что одно или два новых имени также присутствуют в списке current_ users�
• Переберите список new_users и для каждого имени в этом списке проверьте, было ли оно использовано ранее� Если имя уже использовалось, выведите сообщение о том,
что пользователь должен выбрать новое имя� Если имя не использовалось, выведите сообщение о его доступности�
• Проследите за тем, чтобы сравнение выполнялось без учета регистра символов� Если имя 'John’ уже используется, в регистрации имени ‘JOHN’ следует отказать�
5-11� Порядковые числительные: порядковые числительные в английском языке заканчива-
ются суффиксом th (кроме 1st, 2nd и 3rd)� • Сохраните числа от 1 до 9 в списке�
• Переберите элементы списка�
• Используйте цепочку if-elif-else в цикле для вывода правильного окончания числи- тельного для каждого числа� Программа должна выводить числительные «1st 2nd 3rd
4th 5th 6th 7th 8th 9th», причем каждый результат должен располагаться в отдельной строке�
Оформление команд if
Во всех примерах этой главы применялись правила стилевого оформления В E
приведена только одна рекомендация, касающаяся проверки условий заключать

100 Глава 5 • Команды if
операторы сравнения (такие, как ==, >= , <= и т д) в одиночные пробелы Напри
мер, запись
if age < 4:
лучше, чем if age<4:
Пробелы не влияют на интерпретацию вашего кода th они только упрощают чтение кода вами и другими разработчиками
УПРАЖНЕНИЯ
5-12� Стиль оформления команд if: проанализируйте программы, написанные в этой главе, и проверьте, правильно ли вы оформляли условия�
5-13� Ваши идеи: к этому моменту вы уже стали более квалифицированным программистом,
чем в начале книги� Теперь вы лучше представляете, как в программах моделируются яв-
ления реального мира, и сможете сами придумать задачи, которые будут решаться в ваших
программах� Запишите несколько задач, которые вам хотелось бы решить с ростом вашего
профессионального мастерства� Может быть, это какие-то компьютерные игры, задачи ана-лиза наборов данных или веб-приложения?
Итоги
В этой главе вы научились писать условия, результатом которых всегда яв
ляется логическое значение ( True или False ) Вы научились писать простые
команды if, цепочки ifelse и цепочки ifelif else Вы начали использовать
эти структуры для выявления конкретных условий, которые необходимо про
верить, и проверки этих условий в ваших программах Вы узнали, как обес
печить специальную обработку некоторых элементов в списке с сохранением
эффективности циклов for Также мы вернулись к стилевым рекомендациям
th, с которыми более сложные программы становятся относительно про
стыми для чтения и понимания
В главе рассматриваются словари th Словарь отчасти напоминает список,
но он позволяет связывать разные виды информации Вы научитесь со здавать
словари, перебирать их элементы, использовать их в сочетании со списками
и командами if Словари помогут вам моделировать еще более широкий спектр
реальных ситуаций

6 Словари
В этой главе речь пойдет о словарях — структурах данных, предназначенных
для объединения взаимосвязанной информации Вы узнаете, как получить до
ступ к информации, хранящейся в словаре, и как изменить эту информацию
Так как объем данных в словаре практически безграничен, мы рассмотрим
средства перебора данных в словарях Кроме того, вы научитесь использовать
вложенные словари в списках, вложенные списки в словарях и даже словари в других словарях
Операции со словарями позволяют моделировать всевозможные реальные объекты
с большей точностью Вы узнаете, как создать словарь, описывающий человека,
и сохранить в нем сколько угодно информации об этом человеке В словаре может
храниться имя, возраст, место жительства, профессия и любые другие атрибуты
Вы узнаете, как сохранить любые два вида информации, способные образовать
пары список слов и их значений, список имен людей и их любимых чисел, список гор и их высот и т д Простой словарь
Возьмем игру с инопланетными пришельцами, которые имеют разные цвета и при
носят разное количество очков игроку В следующем простом словаре хранится информация об одном конкретном пришельце
alien.py alien_0 = {'color': 'green', 'points': 5} print(alien_0['color']) print(alien_0['points'])
В словаре alien_0 хранятся два атрибута цвет ( color) и количество очков ( points)
Следующие две команды print читают эту информацию из словаря и выводят ее
на экран
green5
Работа со словарями, как и большинство других новых концепций, требует опре
деленного опыта Стоит вам немного поработать со словарями, и вы увидите, как эффективно они работают при моделировании реальных ситуаций

102 Глава 6 • Словари
Работа со словарями Словарь в языке th представляет собой совокупность пар «ключ—значение»
Каждый ключ связывается с некоторым значением, и программа может получить
значение, связанное с заданным ключом Значением может быть число, строка,
список и даже другой словарь Собственно, любой объект, создаваемый в программе
th, может стать значением в словаре
В th словарь заключается в фигурные скобки {}, в которых приводится по
следовательность пар «ключ—значение», как в предыдущем примере
alien_0 = {'color': 'green', 'points': 5}
Пара «ключ—значение» представляет собой данные, связанные друг с другом Если
вы укажете ключ, то th вернет значение, связанное с этим ключом Ключ отде
ляется от значения двоеточием, а отдельные пары разделяются запятыми В словаре может храниться любое количество пар «ключ—значение»
Простейший словарь содержит ровно одну пару «ключ—значение», как в следу ющей измененной версии словаря alien_0
alien_0 = {'color': 'green'}
В этом словаре хранится ровно один фрагмент информации о пришельце alien_0,
а именно — его цвет Строка 'color' является ключом в словаре с этим ключом
связано значение 'green'
Обращение к значениям в словаре
Чтобы получить значение, связанное с ключом, укажите имя словаря, а затем ключ в квадратных скобках
alien_0 = {'color': 'green'}print(alien_0['color'])
Эта конструкция возвращает значение, связанное с ключом 'color', из словаря
alien_0
green
Количество пар «ключ—значение» в словаре не ограничено Например, вот как выглядит исходный словарь alien_0 с двумя парами «ключ—значение»
alien_0 = {'color': 'green', 'points': 5}
Теперь программа может получить значение, связанное с любым из ключей в alien_0 color или points Если игрок сбивает корабль пришельца, для получения
заработанных им очков может использоваться код следующего вида
alien_0 = {'color': 'green', 'points': 5}
 new_points = alien_0['points']
 print("You just earned " + str(new_points) + " points!")

Работа со словарями 103
После того как словарь будет определен, код  извлекает значение, связанное
с ключом 'points', из словаря Затем это значение сохраняется в переменной
new_points Строка  преобразует целое значение в строку и выводит сообщение
с количеством заработанных очков You just earned 5 points!
Если этот код будет выполняться каждый раз, когда игрок сбивает очередного при
шельца, программа будет получать правильное количество очков Добавление новых пар «ключ—значение»
Словари относятся к динамическим структурам данных в словарь можно в любой
момент добавлять новые пары «ключ—значение» Для этого указывается имя сло
варя, за которым в квадратных скобках следует новый ключ с новым значением
Добавим в словарь alien_0 еще два атрибута координаты x и y для вывода изобра
жения пришельца в определенной позиции экрана Допустим, пришелец должен
отображаться у левого края экрана, в пикселах от верхнего края Так как система
экранных координат обычно располагается в левом верхнем углу, для размещения пришельца у левого края координата x должна быть равна , а координата y — ʽ
alien_0 = {'color': 'green', 'points': 5} print(alien_0)
 alien_0['x_position'] = 0
 alien_0['y_position'] = 25
print(alien_0)
Программа начинается с определения того же словаря, с которым мы уже работали
ранее После этого выводится «снимок» текущего состояния словаря В точке 
в словарь добавляется новая пара «ключ—значение» ключ 'x_position' и значе
ние 0 То же самое делается для ключа 'y_position' в точке  При выводе изме
ненного словаря мы видим две дополнительные пары «ключ—значение»
{'color': 'green', 'points': 5} {'color': 'green', 'points': 5, 'y_position': 25, 'x_position': 0}
Окончательная версия словаря содержит четыре пары «ключ—значение» Первые
две определяют цвет и количество очков, а другие две — координаты Обратите
внимание порядок пар «ключ—значение» не соответствует порядку их добавления
th не интересует, в каком порядке добавлялись пары важна лишь связь между каждым ключом и его значением Создание пустого словаря
В некоторых ситуациях бывает удобно (или даже необх одимо) начать с пустого
словаря, а затем добавлять в него новые элементы Чтобы начать заполнение пу
стого словаря, определите словарь с пустой парой фигурных скобок, а затем добав
ляйте новые пары «ключ—значение» (каждая пара в отд ельной строке) Например,
вот как строится словарь aieν

104 Глава 6 • Словари
alien_0 = {} alien_0['color'] = 'green' alien_0['points'] = 5 print(alien_0)
Программа определяет пустой словарь alien_0, после чего добавляет в него зна
чения для цвета и количества очков В результате создается словарь, который ис пользовался в предыдущих примерах
{'color': 'green', 'points': 5}
Обычно пустые словари используются при хранении данных, введенных пользова
телем, или при написании кода, автоматически генерирующего большое количество пар «ключ—значение» Изменение значений в словаре
Чтобы изменить значение в словаре, укажите имя словаря с ключом в квадратных
скобках, а затем новое значение, которое должно быть связано с этим ключом До
пустим, в процессе игры цвет пришельца меняется с зеленого на желтый
alien_0 = {'color': 'green'} print("The alien is " + alien_0['color'] + ".") alien_0['color'] = 'yellow' print("The alien is now " + alien_0['color'] + ".")
Сначала определяется словарь alien_0, который содержит только цвет пришельца
затем значение, связанное с ключом 'color', меняется на 'yellow' Из выходных
данных видно, что цвет пришельца действительно сменился с зеленого на желтый
The alien is green.The alien is now yellow.
Рассмотрим более интересный пример отслеживание позиции пришельца, который
может двигаться с разной скоростью Мы сохраним значение, представляющее
текущую скорость пришельца, и используем его для определения величины горизонтального смещения alien_0 = {'x_position': 0, 'y_position': 25, 'speed': 'medium'} print("Original x-position: " + str(alien_0['x_position']))# Пришелец перемещается вправо.# Вычисляем величину смещения на основании текущей скорости.
 if alien_0['speed'] == 'slow':
x_increment = 1elif alien_0['speed'] == 'medium': x_increment = 2else: # Пришелец двигается быстро. x_increment = 3# Новая позиция равна сумме старой позиции и приращения.

Работа со словарями 105
 alien_0['x_position'] = alien_0['x_position'] + x_increment
print("New x-position: " + str(alien_0['x_position']))
Сначала определяется словарь с исходной позицией (координаты x и y) и ско
ростью 'medium' Значения цвета и количества очков для простоты опущены, но
с ними этот пример работал бы точно так же Также выводится исходное значение x_position
В точке  цепочка ifelif else определяет, на какое расстояние пришелец должен
переместиться вправо полученное значение сохраняется в переменной x_increment
Если пришелец двигается медленно ( 'slow'), то он перемещается на одну единицу
вправо при средней скорости ( 'medium') он перемещается на две единицы вправо
наконец, при высокой скорости ( 'fast') он перемещается на три единицы вправо
Вычисленное смещение прибавляется к значению x_position в , а результат со
храняется в словаре с ключом x_position
Для пришельца со средней скоростью позиция смещается на две единицы
Original x-position: 0 New x-position: 2
Получается, что изменение одного значения в словаре изменяет все поведение при
шельца Например, чтобы превратить пришельца со средней скоростью в быстрого, добавьте следующую строку
alien_0['speed'] = fast
При следующем выполнении кода блок ifelif else присвоит x_increment большее
значение Удаление пар «ключ—значение»
Когда информация, хранящаяся в словаре, перестает быть ненужной, пару «ключ—
значение» можно полностью удалить при помощи команды del При вызове до
статочно передать имя словаря и удаляемый ключ
Например, в следующем примере из словаря alien_0 удаляется ключ 'points'
вместе со значением
alien_0 = {'color': 'green', 'points': 5} print(alien_0)
 del alien_0['points']
print(alien_0)
Строка  приказывает th удалить ключ 'points' из словаря alien_0, а также
удалить значение, связанное с этим ключом Из вывода видно, что ключ 'points'
и его значение исчезли из словаря, но остальные данные остались без измене
ний
{'color': 'green', 'points': 5}{'color': 'green'}

106 Глава 6 • Словари
ПРИМЕЧАНИЕ Учтите, что удаление пары «ключ—значение» отменить уже не удастся� Словарь с однотипными объектами
В предыдущем примере в словаре сохранялась разнообразная информация
об одном объекте (пришельце из компьютерной игры) Словарь также может ис
пользоваться для хранения одного вида информации о многих объектах Допустим,
вы хотите провести опрос среди коллег и узнать их любимый язык программирования Результаты простого опроса удобно сохранить в словаре
favorite_languages = { 'jen': 'python', 'sarah': 'c', 'edward': 'ruby', 'phil': 'python', }
Пары в словаре в этой записи разбиты по строкам Ключами являются имена участ
ников опроса, а значениями — выбранные ими языки Если вы знаете, что для
определения словаря потребуется более одной строки, нажмите клавишу Eter по
сле ввода открывающей фигурной скобки Снабдите следующую строку отступом
на один уровень (четыре пробела) и запишите первую пару «ключ—значение», по
ставив за ней запятую После этого при нажатии Ete r ваш текстовый редактор будет
автоматически снабжать все последующие пары таким же отступом, как у первой
Завершив определение словаря, добавьте закрывающую фигурную скобку в новой
строке после последней пары «ключ—значение» и снабд ите ее отступом на один
уровень, чтобы она была выровнена по ключам За последней парой также рекомен
дуется включить запятую, чтобы при необходимости все было готово к добавлению новой пары «ключ—значение» в следующей строке ПРИМЕЧАНИЕ
В большинстве редакторов предусмотрены функции, упрощающие форматирование расширенных
списков и словарей в описанном стиле� Также существуют другие распространенные способы фор-
матирования длинных словарей — вы можете столкнуться с ними в вашем редакторе или в другом источнике�
Для заданного имени участника опроса этот словарь позволяет легко определить его любимый язык
favorite_languages.py
favorite_languages = { 'jen': 'python','sarah': 'c','edward': 'ruby','phil': 'python',}
 print("Sarah's favorite language is " +
 favorite_languages['sarah'].title() +
 ".")

Перебор словаря 107
Чтобы узнать, какой язык выбран пользователем с именем Sarah, мы запрашиваем
следующее значение
favorite_languages['sarah']
Этот синтаксис используется в команде вывода , а результат содержит значение,
связанное с ключом
Sarah's favorite language is C.
Этот пример также показывает, как разбить длинную команду print на несколько
строк Слово print короче большинства имен словарей, поэтому есть смысл вклю
чить первую часть выводимого текста сразу же за открывающей круглой скоб
кой  Выберите точку, в которой будет разбиваться вывод, и добавьте оператор
конкатенации ( +) в конец первой строки  Нажмите Eter, а затем клавишу a
для выравнивания всех последующих строк на один уровень отступа под командой print Завершив построение вывода, поставьте закрывающую круглую скобку в по
следней строке блока print 
УПРАЖНЕНИЯ
6-1� Человек: используйте словарь для сохранения информации об известном вам чело-
веке� Сохраните имя, фамилию, возраст и город, в котором живет этот человек� Словарь
должен содержать ключи с такими именами, как first_name, last_name, age и city� Выведите каждый фрагмент информации, хранящийся в словаре�
6-2� Любимые числа: используйте словарь для хранения любимых чисел� Возьмите пять
имен и используйте их как ключи словаря� Придумайте любимое число для каждого чело-
века и сохраните его как значение в словаре� Выведите имя каждого человека и его люби-
мое число� Чтобы задача стала более интересной, опросите нескольких друзей и соберите реальные данные для своей программы�
6-3� Глоссарий: словари Python могут использоваться для моделирования «настоящего» словаря (чтобы не создавать путаницы, назовем его «глоссарием»)� • Вспомните пять терминов из области программирования, которые вы узнали в пре- дыдущих главах� Используйте эти слова как ключи глоссария, а их определения — как значения�
• Выведите каждое слово и его определение в аккуратно отформатированном виде� Например, вы можете вывести слово, затем двоеточие и определение; или же слово
в одной строке, а его определение — с отступом в следующей строке� Используйте
символ новой строки (\n) для вставки пустых строк между парами «слово-определе-ние» в выходных данных�
Перебор словаря
Словарь th может содержать как несколько пар «ключ—значение», так и мил
лионы таких пар Поскольку в словаре может храниться большой объем данных,
th предоставляет средства для перебора элементов словаря Информация
может храниться в словарях поразному, поэтому предусмотрены разные способы
перебора Программа может перебрать все пары «ключ—значение» в словаре, толь
ко ключи или только значения

108 Глава 6 • Словари
Перебор всех пар «ключ—значение»
Прежде чем ознакомиться с разными способами перебора, рассмотрим новый
словарь, предназначенный для хранения информации о пользователе вебсайта В следующем словаре хранится имя пользователя, его имя и фамилия user_0 = { 'username': 'efermi', 'first': 'enrico', 'last': 'fermi', }
То, что вы уже узнали в этой главе, позволит вам обратиться к любому отдель
ному атрибуту
user_0 Но что если вы хотите просмотреть все данные
из словаря этого пользователя Для этого можно воспользоваться перебором в цикле for
user.py
user_0 = { 'username': 'efermi','first': 'enrico','last': 'fermi',}
 for key, value in user_0.items():
 print("\nKey: " + key)
 print("Value: " + value)
Как мы видим в точке , чтобы написать цикл for для словаря, необходимо создать
имена для двух переменных, в которых будет храниться ключ и значение из каждой
пары «ключ—значение» Этим двум переменным можно присвоить любые имена — с короткими однобуквенными именами код будет работать точно так же
for k, v in user_0.items()
Вторая половина команды for в точке  включает в себя имя словаря, за которым
следует вызов метода items(), возвращающий список пар «ключ—значение» Цикл
for сохраняет компоненты пары в двух указанных переменных В предыдущем при
мере мы используем переменные для вывода каждого ключа v, за которым следует
связанное значение w "\n" в первой команде print гарантирует, что перед каждой
парой «ключ—значение» в выводе будет вставлена пустая строка
Key: last Value: fermi Key: first Value: enrico Key: username Value: efermi
Снова обратите внимание на то, что пары «ключ—значение» не возвращаются
в порядке их хранения даже при переборе в словаре th не интересует порядок

Перебор словаря 109
хранения пар «ключ—значение» отслеживаются только связи между отдельными ключами и их значениями
Перебор всех пар «ключ—значение» особенно хорошо работает для таких сло
варей, как в примере favorite_languages.py на с ؽ то есть для словарей, хра
нящих один вид информации со многими разными ключами Перебрав словарь favorite_languages , вы получите имя каждого человека и его любимый язык
программирования Так как ключ всегда содержит имя, а значение — язык про
граммирования, в цикле вместо имен key и value используются переменные name
и language С таким выбором имен читателю кода будет проще следить за тем, что
происходит в цикле
favorite_languages.py
favorite_languages = { 'jen': 'python','sarah': 'c','edward': 'ruby','phil': 'python',}
 for name, language in favorite_languages.items():
 print(name.title() + "'s favorite language is " +
language.title() + ".")
Код в точке  приказывает th перебрать все пары «ключ—значение» в словаре
В процессе перебора пар ключ сохраняется в переменной name, а значение — в пере
менной language С этими содержательными именами намного проще понять, что
делает команда print в точке 
Всего в нескольких строках кода выводится вся информация из опроса
Jen's favorite language is Python. Sarah's favorite language is C.Phil's favorite language is Python.Edward's favorite language is Ruby.
Такой способ перебора точно так же работает и в том случае, если в словаре будут храниться результаты опроса тысяч и даже миллионов людей Перебор всех ключей в словаре Метод keys() удобен в тех случаях, когда вы не собираетесь работать со всеми
значениями в словаре Переберем словарь favorite_languages и выведем имена
всех людей, участвовавших в опросе
favorite_languages = { 'jen': 'python','sarah': 'c','edward': 'ruby','phil': 'python',}
 for name in favorite_languages.keys():
print(name.title())

110 Глава 6 • Словари
Строка  приказывает th извлечь из словаря favorite_languages все ключи
и последовательно сохранять их в переменной name В выходных данных представ
лены имена всех людей, участвовавших в опросе
Jen SarahPhilEdward
На самом деле перебор ключей используется по умолчанию при переборе словаря, так что этот код будет работать точно так же, как если бы вы написали for name in favorite_languages:
вместо… for name in favorite_languages.keys():
Используйте явный вызов метода keys(), если вы считаете, что он упростит чтение
вашего кода, — или опустите его при желании
Чтобы обратиться в цикле к значению, связанному с интересующим вас ключом,
используйте текущий ключ Для примера выведем для пары друзей сообщение
о выбранном ими языке Мы переберем имена в словаре, как это делалось ранее,
но, когда имя совпадает с именем одного из друзей, программа будет выводить специальное сообщение об их любимом языке
favorite_languages = { 'jen': 'python','sarah': 'c','edward': 'ruby','phil': 'python',}
 friends = ['phil', 'sarah']
for name in favorite_languages.keys(): print(name.title())
 if name in friends:
print(" Hi " + name.title() + ", I see your favorite language is " +
 favorite_languages[name].title() + "!")
В точке  строится список друзей, для которых должно выводиться сообщение
В цикле выводится имя очередного участника опроса, а затем в точке  програм
ма проверяет, входит ли текущее имя в список друзей Если имя входит в список,
выводится специальное приветствие с упоминанием выбранного языка Чтобы
получить язык в точке , мы используем имя словаря и текущее значение name
как ключ Имя выводится для всех участников, но только друзья получают еще и специальное сообщение
Edward Phil Hi Phil, I see your favorite language is Python!Sarah

Перебор словаря 111
Hi Sarah, I see your favorite language is C! Jen
Метод keys() также может использоваться для проверки того, участвовал ли кон
кретный человек в опросе
favorite_languages = { 'jen': 'python', 'sarah': 'c', 'edward': 'ruby', 'phil': 'python', }
 if 'erin' not in favorite_languages.keys():
print("Erin, please take our poll!")
Метод keys() не ограничивается перебором он возвращает список всех ключей,
и строка  просто проверяет, входит ли ключ 'erin' в список Так как ключ в спи
ске отсутствует, программа выводит сообщение
Erin, please take our poll!
Упорядоченный перебор ключей словаря
Словарь всегда поддерживает связь между ключом и связанным с ним значением,
но порядок получения элементов из словаря непредсказуем Впрочем, это не со
здает проблем, потому что обычно требуется лишь получить правильное значение, связанное с каждым ключом
Один из способов получения элементов в определенном порядке основан на сор
тировке ключей, возвращаемых циклом for Для получения упорядоченной копии
ключей можно воспользоваться функцией sorted()
favorite_languages = { 'jen': 'python','sarah': 'c','edward': 'ruby','phil': 'python',}
for name in sorted(favorite_languages.keys()): print(name.title() + ", thank you for taking the poll.")
Эта команда for не отличается от других команд for, если не считать того, что
метод dictionary.keys() заключен в вызов функции sorted() Эта конструкция
приказывает th выдать список всех ключей в словаре и отсортировать его
перед тем, как перебирать элементы В выводе перечислены все пользователи, участвовавшие в опросе, а их имена упорядочены по алфавиту
Edward, thank you for taking the poll. Jen, thank you for taking the poll. Phil, thank you for taking the poll.
Sarah, thank you for taking the poll.

112 Глава 6 • Словари
Перебор всех значений в словаре
Если вас прежде всего интересуют значения, содержащиеся в словаре, используйте метод values() для получения списка значений без ключей Допустим, вы хотите
просто получить список всех языков, выбранных в опросе, и вас не интересуют имена людей, выбравших каждый язык
favorite_languages = { 'jen': 'python','sarah': 'c','edward': 'ruby','phil': 'python',}
print("The following languages have been mentioned:") for language in favorite_languages.values(): print(language.title())
Команда for читает каждое значение из словаря и сохраняет его в переменной
language При выводе этих значений будет получен список всех выбранных языков
The following languages have been mentioned: Python C Python Ruby
Значения извлекаются из словаря без проверки на возможные повторения Для не
больших словарей это может быть приемлемо, но в опросах с большим количеством
респондентов список будет содержать слишком много дубликатов Чтобы получить
список выбранных языков без повторений, можно воспользоваться множеством (set)
Множество в целом похоже на список, но все его элементы должны быть уникальными
favorite_languages = { 'jen': 'python','sarah': 'c','edward': 'ruby','phil': 'python',} print("The following languages have been mentioned:")
 for language in set(favorite_languages.values()): print(language.title())
Когда список, содержащий дубликаты, заключается в вызов set(), th находит
уникальные элементы списка и строит множество из этих элементов В точке 
set() используется для извлечения уникальных языков из favorite_languages.
values() В результате создается не содержащий дубликатов список языков про
граммирования, упомянутых участниками опроса
The following languages have been mentioned: PythonCRuby

Вложение 113
В ходе дальнейшего изучения th вы часто будете обнаруживать встроенные
возможности языка, которые помогают сделать с данными именно то, что вам требуется
УПРАЖНЕНИЯ
6-4� Глоссарий 2: теперь, когда вы знаете, как перебрать элементы словаря, упростите код
из упражнения 6-3, заменив серию команд print циклом, перебирающим ключи и значения
словаря� Когда вы будете уверены в том, что цикл работает, добавьте в глоссарий еще пять
терминов Python� При повторном запуске программы новые слова и значения должны быть автоматически включены в вывод�
6-5� Реки: создайте словарь с тремя большими реками и странами, по которым протекает
каждая река� Одна из возможных пар «ключ—значение» — ‘nile’: ‘egypt’�• Используйте цикл для вывода сообщения с упоминанием реки и страны — например, «The Nile runs through Egypt�»
• Используйте цикл для вывода названия каждой реки, включенной в словарь�
• Используйте цикл для вывода названия каждой страны, включенной в словарь�
6-6� Опрос: Возьмите за основу код favorite_languages�py (с� 106)� • Создайте список людей, которые должны участвовать в опросе по поводу любимо- го языка программирования� Включите некоторые имена, которые уже присутствуют в списке, и некоторые имена, которых в списке еще нет�
• Переберите список людей, которые должны участвовать в опросе� Если они уже прош- ли опрос, выведите сообщение с благодарностью за участие� Если они еще не про-ходили опрос, выведите сообщение с предложением принять участие�
Вложение
Иногда нужно сохранить множество словарей в списке или сохранить спи
сок как значение элемента словаря Создание сложных структур такого рода
называется вложением Вы можете вложить множество словарей в список,
список элементов в словарь или даже словарь внутрь другого словаря Как
наглядно показывают следующие примеры, вложение — чрезвычайно мощный механизм
Список словарей Словарь alien_0 содержит разнообразную информацию об одном пришельце, но
в нем нет места для хранения информации о втором пр ишельце, не говоря уже
о целом экране, забитом пришельцами Как смоделировать флот вторжения На
пример, можно создать список пришельцев, в котором каждый элемент представ
ляет собой словарь с информацией о пришельце Следующий код строит список из трех пришельцев
aliens.py alien_0 = {'color': 'green', 'points': 5} alien_1 = {'color': 'yellow', 'points': 10}alien_2 = {'color': 'red', 'points': 15}

114 Глава 6 • Словари
 aliens = [alien_0, alien_1, alien_2]
for alien in aliens: print(alien)
Сначала создаются три словаря, каждый из которых представляет отдельного при
шельца В точке  каждый словарь заносится в список с именем aliens Наконец,
программа перебирает список и выводит каждого пришельца
{'color': 'green', 'points': 5} {'color': 'yellow', 'points': 10} {'color': 'red', 'points': 15}
Конечно, в реалистичном примере будут использоваться более трех пришельцев,
которые будут генерироваться автоматически В следующем примере функция range() создает флот из пришельцев
# Создание пустого списка для хранения пришельцев. aliens = []# Создание 30 зеленых пришельцев.
 for alien_number in range(30):
 new_alien = {'color': 'green', 'points': 5, 'speed': 'slow'}
 aliens.append(new_alien)
# Вывод первых 5 пришельцев:
 for alien in aliens[:5]:
print(alien)print("...") # Вывод количества созданных пришельцев.
 print("Total number of aliens: " + str(len(aliens)))
В начале примера список для хранения всех пришельцев, которые будут созданы,
пуст В точке  функция range() возвращает множество чисел, которое просто
сообщает th, сколько раз должен повторяться цикл При каждом выполнении
цикла создается новый пришелец , который затем добавляется в список aliens 
В точке  срез используется для вывода первых пяти пришельцев, а в точке 
выводится длина списка (для демонстрации того, что программа действительно
сгенерировала весь флот из пришельцев)
{'speed': 'slow', 'color': 'green', 'points': 5} {'speed': 'slow', 'color': 'green', 'points': 5} {'speed': 'slow', 'color': 'green', 'points': 5} {'speed': 'slow', 'color': 'green', 'points': 5} {'speed': 'slow', 'color': 'green', 'points': 5} ...Total number of aliens: 30
Все пришельцы обладают одинаковыми характеристиками, но th рассматри
вает каждого пришельца как отдельный объект, что позволяет изменять атрибуты каждого владельца по отдельности
Как работать с таким множеством Представьте, что в этой игре некоторые при
шельцы изменяют цвет и начинают двигаться быстрее Когда приходит время

Вложение 115
смены цветов, мы можем воспользоваться циклом for и командой if для изме
нения цвета Например, чтобы превратить первых трех пришельцев в желтых,
двигающихся со средней скоростью и приносящих игрок у по очков, можно
действовать так
# Создание пустого списка для хранения пришельцев. aliens = [] # Создание 30 зеленых пришельцев. for alien_number in range (0,30):new_alien = {'color': 'green', 'points': 5, 'speed': 'slow'}aliens.append(new_alien)
for alien in aliens[0:3]: if alien['color'] == 'green': alien['color'] = 'yellow' alien['speed'] = 'medium' alien['points'] = 10
# Вывод первых 5 пришельцев: for alien in aliens[0:5]:print(alien)print("...")
Чтобы изменить первых трех пришельцев, мы перебираем элементы среза, включа
ющего только первых трех пришельцев В данный момент все пришельцы зеленые ( 'green' ), но так будет не всегда, поэтому мы пишем команду if, которая гаранти
рует, что изменяться будут только зеленые пришельцы Если пришелец зеленый,
то его цвет меняется на желтый ( 'yellow'), скорость на среднюю ( 'medium'), а на
града увеличивается до очков
{'speed': 'medium', 'color': 'yellow', 'points': 10} {'speed': 'medium', 'color': 'yellow', 'points': 10} {'speed': 'medium', 'color': 'yellow', 'points': 10} {'speed': 'slow', 'color': 'green', 'points': 5} {'speed': 'slow', 'color': 'green', 'points': 5} ...
Цикл можно расширить, добавив блок elif для превращения желтых пришельцев
в красных — быстрых и приносящих игроку по очков Мы не станем приводить весь код, а цикл выглядит так
for alien in aliens[0:3]: if alien['color'] == 'green':alien['color'] = 'yellow'alien['speed'] = 'medium'alien['points'] = 10
elif alien['color'] == 'yellow': alien['color'] = 'red' alien['speed'] = 'fast' alien['points'] = 15
Решение с хранением словарей в списке достаточно часто встречается тогда, когда
каждый словарь содержит разные атрибуты одного объек та Например, вы можете

116 Глава 6 • Словари
создать словарь для каждого пользователя сайта, как это было сделано в програм ме user�py на с , и сохранить отдельные словари в списке с именем users Все
словари в списке должны иметь одинаковую структуру, чтобы вы могли перебрать список и выполнить с каждым объектом словаря одни и те же операции Список в словаре
Иногда бывает удобно поместить список в словарь, вместо того чтобы помещать
словарь в список Представьте, как бы вы описали в программе заказанную пиццу
Если ограничиться только списком, сохранить удастся разве что список дополне
ний к пицце При использовании словаря список дополнений может быть всего лишь одним аспектом описания пиццы
В следующем примере для каждой пиццы сохраняются два вида информации тип
теста и список дополнений Список дополнений представляет собой значение,
связанное с ключом 'toppings' Чтобы использовать элементы в списке, нужно
указать имя словаря и ключ 'toppings', как и для любого другого значения в сло
варе Вместо одного значения будет получен список дополнений
pizza.py # Сохранение информации о заказанной пицце.
 pizza = {
'crust': 'thick', 'toppings': ['mushrooms', 'extra cheese'], } # Описание заказа.
 print("You ordered a " + pizza['crust'] + "-crust pizza " +
"with the following toppings:")
 for topping in pizza['toppings']:
print("\t" + topping)
Работа начинается в точке  со словаря с информацией о заказанной пицце
С ключом в словаре 'crust' связано строковое значение 'thick' С другим ключом
'toppings' связано значениесписок, в котором хранятся все заказанные допол
нения В точке  выводится описание заказа перед созданием пиццы Чтобы вы
вести список дополнений, мы используем ключ 'toppings', а th берет список
дополнений из словаря Следующее сообщение описывает пиццу, которую мы собираемся создать
You ordered a thick-crust pizza with the following toppings: mushrooms extra cheese
Вложение списка в словарь может применяться каждый раз, когда с одним ключом
словаря должно быть связано более одного значения Если бы в предыдущем при
мере с языками программирования ответы сохранялись в списке, один участник
опроса мог бы выбрать сразу несколько любимых языков При переборе словаря
значение, связанное с каждым человеком, представляло бы собой список языков

Вложение 117
(вместо одного языка) В цикле for словаря создается другой цикл для перебора
списка языков, связанных с каждым участником
favorite_languages.py
 favorite_languages = {
'jen': ['python', 'ruby'], 'sarah': ['c'], 'edward': ['ruby', 'go'], 'phil': ['python', 'haskell'], }
 for name, languages in favorite_languages.items():
print("\n" + name.title() + "'s favorite languages are:")
 for language in languages:
print("\t" + language.title())
Вы видите в точке , что значение, связанное с каждым именем, теперь представ
ляет собой список У некоторых участников только один любимый язык програм
мирования, у других таких языков несколько При переборе словаря в точке 
переменная с именем languages используется для хранения каждого значения
из словаря, потому что мы знаем, что каждое значение будет представлять собой
список В основном цикле по элементам словаря другой цикл  перебирает эле
менты списка любимых языков каждого участника
Теперь каждый участник опроса может указать сколько угодно любимых языков программирования
Jen's favorite languages are: Python Ruby Sarah's favorite languages are: C Phil's favorite languages are: Python Haskell Edward's favorite languages are: Ruby Go
Чтобы дополнительно усовершенствовать программу, включите в начало цикла for
словаря команду if для проверки того, выбрал ли данный участник более одного
языка программирования (проверка основана на значении len(languages)) Если
у участника только один любимый язык, текст сообщения изменяется для един
ственного числа (например, «arah’s arite aae is »)
ПРИМЕЧАНИЕ
Глубина вложения списков и словарей не должна быть слишком большой� Если вам при-
ходится вкладывать элементы на глубину существенно бо ́льшую, чем в предыдущих при-
мерах, или если вы работаете с чужим кодом со значительной глубиной вложения, скорее всего, у задачи существует более простое решение�

118 Глава 6 • Словари
Словарь в словаре
Словарь также можно вложить в другой словарь, но в таких случаях код быстро
усложняется Например, если на сайте есть несколько пользователей с уникаль
ными именами, вы можете использовать имена пользователей как ключи в сло
варе Информация о каждом пользователе при этом хранится в словаре, который
используется как значение, связанное с именем В следующем примере о каждом
пользователе хранятся три вида информации имя, фамилия и место жительства
Чтобы получить доступ к этой информации, переберите имена пользователей и словарь с информацией, связанной с каждым именем
many_users.py users = { 'aeinstein': { 'first': 'albert', 'last': 'einstein', 'location': 'princeton', }, 'mcurie': { 'first': 'marie', 'last': 'curie', 'location': 'paris', }, }
 for username, user_info in users.items():
 print("\nUsername: " + username)
 full_name = user_info['first'] + " " + user_info['last']
location = user_info['location']
 print("\tFull name: " + full_name.title())
print("\tLocation: " + location.title())
В программе определяется словарь с именем users, содержащий два ключа для
пользователей 'aeinstein' и 'mcurie' Значение, связанное с каждым ключом,
представляет собой словарь с именем, фамилией и местом жительства пользова
теля В процессе перебора словаря users в точке  th сохраняет каждый ключ
в переменной username, а словарь, связанный с каждым именем пользователя, со
храняется в переменной user_info Внутри основного цикла в словаре выводится
имя пользователя 
В точке  начинается работа с внутренним словарем Переменная user_info, со
держащая словарь с информацией о пользователе, содержит три ключа 'first',
'last' и 'location' Каждый ключ используется для построения аккуратно от
форматированных данных с полным именем и местом жительства пользователя, с последующим выводом сводки известной информации о пользователе 
Username: aeinstein Full name: Albert Einstein Location: Princeton Username: mcurie Full name: Marie Curie Location: Paris

Итоги 119
Обратите внимание на идентичность структур словарей всех пользователей Хотя
th этого и не требует, наличие единой структуры упрощает работу с вложен
ными словарями Если словари разных пользователей будут содержать разные ключи, то код в цикле for заметно усложнится
УПРАЖНЕНИЯ
6-7� Люди: начните с программы, написанной для упражнения 6-1 (с� 107)� Создайте два
новых словаря, представляющих разных людей, и сохраните все три словаря в списке
с именем people� Переберите элементы списка людей� В процессе перебора выведите всю
имеющуюся информацию о каждом человеке�
6-8� Домашние животные: создайте несколько словарей, имена которых представляют
клички домашних животных� В каждом словаре сохраните информацию о виде животно-
го и имени владельца� Сохраните словари в списке с именем pets� Переберите элементы списка� В процессе перебора выведите всю имеющуюся информацию о каждом животном�
6-9� Любимые места: создайте словарь с именем favorite_places� Придумайте названия трех
мест, которые станут ключами словаря, и сохраните для каждого человека от одного до
трех любимых мест� Чтобы задача стала более интересной, опросите нескольких друзей
и соберите реальные данные для своей программы� Переберите данные в словаре, выведи-те имя каждого человека и его любимые места�
6-10� Любимые числа: измените программу из упражнения 6-2 (с� 107), чтобы для каждого
человека можно было хранить более одного любимого числа� Выведите имя каждого чело-века в списке и его любимые числа�
6-11� Города: создайте словарь с именем cities� Используйте названия трех городов в каче-
стве ключей словаря� Создайте словарь с информацией о каждом городе; включите в него
страну, в которой расположен город, примерную численность населения и один примеча-
тельный факт, относящийся к этому городу� Ключи словаря каждого города должны на-
зываться country, population и fact� Выведите название каждого города и всю сохраненную информацию о нем�
6-12� Расширение: примеры, с которыми мы работаем, стали достаточно сложными, и в них
можно вносить разного рода усовершенствования� Воспользуйтесь одним из примеров этой
главы и расширьте его: добавьте новые ключи и значения, измените контекст программы или улучшите форматирование вывода�
Итоги
В этой главе вы научились определять словари и работать с хранящейся в них
информацией Вы узнали, как обращаться к отдельным элемента м словаря и из
менять их, как перебрать всю информацию в словаре Вы научились перебирать
пары «ключ—значение», ключи и значения словаря Также были рассмотрены воз
можности вложения словарей в список, вложения списков в словари и вложения словарей в другие словари
В следующей главе будут рассмотрены циклы while и получение входных данных
от пользователей программ Эта глава будет особенно интересной, потому что вы
наконецто сможете сделать свои программы интерактивными они начнут реагировать на действия пользователя

7 Ввод данных и циклы while
Программы, как правило, пишутся для решения задач конечного пользователя Для
этого им обычно нужна некоторая информация, которую должен ввести пользо
ватель Простой пример допустим, пользователь хочет узнать, достаточен ли его
возраст для голосования Если вы пишете программу для ответа на этот вопрос, то
вам нужно будет узнать возраст пользователя Программа должна запросить у поль
зователя значение — его возраст когда у программы появятся данные, она может сравнить их с возрастом, дающим право на голосование, и сообщить результат
В этой главе вы узнаете, как получить пользовательский ввод (то есть входные
данные), чтобы программа могла работать с ним Например, таким вводом может
быть отдельное имя или список имен Для получения данных в программах ис
пользуется функция input()
Вы также научитесь продолжать работу программы, пока пользователь вводит
новые данные после получения всех данных программа переходит к работе с полу
ченной информацией Цикл while в языке th позволяет выполнять программу,
пока некоторое условие остается истинным
Когда вы научитесь работать с пользовательским вводом и управлять продолжи
тельностью выполнения программы, вы сможете создавать полностью интерак
тивные программы Как работает функция input() Функция input() приостанавливает выполнение программы и ожидает, пока
пользователь введет некоторый текст Получив ввод, th сохраняет его в пере
менной, чтобы вам было удобнее работать с ним
Например, следующая программа предлагает пользователю ввести текст, а затем выводит сообщение для пользователя
parrot.py message = input("Tell me something, and I will repeat it back to you: ") print(message)
Функция input() получает один аргумент текст подсказки (или инструкции),
который выводится на экран, чтобы пользователь понимал, что от него требуется
В данном примере при выполнении первой строки пользователь видит подсказку
с предложением ввести любой текст Программа ожидает, пока пользователь введет

Как работает функция input() 121
ответ, и продолжает работу после нажатия Enter Ответ сохраняется в переменной
message , после чего вызов print(message) дублирует введенные данные
Tell me something, and I will repeat it back to you: Hello everyone!
Hello everyone! ПРИМЕЧАНИЕ
Sublime Text не запускает программы, запрашивающие входные данные у пользователя� Вы
можете использовать Sublime Text для создания таких программ, но запускать их придется из терминального окна� См� «Запуск программ Python в терминале», с� 29�
Содержательные подсказки
Каждый раз, когда в вашей программе используется функция input(), вы должны
включать четкую, понятную подсказку, которая точно сообщит пользователю,
какую информацию вы от него хотите получить Подойдет любое предложение, которое сообщает пользователю, что нужно вводить Пример
greeter.py name = input("Please enter your name: ") print("Hello, " + name + "!")
Добавьте пробел в конце подсказки (после двоеточия в предыдущем примере),
чтобы отделить подсказку от данных, вводимых пользователем, и четко показать, где должен вводиться текст Пример Please enter your name: Eric
Hello, Eric!
Иногда подсказка занимает более одной строки Например, вы можете сообщить поль
зователю, для чего программа запрашивает данные Текст подсказки можно сохранить
в переменной и передать эту переменную функции input() вы строите длинное при
глашение из нескольких строк, а потом выполняете одну компактную команду input()
greeter.py prompt = "If you tell us who you are, we can personalize the messages you see." prompt += "\nWhat is your first name? " name = input(prompt) print("\nHello, " + name + "!")
В этом примере продемонстрирован один из способов построения длинных строк
Первая часть длинного сообщения сохраняется в переменной prompt Затем опера
тор += объединяет текст, хранящийся в prompt, с новым фрагментом текста
Теперь содержимое prompt занимает две строки (вопросительный знак снова от
деляется от ввода пробелом)
If you tell us who you are, we can personalize the messages you see. What is your first name? Eric
Hello, Eric!

122 Глава 7 • Ввод данных и циклы while
Использование int() для получения числового ввода
При использовании функции input() th интерпретирует все данные, введен
ные пользователем, как строку В следующем сеансе интерпретатор а программа
запрашивает у пользователя возраст
>>> age = input("How old are you? ")
How old are you? 21
>>> age
'21'
Пользователь вводит число , но, когда мы запрашиваем у th значение age , выводится '21' — представление введенного числа в строковом формате
Кавычки, в которые заключены данные, указывают на то, что th интерпре
тирует ввод как строку Но попытка использовать данные как число приведет к ошибке
>>> age = input("How old are you? ")
How old are you? 21
 >>> age >= 18
Traceback (most recent call last): File "", line 1, in
 TypeError: unorderable types: str() >= int()
Когда вы пытаетесь сравнить введенные данные с числом в точке , th выдает
ошибку, потому что не может сравнить строку с числом строка '21', хранящаяся
в age , не сравнивается с числовым значением ͼ происходит ошибка 
Проблему можно решить при помощи функции int(), интерпретирующей строку
как числовое значение Функция int() преобразует строковое представление числа
в само число
>>> age = input("How old are you? ")
How old are you? 21
 >>> age = int(age)
>>> age >= 18
True
В этом примере введенный текст 21 интерпретируется как строка, но затем он
преобразуется в числовое представление вызовом int() в точке  Теперь th
может проверить условие сравнить переменную age (которая теперь содержит
числовое значение ) с ͆ Условие «значение age больше или равно » выпол
няется, и результат проверки равен True
Как использовать функцию int() в реальной программе Допустим, программа
проверяет рост пользователя и определяет, достаточен ли он для катания на ат
тракционе
rollercoaster.py height = input("How tall are you, in inches? ") height = int(height)

Как работает функция input() 123
if height >= 36: print("\nYou're tall enough to ride!")else: print("\nYou'll be able to ride when you're a little older.")
Программа может сравнить height с , потому что строка height = int(height)
преобразует входное значение в число перед проведением сравнения Если введен
ное число больше или равно , программа сообщает пользователю, что он прошел проверку
How tall are you, in inches? 71
You're tall enough to ride!
Если пользователь вводит числовые данные, которые используются в вашей про
грамме для вычислений и сравнений, обязательно преобразуйте введенное значение в его числовой эквивалент Оператор вычисления остатка
При работе с числовыми данными может пригодиться оператор вычисления остат
ка ( %), который делит одно число на другое и возвращает остаток
>>> 4 % 3
1 >>> 5 % 3
2>>> 6 % 3
0>>> 7 % 3
1
Оператор % не сообщает частное от целочисленного деления он возвращает только
остаток
Когда одно число нацело делится на другое, остаток равен , и оператор % возвра
щает Ά Например, этот факт может использоваться для проверки четности или нечетности числа
even_or_odd.py number = input("Enter a number, and I'll tell you if it's even or odd: ") number = int(number) if number % 2 == 0: print("\nThe number " + str(number) + " is even.")else: print("\nThe number " + str(number) + " is odd.")
Четные числа всегда делятся на Ć Следовательно, если остаток от деления на равен ( number % 2 == 0 ), число четное, а если нет — нечетное
Enter a number, and I'll tell you if it's even or odd: 42
The number 42 is even.

124 Глава 7 • Ввод данных и циклы while
Ввод данных в Python 2�7
Если вы работаете с th Ć, для запроса данных у пользователя следует ис
пользовать функцию raw_input() Эта функция интерпретирует весь ввод как
строку — точно так же, как функция input() в th ҆
В th Ć также есть функция input(), но эта функция интерпретирует пользо
вательский ввод как код th и пытается выполнить его В лучшем случае th
не сможет интерпретировать введенные данные, и вы получите сообщение об ошиб
ке в худшем случае будет выполнен код, который вы выполнять не собирались
Итак, в th Ć вместо функции input() используется функция raw_input()
УПРАЖНЕНИЯ
7-1� Прокат машин: напишите программу, которая спрашивает у пользователя, какую ма-
шину он хотел бы взять напрокат� Выведите сообщение с введенными данными (например,
“Let me see if I can find you a Subaru”)�
7-2� Заказ стола: напишите программу, которая спрашивает у пользователя, на сколько
мест он хочет забронировать стол в ресторане� Если введенное число больше 8, выведите
сообщение о том, что пользователю придется подождать� В противном случае сообщите, что стол готов�
7-3� Числа, кратные 10: запросите у пользователя число и сообщите, кратно оно 10 или нет�
Циклы while Цикл for получает коллекцию элементов и выполняет блок кода по одному разу
для каждого элемента в коллекции В отличие от него, цикл while продолжает вы
полняться, пока остается истинным некоторое условие
Цикл while в действии
Цикл while может использоваться для перебора числовой последо вательности
Например, следующий цикл считает от до ʽ
counting.py current_number = 1 while current_number <= 5: print(current_number) current_number += 1
В первой строке отсчет начинается с , для чего current_number присваивается
значение Æ Далее запускается цикл while, который продолжает работать, пока
значение current_number остается меньшим или равным ʆ Код в цикле выводит
значение current_number и увеличивает его на командой current_number += 1
(Оператор += является сокращенной формой записи для current_number =
current_number + 1 )
Цикл повторяется, пока условие current_number <= 5 остается истинным Так как
меньше , th выводит , а затем увеличивает значение на , отчего current_
number становится равным Ć Так как меньше , th выводит и снова при

Циклы while 125
бавляет и т д Как только значение current_number превысит , цикл останавли
вается, а программа завершается
1 2345
Очень многие повседневные программы содержат циклы while Например,
представьте компьютерную игру цикл while выполняется, пока игра про
должается, и завершается, как только игрок захочет остановить игру Вряд ли
когонибудь обрадует, если программа завершает работу преждевременно или
продолжает работать, когда ей приказали остановиться, так что циклы while
весьма полезны
Пользователь решает прервать работу программы
Программа parrot�py может выполняться, пока пользователь не захочет остановить
ее, — для этого б ульшая часть кода заключается в цикл while В программе опре
деляется признак завершения , и программа работает, пока пользователь не введет
нужное значение
parrot.py
 prompt = "\nTell me something, and I will repeat it back to you:"
prompt += "\nEnter 'quit' to end the program. "
 message = ""
 while message != 'quit':
message = input(prompt) print(message)
В точке  определяется сообщение, которое объясняет, что у пользователя есть
два варианта ввести сообщение или ввести признак завершения (в данном случае
это строка 'quit') Затем переменной message  присваивается значение, введен
ное пользователем В программе переменная message инициализируется пустой
строкой "", чтобы значение проверялось без ошибок при первом выполнении
строки while Когда программа только запускается и выполнение достигает ко
манды while, значение message необходимо сравнить с 'quit', но пользователь еще
не вводил никакие данные Если у th нет данных для сравнения, продолжение
выполнения становится невозможным Чтобы решить эту проблему, необходимо предоставить message исходное значение И хотя это всего лишь пустая строка, для
th такое значение выглядит вполне осмысленно программа сможет выполнить
сравнение, на котором основана работа цикла while Цикл while  выполняется,
пока значение message не равно 'quit'
При первом выполнении цикла message содержит пустую строку, и th входит
в цикл При выполнении команды message = input(prompt) th отображает
подсказку и ожидает, пока пользователь введет данные Эти данные сохраняются в message и выводятся командой print после этого th снова проверяет усло
вие команды while Пока пользователь не введет слово 'quit', приглашение будет

126 Глава 7 • Ввод данных и циклы while
выводиться снова и снова, а th будет ожидать новых данных При вводе слова 'quit' th перестает выполнять цикл while, а программа завершается
Tell me something, and I will repeat it back to you: Enter 'quit' to end the program. Hello everyone!
Hello everyone! Tell me something, and I will repeat it back to you: Enter 'quit' to end the program. Hello again.
Hello again. Tell me something, and I will repeat it back to you: Enter 'quit' to end the program. quit
quit
Программа работает неплохо, если не считать того, что она выводит слово 'quit',
словно оно является обычным сообщением Простая проверка if решает про
блему
prompt = "\nTell me something, and I will repeat it back to you:"
prompt += "\nEnter 'quit' to end the program. " message = ""while message != 'quit':message = input(prompt)
if message != 'quit':print(message)
Теперь программа проводит проверку перед выводом сообщения и выводит со
общение только в том случае, если оно не совпадает с признаком завершения
Tell me something, and I will repeat it back to you: Enter 'quit' to end the program. Hello everyone!
Hello everyone! Tell me something, and I will repeat it back to you: Enter 'quit' to end the program. Hello again.
Hello again. Tell me something, and I will repeat it back to you: Enter 'quit' to end the program. quit
Флаги
В предыдущем примере программа выполняла некоторые операции, пока заданное
условие оставалось истинным А что если вы пишете более сложную программу, выполнение которой может прерываться по нескольким разным условиям
Например, компьютерная игра может завершаться по разным причинам у игро
ка кончились все «жизни» прошло отведенное время все города, которые он
должен был защищать, были уничтожены и т д Игра должна завершаться
при выполнении любого из этих условий Попытки проверять все возможные
условия в одной команде while быстро усложняются и становятся слишком
громоздкими

Циклы while 127
Если программа должна выполняться только при истинн ости нескольких условий,
определите одну переменную флаг Эта переменная сообщает, должна ли програм
ма выполняться далее Программу можно написать так, чтобы она продолжала
выполнение, если флаг находится в состоянии True, и завершалась, если любое
из нескольких событий перевело флаг в состояние False В результате в команде
while достаточно проверить всего одно условие находится ли флаг в состоянии
True Все остальные проверки (которые должны определить, произошло ли собы
тие, переводящее флаг в состояние False) удобно организуются в остальном коде
Добавим флаг в программу parrot�py из предыдущего раздела Этот флаг, который
мы назовем active (хотя переменная может называться как угодно), управляет тем,
должно ли продолжаться выполнение программы
prompt = "\nTell me something, and I will repeat it back to you:" prompt += "\nEnter 'quit' to end the program. "
 active = True
 while active:
message = input(prompt)
 if message == 'quit':
active = False
 else:
print(message)
В точке  переменной active присваивается True, чтобы программа начинала
работу в активном состоянии Это присваивание упрощает команду while, потому
что в самой команде while никакие сравнения не выполняются вся логика реали
зуется в других частях программы Пока переменная active остается равной True,
цикл выполняется
В команде if внутри цикла while значение message проверяется после того, как
пользователь введет данные Если пользователь ввел строку 'quit'  , флаг active
переходит в состояние False, а цикл while останавливается Если пользователь ввел
любой текст, кроме 'quit'  , то введенные им данные выводятся как сообщение
Результаты работы этой программы ничем не отличаются от предыдущего приме
ра, в котором условная проверка выполняется прямо в команде while Но теперь
в программе имеется флаг, указывающий, находится ли она в активном состоянии,
и вы сможете легко добавить новые проверки (в форме команд elif) для событий,
с которыми переменная active может перейти в состояние False Это может быть
удобно в сложных программах — например, в компьютерных играх с многочислен
ными событиями, каждое из которых может привести к завершению программы
Когда по любому из этих событий флаг active переходит в состояние False, основ
ной игровой цикл прервется, выводится сообщение о завершении игры, и у игрока появляется возможность сыграть еще раз
Команда break и выход из цикла
Чтобы немедленно прервать цикл while без выполнения оставшегося кода в цикле
независимо от состояния условия, используйте команду break Команда break

128 Глава 7 • Ввод данных и циклы while
управляет ходом выполнения программы она позволит вам управлять тем, какая часть кода выполняется, а какая нет
Рассмотрим пример — программу, которая спрашивает у по льзователя, в каких го
родах он бывал Чтобы прервать цикл while, программа выполняет команду break,
как только пользователь введет значение 'quit'
cities.py prompt = "\nPlease enter the name of a city you have visited:" prompt += "\n(Enter 'quit' when you are finished.) "
 while True:
city = input(prompt) if city == 'quit': break else: print("I'd love to go to " + city.title() + "!")
Цикл, который начинается с while True  , будет выполняться бесконечно — если
только в нем не будет выполнена команда break Цикл в программе продолжает
запрашивать у пользователя названия городов, пока пользователь не введет строку 'quit' При вводе строки 'quit' выполняется команда break, по которой th
выходит из цикла
Please enter the name of a city you have visited: (Enter 'quit' when you are finished.) New York
I'd love to go to New York! Please enter the name of a city you have visited: (Enter 'quit' when you are finished.) San Francisco
I'd love to go to San Francisco! Please enter the name of a city you have visited: (Enter 'quit' when you are finished.) quit
ПРИМЕЧАНИЕ
Команда break может использоваться в любых циклах Python� Например, ее можно вклю-
чить в цикл for для перебора элементов словаря�
Команда continue и продолжение цикла
Вместо того чтобы полностью прерывать выполнение цикла без выполнения остав
шейся части кода, вы можете воспользоваться командой continue для возвращения
к началу цикла и проверке условия Например, возьмем цикл, который считает от до , но выводит только нечетные числа в этом диапазоне
counting.py current_number = 0 while current_number < 10:
 current_number += 1
if current_number % 2 == 0:

Циклы while 129
continue print(current_number)
Сначала переменной current_number присваивается Ά Так как значение меньше ,
th входит в цикл while При входе в цикл счетчик увеличивается на в точ
ке , поэтому current_number принимает значение Æ Затем команда if проверяет
остаток от деления current_number на Ć Если остаток равен (это означает, что
current_number делится на ), команда continue приказывает th проигно
рировать оставшийся код цикла и вернуться к началу Если счетчик не делится
на , то оставшаяся часть цикла выполняется, и th выводит текущее значение счетчика
1 3579
Предотвращение зацикливания
У каждого цикла while должна быть предусмотрена возможность завершения, что
бы цикл не выполнялся бесконечно Например, следующий цикл считает от до ʽ
counting.py x = 1 while x <= 5: print(x) x += 1
Но если случайно пропустить строку x += 1 (см далее), то цикл будет выполняться
бесконечно
# Бесконечный цикл!x = 1while x <= 5: print(x)
Теперь переменной x присваивается начальное значение , но это значение никог
да не изменяется в программе В результате проверка условия x <= 5 всегда дает
результат True, и цикл while выводит бесконечную серию единиц
1111…
Любой программист время от времени пишет бесконечный цикл, особенно если
в программе используются неочевидные условия завершения Если ваша про
грамма зациклилась, нажмите tr׮ или просто закройте терминальное окно с выводом программы

130 Глава 7 • Ввод данных и циклы while
Чтобы избежать зацикливания, тщательно проверьте каждый цикл while и убеди
тесь в том, что цикл прерывается именно тогда, когда предполагается Если про
грамма должна завершаться при вводе некоторого значения, запустите программу
и введите это значение Если программа не завершилась, проанализируйте обра
ботку значения, которое должно приводить к выходу из цикла Проверьте, чтобы
хотя бы одна часть программы могла привести к тому, что условие цикла станет равно False или будет выполнена команда break
ПРИМЕЧАНИЕ
В некоторых редакторах — например, в Sublime Text — используется встроенное окно вывода� Оно
может усложнить прерывание бесконечных циклов; возможно, для выхода из цикла придется за-крыть редактор�
УПРАЖНЕНИЯ
7-4� Дополнения для пиццы: напишите цикл, который предлагает пользователю вводить
дополнения для пиццы до тех пор, пока не будет введено значение 'quit’� При вводе каждо-го дополнения выведите сообщение о том, что это дополнение включено в заказ�
7-5� Билеты в кино: кинотеатр установил несколько вариантов цены на билеты в зависимо-
сти от возраста посетителя� Для посетителей младше 3 лет билет бесплатный; в возрасте
от 3 до 12 билет стоит $10; наконец, если возраст посетителя больше 12, билет стоит $15�
Напишите цикл, который предлагает пользователю ввести возраст и выводит цену билета�
7-6� Три выхода: напишите альтернативную версию упражнения 7-4 или упражнения 7-5, в которой каждый пункт следующего списка встречается хотя бы один раз�• Завершение цикла по проверке условия в команде while�
• Управление продолжительностью выполнения цикла в зависимости от переменной active�
• Выход из цикла по команде break, если пользователь вводит значение ‘quit’�
7-7� Бесконечный цикл: напишите цикл, который никогда не завершается, и выполните его�
(Чтобы выйти из цикла, нажмите Ctrl+C или закройте окно с выводом�)
Использование цикла while со списками и словарями
До настоящего момента мы работали только с одним фрагментом информации,
полученной от пользователя Мы получали ввод пользователя, а затем выводили
ответ на него При следующем проходе цикла while программа получала новое
входное значение и реагировала на него Но, чтобы работать с несколькими
фрагментами информации, необходимо использовать в циклах while списки
и словари Цикл for хорошо подходит для перебора списков, но, скорее всего, список не дол
жен изменяться в цикле, потому что у th возникнут проблемы с отслежива
нием элементов Чтобы изменять список в процессе обработки, используйте цикл
while Использование циклов while со списками и словарями позволяет собирать,
хранить и упорядочивать большие объемы данных для последующего анализа и обработки
Возьмем список недавно зарегистрированных, но еще не проверенных пользова
телей сайта Как переместить пользователей после проверки в отдельный список

Использование цикла while со списками и словарями 131
проверенных пользователей Одно из возможных решени й используем цикл while
для извлечения пользователей из списка непроверенных, проверяем их и включаем в отдельный список проверенных пользователей Код может выглядеть так
confirmed_users.py # Начинаем с двух списков: пользователей для проверки # и пустого списка для хранения проверенных пользователей.
 unconfirmed_users = ['alice', 'brian', 'candace']
confirmed_users = [] # Проверяем каждого пользователя, пока остаются непроверенные # пользователи. Каждый пользователь, прошедший проверку,# перемещается в список проверенных.
 while unconfirmed_users:
 current_user = unconfirmed_users.pop()
print("Verifying user: " + current_user.title())
 confirmed_users.append(current_user)
# Вывод всех проверенных пользователей.print("\nThe following users have been confirmed:")for confirmed_user in confirmed_users: print(confirmed_user.title())
Работа программы начинается с двух списков непроверенных пользователей 
и пустого списка для проверенных пользователей Цикл while в точке  выпол
няется, пока в списке unconfirmed_users остаются элементы Внутри этого списка
функция pop() в точке  извлекает очередного непроверенного пользователя
с конца списка unconfirmed_users В данном примере список unconfirmed_users
завершается пользователем Candace это имя первым извлекается из списка, со
храняется в current_user и добавляется в список confirmed_users в точке  Далее
следуют пользователи Brian и Alice
Программа моделирует проверку каждого пользователя выводом сообщения, после
чего переносит пользователя в список проверенных По мере сокращения списка
непроверенных пользователей список проверенных поль зователей растет Когда
в списке непроверенных пользователей не остается ни одного элемента, цикл оста
навливается, и выводится список проверенных пользователей
Verifying user: Candace Verifying user: Brian Verifying user: Alice The following users have been confirmed: Candace Brian Alice
Удаление всех вхождений конкретного значения из списка
В главе функция remove() использовалась для удаления конкретного значения
из списка Функция remove() работала, потому что интересующее нас значение

132 Глава 7 • Ввод данных и циклы while
встречалось в списке только один раз Но что если вы захотите удалить все вхож дения значения из списка
Допустим, имеется список pets, в котором значение 'cat' встречается многократно
Чтобы удалить все экземпляры этого значения, можно выполнять цикл while до
тех пор, пока в списке не останется ни одного экземпляра 'cat'
pets.py pets = ['dog', 'cat', 'dog', 'goldfish', 'cat', 'rabbit', 'cat'] print(pets) while 'cat' in pets: pets.remove('cat') print(pets)
Программа начинает со списка, содержащего множественные экземпляры 'cat'
После вывода списка th входит в цикл while, потому что значение 'cat' при
сутствует в списке хотя бы в одном экземпляре После входа цикл th удаляет
первое вхождение 'cat', возвращается к строке while, а затем обнаруживает, что
экземпляры 'cat' все еще присутствуют в списке, и проходит цикл заново Вхож
дения 'cat' удаляются до тех пор, пока не окажется, что в списке значений 'cat'
не осталось в этот момент th завершает цикл и выводит список заново
['dog', 'cat', 'dog', 'goldfish', 'cat', 'rabbit', 'cat'] ['dog', 'dog', 'goldfish', 'rabbit']
Заполнение словаря данными, введенными пользователем
При каждом проходе цикла while ваша программа может запрашивать любое
необходимое количество данных Напишем программу, которая при каждом про
ходе цикла запрашивает имя участника и его ответ Собранные данные будут со
храняться в словаре, потому что каждый ответ должен быть связан с конкретным пользователем
mountain_poll.py responses = {} # Установка флага продолжения опроса. polling_active = True while polling_active: # Запрос имени и ответа пользователя.
 name = input("\nWhat is your name? ")
response = input("Which mountain would you like to climb someday? ") # Ответ сохраняется в словаре:
 responses[name] = response
# Проверка продолжения опроса.

Использование цикла while со списками и словарями 133
 repeat = input("Would you like to let another person respond? (yes/ no) ")
if repeat == 'no': polling_active = False # Опрос завершен, вывести результаты.print("\n--- Poll Results ---")
 for name, response in responses.items():
print(name + " would like to climb " + response + ".")
Сначала программа определяет пустой словарь ( responses) и устанавливает флаг
( polling_active ), показывающий, что опрос продолжается Пока polling_active
содержит True, th будет выполнять код в цикле while
В цикле пользователю предлагается ввести имя и назв ание горы, на которую
ему хотелось бы подняться  Эта информация сохраняется в словаре responses
в строке , после чего программа спрашивает у пользователя, нужно ли продол
жать опрос  Если пользователь отвечает положительно, то программа снова
входит в цикл while Если же ответ отрицателен, флаг polling_active переходит
в состояние False, цикл while перестает выполняться, и завершающий блок кода 
выводит результаты опроса
Если вы запустите эту программу и введете пару ответов, результат будет выгля деть примерно так
What is your name? Eric
Which mountain would you like to climb someday? Denali Would you like to let another person respond? (yes/ no) yes
What is your name? Lynn
Which mountain would you like to climb someday? Devil's Thumb
Would you like to let another person respond? (yes/ no) no
--- Poll Results --- Lynn would like to climb Devil's Thumb. Eric would like to climb Denali. УПРАЖНЕНИЯ
7-8� Сэндвичи: создайте список с именем sandwich_orders, заполните его названиями раз-
личных видов сэндвичей� Создайте пустой список с именем finished_sandwiches� В цикле
переберите элементы первого списка и выведите сообщение для каждого элемента (напри-
мер, «I made your tuna sandwich»)� После этого каждый сэндвич из первого списка пере-
мещается в список finished_sandwiches� После того как все элементы первого списка будут обработаны, выведите сообщение с перечислением всех изготовленных сэндвичей�
7-9� Без пастрами: используя список sandwich_orders из упражнения 7-8, проследите за
тем, чтобы значение ‘pastrami’ встречалось в списке как минимум три раза� Добавьте в на-
чало программы код для вывода сообщения о том, что пастрами больше нет, и напишите
цикл while для удаления всех вхождений ‘pastrami’ из sandwich_orders� Убедитесь в том, что
в finished_sandwiches значение ‘pastrami’ не встречается ни одного раза�
7-10� Отпуск мечты: напишите программу, которая опрашивает пользователей, где бы они хотели провести отпуск� Включите блок кода для вывода результатов опроса�

134 Глава 7 • Ввод данных и циклы while
Итоги
В этой главе вы научились использовать input() для того, чтобы пользователи
могли вводить собственную информацию в своих программах Вы научились рабо
тать с числовыми и текстовыми данными, а также управлять продолжительностью
выполнения своих программ с помощью циклов while Также мы рассмотрели
несколько способов управления циклами while установку флага, команду break
и команду continue Вы узнали, как использовать цикл while для перемещения
элементов из одного списка в другой и как удалить все вхождения некоторого
значения из списка Также были рассмотрены возможности применения циклов while со словарями
Глава посвящена функциям Функции позволяют разделить программу на мень
шие части, каждая из которых решает одну конкретную задачу Функции можно
хранить в отдельных файлах и вызывать их столько раз, сколько потребуется
Благодаря функциям вы сможете писать более эффективный код, более простой
в отладке и сопровождении, который к тому же можно повторно использовать в разных программах

8 Функции
Эта глава посвящена функциям — именованным блокам кода, предназначенным
для решения одной конкретной задачи Чтобы выполнить задачу, определенную
в виде функции, вы указываете имя функции, отвечающей за эту задачу Если зада
ча должна многократно выполняться в программе, вам не придется заново вводить
весь необходимый код просто вызовите функцию, предназначе нную для решения
задачи, и этот вызов приказывает th выполнить код, содержащийся внутри
функции Как вы вскоре убедитесь, использование функций упрощает чтение, написание, тестирование кода и исправление ошибок
В этой главе также рассматриваются возможности передачи информации функ
циям Вы узнаете, как писать функции, основной задачей которых является вывод
информации, и другие функции, предназначенные для обработки данных и возвра
щения значения (или набора значений) Наконец, вы н аучитесь хранить функции
в отдельных файлах, называемых модулями, для упорядочения файлов основной
программы Определение функции Вот простая функция greet_user(), которая выводит приветствие
greeter.py
 def greet_user():
 """Выводит простое приветствие."""
 print("Hello!")

 greet_user()
В этом примере представлена простейшая структура функции Строка  при по
мощи ключевого слова def сообщает th, что вы определяете функцию В опре
делении функции указывается имя функции и, если нужно, описание информации,
необходимой функции для решения ее задачи Эта информация заключается
в круглые скобки В данном примере функции присвоено имя greet_user(), и она
не нуждается в дополнительной информации для решения своей задачи, поэтому
круглые скобки пусты (Впрочем, даже в этом случае они обязательны) Наконец, определение завершается двоеточием
Все строки с отступами, следующие за def greet_user():, образуют тело функ
ции Текст в точке  представляет собой комментарий — строку документации

136 Глава 8 • Функции
с описанием функции Строки документации заключаются в утроенные кавычки
th опознает их по этой последовательности символов во время генерирования документации к функциям в ваших программах
«Настоящий» код в теле этой функции состоит всего из одной строки print("Hello!") — см  Таким образом, функция greet_user() решает всего
одну задачу выполнение команды print("Hello!")
Когда потребуется использовать эту функцию, вызовите ее Вызов функции при
казывает th выполнить содержащийся в ней код Чтобы вызвать функцию,
укажите ее имя, за которым следует вся необходимая информация, заключенная
в круглые скобки, как показано в строке  Так как никакая дополнительная ин
формация не нужна, вызов функции эквивалентен простому выполнению команды greet_user() Как и ожидалось, функция выводит сообщение Hello!
Hello!
Передача информации функции
С небольшими изменениями функция greet_user() сможет не только сказать
«Привет» пользователю, но и поприветствовать его по имени Для этого следует
включить имя username в круглых скобках в определение функции def greet_
user() С добавлением username функция примет любое значение, которое будет
заключено в скобки при вызове Теперь функция ожидает, что при каждом вызове
будет передаваться имя пользователя При вызове greet_user() укажите имя (на
пример, 'jesse') в круглых скобках
def greet_user(username):
"""Выводит простое приветствие.""" print("Hello, " + username.title() + "!") greet_user('jesse')
Команда greet_user('jesse') вызывает функцию greet_user() и передает ей
информацию, необходимую для выполнения команды print Функция получает
переданное имя и выводит приветствие для этого имени
Hello, Jesse!
Точно так же команда greet_user('sarah') вызывает функцию greet_user()
и передает ей строку 'sarah', что приводит к выводу сообщения Hello, Sarah!
Функцию greet_user() можно вызвать сколько угодно раз и передать ей любое
имя на ваше усмотрение — и вы будете получать ожидаемый результат Аргументы и параметры Функция greet_user() определена так, что для работы она должна получить
значение переменной username После того как функция будет вызвана и полу
чит необходимую информацию (имя пользователя), она выведет правильное приветствие

Передача аргументов 137
Переменная username в определении greet_user() — параметр , то есть условные
данные, необходимые функции для выполнения ее работы Значение 'jesse'
в greet_user('jesse') — аргумент , то есть конкретная информация, переданная
при вызове функции Вызывая функцию, вы заключаете значение, с которым
функция должна работать, в круглые скобки В данном случае аргумент 'jesse'
был передан функции greet_user(), а его значение было сохранено в переменной
username
ПРИМЕЧАНИЕ
Иногда в литературе термины «аргумент» и «параметр» используются как синонимы� Не удив-
ляйтесь, если переменные в определении функции вдруг будут названы аргументами, а значения, переданные при вызове функции, — параметрами�
УПРАЖНЕНИЯ
8-1� Сообщение: напишите функцию display_message() для вывода сообщения по теме, рас-
сматриваемой в этой главе� Вызовите функцию и убедитесь в том, что сообщение выво-дится правильно�
8-2� Любимая книга: напишите функцию favorite_book(), которая получает один пара-
метр title� Функция должна выводить сообщение вида «One of my favorite books is Alice in
Wonderland»� Вызовите функцию и убедитесь в том, что название книги правильно пере-дается как аргумент при вызове функции�
Передача аргументов
Определение функции может иметь несколько параметров, и может оказаться, что
при вызове функции должны передаваться несколько аргументов Существуют
несколько способов передачи аргументов функциям Позиционные аргументы
перечисляются в порядке, точно соответствующем порядку записи параметров
именованные аргументы состоят из имени переменной и значения наконец, суще
ствуют списки и словари значений Рассмотрим все эти способы Позиционные аргументы
При вызове функции каждому аргументу должен быть поставлен в соответствие
параметр в определении функции Проще всего сделать это на основании порядка
перечисления аргументов Значения, связываемые с аргументами подобным об
разом, называются позиционными аргументами
Чтобы понять, как работает эта схема, рассмотрим функцию для вывода информа
ции о домашних животных Функция сообщает тип животного и его имя
pets.py
 def describe_pet(animal_type, pet_name):
"""Выводит информацию о животном.""" print("\nI have a " + animal_type + ".") print("My " + animal_type + "'s name is " + pet_name.title() + ".")
 describe_pet('hamster', 'harry')

138 Глава 8 • Функции
Из определения  видно, что функции должен передаваться тип животного
( animal_type ) и его имя ( pet_name) При вызове describe_pet() необходимо пере
дать тип и имя — именно в таком порядке В этом примере аргумент 'hamster'
сохраняется в параметре animal_type, а аргумент 'harry' сохраняется в параметре
pet_name  В теле функции эти два параметра используются для вывода инфор
мации
I have a hamster. My hamster's name is Harry.
Многократные вызовы функций
Функция может вызываться в программе столько раз, сколько потребуется Для
вывода информации о другом животном достаточно одного вызова describe_pet()
def describe_pet(animal_type, pet_name): """Выводит информацию о животном.""" print("\nI have a " + animal_type + ".") print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet('hamster', 'harry') describe_pet('dog', 'willie')
Во втором вызове функции describe_pet() передаются аргументы 'dog' и 'willie'
По аналогии с предыдущей парой аргументов th сопоставляет аргумент 'dog'
с параметром animal_type, а аргумент 'willie' с параметром pet_name
Как и в предыдущем случае, функция выполняет свою задачу, но на этот раз вы водятся другие значения
I have a hamster. My hamster's name is Harry. I have a dog. My dog's name is Willie.
Многократный вызов функции — чрезвычайно эффективный способ работы Код
вывода информации о домашнем животном пишется один раз в функции Каждый
раз, когда вы захотите вывести информацию о новом животном, вы вызываете
функцию с данными нового животного Даже если код вывода информации раз
растется до строк, вы все равно сможете вывести информацию всего одной командой — для этого достаточно снова вызвать функцию
Функция может иметь любое количество позиционных аргументов При вызове
функции th перебирает аргументы, приведенные в вызове, и сопоставляет каждый аргумент с соответствующим параметром из определения функции О важности порядка позиционных аргументов
Если нарушить порядок следования аргументов в вызове при использовании по зиционных аргументов, возможны неожиданные результаты
def describe_pet(animal_type, pet_name): """Выводит информацию о животном."""

Передача аргументов 139
print("\nI have a " + animal_type + ".") print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet('harry', 'hamster')
В этом вызове функции сначала передается имя, а потом тип животного Так как аргумент 'harry' находится в первой позиции, значение сохраняется в параметре
animal_type , а аргумент 'hamster' сохраняется в pet_name На этот раз вывод полу
чается бессмысленным
I have a harry. My harry's name is Hamster.
Если вы получили подобные странные результаты, проверьте, соответствует ли
порядок следования аргументов в вызове функции порядку параметров в ее определении Именованные аргументы
Именованный аргумент представляет собой пару «имя—значение», передаваемую
функции Имя и значение связываются с аргументом напрямую, так что при пере
даче аргумента путаница с порядком исключается Именованные аргументы из
бавляют от хлопот с порядком аргументов при вызове функции, а также проясняют роль каждого значения в вызове функции
Перепишем программу pets�py с использованием именованных аргументов
при вызове describe_pet()
def describe_pet(animal_type, pet_name): """Выводит информацию о животном.""" print("\nI have a " + animal_type + ".") print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet(animal_type='hamster', pet_name='harry')
Функция describe_pet() не изменилась Однако на этот раз при вызове функ
ции мы явно сообщаем th, с каким параметром должен быть связан каждый
аргумент При обработке вызова функции th знает, что аргумент 'hamster'
должен быть сохранен в параметре animal_type, а аргумент 'harry' в параметре
pet_name
Порядок следования именованных аргументов в данном случае не важен, потому
что th знает, где должно храниться каждое значение Следующие два вызова функции эквивалентны
describe_pet(animal_type='hamster', pet_name='harry') describe_pet(pet_name='harry', animal_type='hamster')
ПРИМЕЧАНИЕ
При использовании именованных аргументов будьте внимательны — имена должны точно совпа- дать с именами параметров из определения функции�

140 Глава 8 • Функции
Значения по умолчанию
Для каждого параметра вашей функции можно определить значение по умолча
нию Если при вызове функции передается аргумент, соответствующий данному
параметру, th использует значение аргумента, а если нет — использует зна
чение по умолчанию Таким образом, если для параметра определено значение
по умолчанию, вы можете опустить соответствующий аргумент, который обычно
включается в вызов функции Значения по умолчанию упрощают вызовы функций и проясняют типичные способы использования функций
Например, если вы заметили, что большинство вызовов describe_pet() исполь
зуется для описания собак, задайте animal_type значение по умолчанию 'dog'
Теперь в любом вызове describe_pet() для собаки эту информацию можно
опустить
def describe_pet(pet_name, animal_type='dog'):
"""Выводит информацию о животном.""" print("\nI have a " + animal_type + ".") print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet(pet_name='willie')
Мы изменили определение describe_pet() и включили для параметра animal_type
значение по умолчанию 'dog' Если теперь функция будет вызвана без указания
animal_type , th знает, что для этого параметра следует использовать значение
'dog'
I have a dog. My dog's name is Willie.
Обратите внимание в определении функции пришлось изменить порядок параме
тров Так как благодаря значению по умолчанию указывать аргумент с типом жи
вотного не обязательно, единственным оставшимся аргументом в вызове функции
остается имя домашнего животного th интерпретирует его как позиционный
аргумент, и если функция вызывается только с именем животного, этот аргумент
ставится в соответствие с первым параметром в определении функции Именно по этой причине имя животного должно быть первым параметром
В простейшем варианте использования этой функции при вызове передается только имя собаки describe_pet('willie')
Вызов функции выводит тот же результат, что и в предыдущем примере Един
ственный переданный аргумент 'willie' ставится в соответствие с первым па
раметром в определении, pet_name Так как для animal_type аргумент не указан,
th использует значение по умолчанию 'dog' Для вывода информации о лю
бом другом животном, кроме собаки, используется вызов функции следующего вида describe_pet(pet_name='harry', animal_type='hamster')
Так как аргумент для параметра animal_type задан явно, th игнорирует зна
чение параметра по умолчанию

Передача аргументов 141
ПРИМЕЧАНИЕ
Если вы используете значения по умолчанию, все параметры со значением по умолчанию должны
следовать после параметров, у которых значений по умолчанию нет� Это необходимо для того, чтобы Python правильно интерпретировал позиционные аргументы� Эквивалентные вызовы функций
Так как позиционные аргументы, именованные аргументы и значения по умол
чанию могут использоваться одновременно, часто существуют несколько эквива
лентных способов вызова функций Возьмем оператор describe_pets() с одним
значением по умолчанию
def describe_pet(pet_name, animal_type='dog'):
При таком определении аргумент для параметра pet_name должен задаваться в лю
бом случае, но это значение может передаваться как в позиционном, так и в име
нованном формате Если описываемое животное не является собакой, то аргумент animal_type тоже должен быть включен в вызов, и этот аргумент тоже может быть
задан как в позиционном, так и в именованном формате Все следующие вызовы являются допустимыми для данной функции
describe_pet('willie') describe_pet(pet_name='willie') describe_pet('harry', 'hamster') describe_pet(pet_name='harry', animal_type='hamster')describe_pet(animal_type='hamster', pet_name='harry')
Все вызовы функции выдадут такой же результат, как и в предыдущих примерах ПРИМЕЧАНИЕ
На самом деле не так важно, какой стиль вызова вы используете� Если ваша функция выдает нуж- ный результат, выберите тот стиль, который вам кажется более понятным� Предотвращение ошибок в аргументах
Не удивляйтесь, если на первых порах вашей работы с функциями будут встре
чаться ошибки несоответствия аргументов Такие ошибки происходят в том слу
чае, если вы передали меньше или больше аргументов, чем необходимо функции
для выполнения ее работы Например, вот что произойдет при попытке вызвать describe_pet() без аргументов
def describe_pet(animal_type, pet_name): """Выводит информацию о животном.""" print("\nI have a " + animal_type + ".") print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet()

142 Глава 8 • Функции
th понимает, что при вызове функции часть информации отсутствует, и мы видим это в данных трассировки
Traceback (most recent call last):
 File "pets.py", line 6, in
 describe_pet()
 TypeError: describe_pet() missing 2 required positional arguments:
'animal_ type' and 'pet_name'
В точке  сообщается местонахождение проблемы, чтобы вы поняли, что с вызовом
функции чтото пошло не так В точке  приводится вызов функции, приведший
к ошибке В точке  th сообщает, что при вызове пропущены два аргумента,
и сообщает имена этих аргументов Если бы функция размещалась в отдельном
файле, вероятно, вы смогли бы исправить вызов, и вам не пришлось бы открывать этот файл и читать код функции
th помогает еще и тем, что он читает код функции и сообщает имена аргумен
тов, которые необходимо передать при вызове Это еще одна причина для того,
чтобы присваивать переменным и функциям содержатель ные имена В этом случае
сообщения об ошибках th принесут больше пользы как вам, так и любому другому разработчику, который будет использовать ваш код
Если при вызове будут переданы лишние аргументы, вы получите похожую
трассировку, которая поможет привести вызов функции в соответствие с ее определением
УПРАЖНЕНИЯ
8-3� Футболка: напишите функцию make_shirt(), которая получает размер футболки и текст,
который должен быть напечатан на ней� Функция должна выводить сообщение с размером
и текстом� Вызовите функцию с использованием позиционных аргументов� Вызовите функ-цию во второй раз с использованием именованных аргументов�
8-4� Большие футболки: измените функцию make_shirt(), чтобы футболки по умолчанию
имели размер L, и на них выводился текст «I love Python�»� Создайте футболку с размером L и текстом по умолчанию, а также футболку любого размера с другим текстом�
8-5� Города: напишите функцию describe_city(), которая получает названия города и стра-
ны� Функция должна выводить простое сообщение (например, «Reykjavik is in Iceland»)� За-
дайте параметру страны значение по умолчанию� Вызовите свою функцию для трех разных городов, по крайней мере один из которых не находится в стране по умолчанию�
Возвращаемое значение
Функция не обязана выводить результаты своей работы Вместо этого она может
обработать данные, а затем вернуть значение или набор сообщений Значение,
возвращаемое функцией, называется возвращаемым значением Команда return
передает значение из функции в строку, в которой эта функция была вызвана
Возвращаемые значения помогают переместить б ульшую часть рутинной работы
в вашей программе в функции, чтобы упростить основной код программы

Возвращаемое значение 143
Возвращение простого значения
Рассмотрим функцию, которая получает имя и фамилию и возвращает аккуратно отформатированное полное имя formatted_name.py
 def get_formatted_name(first_name, last_name):
"""Возвращает аккуратно отформатированное полное имя."""
 full_name = first_name + ' ' + last_name
 return full_name.title()
 musician = get_formatted_name('jimi', 'hendrix')
print(musician)
Определение get_formatted_name() получает в параметрах имя и фамилию 
Функция объединяет эти два имени, добавляет между ними пробел и сохраняет
результат в full_name  Значение full_name преобразуется в формат с начальной
буквой верхнего регистра, а затем возвращается в точку вызова 
Вызывая функцию, которая возвращает значение, необходимо предоставить пере
менную, в которой должно храниться возвращаемое значение В данном случае
возвращаемое значение сохраняется в переменной musician  Результат содержит
аккуратно отформатированное полное имя, построенное из имени и фамилии
Jimi Hendrix
Может показаться, что все эти хлопоты излишни — с таким же успехом можно было использовать команду print("Jimi Hendrix")
Но если представить, что вы пишете большую программу, в которой многочис
ленные имена и фамилии должны храниться по отдельности, такие функции, как get_formatted_name() , становятся чрезвычайно полезными Вы храните имена
отдельно от фамилий, а затем вызываете функцию везде, где потребуется вывести полное имя Необязательные аргументы
Иногда бывает удобно сделать аргумент необязательным, чтобы разработчик, ис
пользующий функцию, мог передать дополнительную информацию только в том
случае, если он этого захочет Чтобы сделать аргумент необязательным, можно
воспользоваться значением по умолчанию Допустим, вы захотели расширить функцию get_formatted_name() , чтобы она также работала и со вторыми именами
Первая попытка могла бы выглядеть так
def get_formatted_name(first_name, middle_name, last_name): """Возвращает аккуратно отформатированное полное имя.""" full_name = first_name + ' ' + middle_name + ' ' + last_name return full_name.title() musician = get_formatted_name('john', 'lee', 'hooker')print(musician)

144 Глава 8 • Функции
Функция работает при получении имени, второго имени и фамилии Она получает
все три части имени, а затем строит из них строку Функция добавляет пробелы там, где это уместно, и преобразует полное имя в формат с капитализацией John Lee Hooker
Однако вторые имена нужны не всегда, а в такой записи функция не будет работать,
если при вызове ей передаются только имя и фамилия Чтобы средний аргумент
был необязательным, можно присвоить аргументу middle_name пустое значение
по умолчанию этот аргумент игнорируется, если пользователь не передал для него
значение Чтобы функция get_formatted_name() работала без второго имени, сле
дует назначить для параметра middle_name пустую строку значением по умолчанию
и переместить его в конец списка параметров  def get_formatted_name(first_name, last_name, middle_name=''):
"""Возвращает аккуратно отформатированное полное имя."""  if middle_name:
full_name = first_name + ' ' + middle_name + ' ' + last_ name
 else:
full_name = first_name + ' ' + last_name
return full_name.title()
musician = get_formatted_name('jimi', 'hendrix') print(musician)
 musician = get_formatted_name('john', 'hooker', 'lee')
print(musician)
В этом примере имя строится из трех возможных частей Поскольку имя и фамилия
указываются всегда, эти параметры стоят в начале списка в определении функции
Второе имя не обязательно, поэтому оно находится на последнем месте в определении, а его значением по умолчанию является пустая строка 
В теле функции мы сначала проверяем, было ли задано второе имя th ин
терпретирует непустые строки как истинное значение, и, если при вызове задан
аргумент второго имени, middle_name дает результат True  Если второе имя
указано, то из имени, второго имени и фамилии строится полное имя Затем имя
преобразуется с капитализацией символов и возвращается в строку вызова функ
ции, где оно сохраняется в переменной musician и выводится Если второе имя не
указано, то пустая строка не проходит проверку if и выполняет блок else  В этом
случае полное имя строится только из имени и фамилии, и отформатированное
имя возвращается в строку вызова, где оно сохраняется в переменной musician
и выводится
Вызов этой функции с именем и фамилией достаточно т ривиален Но при ис
пользовании второго имени придется проследить за тем, чтобы второе имя было
последним из передаваемых аргументов Это необходимо для правильного связы
вания позиционных аргументов в строке 
Обновленная версия этой функции подойдет как для людей, у которых задается
только имя и фамилия, так и для людей со вторым именем

Возвращаемое значение 145
Jimi Hendrix John Lee Hooker
Необязательные значения позволяют функциям работать в максимально широком спектре сценариев использования без усложнения вызовов Возвращение словаря
Функция может вернуть любое значение, нужное вам, в том числе и более сложную
структуру данных (например, список или словарь) Так, следующая функция полу
чает части имени и возвращает словарь, представляющий человека
person.py def build_person(first_name, last_name): """Возвращает словарь с информацией о человеке."""
 person = {'first': first_name, 'last': last_name}
 return person
musician = build_person('jimi', 'hendrix')
 print(musician)
Функция build_person() получает имя и фамилию и сохраняет полученные зна
чения в словаре в точке  Значение first_name сохраняется с ключом 'first',
а значение last_name — с ключом 'last' Весь словарь с описанием человека
возвращается в точке  Возвращаемое значение выводится в точке  с двумя
исходными фрагментами текстовой информации, теперь хранящимися в словаре
{'first': 'jimi', 'last': 'hendrix'}
Функция получает простую текстовую информацию и помещает ее в более удоб
ную структуру данных, которая позволяет работать с информацией (помимо про
стого вывода) Строки 'jimi' и 'hendrix' теперь помечены как имя и фамилия
Функцию можно легко расширить так, чтобы она принимала дополнительные
значения — второе имя, возраст, профессию или любую другую информацию о че
ловеке, которую вы хотите сохранить Например, следующее изменение позволяет также сохранить возраст человека
def build_person(first_name, last_name, age=''):
"""Возвращает словарь с информацией о человеке.""" person = {'first': first_name, 'last': last_name}
if age: person['age'] = age
return person
musician = build_person('jimi', 'hendrix', age=27)print(musician)
В определение функции добавляется новый необязательный параметр age, кото
рому назначается пустое значение по умолчанию Если вызов функции включает
значение этого параметра, то значение сохраняется в словаре Функция всегда
сохраняет имя, но ее также можно модифицировать, чтобы она сохраняла любую необходимую информацию о человеке

146 Глава 8 • Функции
Использование функции в цикле while
Функции могут использоваться со всеми структурами th, уже известными
вам Например, используем функцию get_formatted_name() в цикле while, чтобы
поприветствовать пользователей более официально Пе рвая версия программы,
приветствующей пользователей по имени и фамилии, может выглядеть так
greeter.py
def get_formatted_name(first_name, last_name): """Возвращает аккуратно отформатированное полное имя.""" full_name = first_name + ' ' + last_name return full_name.title()
# Бесконечный цикл! while True:
 print("\nPlease tell me your name:")
f_name = input("First name: ") l_name = input("Last name: ") formatted_name = get_formatted_name(f_name, l_name) print("\nHello, " + formatted_name + "!")
В этом примере используется простая версия get_formatted_name(), без вторых
имен В цикле while  имя и фамилия пользователя запрашиваются по отдель
ности
Но у этого цикла while есть один недостаток в нем не определено условие завер
шения Где следует разместить условие завершения при запросе серии данных
Пользователю нужно предоставить возможность выйти из цикла как можно рань
ше, так что в приглашении должен содержаться способ завершения Команда break
позволяет немедленно прервать цикл при запросе любого из компонентов
def get_formatted_name(first_name, last_name): """Возвращает аккуратно отформатированное полное имя.""" full_name = first_name + ' ' + last_name return full_name.title() while True: print("\nPlease tell me your name:")
print("(enter 'q' at any time to quit)")
f_name = input("First name: ") if f_name == 'q': break
l_name = input("Last name: ") if l_name == 'q': break
formatted_name = get_formatted_name(f_name, l_name) print("\nHello, " + formatted_name + "!")
В программу добавляется сообщение, которое объясняет пользователю, как за
вершить ввод данных, и при вводе признака завершения в любом из приглашений

Передача списка 147
цикл прерывается Теперь программа будет приветствовать пользователя до тех пор, пока вместо имени или фамилии не будет введен символ 'q'
Please tell me your name: (enter 'q' at any time to quit)First name: eric
Last name: matthes
Hello, Eric Matthes! Please tell me your name: (enter 'q' at any time to quit)First name: q
УПРАЖНЕНИЯ
8-6� Названия городов: напишите функцию city_country(), которая получает название го-
рода и страну� Функция должна возвращать строку в формате “Santiago, Chile”� Вызовите
свою функцию по крайней мере для трех пар «город—страна» и выведите возвращенное значение�
8-7� Альбом: напишите функцию make_album(), которая строит словарь с описанием му -
зыкального альбома� Функция должна получать имя исполнителя и название альбома
и возвращать словарь, содержащий эти два вида информации� Используйте функцию
для создания трех словарей, представляющих разные альбомы� Выведите все возвраща -
емые значения, чтобы показать, что информация правильно сохраняется во всех трех словарях�
Добавьте в make_album() дополнительный параметр для сохранения количества дорожек
в альбоме� Если в строку вызова включено значение количества дорожек, добавьте это зна-
чение в словарь альбома� Создайте как минимум один новый вызов функции с передачей количества дорожек в альбоме�
8-8� Пользовательские альбомы: начните с программы из упражнения 8-7� Напишите цикл
while, в котором пользователь вводит исполнителя и название альбома� Затем в цикле вы-
зывается функция make_album() для введенных пользователей и выводится созданный
словарь� Не забудьте предусмотреть признак завершения в цикле while�
Передача списка
Часто при вызове функции удобно передать список — имен, чисел или более
сложных объектов (например, словарей) При передаче списка функция получает
прямой доступ ко всему его содержимому Мы воспользуемся функциями для того, чтобы сделать работу со списком более эффективной
Допустим, вы хотите вывести приветствие для каждого пользователя из списка
В следующем примере список имен передается функции greet_users(), которая
выводит приветствие для каждого пользователя по отдельности
greet_users.py def greet_users(names): """Вывод простого приветствия для каждого пользователя в списке.""" for name in names: msg = "Hello, " + name.title() + "!" print(msg)

148 Глава 8 • Функции
 usernames = ['hannah', 'ty', 'margot']
greet_users(usernames)
В соответствии со своим определением функция greet_users() рассчитывает полу
чить список имен, который сохраняется в параметре names Функция перебирает
полученный список и выводит приветствие для каждого пользователя В точке 
мы определяем список пользователей usernames, который затем передается greet_
users() в вызове функции
Hello, Hannah! Hello, Ty! Hello, Margot!
Результат выглядит именно так, как ожидалось Каждый пользователь получает
персональное сообщение, и эту функцию можно вызвать для каждого нового набора пользователей Изменение списка в функции
Если вы передаете список функции, код функции сможет изменить список Все
изменения, внесенные в список в теле функции, закрепляются, что позволяет эффективно работать со списком даже при больших объемах данных
Допустим, компания печатает на қпринтере модели, предоставленные пользо
вателем Проекты хранятся в списке, а после печати перемещаются в отдельный
список В следующем примере приведена реализация, не использующая функции
printing_models.py # Список моделей, которые необходимо напечатать. unprinted_designs = ['iphone case', 'robot pendant', 'dodecahedron']completed_models = [] # Цикл последовательно печатает каждую модель до конца списка. # После печати каждая модель перемещается в список completed_models.while unprinted_designs: current_design = unprinted_designs.pop() # Печать модели на 3D-принтере. print("Printing model: " + current_design) completed_models.append(current_design) # Вывод всех готовых моделей.print("\nThe following models have been printed:")for completed_model in completed_models: print(completed_model)
В начале программы создается список моделей и пустой список completed_models,
в который каждая модель перемещается после печати Пока в unprinted_designs
остаются модели, цикл while имитирует печать каждой модели модель удаляется
с конца списка, сохраняется в current_design, а пользователь получает сообщение
о том, что текущая модель была напечатана Затем модель перемещается в спи
сок напечатанных После завершения цикла выводится список напечатанных моделей

Передача списка 149
Printing model: dodecahedron Printing model: robot pendant Printing model: iphone case The following models have been printed: dodecahedron robot pendant iphone case
Мы можем изменить структуру этого кода для этого следует написать две функ
ции, каждая из которых решает одну конкретную задачу Б ульшая часть кода
останется неизменной просто программа становится более эффективной Первая
функция занимается печатью, а вторая выводит сводку напечатанных моделей  def print_models(unprinted_designs, completed_models):
""" Имитирует печать моделей, пока список не станет пустым. Каждая модель после печати перемещается в completed_models. """ while unprinted_designs: current_design = unprinted_designs.pop() # Имитация печати модели на 3D-принтере. print("Printing model: " + current_design) completed_models.append(current_design)
 def show_completed_models(completed_models):
"""Выводит информацию обо всех напечатанных моделях.""" print("\nThe following models have been printed:") for completed_model in completed_models: print(completed_model) unprinted_designs = ['iphone case', 'robot pendant', 'dodecahedron']completed_models = []print_models(unprinted_designs, completed_models)show_completed_models(completed_models)
В точке  определяется функция print_models() с двумя параметрами список
моделей для печати и список готовых моделей Функция имитирует печать каж
дой модели, последовательно извлекая модели из первого списка и перемещая
их во второй список В точке  определяется функция show_completed_models()
с одним параметром списком напечатанных моделей Функция show_completed_
models() получает этот список и выводит имена всех напечатанных моделей
Программа выводит тот же результат, что и версия без функций, но структура
кода значительно улучшилась Код, выполняющий б ульшую часть работы, разне
сен по двум разным функциям это упрощает чтение основной части программы
Теперь любому разработчику будет намного проще просмотреть код программы и понять, что делает программа
unprinted_designs = ['iphone case', 'robot pendant', 'dodecahedron'] completed_models = [] print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)

150 Глава 8 • Функции
Программа создает список моделей для печати и пустой список для готовых моде
лей Затем, поскольку обе функции уже определены, остается вызвать их и передать
правильные аргументы Мы вызываем print_models() и передаем два необходимых
списка как и ожидалось, print_models() имитирует печать моделей Затем вызыва
ется функция show_completed_models() , и ей передается список готовых моделей,
чтобы функция могла вывести информацию о напечатанных моделях Благодаря
содержательным именам функций другой разработчик сможет прочитать этот код и понять его даже без комментариев
Вдобавок эта программа создает меньше проблем с расширением и сопровождени
ем, чем версия без функций Если позднее потребуется напечатать новую партию
моделей, достаточно снова вызвать print_models() Если окажется, что код печати
необходимо модифицировать, изменения достаточно внести в одном месте, и они
автоматически распространятся на все вызовы функции Такой подход намного эффективнее независимой правки кода в нескольких местах программы
Этот пример также демонстрирует принцип, в соответствии с которым каждая
функция должна решать одну конкретную задачу Первая функция печатает
каждую модель, а вторая выводит информацию о готовых моделях Такой подход
предпочтительнее решения обеих задач в функции Если вы пишете функцию
и видите, что она решает слишком много разных задач , попробуйте разделить ее
код на две функции
Помните, что функции всегда можно вызывать из других функций Эта возмож ность может пригодиться для разбиения сложных задач на серию составляющих Запрет изменения списка в функции
Иногда требуется предотвратить изменение списка в функции Допустим, у вас
имеется список моделей для печати, и вы пишете функцию для перемещения их
в список готовых моделей, как в предыдущем примере Возможно, даже после пе
чати всех моделей исходный список нужно оставить для отчетности Но, поскольку
все имена моделей были перенесены из списка unprinted_designs, остался только
пустой список исходная версия списка потеряна Проблему можно решить пере
дачей функции копии списка вместо оригинала В этом случае все изменения,
вносимые функцией в список, будут распространяться только на копию, а оригинал списка остается неизменным Чтобы передать функции копию списка, можно поступить так
имя_функции (имя_списка [:])
Синтаксис среза [:] создает копию списка для передачи функции Если удаление
элементов из списка unprinted_designs в print_models�py нежелательно, функцию
print_models() можно вызвать так
print_models(unprinted_designs[:], completed_models)
Функция print_models() может выполнить свою работу, потому что она все равно
получает имена всех ненапечатаных моделей Но на этот раз она получает не сам список unprinted_designs , а его копию Список completed_models заполняется

Передача списка 151
именами напечатанных моделей, как и в предыдущем случае, но исходный список функцией не изменяется
Несмотря на то что передача копии позволяет сохранить содержимое списка, обыч
но функциям следует передавать исходный список (если у вас нет веских причин
для передачи копии) Работа с существующим списком более эффективна, потому
что программе не приходится тратить время и память на создание отдельной копии (лишние затраты особенно заметны при работе с большими списками)
УПРАЖНЕНИЯ
8-9� Фокусники: создайте список с именами фокусников� Передайте список функции show_ magicians(), которая выводит имя каждого фокусника в списке�
8-10� Великие фокусники: начните с копии вашей программы из упражнения 8-9� Напишите
функцию make_great(), которая изменяет список фокусников, добавляя к имени каждого
фокусника приставку «Great»� Вызовите функцию show_magicians() и убедитесь в том, что список был успешно изменен�
8-11� Фокусники без изменений: начните с программы из упражнения 8-10� Вызовите функ-
цию make_great() и передайте ей копию списка имен фокусников� Поскольку исходный
список остался неизменным, верните новый список и сохраните его в отдельном списке�
Вызовите функцию show_magicians() с каждым списком, чтобы показать, что в одном спи-
ске остались исходные имена, а в другом к имени каждого фокусника добавилась приставка
«Great»�
Передача произвольного набора аргументов
В некоторых ситуациях вы не знаете заранее, сколько аргументов должно быть
передано функции К счастью, th позволяет функции получить произвольное количество аргументов из вызывающей команды
Для примера рассмотрим функцию для создания пиццы Функция должна полу
чить набор дополнений к пицце, но вы не знаете заранее, сколько дополнений за
кажет клиент Функция в следующем примере получает один параметр *toppings,
но этот параметр объединяет все аргументы, заданные в командной строке
pizza.py def make_pizza(*toppings): """Вывод списка заказанных дополнений.""" print(toppings) make_pizza('pepperoni')make_pizza('mushrooms', 'green peppers', 'extra cheese')
Звездочка в имени параметра *toppings приказывает th создать пустой кор
теж с именем toppings и упаковать в него все полученные значения Результат
команды print в теле функции показывает, что th успешно справляется
и с вызовом функции с одним значением, и с вызовом с тремя значениями
Разные вызовы обрабатываются похожим образом Обратите внимание th
упаковывает аргументы в кортеж даже в том случае, если функция получает всего одно значение

152 Глава 8 • Функции
('pepperoni',) ('mushrooms', 'green peppers', 'extra cheese')
Теперь команду print можно заменить циклом, который перебирает список
дополнений и выводит описание заказанной пиццы
def make_pizza(*toppings): """Выводит описание пиццы.""" print("\nMaking a pizza with the following toppings:") for topping in toppings: print("- " + topping)
make_pizza('pepperoni') make_pizza('mushrooms', 'green peppers', 'extra cheese')
Функция реагирует соответственно независимо от того, сколько значений она получила — одно или три
Making a pizza with the following toppings: - pepperoni Making a pizza with the following toppings: - mushrooms - green peppers - extra cheese
Этот синтаксис работает независимо от количества аргументов, переданных
функции Позиционные аргументы с произвольными наборами аргументов
Если вы хотите, чтобы функция могла вызываться с разными количествами аргу
ментов, параметр для получения произвольного количества аргументов должен
стоять на последнем месте в определении функции th сначала подбирает со
ответствия для позиционных и именованных аргументов, а потом объединяет все остальные аргументы в последнем параметре
Например, если функция должна получать размер пиццы, этот параметр должен стоять в списке до параметра *toppings
def make_pizza(size, *toppings):
"""Выводит описание пиццы.""" print("\nMaking a " + str(size) + "-inch pizza with the following toppings:")
for topping in toppings: print("- " + topping)
make_pizza(16, 'pepperoni') make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
В определении функции th сохраняет первое полученное значение в параметре size Все остальные значения, следующие за ним, сохраняются в кортеже toppings
В вызовах функций на первом месте располагается аргумент для параметра size,
а за ним следует сколько угодно дополнений

Передача списка 153
В итоге для каждой пиццы указывается размер и колич ество дополнений, и каждый
фрагмент информации выводится в положенном месте сначала размер, а потом дополнения
Making a 16-inch pizza with the following toppings: - pepperoni Making a 12-inch pizza with the following toppings: - mushrooms - green peppers - extra cheese
Использование произвольного набора именованных аргументов
Иногда программа должна получать произвольное количество аргументов, но вы
не знаете заранее, какая информация будет передаваться функции В таких случаях
можно написать функцию, получающую столько пар «ключ—значение», сколько
указано в команде вызова Один из возможных примеров — построение пользова
тельских профилей вы знаете, что вы получите информацию о пользователе, но
не знаете заранее, какую именно Функция build_profile() в следующем примере
всегда получает имя и фамилию, но также может получать произвольное количество именованных аргументов
user_profile.py def build_profile(first, last, **user_info): """Строит словарь с информацией о пользователе.""" profile = {}
 profile['first_name'] = first
profile['last_name'] = last
 for key, value in user_info.items():
profile[key] = value return profile user_profile = build_profile('albert', 'einstein', location='princeton', field='physics')print(user_profile)
Определение build_profile() ожидает получить имя и фамилию пользователя,
а также позволяет передать любое количество пар «имя—значение» Две звездочки
перед параметром **user_info заставляют th создать пустой словарь с име
нем user_info и упаковать в него все полученные пары «имя—значение» Внутри
функции вы можете обращаться к парам «имя–значение» из user_info точно так
же, как в любом словаре
В теле build_profile() создается пустой словарь с именем profile для хранения
профиля пользователя В точке  в словарь добавляется имя и фамилия, потому
что эти два значения всегда передаются пользователем В точке  функция пере
бирает дополнительные пары «ключ—значение» в словаре user_info и добавляет
каждую пару в словарь profile Наконец, словарь profile возвращается в точку
вызова функции

154 Глава 8 • Функции
Вызовем функцию build_profile() и передадим ей имя 'albert', фами
лию 'einstein' , и еще две пары «ключ—значение» location='princeton'
и field='physics' Программа сохраняет возвращенный словарь в user_profile
и выводит его содержимое
{'first_name': 'albert', 'last_name': 'einstein', 'location': 'princeton', 'field': 'physics'}
Возвращаемый словарь содержит имя и фамилию пользователя, а в данном случае
еще и местонахождение и область исследований Функция будет работать, сколько
бы дополнительных пар «ключ—значение» ни было перед ано при вызове функции
При написании функций допускаются самые разнообразные варианты смешения
позиционных, именованных и произвольных значений Полезно знать о существо
вании всех этих типов аргументов, потому что они часто будут встречаться вам
при чтении чужого кода Только с опытом вы научитесь правильно использовать
разные типы аргументов и поймете, когда следует применять каждый тип а пока
просто используйте самый простой способ, который позволит решить задачу
С течением времени вы научитесь выбирать наиболее эффективный вариант для каждой конкретной ситуации
УПРАЖНЕНИЯ
8-12� Сэндвичи: напишите функцию, которая получает список компонентов сэндвича� Функ-
ция должна иметь один параметр для любого количества значений, переданных при вызове
функции, и выводить описание заказанного сэндвича� Вызовите функцию три раза с разны-ми количествами аргументов�
8-13� Профиль: начните с копии программы user_profile�py� Создайте собственный профиль
вызовом build_profile(), укажите имя, фамилию и три другие пары «ключ—значение» для вашего описания�
8-14� Автомобили: напишите функцию для сохранения информации об автомобиле в слова-
ре� Функция всегда должна возвращать производителя и название модели, но при этом она
может получать произвольное количество именованных аргументов� Вызовите функцию
с передачей обязательной информации и еще двух пар «имя—значение» (например, цвет и комплектация)� Ваша функция должна работать для вызовов следующего вида:
car = make_car(‘subaru’, ‘outback’, color=’blue’, tow_package=True)
Выведите возвращаемый словарь и убедитесь в том, что вся информация была сохранена правильно�
Хранение функций в модулях
Одно из преимуществ функций заключается в том, что они отделяют блоки кода
от основной программы Если для функций были выбраны содержательные имена,
ваша программа будет намного проще читаться Можно пойти еще дальше и сохра
нить функции в отдельном файле, называемом модулем, а затем импортировать
модуль в свою программу Команда import сообщает th, что код модуля дол
жен быть доступен в текущем выполняемом программном файле
Хранение функций в отдельных файлах позволяет скрыть второстепенные детали
кода и сосредоточиться на логике более высокого уровня Кроме того, функции

Хранение функций в модулях 155
можно использовать во множестве разных программ Функции, хранящиеся в от
дельных файлах, можно передать другим программистам без распространения
полного кода программы А умение импортировать функции позволит вам исполь
зовать библиотеки функций, написанные другими программистами
Существует несколько способов импортирования модулей все они кратко рас
сматриваются ниже Импортирование всего модуля
Чтобы заняться импортированием функций, сначала необходимо создать модуль
Модуль представляет собой файл с расширением �py, содержащий код, который
вы хотите импортировать в свою программу Давайте создадим модуль с функцией make_pizza() Для этого из файла pizza�py следует удалить все, кроме функции
make_pizza()
pizza.py def make_pizza(size, *toppings): """Выводит описание пиццы.""" print("\nMaking a " + str(size) + "-inch pizza with the following toppings:") for topping in toppings: print("- " + topping)
Теперь создайте отдельный файл с именем making_pizzas�py в одном каталоге с pizza�
py Файл импортирует только что созданный модуль, а затем дважды вызывает
make_pizza()
making_pizzas.py import pizza
 pizza.make_pizza(16, 'pepperoni')
pizza.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
В процессе обработки этого файла строка import pizza приказывает th от
крыть файл pizza�py и скопировать все функции из него в программу Вы не види
те, как происходит копирование, потому что th копирует код незаметно для
пользователя во время выполнения программы Вам необходимо знать одно любая функция, определенная в pizza�py, будет доступна в making_pizzas�py
Чтобы вызвать функцию из импортированного модуля, укажите имя модуля ( pizza ), точку и имя функции ( make_pizza()), как показано в строке  Код вы
дает тот же результат, что и исходная программа, в которой модуль не импорти
ровался
Making a 16-inch pizza with the following toppings: - pepperoni Making a 12-inch pizza with the following toppings: - mushrooms - green peppers - extra cheese

156 Глава 8 • Функции
Первый способ импортирования, при котором записывается команда import с име
нем модуля, открывает доступ программе ко всем функциям из модуля
имя_модуля .имя_функции ()
Импортирование конкретных функций
Также возможно импортировать конкретную функцию из модуля Общий синтак
сис выглядит так
from имя_модуля import имя_функции
Вы можете импортировать любое количество функций из моду ля, разделив их
имена запятыми
from имя_модуля import функция_0, функция _1, функция _2
Если ограничиться импортированием только той функции, которую вы намерева
етесь использовать, пример making_pizzas�py будет выглядеть так
from pizza import make_pizza make_pizza(16, 'pepperoni') make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
При таком синтаксисе использовать точечную запись при вызове функции не обя
зательно Так как функция make_pizza() явно импортируется в команде import, при
использовании ее можно вызывать прямо по имени Назначение псевдонима для функции
Если имя импортируемой функции может конфликтовать с именем существующей
функции или функция имеет слишком длинное имя, его можно заменить коротким уникальным псевдонимом (aias) — альтернативным именем для функции Псевдо
ним назначается функции при импортировании
В следующем примере функции make_pizza() назначается псевдоним mp(), для чего
при импортировании используется конструкция make_pizza as mp Ключевое слово
as переименовывает функцию, используя указанный псевдоним
from pizza import make_pizza as mp mp(16, 'pepperoni') mp(12, 'mushrooms', 'green peppers', 'extra cheese')
Команда import в этом примере назначает функции make_pizza() псевдоним mp()
для этой программы Каждый раз, когда потребуется вызвать make_pizza(), доста
точно включить вызов mp() — th выполнит код make_pizza() без конфликтов
с другой функцией make_pizza(), которую вы могли включить в этот файл про
граммы Общий синтаксис назначения псевдонима выглядит так
from имя_модуля import имя_функции as псевдоним

Хранение функций в модулях 157
Назначение псевдонима для модуля
Псевдоним также можно назначить для всего модуля Назначение короткого
имени для модуля — скажем, p для pizza — позволит вам быстрее вызывать функ
ции модуля Вызов p.make_pizza() получается более компактным, чем pizza.
make_pizza()
import pizza as p p.make_pizza(16, 'pepperoni') p.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
Модулю pizza в команде import назначается псевдоним p, но все функции
модуля сохраняют свои исходные имена Вызов функций в записи p.make_
pizza() не только компактнее pizza.make_pizza() он также отвлекает внима
ние от имени модуля и помогает сосредоточиться на содержательных именах
функций Эти имена функций, четко показывающие, что делает каждая функ
ция, важнее для удобочитаемости вашего кода, чем использование полного имени модуля
Общий синтаксис выглядит так
import имя_модуля as псевдоним
Импортирование всех функций модуля
Также можно приказать th импортировать каждую функцию в модуле для этого используется оператор *
from pizza import * make_pizza(16, 'pepperoni') make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
Звездочка в команде import приказывает th скопировать каждую функцию
из модуля pizza в файл программы После импортирования всех функций вы
сможете вызывать каждую функцию по имени без точечной записи Тем не менее
лучше не использовать этот способ с большими модулями, написанными дру
гими разработчиками если модуль содержит функцию, имя которой совпадает
с существующим именем из вашего проекта, возможны неожиданные результа
ты th обнаруживает несколько функций или переменных с одинаковыми
именами, и вместо импортирования всех функций по отдельности происходит замена этих функций
В таких ситуациях лучше всего импортировать только нужную функцию или функ
ции или же импортировать весь модуль с последующим применением точечной
записи При этом создается чистый код, легко читаемый и понятный Я включил
этот раздел только для того, чтобы вы понимали команды import вроде следующей,
когда вы встретите их в чужом коде
from имя_модуля import *

158 Глава 8 • Функции
Стилевое оформление функций
В стилевом оформлении функций необходимо учитывать некоторые подробности
Функции должны иметь содержательные имена, состоящие из букв нижнего реги
стра и символов подчеркивания Содержательные имена помогают вам и другим
разработчикам понять, что же делает ваш код Эти соглашения следует соблюдать и в именах модулей
Каждая функция должна быть снабжена комментарием, который кратко поясняет,
что же делает эта функция Комментарий должен следовать сразу же за определе
нием функции в формате строк документации Если функция хорошо документи
рована, другие разработчики смогут использовать ее, прочитав только описание
Конечно, для этого они должны доверять тому, что код работает в соответствии
с описанием, — но, если знать имя функции, какие аргументы ей нужны и какое значение она возвращает, они смогут использовать ее в своих программах
Если для параметра задается значение по умолчанию, слева и справа от знака ра венства не должно быть пробелов
def имя_функции (параметр _0, параметр _1='значение_по_умолчанию ')
Те же соглашения должны применяться для именованных аргументов в вызовах функций имя_функции (значение _0, параметр _1='значение ')
Документ E ( .. ) рекомендует ограни
чить длину строк кода символами, чтобы строки были полностью видны в окне
редактора нормального размера Если изза параметров длина определения функ
ции превышает символов, нажмите Eter после открывающей круглой скобки
в строке определения В следующей строке дважды нажмите a, чтобы отделить
список аргументов от тела функции, которое должно быть снабжено отступом только на один уровень
Многие редакторы автоматически выравнивают дополнительные строки параме тров по отступам, установленным в первой строке
def имя_функции (
параметр_0, параметр _1, параметр _2,
параметр_3, параметр _4, параметр _5):
тело функции ...
Если программа или модуль состоит из нескольких функций, эти функции можно
разделить двумя пустыми строками Так вам будет проще увидеть, где кончается одна функция и начинается другая
Все команды import следует записывать в начале файла У этого правила есть
только одно исключение файл может начинаться с комментариев, описывающих
программу в целом
УПРАЖНЕНИЯ
8-15� Печать моделей: выделите функции примера print_models�py в отдельный файл с име-
нем printing_functions�py� Разместите команду import в начале файла print_models�py и из-мените файл так, чтобы в нем использовались импортированные функции�

Итоги 159
8-16� Импортирование: возьмите за основу одну из написанных вами программ с одной
функцией� Сохраните эту функцию в отдельном файле� Импортируйте функцию в файл основной программы и вызовите функцию каждым из следующих способов: import имя_модуля
from имя_модуля import имя_функции
from имя_модуля import имя_функции as псевдонимimport имя_модуля as псевдоним
from имя_модуля import *
8-17� Стилевое оформление функций: выберите любые три программы, написанные для
этой главы� Убедитесь в том, что в них соблюдаются рекомендации стилевого оформления, представленные в этом разделе�
Итоги
В этой главе вы научились писать функции и передавать аргументы, в которых
функциям сообщается информация, необходимая для их работы Вы узнали, как
использовать позиционные и именованные аргументы и как передать функции про
извольное количество аргументов Вы видели функции, которые выводят данные,
и функции, которые возвращают значения Вы научились использовать функции
со списками, словарями, командами if и циклами while Также вы научились со
хранять функции в отдельных файлах, называемых модулями, чтобы код ваших
программ стал проще и понятнее Глава завершается рекомендациями по стилевому
оформлению функций, чтобы ваши программы были хорошо структурированы и легко читались вами и другими разработчиками
Каждый программист должен стремиться к написанию простого кода, который справ
ляется с поставленной задачей, и функции помогают вам в этом Вы сможете писать
блоки кода и оставлять их на будущее Когда вы знаете, что функция правильно справ
ляется со своей задачей, считайте, что она работает, и переходите к следующей задаче
В программе с использованием функций единожды написанный код может заново
использоваться столько раз, сколько потребуется Чтобы выполнить код, содержа
щийся в функции, достаточно написать всего одну строку с вызовом, а функция
сделает все остальное Если же потребуется модифици ровать поведение функции,
достаточно внести изменения всего в одном месте они в ступят в силу повсюду, где
вызывается эта функция
С функциями ваши программы проще читаются, а хорошо выбранные имена
функций описывают, что делает та или иная часть программы Прочитав серию
вызовов функций, вы гораздо быстрее поймете, что делает функция, чем при чтении длинной серии программных блоков
Функции также упрощают тестирование и отладку кода Когда основная работа про
граммы выполняется набором функций, каждая из которых решает одну конкретную
задачу, вам будет намного проще организовать тестирование и сопровождение вашего
кода Напишите отдельную программу, которая вызывает каждую функцию и про
веряет ее работоспособность во всех типичных ситуациях В этом случае вы можете быть уверены в том, что ваши функции всегда работают правильно
В главе вы научитесь писать классы Классы объединяют функции и данные
в один удобный пакет, с которым вы можете работать гибко и эффективно

9 Классы
Объектноориентированное программирование по праву считается одной из
самых эффективных методологий создания программных продуктов В объ
ектноориентированном программировании вы пишете классы, описывающие
реально существующие предметы и ситуации, а затем создаете объекты на основе
этих описаний При написании класса определяется общее поведение для целой категории объектов
Когда вы создаете конкретные объекты на базе этих классов, каждый объект
автоматически наделяется общим поведением после этого вы можете наделить
каждый объект уникальными особенностями на свой выбор Просто невероятно,
насколько хорошо реальные ситуации моделируются в объектноориентированном программировании
Создание объекта на основе класса называется созданием экземпляра таким об
разом, вы работаете с экземплярами класса В этой главе вы будете писать классы
и создавать экземпляры этих классов Вы укажете, какая информация может
храниться в экземплярах, и определите действия, которые могут выполня ться
с экземплярами
Также вы будете писать классы, расширяющие функциональность существу
ющих классов это позволяет организовать эффективное совместное использо
вание кода похожими классами Вы будете сохранять классы в модулях и импор
тировать классы, написанные другими программистами, в ваши программные файлы
С хорошим пониманием объектноориентированного программирования вы взгля
нете на мир с точки зрения программиста Вам будет проще понять свой код —
увидеть не только то, что происходит в каждой его строке, но и более масштабные
концепции, лежащие в его основе Логика, заложенная в основу классов, научит
вас мыслить последовательно, чтобы ваши программы эффективно решали практически любые задачи, с которыми вы можете столкнуться
Кроме того, классы упрощают жизнь вам и другим программистам, с которыми вам
придется работать совместно над более серьезными проектами Когда вы и другие
программисты пишете код, базирующийся на сходной логике, вам проще понять
код друг друга Ваши программы будут понятны коллегам, и в результате все вы сможете добиться лучших результатов

Создание и использование класса 161
Создание и использование класса
Классы позволяют моделировать практически все что угодно Начнем с написания
простого класса Dog, представляющего собаку — не какуюто конкретную, а собаку
вообще Что мы знаем о собаках У них есть кличка и возраст Также известно, что
большинство собак умеют садиться и перекатываться по команде Эти два вида
информации (кличка и возраст) и два вида поведения (сидеть и перекатываться)
будут включены в класс Dog, потому что они являются общими для большинства
собак Класс сообщает th, как создать объект, представляющий собаку По
сле того как класс будет написан, мы используем его для создания экземпляров, каждый из которых представляет одну конкретную собаку Создание класса Dog
В каждом экземпляре, созданном на основе класса Dog, будет храниться кличка
и возраст кроме того, в нем будут присутствовать методы sit() и roll_over()
dog.py
 class Dog():
 """Простая модель собаки."""

 def __init__(self, name, age):
"""Инициализирует атрибуты name и age."""
 self.name = name
self.age = age
 def sit(self):
"""Собака садится по команде.""" print(self.name.title() + " is now sitting.") def roll_over(self): """Собака перекатывается по команде.""" print(self.name.title() + " rolled over!")
В этом коде есть много мест, заслуживающих вашего внимания, но не беспокойтесь
Эта структура неоднократно встретится вам в этой главе, и вы еще успеете к ней
привыкнуть В точке  определяется класс с именем Dog По общепринятым согла
шениям имена, начинающиеся с символа верхнего регистра, в th обозначают
классы Круглые скобки в определении класса пусты, потому что класс создается
с нуля В точке  приведена строка документации с кратким описанием класса
Метод __init__()
Функция, являющаяся частью класса, называется методом Все, что вы узнали
ранее о функциях, также относится и к методам единственное практическое раз
личие — способ вызова методов Метод __init__() в точке  — специальный метод,
который автоматически выполняется при создании каждого нового экземпляра
на базе класса Dog Имя метода начинается и заканчивается двумя символами
подчеркивания эта схема предотвращает конфликты имен стандартных методов th и методов ваших классов

162 Глава 9 • Классы
Метод __init__() определяется с тремя параметрами self, name и age Пара
метр self обязателен в определении метода он должен предшествовать всем
остальным параметрам Он должен быть включен в определение для того, чтобы
при будущем вызове метода __init__() (для создания экземпляра Dog) автомати
чески передавался аргумент self При каждом вызове метода, связанного с клас
сом, автоматически передается self — ссылка на экземпляр она предоставляет
конкретному экземпляру доступ к атрибутам и методам класса Когда вы создаете экземпляр Dog, th вызывает метод __init__() из класса Dog Мы передаем
Dog() кличку и возраст в аргументах значение self передается автоматически, так
что его передавать не нужно Каждый раз, когда вы захотите создать экземпляр
на основе класса Dog, необходимо предоставить значения только двух последних
аргументов name и age
Каждая из двух переменных, определяемых в точке , имеет префикс self Лю
бая переменная с префиксом self доступна для каждого метода в классе, и вы
также сможете обращаться к этим переменным в каждом экземпляре, созданном
на основе класса Конструкция self.name = name берет значение, хранящееся
в параметре name, и сохраняет его в переменной name, которая затем связыва
ется с создаваемым экземпляром Процесс также повторяется с self.age = age
Переменные, к которым вы обращаетесь через экземпляры, тоже называются атрибутами
В классе Dog также определяются два метода sit() и roll_over()  Так как
этим методам не нужна дополнительная информация (кличка или возраст), они
определяются с единственным параметром self Экземпляры, которые будут
созданы позднее, смогут вызывать эти методы Пока методы sit() и roll_over()
ограничиваются простым выводом сообщения о том, что собака садится или
перекатывается Тем не менее концепцию легко расширить для практического
применения если бы этот класс был частью компьютерной игры, то эти методы
вполне могли бы содержать код для создания анимации садящейся или пере
катывающейся собаки А если бы класс был написан для управления роботом,
то методы могли бы управлять механизмами, заставляющими роботасобаку выполнить соответствующую команду
Создание классов в Python 2�7
При создании классов в th Ć необходимо внести одно незначительное из
менение — заключить в круглые скобки ключевое слово object
class ClassName(object): ...
В этом случае поведение классов th Ć будет приближено к поведению клас
сов th , что упрощает вашу работу в целом Скажем, в th Ć класс Dog будет определяться следующим образом
class Dog(object): ...

Создание и использование класса 163
Создание экземпляра класса
Считайте, что класс — это своего рода инструкция по созданию экземпляров Со
ответственно, класс Dog — инструкция по созданию экземпляров, представляющих
конкретных собак Создадим экземпляр, представляющий конкретную собаку
class Dog(): ...
 my_dog = Dog('willie', 6)
 print("My dog's name is " + my_dog.name.title() + ".")
 print("My dog is " + str(my_dog.age) + " years old.")
Использованный в данном случае класс Dog был написан в предыдущем при
мере В точке  мы приказываем th создать экземпляр собаки с кличкой
'willie' и возрастом ؆ В процессе обработки этой строки th вызывает ме
тод __init__() класса Dog с аргументами 'willie' и ؆ Метод __init__() создает
экземпляр, представляющий конкретную собаку, и присваивает его атрибутам name
и age переданные значения Метод __init__() не содержит явной команды return,
но th автоматически возвращает экземпляр, представляющий собаку Этот
экземпляр сохраняется в переменной my_dog Здесь нелишне вспомнить соглаше
ния по записи имен обычно считается, что имя, начинающееся с символа верхнего
регистра (например, Dog), обозначает класс, а имя, записанное в нижнем регистре
(например, my_dog), обозначает отдельный экземпляр, созданный на базе класса
Обращение к атрибутам
Для обращения к атрибутам экземпляра используется «точечная» запись В стро ке  мы обращаемся к значению атрибута name экземпляра my_dog
my_dog.name
Точечная запись часто используется в th Этот синтаксис показывает, как
th ищет значения атрибутов В данном случае th обращается к экзем
пляру my_dog и ищет атрибут name, связанный с экземпляром my_dog Это тот же
атрибут, который обозначался self.name в классе Dog В точке  тот же прием ис
пользуется для работы с атрибутом age В первой команде print вызов my_dog.name.
title() записывает 'willie' (значение атрибута name экземпляра my_dog) с символа
верхнего регистра Во второй команде print вызов str(my_dog.age) преобразует ,
значение атрибута age экземпляра my_dog, в строку
Пример выводит сводку известных фактов о my_dog
My dog's name is Willie. My dog is 6 years old.
Вызов методов
После создания экземпляра на основе класса Dog можно применять точечную за
пись для вызова любых методов, определенных в Dog

164 Глава 9 • Классы
class Dog(): ... my_dog = Dog('willie', 6)
my_dog.sit() my_dog.roll_over()
Чтобы вызвать метод, укажите экземпляр (в данном случае my_dog) и вызываемый
метод, разделив их точкой В ходе обработки my_dog.sit() th ищет метод sit()
в классе Dog и выполняет его код Строка my_dog.roll_over() интерпретируется
аналогичным образом Теперь экземпляр послушно выполняет полученные команды
Willie is now sitting. Willie rolled over!
Это очень полезный синтаксис Если атрибутам и методам были присвоены содер
жательные имена (например, name, age , sit() и roll_over() ), разработчик сможет
легко понять, что делает блок кода, — даже если он видит этот блок впервые Создание нескольких экземпляров
На основе класса можно создать столько экземпляров, сколько вам потребуется Создадим второй экземпляр Dog с именем your_dog
class Dog(): ...
my_dog = Dog('willie', 6) your_dog = Dog('lucy', 3) print("My dog's name is " + my_dog.name.title() + ".") print("My dog is " + str(my_dog.age) + " years old.") my_dog.sit() print("\nYour dog's name is " + your_dog.name.title() + ".") print("Your dog is " + str(your_dog.age) + " years old.")your_dog.sit()
В этом примере создаются два экземпляра с именами Willie и Lucy Каждый экзем
пляр обладает своим набором атрибутов и способен выполнять действия из общего набора
My dog's name is Willie. My dog is 6 years old. Willie is now sitting. Your dog's name is Lucy. Your dog is 3 years old. Lucy is now sitting.
Даже если второй собаке будут назначены те же имя и возраст, th все
равно создаст отдельный экземпляр класса Dog Вы можете создать сколько
угодно экземпляров одного класса при условии, что эти экземпляры хранятся

Работа с классами и экземплярами 165
в переменных с разными именами или занимают разные позиции в списке или словаре
УПРАЖНЕНИЯ
9-1� Ресторан: создайте класс с именем Restaurant� Метод __init__() класса Restaurant дол-
жен содержать два атрибута: restaurant_name и cuisine_type� Создайте метод describe_
restaurant(), который выводит два атрибута, и метод open_restaurant(), который выводит сообщение о том, что ресторан открыт�
Создайте на основе своего класса экземпляр с именем restaurant� Выведите два атрибута по отдельности, затем вызовите оба метода�
9-2� Три ресторана: начните с класса из упражнения 9-1� Создайте три разных экземпляра,
вызовите для каждого экземпляра метод describe_restaurant()�
9-3� Пользователи: создайте класс с именем User� Создайте два атрибута first_name и last_
name, а затем еще несколько атрибутов, которые обычно хранятся в профиле пользова-
теля� Напишите метод describe_user(), который выводит сводку с информацией о пользо-
вателе� Создайте еще один метод greet_user() для вывода персонального приветствия для пользователя�
Создайте несколько экземпляров, представляющих разных пользователей� Вызовите оба метода для каждого пользователя�
Работа с классами и экземплярами
Классы могут использоваться для моделирования многих р еальных ситуаций По
сле того как класс будет написан, разработчик проводит б ульшую часть времени
за работой с экземплярами, созданными на основе этого класса Одной из первых
задач станет изменение атрибутов, связанных с конкретным экземпляром Атрибу
ты экземпляра можно изменять напрямую или же написать методы, изменяющие атрибуты по особым правилам Класс Car
Напишем класс, представляющий автомобиль Этот класс будет содержать инфор
мацию о типе машины, а также метод для вывода краткого описания
car.py class Car(): """Простая модель автомобиля."""
 def __init__(self, make, model, year):
"""Инициализирует атрибуты описания автомобиля.""" self.make = make self.model = model self.year = year
 def get_descriptive_name(self):
"""Возвращает аккуратно отформатированное описание.""" long_name = str(self.year) + ' ' + self.make + ' ' + self.model return long_name.title()
 my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())

166 Глава 9 • Классы
В точке  в классе Car определяется метод __init__() его список параметров на
чинается с self, как и в классе Dog За ним следуют еще три параметра make, model
и year Метод __init__() получает эти параметры и сохраняет их в атрибутах, ко
торые будут связаны с экземплярами, созданными на основе класса При создании
нового экземпляра Car необходимо указать фирмупроизводителя, модель и год
выпуска для данного экземпляра
В точке  определяется метод get_descriptive_name() , который объединяет год
выпуска, фирмупроизводителя и модель в одну строку с описанием Это избавит
вас от необходимости выводить значение каждого атрибута по отдельности Для
работы со значениями атрибутов в этом методе используется синтаксис self.make,
self.model и self.year
В точке  создается экземпляр класса Car, который сохраняется в переменной
my_new_car Затем вызов метода get_descriptive_name() показывает, с какой ма
шиной работает программа 2016 Audi A4
Чтобы класс был более интересным, добавим атрибут, изменяющийся со време
нем, — в нем будет храниться пробег машины в милях Назначение атрибуту значения по умолчанию
Каждый атрибут класса должен иметь исходное значение, даже если оно равно
или пустой строке В некоторых случаях (например, при задании значений по
умолчанию) это исходное значение есть смысл задавать в теле метода __init__()
в таком случае передавать параметр для этого атрибу та при создании объекта
не обязательно
Добавим атрибут с именем odometer_reading, исходное значение которого всегда
равно Ά Также в класс будет включен метод read_odometer() для чтения текущих
показаний одометра
class Car(): def __init__(self, make, model, year): """Инициализирует атрибуты описания автомобиля."""self.make = makeself.model = modelself.year = year
 self.odometer_reading = 0

def get_descriptive_name(self): ...

 def read_odometer(self):
"""Выводит пробег машины в милях.""" print("This car has " + str(self.odometer_reading) + " miles on it.")
my_new_car = Car('audi', 'a4', 2016) print(my_new_car.get_descriptive_name())
my_new_car.read_odometer()

Работа с классами и экземплярами 167
Когда th вызывает метод __init__() для создания нового экземпляра, этот
метод сохраняет фирмупроизводителя, модель и год выпуска в атрибутах, как
и в предыдущем случае Затем th создает новый атрибут с именем odometer_
reading и присваивает ему исходное значение  Также в класс добавляется
новый метод read_odometer()  , который упрощает чтение пробега машины
в милях Сразу же после создания машины ее пробег равен ν
2016 Audi A4 This car has 0 miles on it.
Впрочем, у продаваемых машин одометр редко показывает ровно , поэтому нам понадобится способ изменения значения этого атрибута Изменение значений атрибутов
Значение атрибута можно изменить одним из трех способов изменить его прямо
в экземпляре, задать значение при помощи метода или измени ть его с приращением
(то есть прибавлением определенной величины) при помощи метода Рассмотрим все эти способы Прямое изменение значения атрибута
Чтобы изменить значение атрибута, проще всего обратиться к нему прямо через эк
земпляр В следующем примере на одометре напрямую выставляется значение ҽ
class Car(): ... my_new_car = Car('audi', 'a4', 2016) print(my_new_car.get_descriptive_name())
 my_new_car.odometer_reading = 23
my_new_car.read_odometer()
В точке  точечная запись используется для обращения к атрибуту odometer_
reading экземпляра и прямого присваивания его значения Эта стро ка приказывает
th взять экземпляр my_new_car, найти связанный с ним атрибут odometer_
reading и задать значение атрибута равным ҽ
2016 Audi A4 This car has 23 miles on it.
Иногда подобные прямые обращения к атрибутам допустимы, но чаще разработчик пишет вспомогательный метод, который изменяет значение за него Изменение значения атрибута с использованием метода
В класс можно включить методы, которые изменяют некоторые атрибуты за вас
Вместо того чтобы изменять атрибут напрямую, вы передаете новое значение методу, который берет обновление атрибута на себя

168 Глава 9 • Классы
В следующем примере в класс включается метод update_odometer() для изменения
показаний одометра
class Car(): ...
 def update_odometer(self, mileage):
"""Устанавливает заданное значение на одометре.""" self.odometer_reading = mileage
my_new_car = Car('audi', 'a4', 2016) print(my_new_car.get_descriptive_name())
 my_new_car.update_odometer(23)
my_new_car.read_odometer()
Класс Car почти не изменился, в нем только добавился метод update_odometer()
Этот метод получает пробег в милях и сохраняет его в self.odometer_reading
В точке  мы вызываем метод update_odometer() и передаем ему значение
в аргументе (соответствующем параметру mileage в определении метода) Метод
устанавливает на одометре значение , а метод read_odometer() выводит текущие
показания
2016 Audi A4 This car has 23 miles on it.
Метод update_odometer() можно расширить так, чтобы при каждом изменении
показаний одометра выполнялась некоторая дополнительная работа Добавим
проверку, которая гарантирует, что никто не будет пытаться сбрасывать показания одометра class Car(): ... def update_odometer(self, mileage): """ Устанавливает на одометре заданное значение. При попытке обратной подкрутки изменение отклоняется. """
 if mileage >= self.odometer_reading:
self.odometer_reading = mileage else:
 print("You can't roll back an odometer!")
Теперь update_odometer() проверяет новое значение перед изменением атрибута
Если новое значение mileage больше или равно текущего, self.odometer_reading,
показания одометра можно обновить новым значением  Если же новое значение
меньше текущего, вы получите предупреждение о недопустимости обратной под крутки 
Изменение значения атрибута с приращением
Иногда значение атрибута требуется изменить с заданным приращением (вме
сто того чтобы присваивать атрибуту произвольное новое значение) Допустим,

Работа с классами и экземплярами 169
вы купили подержанную машину и проехали на ней миль Следующий метод
получает величину приращения и прибавляет ее к текущим показаниям одометра
class Car(): ... def update_odometer(self, mileage): --snip--

 def increment_odometer(self, miles):
"""Увеличивает показания одометра с заданным приращением.""" self.odometer_reading += miles
 my_used_car = Car('subaru', 'outback', 2013)
print(my_used_car.get_descriptive_name())
 my_used_car.update_odometer(23500)
my_used_car.read_odometer()
 my_used_car.increment_odometer(100)
my_used_car.read_odometer()
Новый метод increment_odometer() в точке  получает расстояние в милях
и прибавляет его к self.odometer_reading В точке  создается экземпляр
my_used_car Мы инициализируем показания его одометра значением μ
для этого вызывается метод update_odometer(), которому передается значение
 В точке  вызывается метод increment_odometer() , которому передает
ся значение , чтобы увеличить показания одометра на миль, пройденные с момента покупки
2013 Subaru Outback This car has 23500 miles on it. This car has 23600 miles on it.
При желании можно легко усовершенствовать этот метод, чтобы он отклонял
отрицательные приращения тем самым вы предотвратите обратную подкрутку одометра ПРИМЕЧАНИЕ
Подобные методы управляют обновлением внутренних значений экземпляров (таких, как показа-
ния одометра), однако любой пользователь, имеющий доступ к программному коду, сможет напря-
мую задать атрибуту любое значение� Эффективная схема безопасности должна уделять особое внимание таким подробностям, не ограничиваясь простейшими проверками�
УПРАЖНЕНИЯ
9-4� Посетители: начните с программы из упражнения 9-1 (с� 165)� Добавьте атрибут
number_served со значением по умолчанию 0; он представляет количество обслуженных
посетителей� Создайте экземпляр с именем restaurant� Выведите значение number_served, потом измените и выведите снова�
Добавьте метод с именем set_number_served(), позволяющий задать количество обслужен- ных посетителей� Вызовите метод с новым числом, снова выведите значение�

170 Глава 9 • Классы
Добавьте метод с именем increment_number_served(), который увеличивает количество
обслуженных посетителей на заданную величину� Вызовите этот метод с любым числом,
которое могло бы представлять количество обслуженных клиентов — скажем, за один день�
9-5� Попытки входа: добавьте атрибут login_attempts в класс User из упражнения 9-3
(с� 165)� Напишите метод increment_login_attempts(), увеличивающий значение login_
attempts на 1� Напишите другой метод с именем reset_login_attempts(), обнуляющий значе-
ние login_attempts�
Создайте экземпляр класса User и вызовите increment_login_attempts() несколько раз� Вы-
ведите значение login_attempts, чтобы убедиться в том, что значение было изменено пра-
вильно, а затем вызовите reset_login_attempts()� Снова выведите login_attempts и убеди-тесь в том, что значение обнулилось�
Наследование
Работа над новым классом не обязана начинаться с нуля Если класс, который вы
пишете, представляет собой специализированную версию ранее написанного клас
са, вы можете воспользоваться наследованием Один класс, наследующий от другого,
автоматически получает все атрибуты и методы первого класса Исходный класс называется родителем, а новый класс — потомком Класспотомок наследует
атрибуты и методы родителя, но при этом также может определять собственные атрибуты и методы Метод __init__() класса-потомка
Первое, что делает th при создании экземпляра к лассапотомка, — присваивает
значения всем атрибутам классародителя Для этого методу __init__() классапо
томка необходима помощь со стороны родителя
Например, попробуем построить модель электромобиля Электромобиль пред
ставляет собой специализированную разновидность автомобиля, поэтому новый класс ElectricCar можно создать на базе класса Car, написанного ранее Тогда
нам останется добавить в него код атрибутов и поведения, относящегося только к электромобилям
Начнем с создания простой версии класса ElectricCar, который делает все, что
делает класс Car
electric_car.py

class Car(): """Простая модель автомобиля.""" def __init__(self, make, model, year): self.make = makeself.model = modelself.year = yearself.odometer_reading = 0 def get_descriptive_name(self): long_name = str(self.year) + ' ' + self.make + ' ' + self.modelreturn long_name.title()

Наследование 171
def read_odometer(self): print("This car has " + str(self.odometer_reading) + " miles on it.") def update_odometer(self, mileage): if mileage >= self.odometer_reading:self.odometer_reading = mileageelse:print("You can't roll back an odometer!") def increment_odometer(self, miles): self.odometer_reading += miles
 class ElectricCar(Car):
"""Представляет аспекты машины, специфические для электромобилей."""
 def __init__(self, make, model, year):
"""Инициализирует атрибуты класса-родителя."""
 super().__init__(make, model, year)

 my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
В точке  строится экземпляр Car При создании классапотомка классродитель
должен быть частью текущего файла, а его определение должно предшествовать
определению классапотомка в файле В точке  определяется класспотомок
ElectricCar В определении потомка имя классародителя заключается в круглые
скобки Метод __init__() в точке  получает информацию, необходимую для
создания экземпляра Car
Функция super() в строке  — специальная функция, которая помогает th
связать потомка с родителем Эта строка приказывает th вызвать метод __init__() класса, являющегося родителем ElectricCar, в результате чего экзем
пляр ElectricCar получает все атрибуты классародителя Имя super происходит
из распространенной терминологии классродитель называется суперклассом,
а класспотомок — субклассом
Чтобы проверить, правильно ли сработало наследование, попробуем создать
электромобиль с такой же информацией, которая передается при создании
обычного экземпляра Car В точке  мы создаем экземпляр класса ElectricCar
и сохраняем его в my_tesla Эта строка вызывает метод __init__(), определен
ный в ElectricCar , который в свою очередь приказывает th вызвать метод
__init__() , определенный в классеродителе Car При вызове передаются аргу
менты 'tesla' , 'model s' и 2016
Кроме __init__() класс еще не содержит никаких атрибутов или методов, специ
фических для электромобилей Пока мы просто убеждаемся в том, что класс электромобиля содержит все поведение, присущее классу автомобиля
2016 Tesla Model S
Экземпляр ElectricCar работает так же, как экземпляр Car можно переходить
к определению атрибутов и методов, специфических для электромобилей

172 Глава 9 • Классы
Наследование в Python 2�7
В th Ć наследование реализовано немного иначе Класс ElectricCar будет
выглядеть примерно так
class Car(object):
def __init__(self, make, model, year): ... class ElectricCar(Car): def __init__(self, make, model, year):
super(ElectricCar, self).__init__(make, model, year)...
Функция super() должна получать два аргумента ссылку на класспот омок и объ
ект self Эти аргументы необходимы для того, чтобы th мог правильно связать
родителя с потомком Если вы используете наследование в th Ć, убедитесь в том, что родитель также определяется с синтаксисом object
Определение атрибутов и методов класса-потомка
После создания классапотомка, наследующего от классародителя, можно пере
ходить к добавлению новых атрибутов и методов, необходимых для того, чтобы потомок отличался от родителя
Добавим атрибут, специфический для электромобилей (например, мощность ак кумулятора), и метод для вывода информации об этом атрибуте
class Car(): ... class ElectricCar(Car): """Представляет аспекты машины, специфические для электромобилей."""def __init__(self, make, model, year):
""" Инициализирует атрибуты класса-родителя. Затем инициализирует атрибуты, специфические для электромобиля. """
super().__init__(make, model, year)  self.battery_size = 70

 def describe_battery(self):
"""Выводит информацию о мощности аккумулятора.""" print("This car has a " + str(self.battery_size) + "-kWh battery.")
my_tesla = ElectricCar('tesla', 'model s', 2016) print(my_tesla.get_descriptive_name())
my_tesla.describe_battery()
В точке  добавляется новый атрибут self.battery_size, которому присваивается
исходное значение — скажем, Ά Этот атрибут будет присутствовать во всех экзем
плярах, созданных на основе класса ElectricCar (но не во всяком экземпляре Car)
Также добавляется метод с именем describe_battery(), который выводит инфор

Наследование 173
мацию об аккумуляторе в точке  При вызове этого метода выводится описание,
которое явно относится только к электромобилям
2016 Tesla Model S This car has a 70-kWh battery.
Возможности специализации класса ElectricCar беспредельны Вы можете до
бавить сколько угодно атрибутов и методов, чтобы моделировать электромобиль
с любой нужной точностью Атрибуты или методы, которые могут принадлежать
любой машине (а не только электромобилю), должны добавляться в класс Car вме
сто ElectricCar Тогда эта информация будет доступна всем пользователям класса
Car , а класс ElectricCar будет содержать только код информации и поведения,
специфических для электромобилей Переопределение методов класса-родителя
Любой метод родительского класса, который в моделируемой ситуации делает
не то, что нужно, можно переопределить Для этого в классепотомке определяется
метод с тем же именем, что и у метода классародителя th игнорирует метод родителя и обращает внимание только на метод, определенный в потомке
Допустим, в классе Car имеется метод fill_gas_tank() Для электромобилей за
правка бензином бессмысленна, поэтому этот метод логично переопределить На пример, это можно сделать так
def ElectricCar(Car):
...
def fill_gas_tank(): """У электромобилей нет бензобака.""" print("This car doesn't need a gas tank!")
И если ктото попытается вызвать метод fill_gas_tank() для электромобиля,
th игнорирует метод fill_gas_tank() класса Car и выполнит вместо него этот
код С применением наследования потомок сохраняет те аспекты родителя, которые вам нужны, и переопределяет все ненужное Экземпляры как атрибуты
При моделировании явлений реального мира в программах классы нередко до
полняются все большим количеством подробностей Списки атрибутов и мето
дов растут, и через какоето время файлы становятся длинными и громоздкими
В такой ситуации часть одного класса нередко можно записать в виде отдельного
класса Большой код разбивается на меньшие классы, которые работают во взаимодействии друг с другом
Например, при дальнейшей доработке класса ElectricCar может оказаться, что
в нем появилось слишком много атрибутов и методов, относящихся к аккумулято
ру В таком случае можно остановиться и переместить все эти атрибуты и методы
в отдельный класс с именем Battery Затем экземпляр Battery становится атрибу
том класса ElectricCar

174 Глава 9 • Классы
class Car(): ...
 class Battery():
"""Простая модель аккумулятора электромобиля."""
 def __init__(self, battery_size=70):
"""Инициализирует атрибуты аккумулятора.""" self.battery_size = battery_size
 def describe_battery(self):
"""Выводит информацию о мощности аккумулятора."""
print("This car has a " + str(self.battery_size) + "-kWh battery.")
class ElectricCar(Car): """Представляет аспекты машины, специфические для электромобилей."""def __init__(self, make, model, year):"""Инициализирует атрибуты класса-родителя.Затем инициализирует атрибуты, специфические для электромобиля."""super().__init__(make, model, year)
 self.battery = Battery()
my_tesla = ElectricCar('tesla', 'model s', 2016) print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
В точке  определяется новый класс с именем Battery, который не наследует
ни от одного из других классов Метод __init__() в точке  получает один пара
метр battery_size , кроме self Если значение не предоставлено, этот необязатель
ный параметр задает battery_size значение Ά Метод describe_battery() также
перемещен в этот класс 
Затем в класс ElectricCar добавляется атрибут с именем self.battery  Эта стро
ка приказывает th создать новый экземпляр Battery (со значением battery_
size по умолчанию, равным , потому что значение не задано) и сохранить его
в атрибуте self.battery Это будет происходить при каждом вызове __init__()
теперь любой экземпляр ElectricCar будет иметь автоматически создаваемый
экземпляр Battery
Программа создает экземпляр электромобиля и сохраняет его в переменной my_
tesla Когда потребуется вывести описание аккумулятора, необходимо обратиться
к атрибуту battery
my_tesla.battery.describe_battery()
Эта строка приказывает th обратиться к экземпляру my_tesla, найти его
атрибут battery и вызвать метод describe_battery() , связанный с экземпляром
Battery из атрибута
Результат выглядит так же, как и в предыдущей версии
2016 Tesla Model S This car has a 70-kWh battery.

Наследование 175
Казалось бы, новый вариант требует большой дополнительной работы, но теперь
аккумулятор можно моделировать с любой степенью детализации без загромож
дения класса ElectricCar Добавим в Battery еще один метод, который выводит
запас хода на основании мощности аккумулятора
class Car(): ... class Battery(): ...
 def get_range(self):
"""Выводит приблизительный запас хода для аккумулятора.""" if self.battery_size == 70: range = 240 elif self.battery_size == 85: range = 270 message = "This car can go approximately " + str(range) message += " miles on a full charge." print(message)
class ElectricCar(Car): ... my_tesla = ElectricCar('tesla', 'model s', 2016) print(my_tesla.get_descriptive_name())my_tesla.battery.describe_battery()
 my_tesla.battery.get_range()
Новый метод get_range() в точке  проводит простой анализ Если мощность рав
на , то get_range() устанавливает запас хода миль, а при мощности h
запас хода равен милям Затем программа выводит это значение Когда вы
захотите использовать этот метод, его придется вызывать через атрибут battery
в точке 
Результат сообщает запас хода машины в зависимости от мощности аккумулятора
2016 Tesla Model S This car has a 70-kWh battery. This car can go approximately 240 miles on a full charge.
Моделирование объектов реального мира
Занявшись моделированием более сложных объектов — т аких, как электромо
били, — вы столкнетесь со множеством интересных вопросов Является ли запас
хода электромобиля свойством аккумулятора или машины Если вы описываете
только одну машину, вероятно, можно связать метод get_range() с классом Battery
Но, если моделируется целая линейка машин от производителя, вероятно, метод
get_range() правильнее будет переместить в класс ElectricCar Метод get_range()
попрежнему будет проверять мощность аккумулятора перед определением за
паса хода, но он будет сообщать запас хода для той машины, с которой он связан

176 Глава 9 • Классы
Также возможно связать метод get_range() с аккумулятором, но передавать ему
параметр (например, car_model) Метод get_range() будет определять запас хода
на основании мощности аккумулятора и модели автомобиля
Если вы начнете ломать голову над такими вопросами, это означает, что вы мыс
лите на более высоком логическом уровне, не ограничиваясь уровнем синтаксиса
Вы думаете уже не о th, а о том, как представить реальный мир в коде И, до
стигнув этой точки, вы поймете, что однозначно правильного или неправильного
подхода к моделированию реальных ситуаций часто не существует Некоторые
методы эффективнее других, но для того, чтобы найти наиболее эффективную
реализацию, необходим практический опыт Если ваш код работает именно так,
как вы хотели, — значит, у вас все получается Не огорчайтесь, если окажется, что
вы по несколько раз переписываете свои классы для разных решений На пути
к написанию точного, эффективного кода все программисты проходят через этот процесс
УПРАЖНЕНИЯ
9-6� Киоск с мороженым: киоск с мороженым — особая разновидность ресторана� Напишите
класс IceCreamStand, наследующий от класса Restaurant из упражнения 9-1 (с� 165) или
упражнения 9-4 (с� 169)� Подойдет любая версия класса; просто выберите ту, которая вам
больше нравится� Добавьте атрибут с именем flavors для хранения списка сортов мороже-
ного� Напишите метод, который выводит этот список� Создайте экземпляр IceCreamStand и вызовите этот метод�
9-7� Администратор: администратор — особая разновидность пользователя� Напишите
класс с именем Admin, наследующий от класса User из упражнения 9-3 (с� 165) или
упражнения 9-5 (с� 170)� Добавьте атрибут privileges для хранения списка строк вида
«разрешено добавлять сообщения», «разрешено удалять пользователей», «разрешено
банить пользователей» и т� д� Напишите метод show_privileges() для вывода набора
привилегий администратора� Создайте экземпляр Admin и вызовите свой метод�
9-8� Привилегии: напишите класс Privileges� Класс должен содержать всего один атрибут
privileges со списком строк из упражнения 9-7� Переместите метод show_privileges() в этот
класс� Создайте экземпляр Privileges как атрибут класса Admin� Создайте новый экземпляр Admin и используйте свой метод для вывода списка привилегий�
9-9� Обновление аккумулятора: используйте окончательную версию программы
electric_car�py из этого раздела� Добавьте в класс Battery метод с именем upgrade_battery()�
Этот метод должен проверять размер аккумулятора и устанавливать мощность равной 85,
если она имеет другое значение� Создайте экземпляр электромобиля с аккумулятором
по умолчанию, вызовите get_range(), а затем вызовите get_range() во второй раз после
вызова upgrade_battery()� Убедитесь в том, что запас хода увеличился�
Импортирование классов
С добавлением новой функциональности в классы файлы могут стать слишком
длинными, даже при правильном использовании наследования В соответствии
с общей философией th файлы не должны загромождаться лишними подроб
ностями Для этого th позволяет хранить классы в модулях и импортировать нужные классы в основную программу

Импортирование классов 177
Импортирование одного класса
Начнем с создания модуля, содержащего только класс Car При этом возникает
неочевидный конфликт имен в этой главе уже был создан файл с именем car�py,
но этот модуль тоже должен называться car�py, потому что в нем содержится
код класса Car Мы решим эту проблему, сохранив класс Car в модуле с именем
car�py , заменив им файл car�py, который использовался ранее В дальнейшем
любой программе, использующей этот модуль, придется присвоить более кон
кретное имя файла — например, my_car�py Ниже приведен файл car�py с кодом
класса Car
car.py
 """Класс для представления автомобиля."""
class Car(): """Простая модель автомобиля.""" def __init__(self, make, model, year): """Инициализирует атрибуты описания автомобиля.""" self.make = make self.model = model self.year = year self.odometer_reading = 0 def get_descriptive_name(self): """Возвращает аккуратно отформатированное описание.""" long_name = str(self.year) + ' ' + self.make + ' ' + self.model return long_name.title() def read_odometer(self): """Выводит пробег машины в милях.""" print("This car has " + str(self.odometer_reading) + " miles on it.") def update_odometer(self, mileage): """ Устанавливает на одометре заданное значение. При попытке обратной подкрутки изменение отклоняется. """ if mileage >= self.odometer_reading: self.odometer_reading = mileage else: print("You can't roll back an odometer!") def increment_odometer(self, miles): """Увеличивает показания одометра с заданным приращением.""" self.odometer_reading += miles
В точке  включается строка документации уровня модуля с кратким описанием
содержимого модуля Пишите строки документации для каждого созданного вами модуля
Теперь мы создадим отдельный файл с именем my_car�py Этот файл импортирует
класс Car и создает экземпляр этого класса
my_car.py
 from car import Car

178 Глава 9 • Классы
my_new_car = Car('audi', 'a4', 2016) print(my_new_car.get_descriptive_name()) my_new_car.odometer_reading = 23 my_new_car.read_odometer()
Команда import в точке  приказывает th открыть модуль car и импорти
ровать класс Car Теперь мы можем использовать класс Car так, как если бы он
был определен в этом файле Результат остается тем же, что и в предыдущей версии
2016 Audi A4 This car has 23 miles on it.
Импортирование классов повышает эффективность программирования Пред
ставьте, каким длинным получился бы файл этой программы, если бы в него был
включен весь класс Car Перемещая класс в модуль и импортируя этот модуль,
вы получаете ту же функциональность, но основной файл программы при этом
остается чистым и удобочитаемым Б ульшая часть логики также может храниться
в отдельных файлах когда ваши классы работают так, как положено, вы можете
забыть об этих файлах и сосредоточиться на высокоуровневой логике основной программы Хранение нескольких классов в модуле
В одном модуле можно хранить сколько угодно классов, хотя все эти классы долж
ны быть какимто образом связаны друг с другом Оба класса Battery и ElectricCar
используются для представления автомобилей, поэтому мы добавим их в модуль
car�py
car.py """Классы для представления машин с бензиновым и электродвигателем."""
class Car(): ... class Battery(): """Простая модель аккумулятора электромобиля."""def __init__(self, battery_size=60):"""Инициализация атрибутов аккумулятора."""self.battery_size = battery_sizedef describe_battery(self):"""Выводит информацию о мощности аккумулятора."""print("This car has a " + str(self.battery_size) + "-kWh battery.") def get_range(self): """Выводит приблизительный запас хода для аккумулятора."""if self.battery_size == 70:range = 240elif self.battery_size == 85:range = 270

Импортирование нескольких классов из модуля 179
message = "This car can go approximately " + str(range) message += " miles on a full charge."print(message) class ElectricCar(Car): """Представляет аспекты машины, специфические для электромобилей."""def __init__(self, make, model, year):"""Инициализирует атрибуты класса-родителя.Затем инициализирует атрибуты, специфические для электромобиля."""super().__init__(make, model, year)self.battery = Battery()
Теперь вы можете создать новый файл с именем my_electric_car�py, импортировать
класс ElectricCar и создать новый экземпляр электромобиля
my_electric_car.py from car import ElectricCar my_tesla = ElectricCar('tesla', 'model s', 2016)print(my_tesla.get_descriptive_name()) my_tesla.battery.describe_battery()my_tesla.battery.get_range()
Программа выводит тот же результат, что и в предыдущем случае, хотя б ульшая
часть ее логики скрыта в модуле
2016 Tesla Model SThis car has a 70-kWh battery.This car can go approximately 240 miles on a full charge.
Импортирование нескольких классов из модуля
В файл программы можно импортировать столько классов, сколько потребуется
Если вы захотите создать обычный автомобиль и электромобиль в одном файле, потребуется импортировать оба класса, Car и ElectricCar
my_cars.py
 from car import Car, ElectricCar
 my_beetle = Car('volkswagen', 'beetle', 2016)
print(my_beetle.get_descriptive_name())
 my_tesla = ElectricCar('tesla', 'roadster', 2016)
print(my_tesla.get_descriptive_name())
Чтобы импортировать несколько классов из модуля, разделите их имена запя
тыми  После того как необходимые классы будут импортированы, вы можете
создать столько экземпляров каждого класса, сколько потребуется

180 Глава 9 • Классы
В этом примере создается обычный автомобиль 낧sae eete  и электро
мобиль esa aster 
2016 Volkswagen Beetle 2016 Tesla Roadster
Импортирование всего модуля
Также возможно импортировать весь модуль, а потом обращаться к нужным клас
сам с использованием точечной записи Этот способ п рост, а полученный код легко
читается Так как каждый вызов, создающий экземпляр класса, включает имя моду
ля, в программе не будет конфликтов с именами, испо льзуемыми в текущем файле
my_cars.py
 import car
 my_beetle = car.Car('volkswagen', 'beetle', 2016)
print(my_beetle.get_descriptive_name())
 my_tesla = car.ElectricCar('tesla', 'roadster', 2016) print(my_tesla.get_descriptive_name())
В точке  импортируется весь модуль car, после чего программа обращает
ся к нужным классам с использованием синтаксиса имямодуля.имякласса
В точке  снова создается экземпляр 낧sae eete, а в точке  — экземпляр
esa aster Импортирование всех классов из модуля
Для импортирования всех классов из модуля используется следующий синтаксис
from имя_модуля import *
Использовать этот способ не рекомендуется по двум причинам Прежде всего,
бывает полезно прочитать команды import в начале файла и получить четкое пред
ставление о том, какие классы используются в программе, а при таком подходе
неясно, какие классы из модуля нужны программе Также возможны конфликты
с именами в файле Если вы случайно импортируете класс с именем, уже при
сутствующим в файле, в программе могут возникнуть коварные ошибки Почему
я привожу описание этого способа Хотя использовать его не рекомендуется, ско
рее всего, вы встретите его в коде других разработчиков
Итак, если вам нужно импортировать большое количество классов из модуля,
лучше импортировать весь модуль и воспользоваться синтаксисом имямодуля.
имякласса Хотя вы не видите перечень всех используемых клас сов в начале
файла, по крайней мере ясно видно, где модуль используется в программе Также
предотвращаются потенциальные конфликты имен, которые могут возникнуть при импортировании каждого класса в модуле

Импортирование нескольких классов из модуля 181
Импортирование модуля в модуль
Иногда классы приходится распределять по нескольким модулям, чтобы избежать
чрезмерного разрастания одного файла и хранения несвязанных классов в одном
модуле При хранении классов в нескольких модулях может оказаться, что один
класс из одного модуля зависит от класса из другого модуля В таких случаях необходимый класс можно импортировать в первый модуль
Допустим, класс Car хранится в одном модуле, а классы ElectricCar и Battery —
в другом Мы создадим новый модуль с именем electric_car�py (он заменит файл
electric_car�py , созданный ранее) и скопируем в него только классы Battery
и ElectricCar
electric_car.py """Набор классов для представления электромобилей."""
 from car import Car
class Battery(): ... class ElectricCar(Car):
...
Классу ElectricCar необходим доступ к классуродителю Car, поэтому класс Car
импортируется прямо в модуль в точке  Если вы забудете вставить эту команду,
при попытке создания экземпляра ElectricCar произойдет ошибка Также необ
ходимо обновить модуль Car, чтобы он содержал только класс Car
car.py """Простая модель автомобиля."""
class Car(): ...
Теперь вы можете импортировать классы из каждого модуля по отдельности
и создать ту разновидность машины, которая вам нужна my_cars.py
 from car import Car
from electric_car import ElectricCar my_beetle = Car('volkswagen', 'beetle', 2016) print(my_beetle.get_descriptive_name()) my_tesla = ElectricCar('tesla', 'roadster', 2016) print(my_tesla.get_descriptive_name())
В точке  класс Car импортируется из своего модуля, а класс ElectricCar — из сво
его После этого создаются экземпляры обоих разновидностей Вывод показывает, что экземпляры были созданы правильно
2016 Volkswagen Beetle 2016 Tesla Roadster

182 Глава 9 • Классы
Выработка рабочего процесса
Как видите, th предоставляет много возможностей структурирования кода
в крупных проектах Вы должны знать все эти возможности, чтобы найти лучшие
способы организации своих проектов, а также лучше понимать код других раз
работчиков
На первых порах постарайтесь поддерживать простую структуру своего кода По
пробуйте разместить весь код в одном файле и, только когда все заработает, переме
стите классы в отдельные модули Если вам нравится схема взаимодействия между
модулями и файлами, попробуйте сохранить классы в модулях в начале работы над
проектом Найдите подход, при котором у вас получается работоспособный код, и двигайтесь дальше
УПРАЖНЕНИЯ
9-10� Импортирование класса Restaurant: возьмите последнюю версию класса Restaurant
и сохраните ее в модуле� Создайте отдельный файл, импортирующий класс Restaurant�
Создайте экземпляр Restaurant и вызовите один из методов Restaurant, чтобы показать, что команда import работает правильно�
9-11� Импортирование класса Admin: начните с версии класса из упражнения 9-8 (с� 176)�
Сохраните классы User, Privileges и Admin в одном модуле� Создайте отдельный файл, соз-
дайте экземпляр Admin и вызовите метод show_privileges(), чтобы показать, что все рабо-тает правильно�
9-12� Множественные модули: сохраните класс User в одном модуле, а классы Privileges
и Admin в другом модуле� В отдельном файле создайте экземпляр Admin и вызовите метод show_privileges(), чтобы показать, что все работает правильно�
Стандартная библиотека Python
Стандартная библиотека th представляет собой набор модулей, включаемых
в каждую установленную копию th Сейчас вы уже примерно понимаете, как
работают классы, и можете начать использовать модули, написанные другими
программистами Чтобы использовать любую функцию или класс из стандартной
библиотеки, достаточно включить простую команду import в начало файла Для
примера рассмотрим класс OrderedDict из модуля collections
Как вы уже знаете, словари позволяют связывать информационные фрагменты, но
они не отслеживают порядок добавления пар «ключ—значение» Если вы хотите
создать словарь, но при этом сохранить порядок добавления пар «ключ—значение»,
воспользуйтесь классом OrderedDict из модуля collections Экземпляры класса
OrderedDict ведут себя практически так же, как и словари, если не считать того, что
они отслеживают порядок добавления пар «ключ—значение»
Вернемся к примеру favorite_languages�py из главы ؆ На этот раз программа будет
отслеживать порядок, в котором участники отвечают на опрос
favorite_languages.py
 from collections import OrderedDict
 favorite_languages = OrderedDict()

Стандартная библиотека Python 183
 favorite_languages['jen'] = 'python'
favorite_languages['sarah'] = 'c' favorite_languages['edward'] = 'ruby'favorite_languages['phil'] = 'python'
 for name, language in favorite_languages.items():
print(name.title() + "'s favorite language is " + language.title() + ".")
Сначала программа импортирует класс OrderedDict из модуля collections в точ
ке  В точке  создается экземпляр класса OrderedDict, который сохраняется
в favorite_languages Обратите внимание на отсутствие фигурных скобок вызов
OrderedDict() создает пустой упорядоченный словарь и сохраняет его в favorite_
languages Затем пары из имени и языка последовательно добав ляются в словарь 
Теперь при переборе favorite_languages в точке  данные всегда будут выдаваться
в порядке их добавления
Jen's favorite language is Python. Sarah's favorite language is C. Edward's favorite language is Ruby. Phil's favorite language is Python.
Это чрезвычайно полезный класс, объединяющий основное преимущество списков
(сохранение исходного порядка) с главной особенностью словарей (связывание
двух видов информации) Когда вы займетесь моделированием реальных ситуаций,
может возникнуть ситуация, в которой упорядоченный словарь окажется именно
тем, что вам необходимо А по мере изучения стандартной библиотеки вы узнаете о других полезных модулях, которые помогут вам в решении типичных задач ПРИМЕЧАНИЕ
Модули также можно загружать из внешних источников� Соответствующие примеры встретятся
вам в части II, в которой для завершения работы над проектами мы будем использовать внешние модули�
УПРАЖНЕНИЯ
9-13� Переработка с OrderedDict Rewrite: начните с упражнения 6-4 (с� 113), в котором
стандартный словарь используется для представления глоссария� Перепишите программу
с использованием класса OrderedDict и убедитесь в том, что порядок вывода совпадает с порядком добавления пар «ключ—значение» в словарь�
9-14� Кубики: модуль random содержит функции для генерирования случайных чисел раз-
ными способами� Функция randint() возвращает целое число в заданном диапазоне� Следу-ющий код возвращает число от 1 до 6: from random import randint x = randint(1, 6)
Создайте класс Die с одним атрибутом с именем sides, который содержит значение по умол-
чанию 6� Напишите метод roll_die() для вывода случайного числа от 1 до количества сторон
кубика� Создайте экземпляр, моделирующий 6-гранный кубик, и имитируйте 10 бросков� Создайте модели 10- и 20-гранного кубика� Имитируйте 10 бросков каждого кубика�
9-15� Модуль недели: для знакомства со стандартной библиотекой Python отлично подой-
дет сайт Python Module of the Week� Откройте сайт http://pymotw�com/ и просмотрите оглав-
ление� Найдите модуль, который покажется вам интересным, и прочитайте про него или
изучите документацию по модулям collections и random�

184 Глава 9 • Классы
Оформление классов
В стилевом оформлении классов есть несколько моментов, о которых стоит упо мянуть отдельно, особенно с усложнением ваших программ
Имена классов должны записываться в «верблюжьей» схеме первая буква каждого
слова записывается в верхнем регистре, слова не разделяются пробелами Имена
экземпляров и модулей записываются в нижнем регистре с разделением слов сим
волами подчеркивания
Каждый класс должен иметь строку документации, следующую сразу же за опреде
лением класса Строка документации должна содержать краткое описание того, что
делает класс, и в ней должны соблюдаться те же соглашения по форматированию,
которые вы использовали при написании строк документации в функциях Каждый
модуль также должен содержать строку документации с описанием возможных применений классов в модуле
Пустые строки могут использоваться для структурирования кода, но злоупотреб
лять ими не стоит В классах можно разделять методы одной пустой строкой, а в модулях для разделения классов можно использовать две пустые строки
Если вам потребуется импортировать модуль из стандартной библиотеки и модуль
из библиотеки, написанной вами, начните с команды import для модуля стандарт
ной библиотеки Затем добавьте пустую строку и команду import для модуля,
написанного вами В программах с несколькими командами import выполнение
этого соглашения поможет понять, откуда берутся разные модули, использованные в программе Итоги
В этой главе вы узнали, как написать собственные классы Вы научились хра
нить информацию в классе с использованием атрибутов и наделять свои классы
нужным поведением Вы узнали, как написать методы __init__() для создания
экземпляров ваших классов с нужными значениями атрибутов и как изменять
атрибуты экземпляров напрямую и через методы Также было показано, что
наследование может упростить создание логически связанных классов и что эк
земпляры одного класса могут использоваться как атрибуты другого класса для упрощения кода классов
Вы узнали, что хранение классов в модулях и импортирование необходимых
классов в файлы, где они будут использоваться, улучшает организацию про
ектов Вы познакомились со стандартной библиотекой th и рассмотрели
пример, основанный на классе OrderedDict из модуля collections Наконец, вы
научились оформлять свои классы с использованием общепринятых соглашений th
В главе вы научитесь работать с файлами и сохранять результаты работы, вы
полненной в программе Также будут рассмотрены исключения — экземпляры
специального класса th, предназначенного для передачи информации о возникающих ошибках

10 Файлы и исключения
Вы уже овладели основными навыками, необходимыми для создания хорошо
структурированных и удобных в использовании программ теперь пора подумать
о том, как сделать ваши программы еще более удобными и полезными В этой главе
вы научитесь работать с файлами, чтобы ваши программы могли быстро анализировать большие объемы данных
В этой главе вы научитесь обрабатывать ошибки, чтобы возникновение аномальных
ситуаций не приводило к аварийному завершению ваших программ Мы рассмо
трим исключения — специальные объекты, которые создаются для управления
ошибками, возникающими во время выполнения программ th Также будет
описан модуль json, позволяющий сохранять пользовательские данные, чтоб ы они
не терялись при завершении работы программы
Работа с файлами и сохранение данных упрощают использование ваших программ
Пользователь сам выбирает, какие данные и когда нужно вводить Он может за
пустить вашу программу, выполнить некоторую работу, потом закрыть программу
и позднее продолжить работу с того момента, на котором он прервался Умение об
рабатывать исключения поможет справиться с такими ситуациями, как отсутствие
нужных файлов, а также с другими проблемами, приводящими к сбою программ
Обработка исключений повысит устойчивость ваших программ при работе с не
корректными данными — появившимися как изза случайных ошибок, так и изза
злонамеренных попыток взлома ваших программ Материал, представленный
в этой главе, сделает ваши программы более практичными, удобными и надежными Чтение из файла
Гигантские объемы данных доступны в текстовых файлах В них могут храниться
погодные данные, социальноэкономическая информация, литературные произ
ведения и многое другое Чтение из файла особенно актуально для приложений,
предназначенных для анализа данных, но оно также может пригодиться в любой
ситуации, требующей анализа или изменения информации, хранящейся в фай
ле Например, программа может читать содержимое текстового файла и пере
писывать его с форматированием, рассчитанным на отображение информации в браузере
Работа с информацией в текстовом файле начинается с чтения данных в память
Вы можете прочитать все содержимое файла или же читать данные по строкам

186 Глава 10 • Файлы и исключения
Чтение всего файла
Для начала нам понадобится файл с несколькими строками текста Пусть это будет файл с числом «пи» с точностью до знаков, по знаков на строку pi_digits.txt 3.1415926535 8979323846 2643383279
Чтобы опробовать эти примеры, либо введите данные в редакторе и сохраните файл
с именем pi_digits�txt , либо загрузите файл из ресурсов книги на странице
.. Сохраните файл в каталоге, в котором будут
храниться программы этой главы
Следующая программа открывает этот файл, читает его и выводит содержимое на экран
file_reader.py with open('pi_digits.txt') as file_object: contents = file_object.read() print(contents)
В первой строке этой программы многое заслуживает вашего внимания Начнем
с функции open() Чтобы выполнить любые операции с файлом — даже просто
вывести его содержимое, — сначала необходимо открыть файл Функция open()
получает один аргумент имя открываемого файла th ищет файл с указанным
именем в каталоге, в котором находится файл текущей программы В данном при
мере выполняется программа file_reader�py, поэтому th ищет файл pi_digits�txt
в каталоге, в котором хранится file_reader�py Функция open() возвращает объект,
представляющий файл В данном случае open('pi_digits.txt') возвращает объ
ект, представляющий файл pi_digits�txt th сохраняет этот объект в переменной
file_object , с которой мы будем работать позднее в программе
Конструкция с ключевым словом with закрывает файл после того, как надобность
в нем отпадет Обратите внимание в этой программе есть вызов open(), но нет
вызова close() Файлы можно открывать и закрывать явными вызовами open()
и close() но если изза ошибки в программе команда close() останется невыпол
ненной, то файл не будет закрыт На первый взгляд это не страшно, н о некоррект
ное закрытие файлов может привести к потере или порче данных А если функция close() будет вызвана слишком рано, программа попытается работать с закрытым
(то есть недоступным) файлом, что приведет к новым ошибкам Не всегда можно
заранее определить, когда нужно закрывать файл, но с приведенной конструкцией
th сделает это за вас Вам остается лишь открыть файл и работать с ним так,
как требуется, надеясь на то, что th закроет его автоматически в правильный момент
После того как в программе появится объект, представляющий файл pi_digits�txt, во
второй строке программы используется метод read(), который читает все содер
жимое файла и сохраняет его содержимое в одной длинной строке в переменной

Чтение из файла 187
contents При выводе значения contents на экране появляется все содержимое
файла
3.1415926535 8979323846 2643383279
Единственное различие между выводом и исходным файлом — лишняя пустая
строка в конце вывода Откуда она взялась Метод read() возвращает ее при чте
нии, если достигнут конец файла Если вы хотите удалить лишнюю пустую строку, включите вызов rstrip() в команду print
with open('pi_digits.txt') as file_object: contents = file_object.read()
print(contents.rstrip())
Напомним, что метод rstrip() удаляет все пропуски в конце строки Теперь вывод
точно соответствует содержимому исходного файла
3.1415926535 8979323846 2643383279
Пути к файлам
Если передать функции open() простое имя файла, такое как pi_digits�txt, th
ищет файл в том каталоге, в котором находится файл, выполняемый в настоящий момент (то есть файл программы �py)
В некоторых случаях (в зависимости от того, как орг анизованы ваши рабочие фай
лы) открываемый файл может и не находиться в одном каталоге с файлом програм
мы Например, файл программы может находиться в каталоге python_work в ката
логе python_work создается другой каталог с именем text_files для текстовых файлов,
с которыми работает программа И хотя папка text_files находится в python_work,
простая передача open() имени файла из text_files не подойдет, потому что th
проведет поиск файла в python_work и на этом остановится поиск не будет продол
жен во вложенном каталоге text_files Чтобы открыть файлы из каталога, отличного
от того, в котором хранится файл программы, необходимо указать путь — то есть
приказать th искать файлы в конкретном месте файловой системы
Так как каталог text_files находится в python_work, для открытия файла из text_files
можно воспользоваться относительным путем Относительный путь приказы
вает th искать файлы в каталоге, который задается относительно каталога,
в котором находится текущий файл программы В системе iԡ и это вы
глядит так
with open('text_files/ имя_файла.txt') as file_object:
Эта строка означает, что файл �txt следует искать в каталоге text_files она предпо
лагает, что каталог text_files находится в python_work (так оно и есть) В системах
is в путях к файлам вместо слеша ( /) используется обратный слеш ( \)
with open('text_files\ имя_файла.txt') as file_object:

188 Глава 10 • Файлы и исключения
Также можно точно определить местонахождение файла в вашей системе неза
висимо от того, где хранится выполняемая программа Такие пути называются абсолютными и используются в том случае, если относительный путь не работает
Например, если каталог text_files находится не в python_work, а в другом каталоге
(скажем, в каталоге с именем other_files), то передать open() путь 'text_files/
filename.txt' не получится, потому что th будет искать указанный каталог
только внутри python_work Чтобы объяснить th, где следует искать файл, не
обходимо записать полный путь
Абсолютные пути обычно длиннее относительных, поэтому их лучше сохранять
в переменных, которые затем передаются open() В iԡ и абсолютные пути
выглядят так
file_path = '/home/ehmatthes/other_files/text_files/ имя_файла.txt'
with open(file_path) as file_object:
В is они выглядят так file_path = 'C:\Users\ehmatthes\other_files\text_files\ имя_файла.txt'
with open(file_path) as file_object:
С абсолютными путями вы сможете читать файлы из любого каталога вашей си
стемы Пока будет проще хранить файлы в одном каталоге с файлами программ
или в каталогах, вложенных в каталог с файлами программ (таких как text_files из
рассмотренного примера) ПРИМЕЧАНИЕ
Иногда в системах семейства Windows слеш в пути к файлам интерпретируется правильно� Если вы
используете Windows, но не получаете ожидаемых результатов, попробуйте использовать символы обратного слеша� Чтение по строкам
В процессе чтения файла часто бывает нужно обработать каждую строку Воз
можно, вы ищете некую информацию в файле или собираетесь какимто образом
изменить текст, например при чтении файла с метеорологическими данными вы
обрабатываете каждую строку, у которой в описании погоды встречается слово
«солнечно» Или, допустим, в новостях вы ищете каждую строку с тегом заголовка и заменяете ее специальными элементами форматирования
Для последовательной обработки каждой строки в файле можно воспользоваться циклом for
file_reader.py
 filename = 'pi_digits.txt'
 with open(filename) as file_object:
 for line in file_object:
print(line)
В точке  имя файла, из которого читается информация, сохраняется в перемен
ной filename Это стандартный прием при работе с файлами так как переменная

Чтение из файла 189
filename не представляет конкретный файл (это всего лишь строка, которая со
общает th, где найти файл), вы сможете легко заменить 'pi_digits.txt'
именем другого файла, с которым вы собираетесь работать После вызова open()
объект, представляющий файл и его содержимое, сохраняется в переменной file_object  Мы снова используем синтаксис with, чтобы поручить th
открывать и закрывать файл в нужный момент Для просмотра содержимого все строки файла перебираются в цикле for по объекту файла 
На этот раз пустых строк оказывается еще больше
3.1415926535 8979323846 2643383279
Пустые строки появляются изза того, что каждая строка в текстовом файле за
вершается невидимым символом новой строки Команда print добавляет свой
символ новой строки при каждом вызове, поэтому в резу льтате каждая строка
завершается двумя символами новой строки один прочитан из файла, а другой
добавлен командой print Вызов rstrip() в команде print удаляет лишние пу
стые строки
filename = 'pi_digits.txt' with open(filename) as file_object: for line in file_object:
print(line.rstrip())
Теперь вывод снова соответствует содержимому файла 3.1415926535 8979323846 2643383279
Создание списка строк по содержимому файла
При использовании with объект файла, возвращаемый вызовом open(), доступен
только в пределах содержащего его блока with Если вы хотите, чтобы содержимое
файла оставалось доступным за пределами блока with, сохраните строки файла
в списке внутри блока и в дальнейшем работайте с полученным списком Одни
части файла можно обработать немедленно и отложить другие для обработки в будущем
В следующем примере строки pi_digits�txt сохраняются в списке в блоке with, после
чего выводятся за пределами этого блока
filename = 'pi_digits.txt' with open(filename) as file_object:
 lines = file_object.readlines()

190 Глава 10 • Файлы и исключения
 for line in lines:
print(line.rstrip())
В точке  метод readlines() последовательно читает каждую строку из файла
и сохраняет ее в списке Список сохраняется в переменной lines, с которой можно
продолжить работу после завершения блока with В точке  в простом цикле for
выводятся все элементы списка lines Так как каждый элемент lines соответствует
ровно одной строке файла, вывод точно соответствует его содержимому Работа с содержимым файла
После того как файл будет прочитан в память, вы сможете обрабатывать данные
так, как считаете нужным Для начала попробуем построить одну строку со всеми цифрами из файла без промежуточных пропусков
pi_string.py
filename = 'pi_digits.txt' with open(filename) as file_object: lines = file_object.readlines()
 pi_string = ''
 for line in lines:
pi_string += line.rstrip()
 print(pi_string)
print(len(pi_string))
Сначала программа открывает файл и сохраняет каждую строку цифр в списке —
точно так же, как это делалось в предыдущем примере В точке  создается пере
менная pi_string для хранения цифр числа «пи» Далее следует цикл, который
добавляет к pi_string каждую серию цифр, из которой удаляется символ новой
строки  В точке  программа выводит строку и ее длину
3.1415926535 8979323846 2643383279 36
Переменная pi_string содержит пропуски, которые присутствовали в начале
каждой строки цифр Чтобы удалить их, достаточно использовать strip() вместо
rstrip()
filename = 'pi_30_digits.txt' with open(filename) as file_object: lines = file_object.readlines() pi_string = '' for line in lines:
pi_string += line.strip()
print(pi_string) print(len(pi_string))

Чтение из файла 191
В итоге мы получаем строку, содержащую значение «пи» с точностью до знаков
Длина строки равна символам, потому что в нее также включается начальная цифра и точка 3.141592653589793238462643383279 32 ПРИМЕЧАНИЕ
Читая данные из текстового файла, Python интерпретирует весь текст в файле как строку�
Если вы читаете из текстового файла число и хотите работать с ним в числовом контек -
сте, преобразуйте его в целое число функцией int() или в вещественное число функцией
float()�
Большие файлы: миллион цифр
До настоящего момента мы ограничивались анализом текстового файла, который
состоял всего из трех строк, но код этих примеров будет работать и с намного
большими файлами Начиная с текстового файла, содержащего значение «пи»
до знаков (вместо ), вы сможете создать одну строку, которая содержит
все эти цифры Изменять программу вообще не придется — достаточно передать ей
другой файл Также мы ограничимся выводом первых цифр, чтобы не пришлось ждать, пока в терминале не прокрутится миллион знаков pi_string.py filename = 'pi_million_digits.txt'
with open(filename) as file_object: lines = file_object.readlines()pi_string = ''for line in lines:pi_string += line.strip()
print(pi_string[:52] + "...")
print(len(pi_string))
Из выходных данных видно, что строка действительно содержит значение «пи» с точностью до знаков3.14159265358979323846264338327950288419716939937510... 1000002
th не устанавливает никаких ограничений на длину данных, с которыми
вы можете работать Она ограничивается разве что объемом памяти вашей системы
ПРИМЕЧАНИЕ
Для запуска этой программы (и многих других примеров, приведенных ниже) необходимо загру-
зить ресурсы по адресу https://www�nostarch�com/pythoncrashcourse/�

192 Глава 10 • Файлы и исключения
Проверка дня рождения
Меня всегда интересовало, не встречается ли мой ден ь рождения среди цифр числа
«пи» Воспользуемся только что созданной программой для проверки того, входит
ли запись дня рождения пользователя в первый миллион цифр Для этого можно
записать день рождения в виде строки из цифр и посмотреть, присутствует ли эта строка в pi_string
filename = 'pi_million_digits.txt' with open(filename) as file_object: lines = file_object.readlines() pi_string = '' for line in lines:pi_string += line.rstrip()
 birthday = input("Enter your birthday, in the form mmddyy: ")
 if birthday in pi_string:
print("Your birthday appears in the first million digits of pi!") else: print("Your birthday does not appear in the first million digits of pi.")
В точке  программа запрашивает день рождения пользователя, а за тем в точке 
проверяет вхождение этой строки в pi_string Пробуем
Enter your birthdate, in the form mmddyy: 120372
Your birthday appears in the first million digits of pi!
Оказывается, мой день рождения встречается среди цифр «пи» После того как
данные будут прочитаны из файла, вы сможете делать с ними все, что сочтете нужным
УПРАЖНЕНИЯ
10-1� Изучение Python: откройте пустой файл в текстовом редакторе и напишите несколько
строк текста о возможностях Python� Каждая строка должна начинаться с фразы: «In Python
you can…» Сохраните файл под именем learning_python�txt в каталоге, использованном для
примеров этой главы� Напишите программу, которая читает файл и выводит текст три раза:
с чтением всего файла, с перебором строк объекта файла и с сохранением строк в списке
с последующим выводом списка вне блока with�
10-2� Изучение C: метод replace() может использоваться для замены любого слова в строке
другим словом� В следующем примере слово ‘dog’ заменяется словом ‘cat’: >>> message = "I really like dogs." >>> message.replace('dog', 'cat')'I really like cats.'
Прочитайте каждую строку из только что созданного файла learning_python�txt и замените
слово Python названием другого языка, например C� Выведите каждую измененную строку на экран�

Запись в файл 193
Запись в файл
Один из простейших способов сохранения данных — запись в файл Текст,
записанный в файл, останется доступным и после закрытия терминала с вы
водом вашей программы Вы сможете проанализировать результаты после
завершения программы или передать свои файлы другим Вы также сможете
написать программы, которые снова читают сохраненны й текст в память и ра
ботают с ним
Запись в пустой файл
Чтобы записать текст в файл, необходимо вызвать open() со вторым аргументом,
который сообщает th, что вы собираетесь записывать данные в файл Чтобы
увидеть, как это делается, напишем простое сообщение и сохраним его в файле (вместо того чтобы просто вывести на экран)
write_message.py filename = 'programming.txt'
 with open(filename, 'w') as file_object:
 file_object.write("I love programming.")
При вызове open() в этом примере передаются два аргумента  Первый аргумент,
как и прежде, содержит имя открываемого файла Второй аргумент 'w' сообщает
th, что файл должен быть открыт в режиме записи Файлы можно открывать
в режиме чтения ( 'r'), записи ( 'w'), присоединения ( 'a') или в режиме, допуска
ющем как чтение, так и запись в файл ( 'r+') Если аргумент режима не указан,
th по умолчанию открывает файл в режиме только для чтения
Если файл, открываемый для записи, еще не существует, функция open() автома
тически создает его Будьте внимательны, открывая файл в режиме записи ( 'w')
если файл существует , то th уничтожит его данные перед возвращением
объекта файла
В точке  метод write() используется с объектом файла для записи строки в файл
Программа не выводит данные на терминал, но, открыв файл programming�txt, вы
увидите в нем одну строку
programming.txt I love programming.
Этот файл ничем не отличается от любого другого текстового файла на вашем ком
пьютере Его можно открыть, записать в него новый текст, скопироватьвставить текст и т д ПРИМЕЧАНИЕ
Python может записывать в текстовые файлы только строковые данные� Если вы захотите сохра-
нить в текстовом файле числовую информацию, данные придется предварительно преобразовать в строки функцией str()�

194 Глава 10 • Файлы и исключения
Многострочная запись Функция write() не добавляет символы новой строки в записываемый текст А это
означает, что если вы записываете сразу несколько строк без включения символов
новой строки, полученный файл может выглядеть не так, как вы рассчитывали
filename = 'programming.txt' with open(filename, 'w') as file_object: file_object.write("I love programming.")
file_object.write("I love creating new games.")
Открыв файл programming�txt , вы увидите, что две строки «склеились»
I love programming.I love creating new games.
Если включить символы новой строки в команды write(), текст будет состоять
из двух строк
filename = 'programming.txt' with open(filename, 'w') as file_object:
file_object.write("I love programming.\n") file_object.write("I love creating new games.\n")
Результат выглядит так I love programming.I love creating new games.
Для форматирования вывода также можно использовать пробелы, символы табу
ляции и пустые строки по аналогии с тем, как это делалось с выводом на терминал Присоединение данных к файлу
Если вы хотите добавить в файл новые данные вместо того, чтобы перезаписывать
существующее содержимое, откройте файл в режиме присоединения В этом слу
чае th не уничтожает содержимое файла перед возвращением объекта файла
Все строки, выводимые в файл, будут добавлены в конец файла Если файл еще не существует, то th автоматически создаст пустой файл
Изменим программу write_message�py и дополним существующий файл
programming�txt новыми данными
write_message.py
filename = 'programming.txt'
 with open(filename, 'a') as file_object:
 file_object.write("I also love finding meaning in large datasets.\n")
file_object.write("I love creating apps that can run in a browser.\n")
В точке  аргумент 'a' используется для открытия файла в режиме присоединения
(вместо перезаписи существующего файла) В точке  записываются две новые
строки, которые добавляются к содержимому programming�txt

Исключения 195
programming.txt
I love programming. I love creating new games.
I also love finding meaning in large datasets. I love creating apps that can run in a browser.
В результате к исходному содержимому файла добавляется новый текст
УПРАЖНЕНИЯ
10-3� Гость: напишите программу, которая запрашивает у пользователя его имя� Введенный ответ сохраняется в файле с именем guest�txt�
10-4� Гостевая книга: напишите цикл while, который в цикле запрашивает у пользователей
имена� При вводе каждого имени выведите на экран приветствие и добавьте строку с со-
общением в файл с именем guest_book�txt� Проследите за тем, чтобы каждое сообщение размещалось в отдельной строке файла�
10-5� Опрос: напишите цикл while, в котором программа спрашивает у пользователя, по-
чему ему нравится программировать� Каждый раз, когда пользователь вводит очередную причину, сохраните текст его ответа в файле�
Исключения
Для управления ошибками, возникающими в ходе выполнения программы,
в th используются специальные объекты, называемые исключениями Если
при возникновении ошибки th не знает, что делать дальше, создается объект
исключения Если в программу включен код обработки исключения, то выполне
ние программы продолжится, а если нет — программа останавливается и выводит трассировку с отчетом об исключении
Исключения обрабатываются в блоках tryexcept Блок tryexcept приказывает
th выполнить некоторые действия, но при этом также сообщает, что нужно де
лать при возникновении исключения С блоками tryexcept ваши программы будут
работать даже в том случае, если чтото пошло не так Вместо невразумительной
трассировки выводится понятное сообщение об ошибке, которое вы определяете в программе
Обработка исключения ZeroDivisionError
Рассмотрим простую ошибку, при которой th инициирует исключение Ко
нечно, вы знаете, что деление на ноль невозможно, но мы все же прикажем th выполнить эту операцию
division.py print(5/0)
Из этого ничего не выйдет, поэтому на экран выводятся данные трассировки Traceback (most recent call last): File "division.py", line 1, in print(5/0)
 ZeroDivisionError: division by zero

196 Глава 10 • Файлы и исключения
Ошибка, упоминаемая в трассировке  — ZeroDivisionError , — является объектом
исключения Такие объекты создаются в том случае, если th не может выпол
нить ваши распоряжения Обычно в таких случаях th прерывает выполнение
программы и сообщает тип обнаруженного исключения Эта информация может
использоваться в программе по сути вы сообщаете th, как следует поступить
при возникновении исключения данного типа В таком случае ваша программа будет подготовлена к его появлению
Блоки try-except
Если вы предполагаете, что в программе может произойти ошибка, напишите блок try except для обработки возникающего исключения Такой блок приказывает
th выполнить некоторый код, а также сообщает, что нужно делать, если при его выполнении произойдет исключение конкретного типа
Вот как выглядит блок tryexcept для обработки исключений ZeroDivisionError
try:
print(5/0)except ZeroDivisionError: print("You can't divide by zero!")
Команда print(5/0) , порождающая ошибку, находится в блоке try Если код в бло
ке try выполнен успешно, то th пропускает блок except Если код в блоке
try порождает ошибку, то th ищет блок except с соответствующей ошибкой
и выпускает код в этом блоке
В этом примере код блока try порождает ошибку ZeroDivisionError, поэтому
th ищет блок except с описанием того, как следует действовать в такой ситу
ации При выполнении кода этого блока пользователь видит понятное сообщение об ошибке вместо данных трассировки
You can't divide by zero!
Если бы за кодом tryexcept следовал другой код, то выполнение программы
продолжилось, потому что мы объяснили th, как обрабатывать эту ошибку
В следующем примере обработка ошибки позволяет программе продолжить вы
полнение Использование исключений для предотвращения аварийного завершения программы
Правильная обработка ошибок особенно важна в том случае, если программа
должна продолжить работу после возникновения ошибки Такая ситуация часто
встречается в программах, запрашивающих данные у пользователя Если програм
ма правильно среагировала на некорректный ввод, она может запросить новые данные после сбоя
Создадим простой калькулятор, который выполняет тол ько операцию деления

Исключения 197
division.py print("Give me two numbers, and I'll divide them.") print("Enter 'q' to quit.") while True:
 first_number = input("\nFirst number: ")
if first_number == 'q': break
 second_number = input("Second number: ")
if second_number == 'q': break
 answer = int(first_number) / int(second_number)
print(answer)
Программа запрашивает у пользователя первое число first_number  , а затем,
если пользователь не ввел q для завершения работы, запрашивает второе число
second_number  Далее одно число делится на другое для получения результата
answer  Программа никак не обрабатывает ошибки, так что попытка деления на
ноль приводит к ее аварийному завершению
Give me two numbers, and I'll divide them. Enter 'q' to quit. First number: 5 Second number: 0 Traceback (most recent call last): File "division.py", line 9, in answer = int(first_number) / int(second_number) ZeroDivisionError: division by zero
Конечно, аварийное завершение — это плохо, но еще хуже, если пользователь
увидит данные трассировки Неопытного пользователя они собьют с толку, а при
сознательной попытке взлома злоумышленник сможет получить из них больше
информации, чем вам хотелось бы Например, он узнает имя файла программы
и увидит некорректно работающую часть кода На основании этой информации
опытный хакер иногда может определить, какие атаки следует применять против вашего кода Блок else
Для повышения устойчивости программы к ошибкам можно заключить строку,
выдающую ошибки, в блок tryexcept Ошибка происходит в строке, выполняющей
деление следовательно, именно эту строку следует заключить в блок tryexcept
Данный пример также включает блок else Любой код, зависящий от успешного
выполнения блока try, размещается в блоке else
print("Give me two numbers, and I'll divide them.") print("Enter 'q' to quit.") while True: first_number = input("\nFirst number: ")if first_number == 'q':

198 Глава 10 • Файлы и исключения
break second_number = input("Second number: ")
 try: answer = int(first_number) / int(second_number)  except ZeroDivisionError:
print("You can't divide by 0!")
 else:
print(answer)
Программа пытается выполнить операцию деления в блоке try  , который
включает только код, способный породить ошибку Любой код, зависящий
от успешного выполнения блока try, добавляется в блок else В данном случае,
если операция деления выполняется успешно, блок else используется для вы
вода результата 
Блок except сообщает th, как следует поступать при возникновении ошибки
ZeroDivisionError  Если при выполнении команды из блока try происходит
ошибка, связанная с делением на , программа выводит понятное сообщение,
которое объясняет пользователю, как избежать подобн ых ошибок Выполнение
программы продолжается, и пользователь не сталкивается с трассировкой
Give me two numbers, and I'll divide them. Enter 'q' to quit. First number: 5
Second number: 0
You can't divide by 0! First number: 5
Second number: 2
2.5 First number: q
Блок tryexcept else работает так th пытается выполнить код в блоке try
В блоках try следует размещать только тот код, при выполнении которого может
возникнуть исключение Иногда некоторый код должен выполняться только в том
случае, если выполнение try прошло успешно такой код размещается в блоке else
Блок except сообщает th, что делать, если при выполнении кода try произо
шло определенное исключение Заранее определяя вероятные источники ошибок,
вы повышаете надежность своих программ, которые продолжают работать даже при
вводе некорректных данных или при недоступности ресурсов Ваш код оказывается защищенным от случайных ошибок пользователей и сознательных атак
Обработка исключения FileNotFoundError
Одна из стандартных проблем при работе с файлами — отсутствие необходимых
файлов Тот файл, который вам нужен, может находиться в другом месте, в имени
файла может быть допущена ошибка, или файл может вообще не существовать
Все эти ситуации достаточно прямолинейно обрабатываются в блоках tryexcept

Исключения 199
Попробуем прочитать данные из несуществующего файла Следующая програм
ма пытается прочитать содержимое файла с текстом «Алисы в Стране чудес», но я не сохранил файл alice�txt в одном каталоге с файлом alice�py
alice.py filename = 'alice.txt' with open(filename) as f_obj: contents = f_obj.read()
Прочитать данные из несуществующего файла нельзя, поэтому th выдает исключение Traceback (most recent call last): File "alice.py", line 3, in with open(filename) as f_obj: FileNotFoundError: [Errno 2] No such file or directory: 'alice.txt'
В последней строке трассировки упоминается FileNotFoundError это исключение
выдается в том случае, если th не может найти открываемый файл В данном
примере функция open() порождает ошибку, и, чтобы обработать ее, блок try на
чинается перед строкой с вызовом open()
filename = 'alice.txt'
try:with open(filename) as f_obj: contents = f_obj.read()
except FileNotFoundError: msg = "Sorry, the file " + filename + " does not exist." print(msg)
В этом примере код блока try выдает исключение FileNotFoundError, поэто
му th ищет блок except для этой ошибки Затем выполняется код этого
блока, в результате чего вместо трассировки выдаетс я доступное сообщение
об ошибке
Sorry, the file alice.txt does not exist.
Если файл не существует, программе больше делать нечего, поэтому код обработ
ки ошибок почти ничего не добавляет в эту программу Доработаем этот пример
и посмотрим, как обработка исключений помогает при работе с несколькими файлами Анализ текста
Программа может анализировать текстовые файлы, содержащие целые книги
Многие классические произведения, ставшие общественным достоянием, доступны
в виде простых текстовых файлов Тексты, использованные в этом разделе, взяты
с сайта проекта «Гутенберг» ( .) На этом сайте хранится под

200 Глава 10 • Файлы и исключения
борка литературных произведений, не защищенных авторским правом это превос
ходный ресурс для разработчиков, которые собираются работать с литературными текстами в своих программных проектах
Прочитаем текст «Алисы в Стране чудес» и попробуем подсчитать количество слов
в тексте Мы воспользуемся методом split(), предназначенным для построения
списка слов на основе строки Вот как метод split() работает со строкой, содер
жащей только название книги
>>> title = "Alice in Wonderland"
>>> title.split()
['Alice', 'in', 'Wonderland']
Метод split() разделяет строку на части по всем позициям, в которых обнаружит
пробел, и сохраняет все части строки в элементах списка В результате создается
список слов, входящих в строку (впрочем, вместе с некоторыми словами могут
храниться знаки препинания) Для подсчета слов в книге мы воспользуемся вызо
вом split() для всего текста, а затем подсчитаем элементы списка, чтобы получить
примерное количество слов в тексте
filename = 'alice.txt' try: with open(filename) as f_obj:contents = f_obj.read()except FileNotFoundError:msg = "Sorry, the file " + filename + " does not exist."print(msg)
else: # Подсчет приблизительного количества строк в файле.
 words = contents.split()
 num_words = len(words)
 print("The file " + filename + " has about " + str(num_words) +
" words.")
Затем я переместил файл alice�txt в правильный каталог, чтобы код в блоке try
был выполнен без ошибок В точке  программа загружает текст в переменную
contents , которая теперь содержит весь текст в виде одной д линной строки и ис
пользует метод split() для получения списка всех слов в книге Запрашивая
длину этого списка при помощи функции len(), мы получаем неплохое при
ближенное значение количества слов в исходной строке  В точке  выводится
сообщение с количеством слов, найденных в файле Этот код помещен в блок else , потому что он должен выводиться только в случае успешного выполнения
блока try Выходные данные программы сообщают, сколько слов содержит файл
alice�txt
The file alice.txt has about 29461 words.
Количество слов немного завышено, потому что в нем учитывается дополнительная
информация, включенная в текстовый файл издателем, но в целом оно довольно точно оценивает длину «Алисы в Стране чудес»

Исключения 201
Работа с несколькими файлами
Добавим еще несколько файлов с книгами для анализа Но для начала переместим
основной код программы в функцию с именем count_words() Это упростит про
ведение анализа для нескольких книг
word_count.py def count_words(filename):
 """Подсчет приблизительного количества строк в файле."""
try: with open(filename) as f_obj:contents = f_obj.read()except FileNotFoundError:msg = "Sorry, the file " + filename + " does not exist."print(msg)else:# Подсчет приблизительного количества строк в файле.words = contents.split()num_words = len(words)print("The file " + filename + " has about " + str(num_words) +" words.")
filename = 'alice.txt' count_words(filename)
Б ульшая часть кода не изменилась Мы просто снабдили код отступом и пере
местили его в тело count_words() Кроме того, комментарий был преобразован
в строку документации 
Теперь мы можем написать простой цикл для подсчета слов в любом тексте,
который нужно проанализировать Для этого имена анализируемых файлов со
храняются в списке, после чего для каждого файла в списке вызывается функ
ция count_words() Мы попробуем подсчитать слова в «Алисе в Стране чудес»,
«Сиддхартхе», «Моби Дике» и «Маленьких женщинах» — все эти книги есть
в свободном доступе Я намеренно не стал копировать файл siddhartha�txt в каталог
с программой word_count�py, чтобы выяснить, насколько хорошо наша программа
справляется с отсутствием файла
def count_words(filename): ...
filenames = ['alice.txt', 'siddhartha.txt', 'moby_dick.txt', 'little_women.txt'] for filename in filenames: count_words(filename)
Отсутствие файла siddhartha�txt не влияет на выполнение программы
The file alice.txt has about 29461 words.Sorry, the file siddhartha.txt does not exist.The file moby_dick.txt has about 215136 words.
The file little_women.txt has about 189079 words.

202 Глава 10 • Файлы и исключения
Использование блока tryexcept в этом примере дает два важных преимущества
программа ограждает пользователя от вывода трассировки и продолжает выполне
ние, анализируя тексты, которые ей удается найти Если бы в программе не пере
хватывалось исключение FileNotFoundError, инициированное изза отсутствия
siddhartha�txt , то пользователь увидел бы полную трассировку, а работа программы
прервалась бы после попытки подсчитать слова в тексте «Сиддхартхи» до анализа «Моби Дика» или «Маленьких женщин» дело не дошло бы Ошибки без уведомления пользователя
В предыдущем примере мы сообщили пользователю о том, что один из файлов ока
зался недоступен Тем не менее вы не обязаны сообщать о каждом обнаруженном
исключении Иногда при возникновении исключения программа должна просто
проигнорировать сбой и продолжать работу, словно ничего не произошло Для
этого блок try пишется так же, как обычно, но в блоке except вы явно приказыва
ете th не предпринимать никаких особых действий в случае ошибки В языке th существует команда pass, с которой блок ничего не делает
def count_words(filename): """Подсчет приблизительного количества строк в файле."""try:...except FileNotFoundError:
 pass else: ... filenames = ['alice.txt', 'siddhartha.txt', 'moby_dick.txt', 'little_women.txt'] for filename in filenames:count_words(filename)
Единственное отличие этого листинга от предыдущего — команда pass в точке 
Теперь при возникновении ошибки FileNotFoundError выполняется код в блоке
except , но при этом ничего не происходит Программа не выдает данные трасси
ровки и вообще никакие результаты, указывающие на возникновение ошибки
Пользователи получают данные о количестве слов во всех существующих файлах, однако ничто не сообщает о том, что какойто файл не был найден
The file alice.txt has about 29461 words. The file moby_dick.txt has about 215136 words. The file little_women.txt has about 189079 words.
Команда pass также может служить временным заполнителем Она напоминает,
что в этот конкретный момент выполнения вашей программы вы решили ниче
го не предпринимать, хотя, возможно, сделаете чтото позднее Например, эта
программа может записать все имена отсутствующих файлов в файл с именем
missing_files�txt Пользователи этот файл не увидят, но создатель программы сможет
прочитать его и разобраться с отсутствующими текстами

Исключения 203
О каких ошибках нужно сообщать?
Как определить, в каком случае следует сообщить об ошибке пользователю, а когда
можно просто проигнорировать ее незаметно для пользователя Если пользователь
знает, с какими текстами должна работать программа, вероятно, он предпочтет по
лучить сообщение, объясняющее, почему некоторые тексты были пропущены при
анализе Пользователь ожидает увидеть какието результаты, но не знает, какие
книги должны быть проанализированы Возможно, ему и не нужно знать о недо
ступности какихто файлов Лишняя информация только сделает вашу программу
менее удобной для пользователя Средства обработки ошибок th позволяют
достаточно точно управлять тем, какой объем информации следует предоставить пользователю
Хорошо написанный, правильно протестированный код редко содержит внутрен
ние ошибки (например, синтаксические или логические) Но в любой ситуации,
в которой ваша программа зависит от внешних факторов (пользовательского ввода,
существования файла, доступности сетевого подключения), существует риск воз
никновения исключения С накоплением практического опыта вы начнете видеть,
в каких местах программы следует разместить блоки обработки исключений
и сколько информации предоставлять пользователям о возникающих ошибках
УПРАЖНЕНИЯ
10-6� Сложение: при вводе числовых данных часто встречается типичная проблема: поль-
зователь вводит текст вместо чисел� При попытке преобразовать данные в int происходит
исключение TypeError� Напишите программу, которая запрашивает два числа, складывает
их и выводит результат� Перехватите исключение TypeError, если какое-либо из входных
значений не является числом, и выведите удобное сообщение об ошибке� Протестируйте
свою программу: сначала введите два числа, а потом введите текст вместо одного из чисел�
10-7� Калькулятор: заключите код из упражнения 10-6 в цикл while, чтобы пользователь мог продолжать вводить числа, даже если он допустил ошибку и ввел текст вместо числа�
10-8� Кошки и собаки: создайте два файла с именами cats�txt и dogs�txt� Сохраните минимум
три клички кошек в первом файле и три клички собак во втором� Напишите программу,
которая пытается прочитать эти файлы и выводит их содержимое на экран� Заключите
свой код в блок try-except для перехвата исключения FileNotFoundError и вывода понятного
сообщения об отсутствии файла� Переместите один из файлов в другое место файловой
системы; убедитесь в том, что код блока except выполняется, как и положено�
10-9� Ошибки без уведомления: измените блок except из упражнения 10-8 так, чтобы при отсутствии файла программа продолжала работу, не уведомляя пользователя о проблеме�
10-10� Частые слова: зайдите на сайт проекта «Гутенберг» (http://gutenberg�org/) и найдите
несколько книг для анализа� Загрузите текстовые файлы этих произведений или скопируй-те текст из браузера в текстовый файл на вашем компьютере�
Для подсчета количества вхождений слова или выражения в строку можно воспользовать-
ся методом count()� Например, следующий код подсчитывает количество вхождений ‘row’ в строке: >>> line = "Row, row, row your boat" >>> line.count('row')2>>> line.lower().count('row')3

204 Глава 10 • Файлы и исключения
Обратите внимание: преобразование строки к нижнему регистру функцией lower() позволя- ет найти все вхождения искомого слова независимо от регистра�
Напишите программу, которая читает файлы из проекта «Гутенберг» и определяет количе- ство вхождений слова ‘the’ в каждом тексте�
Сохранение данных
Многие ваши программы будут запрашивать у пользователя информацию Напри
мер, пользователь может вводить настройки для компьютерной игры или данные
для визуального представления Чем бы ни занималась ваша про грамма, инфор
мация, введенная пользователем, будет сохраняться в структурах данных (таких,
как списки или словари) Когда пользователь закрывает программу, введенную
им информацию почти всегда следует сохранять на будущее Простейший способ сохранения данных основан на использовании модуля json
Модуль json обеспечивает запись простых структур данных th в файл и за
грузку данных из файла при следующем запуске программы Модуль json также
может использоваться для обмена данными между программами th Более
того, формат данных 첪 не привязан к th, поэтому данные в этом формате
можно передавать программам, написанным на многих других языках програм
мирования Это полезный и универсальный формат, который к тому же легко изучается ПРИМЕЧАНИЕ
Формат JSON (JavaScript Object Notation) был изначально разработан для JavaScript� Впрочем, с того времени он стал использоваться во многих языках, включая Python� Функции json�dump() и json�load()
Напишем короткую программу для сохранения набора чисел и другую программу,
которая будет читать эти числа обратно в память Первая программа использует функцию json.dump() , а вторая — функцию json.load()
Функция json.dump() получает два аргумента сохраняемые данные и объект фай
ла, используемый для сохранения В следующем примере json.dump() используется
для сохранения списка чисел
number_writer.py import json numbers = [2, 3, 5, 7, 11, 13]
 filename = 'numbers.json'
 with open(filename, 'w') as f_obj:
 json.dump(numbers, f_obj)
Программа импортирует модуль json и создает список чисел для работы В точке 
выбирается имя файла для хранения списка Обычно для таких файлов принято
использовать расширение �json, указывающее, что данные в файле хранятся в фор

Сохранение данных 205
мате 첪 Затем файл открывается в режиме записи, чтобы модуль json мог запи
сать в него данные  В точке  функция json.dump() используется для сохранения
списка numbers в файле numbers�json
Программа ничего не выводит, но давайте откроем файл numbers�json и посмотрим
на его содержимое Данные хранятся в формате, очень похожем на код th
[2, 3, 5, 7, 11, 13]
А теперь напишем следующую программу, которая использует json.load()
для чтения списка обратно в память
number_reader.py import json
 filename = 'numbers.json'
 with open(filename) as f_obj:
 numbers = json.load(f_obj)
print(numbers)
В точке  для чтения данных используется тот же файл, в который эти данные
были записаны На этот раз файл открывается в режиме чтения, потому что th
нужно только прочитать данные из файла  В точке  функция json.load() ис
пользуется для загрузки информации из numbers�json эта информация сохраняется
в переменной numbers
Наконец, программа выводит прочитанный список Как видите, этот тот же список, который был создан в программе number_writer�py
[2, 3, 5, 7, 11, 13]
Модуль json позволяет организовать простейший обмен данными между про
граммами Сохранение и чтение данных, сгенерированных пользователем
Сохранение с использованием модуля json особенно полезно при работе с данны
ми, сгенерированными пользователем, потому что без сохранения эта информа
ция будет потеряна при остановке программы В следующем примере программа
запрашивает у пользователя имя при первом запуске программы и «вспоминает» его при повторных запусках Начнем с сохранения имени пользователя
remember_me.py import json
 username = input("What is your name? ")
filename = 'username.json' with open(filename, 'w') as f_obj:
 json.dump(username, f_obj)
 print("We'll remember you when you come back, " + username +
"!")

206 Глава 10 • Файлы и исключения
В точке  программа запрашивает имя пользователя для сохранения Затем вы
зывается функция json.dump(), которой передается имя пользователя и объект
файла функция сохраняет имя пользователя в файле  Далее выводится сообще
ние о том, что имя пользователя было сохранено 
What is your name? Eric
We'll remember you when you come back, Eric!
А теперь напишем другую программу, которая приветствует пользователя по ранее сохраненному имени
greet_user.py import json filename = 'username.json'with open(filename) as f_obj:
 username = json.load(f_obj)
 print("Welcome back, " + username + "!")
В точке  вызов json.load() читает информацию из файла username�json в пере
менную username После того как данные будут успешно прочитаны, мы можем
поприветствовать пользователя по имени 
Welcome back, Eric!
Теперь эти две программы необходимо объединить в один файл Когда пользова
тель запускает remember_me�py , программа должна взять имя пользователя из памя
ти, если это возможно соответственно, программа начинается с блока try, который
пытается прочитать имя пользователя Если файл username�json не существует,
блок except запросит имя пользователя и сохранит его в username�json на будущее
remember_me.py import json # Программа загружает имя пользователя, если оно было сохранено ранее. # В противном случае она запрашивает имя пользователя и сохраняет его.filename = 'username.json'try:
 with open(filename) as f_obj:
 username = json.load(f_obj)
 except FileNotFoundError:
 username = input("What is your name? ")
 with open(filename, 'w') as f_obj:
json.dump(username, f_obj) print("We'll remember you when you come back, " + username + "!")else: print("Welcome back, " + username + "!")
Никакого нового кода здесь нет просто блоки кода из двух предыдущих приме
ров были объединены в один файл В точке  программа пытается открыть файл
username�json Если файл существует, программа читает имя пользователя в па
мять  и выводит сообщение, приветствующее пользователя, в блоке else Если

Сохранение данных 207
программа запускается впервые, то файл username�json не существует, и происходит
исключение FileNotFoundError  th переходит к блоку except, в котором
пользователю предлагается ввести имя  Затем программа вызывает json.dump()
для сохранения имени пользователя и выводит приветствие 
Какой бы блок ни выполнялся, результатом является имя пользователя и соот
ветствующее сообщение При первом запуске программы результат выглядит так
What is your name? Eric
We'll remember you when you come back, Eric!
Если же программа уже была выполнена хотя бы один раз, результат будет таким Welcome back, Eric!
Рефакторинг
Часто возникает типичная ситуация код работает, но вы понимаете, что его струк
туру можно усовершенствовать, разбив его на функции , каждая из которых ре
шает свою конкретную задачу Этот процесс называется рефакторингом (или
переработкой) Рефакторинг делает ваш код более чистым, понятным и простым в расширении
В процессе рефакторинга remember_me�py мы можем переместить основную часть
логики в одну или несколько функций Основной задачей remember_me�py является
вывод приветствия для пользователя, поэтому весь существующий код будет пере
мещен в функцию greet_user()
remember_me.py
import json
def greet_user():
 """Приветствует пользователя по имени."""
filename = 'username.json' try:with open(filename) as f_obj:username = json.load(f_obj)except FileNotFoundError:username = input("What is your name? ")with open(filename, 'w') as f_obj:json.dump(username, f_obj)print("We'll remember you when you come back, " + username + "!")else:print("Welcome back, " + username + "!")
greet_user()
С переходом на функцию комментарии дополняются строкой документации, кот о
рая описывает работу кода в текущей версии  Код становится немного чище, но
функция greet_user() не только приветствует пользователя — она также загружает
хранимое имя пользователя, если оно существует, и запрашивает новое имя, если
оно не было сохранено ранее

208 Глава 10 • Файлы и исключения
Переработаем функцию greet_user(), чтобы она не решала столько разных задач
Начнем с перемещения кода загрузки хранимого имени пользователя в отдельную функцию
import json
def get_stored_username():
 """Получает хранимое имя пользователя, если оно существует."""
filename = 'username.json' try:with open(filename) as f_obj:username = json.load(f_obj)except FileNotFoundError:
 return None
else: return username
def greet_user(): """Приветствует пользователя по имени."""
username = get_stored_username()
 if username:
print("Welcome back, " + username + "!") else:
username = input("What is your name? ") filename = 'username.json'with open(filename, 'w') as f_obj:json.dump(username, f_obj)print("We'll remember you when you come back, " + username + "!") greet_user()
Новая функция get_stored_username() имеет четкое предназначение, изложенное
в строке документации  Эта функция читает и возвращает сохраненное имя
пользователя, если его удается найти Если файл username�json не существует,
то функция возвращает None  И это правильно функция должна возвращать
либо ожидаемое значение, либо None Это позволяет провести простую проверку
возвращаемого значения функции В точке  программа выводит приветствие
для пользователя, если попытка получения имени пользователя была успешной в противном случае программа запрашивает новое имя пользователя
Из функции greet_user() стоит вынести еще один блок кода Если имя пользова
теля не существует, то код запроса нового имени должен размещаться в функции, специализирующейся на решении этой задачи
import json def get_stored_username(): """Получает хранимое имя пользователя, если оно существует."""...
def get_new_username(): """Запрашивает новое имя пользователя."""
username = input("What is your name? ") filename = 'username.json'with open(filename, 'w') as f_obj: json.dump(username, f_obj)
return username

Итоги 209
def greet_user(): """Приветствует пользователя по имени."""username = get_stored_username()if username:print("Welcome back, " + username + "!")else:
username = get_new_username()print("We'll remember you when you come back, " + username + "!") greet_user()
Каждая функция в окончательной версии remember_me�py имеет четкое, конкретное
предназначение Мы вызываем greet_user(), и эта функция выводит нужное при
ветствие либо для уже знакомого, либо для нового по льзователя Для этого функ
ция вызывает функцию get_stored_username(), которая отвечает только за чтение
хранимого имени пользователя (если оно есть) Наконец, функция greet_user()
при необходимости вызывает функцию get_new_username(), которая отвечает толь
ко за получение нового имени пользователя и его сохранение Такое «разделение
обязанностей» является важнейшим аспектом написания чистого кода, простого в сопровождении и расширении
УПРАЖНЕНИЯ
10-11� Любимое число: напишите программу, которая запрашивает у пользователя его лю-
бимое число� Воспользуйтесь функцией json�dump() для сохранения этого числа в файле�
Напишите другую программу, которая читает это значение и выводит сообщение: «Я знаю ваше любимое число! Это _____»�
10-12� Сохраненное любимое число: объедините две программы из упражнения 10-11
в один файл� Если число уже сохранено, сообщите его пользователю, а если нет — запро-
сите любимое число пользователя и сохраните в файле� Выполните программу дважды, чтобы убедиться в том, что она работает�
10-13� Проверка пользователя: последняя версия remember_me�py предполагает, что поль-
зователь либо уже ввел свое имя, либо программа выполняется впервые� Ее нужно из-
менить на тот случай, если текущий пользователь не является тем человеком, который последним использовал программу�
Прежде чем выводить приветствие в greet_user(), спросите пользователя, правильно
ли определено имя пользователя� Если ответ будет отрицательным, вызовите get_new_username() для получения правильного имени пользователя�
Итоги
В этой главе вы научились работать с файлами Вы узнали, как п рочитать сразу весь
файл и как читать его содержимое по строкам Вы научились записывать в файл
и присоединять текст в конец файла, познакомились с исключениями и средствами
обработки исключений, возникающих в программе В завершающей части главы
рассматриваются структуры данных th для сохранения введенной информации, чтобы пользователю не приходилось каждый раз вводить данные заново при каждом запуске программы
В главе мы займемся эффективной организацией тестирования вашего кода Те
стирование поможет убедиться в том, что написанный код работает правильно, а так
же выявит ошибки, внесенные в процессе расширения уже написанных программ

11 Тестирование
Вместе с функциями и классами вы также можете написать тесты для своего кода
Тестирование доказывает, что код работает так, как положено, для любых разно
видностей входных данных, которые он может получать Тесты позволят вам быть
уверенным в том, что код будет работать правильно и тогда, когда вашими про
граммами станут пользоваться другие люди Тестирование при добавлении нового
кода гарантирует, что внесенные изменения не повлияют на текущее поведение
программы Все программисты допускают ошибки, поэтому каждый программист
должен часто тестировать свой код и выявлять ошибки до того, как с ними столкнутся другие пользователи
В этой главе вы научитесь тестировать код средствами модуля th unittest
Вы узнаете, как построить тестовые сценарии, как проверить, выдает ли программа
для конкретных входных данных ожидаемый результат и как тестировать функ
ции и классы Также вы научитесь оценивать, сколько тестов нужно написать для проекта Тестирование функции
Чтобы потренироваться в тестировании, нам понадобится код Ниже приведена
простая функция, которая получает имя и фамилию и возвращает отформатированное полное имя
name_function.py def get_formatted_name(first, last): """Строит отформатированное полное имя.""" full_name = first + ' ' + last return full_name.title()
Функция get_formatted_name() строит полное имя из имени и фамилии, разде
лив их пробелом, преобразует первый символ каждого слова к верхнему регистру
и возвращает полученный результат Чтобы убедиться в том, что функция get_
formatted_name() работает правильно, мы напишем программу, использующую
эту функцию Программа names�py запрашивает у пользователя имя и фамилию
и выдает отформатированное полное имя
names.py from name_function import get_formatted_name

Тестирование функции 211
print("Enter 'q' at any time to quit.") while True: first = input("\nPlease give me a first name: ") if first == 'q': break last = input("Please give me a last name: ") if last == 'q': break formatted_name = get_formatted_name(first, last) print("\tNeatly formatted name: " + formatted_name + '.')
Программа импортирует функцию get_formatted_name() из модуля
name_function�py Пользователь вводит последовательность имен и фамилий
и видит, что программа сгенерировала отформатированные полные имена
Enter 'q' at any time to quit. Please give me a first name: janis
Please give me a last name: joplin
Neatly formatted name: Janis Joplin.Please give me a first name: bob
Please give me a last name: dylan
Neatly formatted name: Bob Dylan.Please give me a first name: q
Как видно из листинга, имена сгенерированы правильно Но допустим, вы решили
изменить функцию get_formatted_name() , чтобы она также работала со вторыми
именами При этом необходимо проследить за тем, чтобы функция не перестала
правильно работать для имен, состоящих только из имени и фамилии Чтобы про
тестировать код, можно запустить names�py и для проверки вводить имя из двух
компонентов (скажем, Janis Joplin ) при каждом изменении get_formatted_name(),
но это довольно утомительно К счастью, th предоставляет эффективный
механизм автоматизации тестирования вывода функций При автоматизации тестирования get_formatted_name() вы будете уверены в том, что функция успешно
работает для всех видов имен, для которых написаны тесты Прохождение теста
Вы не сразу привыкнете к синтаксису создания тестовых сценариев, но после того,
как тестовый сценарий будет создан, вы сможете легко добавить новые модульные
тесты для своих функций Чтобы написать тестовый сценарий для функции, импор
тируйте модуль unittest и функцию, которую необходимо протестировать Затем
создайте класс, наследующий от unittest.TestCase, и напишите серию методов для
тестирования различных аспектов поведения своей функции
Ниже приведен тестовый сценарий с одним методом, который проверяет, что функ
ция get_formatted_name() правильно работает при передаче имени и фамилии

212 Глава 11 • Тестирование
test_name_function.py import unittest from name_function import get_formatted_name
 class NamesTestCase(unittest.TestCase):
"""Тесты для 'name_function.py'.""" def test_first_last_name(self): """Имена вида 'Janis Joplin' работают правильно?"""
 formatted_name = get_formatted_name('janis', 'joplin')
 self.assertEqual(formatted_name, 'Janis Joplin')
unittest.main()
Сначала мы импортируем unittest и тестируемую функцию get_formatted_name()
В точке  создается класс NamesTestCase, который содержит серию модульных
тестов для get_formatted_name() Имя класса выбирается произвольно, но лучше
выбрать имя, связанное с функцией, которую вы собираетесь тестировать, и вклю
чить в имя класса слово Test Этот класс должен наследовать от класса unittest.
TestCase , чтобы th знал, как запустить написанные вами тесты
Класс NamesTestCase содержит один метод, который тестирует всего один аспект
get_formatted_name() — правильность форматирования имен, состоящих только из
имени и фамилии Мы назвали этот метод test_first_last_name() Любой метод,
имя которого начинается с test_, будет выполняться автоматически при запуске
test_name_function�py В тестовом методе вызывается тестируемая функция и сохра
няется возвращаемое значение, которое необходимо проверить В данном примере
вызывается функция get_formatted_name() с аргументами 'janis' и 'joplin' ,
а результат сохраняется в переменной formatted_name 
В точке  используется одна из самых полезных особенностей unittest метод
assert Методы assert проверяют, что полученный результат соответствует тому
результату, который вы рассчитывали получить В данном случае известно, что функция get_formatted_name() должна вернуть полное имя с пробелами и капита
лизацией слов, поэтому переменная formatted_name должна содержать текст «ais
ɧi» Чтобы убедиться в этом, мы используем метод assertEqual() из модуля
unittest и передаем ему переменную formatted_name и строку 'Janis Joplin'
Вызов self.assertEqual(formatted_name, 'Janis Joplin')
означает «Сравни значение formatted_name со строкой 'Janis Joplin' Если они
равны, как и ожидалось, — хорошо Но если они не равны, обязательно сообщи мне» Строка unittest.main() приказывает th выполнить тесты из этого файла
При запуске test_name_function�py будет получен следующий результат
. ---------------------------------------------------------------------- Ran 1 test in 0.000s OK

Тестирование функции 213
Точка в первой строке вывода сообщает, что один тест прошел успешно Следу
ющая строка говорит, что для выполнения одного теста th потребовалось
менее , секунды Наконец, завершающее сообщение OK говорит о том, что все
модульные тесты в тестовом сценарии прошли
Этот результат показывает, что функция get_formatted_name() успешно работает
для полных имен, состоящих из имени и фамилии, если только функция не была
изменена В случае внесения изменений в get_formatted_name() тест можно запу
стить снова И если тестовый сценарий снова пройдет, мы будем знать, что функция продолжает успешно работать с полными именами из двух компонентов Сбой теста
Как выглядит сбойный тест Попробуем изменить функцию get_formatted_name(),
чтобы она работала со вторыми именами, — но сделаем это так, чтобы она перестала работать с полными именами из двух компонентов
Новая версия get_formatted_name() с дополнительным аргументом второго имени
выглядит так
name_function.py def get_formatted_name(first, middle, last):
"""Строит отформатированное полное имя.""" full_name = first + ' ' + middle + ' ' + last return full_name.title()
Эта версия должна работать для полных имен из трех компонентов, но тестирова
ние показывает, что она перестала работать для полных имен из двух ко мпонентов
На этот раз файл test_name_function�py выдает следующий результат
 E
======================================================================
 ERROR: test_first_last_name (__main__.NamesTestCase)
----------------------------------------------------------------------
 Traceback (most recent call last):
File "test_name_function.py", line 8, in test_first_last_name formatted_name = get_formatted_name('janis', 'joplin') TypeError: get_formatted_name() missing 1 required positional argument: 'last' ----------------------------------------------------------------------
 Ran 1 test in 0.000s
 FAILED (errors=1)
На этот раз информации гораздо больше, потому что при сбое теста разработчик
должен знать, почему это произошло Вывод начинается с одной буквы E , которая
сообщает, что один модульный тест в тестовом сценарии привел к ошибке Затем мы
видим, что ошибка произошла в тесте test_first_last_name() в NamesTestCase 
Конкретная информация о сбойном тесте особенно важна в том случае, если тестовый
сценарий состоит из нескольких модульных тестов В точке  мы видим стандартную
трассировку, из которой понятно, что вызов функции get_formatted_name('janis',
'joplin') перестал работать изза необходимого позиционного аргумента

214 Глава 11 • Тестирование
Также из вывода следует, что был выполнен один модульный тест  Наконец, до
полнительное сообщение информирует, что тестовый сценарий в целом не прошел
и произошла одна ошибка при выполнении тестового сценария  Эта информация
размещается в конце вывода, чтобы она была видна сразу разработчику не придется прокручивать длинный протокол, чтобы узнать количество сбойных тестов
Реакция на сбойный тест
Что делать, если тест не проходит Если предположить, что проверяются правильные
условия, прохождение теста означает, что функция работает правильно, а сбой — что
в новом коде добавилась ошибка Итак, если тест не прошел, изменять нужно не тест,
а код, который привел к сбою теста Проанализируйте изменения, внесенные в функ
цию, и разберитесь, как они привели к нарушению ожидаемого поведения
В данном случае у функции get_formatted_name() было всего два обязательных
параметра имя и фамилия Теперь она требует три обязательных параметра имя,
второе имя и фамилию Добавление обязательного параметра для второго имени
нарушило ожидаемое поведение get_formatted_name() В таком случае лучше все
го сделать параметр второго имени необязательным После этого тесты для имен
с двумя компонентами снова будут проходить, и программа сможет получать также вторые имена Изменим функцию get_formatted_name(), чтобы параметр второго
имени перестал быть обязательным, и снова выполним тестовый сценарий Если он пройдет, можно переходить к проверке правильности обработки вторых имен
Чтобы сделать второе имя необязательным, нужно переместить параметр middle
в конец списка параметров в определении функции и задать ему пустое значение
по умолчанию Также будет добавлена проверка if, которая правильно строит
полное имя в зависимости от того, передается второе имя или нет
name_function.py def get_formatted_name(first, last, middle=''):
"""Строит отформатированное полное имя.""" if middle: full_name = first + ' ' + middle + ' ' + last else: full_name = first + ' ' + last
return full_name.title()
В новой версии get_formatted_name() параметр middle не обязателен Если второе
имя передается функции ( if middle: ), то полное имя будет содержать имя, второе
имя и фамилию В противном случае полное имя состоит только из имени и фа
милии Теперь функция должна работать для обеих разновидностей имен Чтобы
узнать, работает ли функция для имен из двух компонентов, снова запустите
test_name_function�py
. ---------------------------------------------------------------------- Ran 1 test in 0.000s OK

Тестирование функции 215
Теперь тестовый сценарий проходит Такой исход идеален он означает, что функ
ция снова работает для имен из двух компонентов и нам не пришлось тестировать
функцию вручную Исправить ошибку было несложно, потому что сбойный тест помог выявить новый код, нарушивший существующее поведение Добавление новых тестов
Теперь мы знаем, что get_formatted_name() работает для простых имен, и мо
жем написать второй тест для имен из трех компонентов Для этого в класс NamesTestCase добавляется еще один метод
import unittest from name_function import get_formatted_name class NamesTestCase(unittest.TestCase): """Тесты для 'name_function.py'."""
def test_first_last_name(self): """Работают ли такие имена, как 'Janis Joplin'?"""formatted_name = get_formatted_name('janis', 'joplin')self.assertEqual(formatted_name, 'Janis Joplin')
def test_first_last_middle_name(self): """Работают ли такие имена, как 'Wolfgang Amadeus Mozart'?"""
 formatted_name = get_formatted_name(
'wolfgang', 'mozart', 'amadeus') self.assertEqual(formatted_name, 'Wolfgang Amadeus Mozart')
unittest.main()
Новому методу присваивается имя test_first_last_middle_name() Имя метода
должно начинаться с test_, чтобы этот метод выполнялся автоматически при запуске
test_name_function�py В остальном имя выбирается так, чтобы оно четко показывало,
какое именно поведение get_formatted_name() мы тестируем В результате при сбое
теста вы сразу видите, к каким именам он относится Не нужно опасаться длинных
имен методов в классах TestCase имена должны быть содержательными, чтобы до
нести информацию до разработчика в случае сбоя, а поскольку th вызывает их
автоматически, вам никогда не придется вручную вводить эти имена при вызове
Чтобы протестировать функцию, мы вызываем get_formatted_name() c тремя
компонентами , после чего используем assertEqual() для проверки того, что
возвращенное полное имя совпадает с ожидаемым При повторном запуске test_
name_function�py оба теста проходят успешно
.. ----------------------------------------------------------------------Ran 2 tests in 0.000s OK
Отлично Теперь мы знаем, что функция попрежнему работает с именами
из двух компонентов, как Janis Joplin , но можем быть уверены в том, что она

216 Глава 11 • Тестирование
сработает и для имен с тремя компонентам — такими, как Wolfgang Amadeus
Mozart
УПРАЖНЕНИЯ
11-1� Город, страна: напишите функцию, которая получает два параметра: название
страны и название города� Функция должна возвращать одну строку в формате «Го -
род, Страна», например «Santiago, Chile»� Сохраните функцию в модуле с именем city_
functions�py�
Создайте файл test_cities�py для тестирования только что написанной функции (не забудьте
импортировать unittest и тестируемую функцию)� Напишите метод test_city_country() для
проверки того, что вызов функции с такими значениями, как ‘santiago’ и ‘chile’, дает пра-
вильную строку� Запустите test_cities�py и убедитесь в том, что тест test_city_country() про-ходит успешно�
11-2� Население: измените свою функцию так, чтобы у нее был третий обязательный
параметр — население� В новой версии функция должна возвращать одну строку вида
«Santiago, Chile — population 5000000�» Снова запустите программу test_cities�py� Убедитесь
в том, что тест test_city_country() на этот раз не проходит�
Измените функцию так, чтобы параметр населения стал необязательным� Снова запустите
test_cities�py и убедитесь в том, что тест test_city_country() проходит успешно�
Напишите второй тест test_city_country_population(), который проверяет вызов функции
со значениями ‘santiago’, ‘chile’ и ‘population=5000000’� Снова запустите test_cities�py и убе-дитесь в том, что новый тест проходит успешно�
Тестирование класса
В первой части этой главы мы писали тесты для отдельной функции Сей
час мы займемся написанием тестов для класса Классы будут использовать
ся во многих ваших программах, поэтому возможность доказать, что ваши
классы работают правильно, будет безусловно полезной Если тесты для класса,
над которым вы работаете, проходят успешно, вы можете быть уверены в том,
что усовершенствования класса не приведут к случайному нарушению его те
кущего поведения
Разные методы assert Класс unittest.TestCase содержит целое семейство проверочных методов assert
Как упоминалось ранее, эти методы проверяют, выполняется ли условие, которое
должно выполняться в определенной точке вашего кода Если условие истинно,
как и предполагалось, то ваши ожидания относительно поведения части вашей
программы подтверждаются вы можете быть уверены в отсутствии ошибок Если
же условие, которое должно быть истинным, окажется ложным, то th выдает исключение
В табл Æ перечислены шесть часто используемых методов assert С их помощью
можно проверить, что возвращаемые значения равны или не равны ожидаемым,
что значения равны True или False или что значения входят или не входят в за
данный список Эти методы могут использоваться только в классах, наследующих

Тестирование класса 217
от unittest.TestCase рассмотрим пример использования такого метода в контексте
тестирования реального класса Таблица 11.1. Методы assert, предоставляемые модулем unittest
Метод Использование
assertEa(a, ) Проверяет, что a
asserttEa(a, ) Проверяет, что a
assertre() Проверяет, что значение истинно
assertase() Проверяет, что значение ложно
assertƒ(элемент, список) Проверяет, что элемент входит в список
asserttƒ(элемент, список) Проверяет, что элемент не входит в список
Класс для тестирования
Тестирование класса имеет много общего с тестированием функции — значитель
ная часть работы направлена на тестирование поведения методов клас са Впрочем,
существуют и различия, поэтому мы напишем отдельный класс для тестирования
Возьмем класс для управления проведением анонимных опросов
survey.py class AnonymousSurvey(): """Сбор анонимных ответов на опросы."""
 def __init__(self, question):
"""Сохраняет вопрос и готовится к сохранению ответов.""" self.question = question self.responses = []
 def show_question(self):
"""Выводит вопрос.""" print(question)
 def store_response(self, new_response):
"""Сохраняет один ответ на опрос.""" self.responses.append(new_response)
 def show_results(self):
"""Выводит все полученные ответы.""" print("Survey results:") for response in responses: print('- ' + response)
Класс начинается с вопроса, предоставленного администратором , и включает
пустой список для хранения ответов Класс содержит методы для вывода во проса , добавления нового ответа в список ответов  и вывода всех ответов,
хранящихся в списке  Чтобы создать экземпляр на основе этого класса, не
обходимо предоставить вопрос После того как будет создан экземпляр, пред
ставляющий конкретный опрос, программа выводит вопрос методом show_

218 Глава 11 • Тестирование
question() , сохраняет ответ методом store_response() и выводит результаты
вызовом show_results()
Чтобы продемонстрировать, что класс AnonymousSurvey работает, напишем про
грамму, которая использует этот класс
language_survey.py from survey import AnonymousSurvey # Определение вопроса с созданием экземпляра AnonymousSurvey. question = "What language did you first learn to speak?"my_survey = AnonymousSurvey(question) # Вывод вопроса и сохранение ответов. my_survey.show_question()print("Enter 'q' at any time to quit.\n")while True: response = input("Language: ") if response == 'q': break my_survey.store_response(response) # Вывод результатов опроса. print("\nThank you to everyone who participated in the survey!")my_survey.show_results()
Программа определяет вопрос и создает объект AnonymousSurvey на базе этого во
проса Программа вызывает метод show_question() для вывода вопроса, после чего
переходит к получению ответов Каждый ответ сохраняется сразу же при получе
нии Когда ввод ответов был завершен (пользователь ввел q), метод show_results()
выводит результаты опроса
What language did you first learn to speak? Enter 'q' at any time to quit. Language: English
Language: Spanish
Language: English
Language: Mandarin
Language: q
Thank you to everyone who participated in the survey! Survey results: - English - Spanish - English- Mandarin
Этот класс работает для простого анонимного опроса Но допустим, вы решили
усовершенствовать класс AnonymousSurvey и модуль survey, в котором он находит
ся Например, каждому пользователю будет разрешено ввести несколько ответов
Или вы напишете метод, который будет выводить только уникальные ответы
и сообщать, сколько раз был дан тот или иной ответ Или вы напишете другой класс для проведения неанонимных опросов

Тестирование класса 219
Реализация таких изменений грозит повлиять на текущее поведение класса AnonymousSurvey Например, может оказаться, что поддержка ввода нескольких
ответов случайно повлияет на процесс обработки одиночных ответов Чтобы га
рантировать, что доработка модуля не нарушит существующего поведения, для класса нужно написать тесты
Тестирование класса AnonymousSurvey
Напишем тест, проверяющий всего один аспект поведения AnonymousSurvey Этот
тест будет проверять, что один ответ на опрос сохраняется правильно После того
как метод будет сохранен, метод assertIn() проверяет, что он действительно на
ходится в списке ответов
test_survey.py import unittest from survey import AnonymousSurvey
 class TestAnonmyousSurvey(unittest.TestCase):
"""Тесты для класса AnonymousSurvey"""
 def test_store_single_response(self):
"""Проверяет, что один ответ сохранен правильно.""" question = "What language did you first learn to speak?"
 my_survey = AnonymousSurvey(question)
my_survey.store_response('English')
 self.assertIn('English', my_survey.responses)
unittest.main()
Программа начинается с импортирования модуля unittest и тестируемого класса
AnonymousSurvey Тестовый сценарий TestAnonymousSurvey, как и в предыдущих
случаях, наследует от unittest.TestCase  Первый тестовый метод проверяет, что
сохраненный ответ действительно попадает в список ответов опроса Этому методу
присваивается хорошее содержательное имя test_store_single_response() 
Если тест не проходит, имя метода в выходных данных сбойного теста ясно показывает, что проблема связана с сохранением отдельного ответа на опрос
Чтобы протестировать поведение класса, необходимо создать экземпляр класса
В точке  создается экземпляр с именем my_survey для вопроса "What language
did you first learn to speak?" , Один ответ ( English) сохраняется с использова
нием метода store_response() Затем программа убеждается в том, что ответ был
сохранен правильно для этого она проверяет, что значение English присутствует
в списке my_survey.responses 
При запуске программы test_survey�py тест проходит успешно
. ---------------------------------------------------------------------- Ran 1 test in 0.001s OK

220 Глава 11 • Тестирование
Неплохо, но опрос с одним ответом вряд ли можно назвать полезным Убедимся
в том, что три ответа сохраняются правильно Для этого в TestAnonymousSurvey
добавляется еще один метод
import unittest from survey import AnonymousSurvey class TestAnonymousSurvey(unittest.TestCase): """Тесты для класса AnonymousSurvey""" def test_store_single_response(self): """Проверяет, что один ответ сохранен правильно."""...
def test_store_three_responses(self): """Проверяет, что три ответа были сохранены правильно.""" question = "What language did you first learn to speak?" my_survey = AnonymousSurvey(question)
 responses = ['English', 'Spanish', 'Mandarin']
for response in responses: my_survey.store_response(response)
 for response in responses:
self.assertIn(response, my_survey.responses)
unittest.main()
Новому методу присваивается имя test_store_three_responses() Мы создаем
объект опроса по аналогии с тем, как это делалось в test_store_single_response()
Затем определяется список, содержащий три разных ответа , и для каждого из
этих ответов вызывается метод store_response() После того как ответы будут
сохранены, следующий цикл проверяет, что каждый ответ теперь присутствует в my_survey.responses 
Если снова запустить test_survey�py, оба теста (для одного ответа и для трех ответов)
проходят успешно
.. ----------------------------------------------------------------------Ran 2 tests in 0.000s OK
Все прекрасно работает Тем не менее тесты выглядят немного однообразно, по
этому мы воспользуемся еще одной возможностью unittest для повышения их
эффективности Метод setUp()
В программе test_survey�py в каждом тестовом методе создавался новый экземпляр
AnonymousSurvey , а также новые ответы Класс unittest.TestCase содержит метод

Тестирование класса 221
setUp() , который позволяет создать эти объекты один раз, а затем использовать их
в каждом из тестовых методов Если в класс TestCase включается метод setUp(),
th выполняет метод setUp() перед запуском каждого метода, имя которого на
чинается с test_ Все объекты, созданные методом setUp(), становятся доступными
во всех написанных вами тестовых методах Используем setUp() для создания экземпляра AnonymousSurvey и набора ответов,
которые могут использоваться в test_store_single_response() и test_store_
three_responses()
import unittest from survey import AnonymousSurvey class TestAnonymousSurvey(unittest.TestCase): """Тесты для класса AnonymousSurvey."""
def setUp(self): """ Создание опроса и набора ответов для всех тестовых методов. """ question = "What language did you first learn to speak?"
 self.my_survey = AnonymousSurvey(question)
 self.responses = ['English', 'Spanish', 'Mandarin']
def test_store_single_response(self): """Проверяет, что один ответ сохранен правильно."""
self.my_survey.store_response(self.responses[0]) self.assertIn(self.responses[0], self.my_survey.responses)
def test_store_three_responses(self): """Проверяет, что три ответа были сохранены правильно."""
for response in self.responses: self.my_survey.store_response(response) for response in self.responses: self.assertIn(response, self.my_survey.responses)
unittest.main()
Метод setUp() решает две задачи он создает экземпляр опроса  и список отве
тов  Каждый из этих атрибутов снабжается префиксом self, поэтому он может
использоваться где угодно в классе Это обстоятельство упрощает два тестовых
метода, потому что им уже не нужно создавать экземпляр опроса или ответы Метод test_store_single_response() убеждается в том, что первый ответ в self.
responses — self.responses[0] — сохранен правильно, а метод test_store_single_
response() убеждается в том, что правильно были сохранены все три ответа в self.
responses
При повторном запуске test_survey�py оба теста попрежнему проходят Эти тесты
будут особенно полезными при расширении AnonymousSurvey с поддержкой не
скольких ответов для каждого участника После внесения измен ений вы можете
повторить тесты и убедиться в том, что изменения не повлияли на возможность
сохранения отдельного ответа или серии ответов

222 Глава 11 • Тестирование
При тестировании классов, написанных вами, метод setUp() упрощает написание
тестовых методов Вы создаете один набор экземпляров и атрибутов в setUp(),
а затем используете эти экземпляры во всех тестовых методах Это намного проще
и удобнее, чем создавать новый набор экземпляров и атрибутов в каждом тестовом методе ПРИМЕЧАНИЕ
Во время работы тестового сценария Python выводит один символ для каждого модульного теста
после его завершения� Для прошедшего теста выводится точка; если при выполнении произошла
ошибка, выводится символ E, а если не прошла проверка условия assert, выводится символ F� Вот
почему вы увидите другое количество точек и символов в первой строке вывода при выполнении
ваших тестовых сценариев� Если выполнение тестового сценария занимает слишком много време-
ни, потому что сценарий содержит слишком много тестов, эти символы дадут некоторое представ-ление о количестве прошедших тестов�
УПРАЖНЕНИЯ
11-3� Работник: напишите класс Employee, представляющий работника� Метод __init__()
должен получать имя, фамилию и ежегодный оклад; все эти значения должны сохраняться
в атрибутах� Напишите метод give_raise(), который по умолчанию увеличивает ежегодный оклад на $5000 — но при этом может получать другую величину прибавки�
Напишите тестовый сценарий для Employee� Напишите два тестовых метода, test_give_
default_raise() и test_give_custom_raise()� Используйте метод setUp(), чтобы вам не при-
ходилось заново создавать экземпляр Employee в каждом тестовом методе� Запустите свой тестовый сценарий и убедитесь в том, что оба теста прошли успешно�
Итоги
В этой главе вы научились писать тесты для функций и классов с использованием
средств модуля unittest Вы узнали, как написать класс, наследующий от unittest.
TestCase , и как писать тестовые методы для проверки конкретных аспектов пове
дения ваших функций и классов Вы научились использовать метод setUp() для
эффективного создания экземпляров и атрибутов, которые могут использоваться во всех методах для тестирования класса
Тестирование — важная тема, на которую многие новички не обращают вни
мания Пока вы делаете свои первые шаги в программировании, писать тесты
для простых проектов не нужно Но как только вы начинаете работать над про
ектами, требующими значительных затрат ресурсов на разработку, непременно
обеспечьте тестирование критических аспектов поведения ваших функций
и классов С эффективными тестами вы можете быть уверены в том, что измене
ния в проекте не повредят тому, что уже работает, а это развяжет вам руки для
усовершенствования кода Случайно нарушив существующую функциональность,
вы немедленно узнаете об этом, что позволит вам быстро исправить проблему
Отреагировать на сбой теста всегда намного проще, чем на отчет об ошибке от не
довольного пользователя
Другие программисты будут более уважительно относиться к вашим проектам, если
вы включите в них исходные тесты Они будут чувствовать себя более комфортно,

Итоги 223
экспериментируя с вашим кодом, и с большей готовностью присоединятся к уча
стию в ваших проектах Если вы будете участвовать в проекте, над которым работа
ют другие программисты, вам придется продемонстрировать, что ваш код проходит
существующие тесты кроме того, от вас будут ждать, что вы напишете тесты для нового поведения, добавленного вами в проект
Поэкспериментируйте с тестами и освойтесь с процессом тестирования кода
Пишите тесты для критических аспектов поведения ваших функций и классов,
но не стремитесь к полному тестовому покрытию своих ранних проектов (если у вас для этого нет особых причин)

Часть II �
Проекты
Поздравляем! Вы знаете о Python достаточно для того, чтобы взяться за построение
интерактивных, осмысленных проектов� Создание собственных проектов закрепит новые навыки и упрочит ваше понимание концепций, представленных в части I�
В части II представлены три типа проектов; вы можете взяться за любые из них в том
порядке, который вам больше нравится� Ниже приведено краткое описание каждого проекта, чтобы вам было проще решить, с чего начать� Программирование игры на языке Python
В проекте ie ƒasi (главы , и ) мы воспользуемся пакетом ae
для написания ěигры, в которой игрок должен сбивать корабли пришельцев,
падающие по экрану с нарастающей скоростью и сложностью К концу этого про
екта вы будете знать достаточно для того, чтобы создавать собственные ěигры с использованием ae Визуализация данных
Проект ata isaiati начинается с главы ʆ В этом проекте вы научитесь
генерировать данные и создавать практичные, элегант ные визуализации этих
данных с использованием пакетов atɧti и a Глава научит вас работать
с данными из сетевых источников и передавать их пакету визуализации для по
строения графиков погодных данных и карты с населением мира Наконец, глава
показывает, как написать программу для автоматической загрузки и визуализации
данных Навыки визуализации пригодятся вам в изучении области анализа дан
ных — в современном мире это умение ценится очень высоко Веб-приложения
В проекте e ɧicatis (главы , и ) мы при помощи пакета a соз
дадим простое вебприложение для ведения вебдневника по произвольным темам
Пользователь создает учетную запись с именем и паролем, вводит тему и делает
заметки Вы также научитесь развертывать свои приложения так, чтобы сделать их доступными для потенциальных пользователей со всего мира
После завершения проекта вы сможете заняться построением собственных про
стых вебприложений Кроме того, вы будете готовы к изучению более серьезных ресурсов, посвященных построению приложений с использованием a

Проект I �
Инопланетное вторжение

12 Стреляющий корабль
Давайте создадим собственную игру Мы воспользуемся ae — подборкой ин
тересных, мощных модулей th для управления графикой, анимацией и даже
звуком, упрощающей построение сложных игр ae берет на себя такие задачи,
как перерисовка изображений на экране, что позволяет вам пропустить б ульшую
часть рутинного, сложного программирования и сосредоточиться на высокоуровневой логике игровой динамики
В этой главе мы настроим ae и создадим корабль, который движется вле
во и вправо и стреляет по приказу пользователя В следующих двух главах вы
создадите флот инопланетного вторжения, а затем займетесь внесением усовер
шенствований — например, ограничением количества попыток и добавлением таблицы рекордов
Эта глава также научит вас управлять большими проектами, состоящими из многих
файлов Мы часто будем проводить рефакторинг и изменять структуру содержи
мого файлов, чтобы проект был четко организован, а код оставался эффективным
Программирование игр — идеальный способ совместить изучение языка с раз
влечением Написание простой игры поможет вам понять, как пишутся профес
сиональные игры В процессе работы над этой главой вводите и запускайте код,
чтобы понять, как каждый блок кода участвует в общем игров ом процессе Экс
периментируйте с разными значениями и настройками, чтобы лучше понять, как
следует организовать взаимодействие с пользователем в ваших собственных играх ПРИМЕЧАНИЕ
Игра Alien Invasion состоит из множества файлов; создайте в своей системе новый каталог с име-
нем alien_invasion� Чтобы команды import работали правильно, все файлы проекта должны нахо-диться в этой папке� Планирование проекта
Построение крупного проекта должно начинаться не с написания кода, а с плани
рования План поможет вам направить усилия в нужном направлении и повысит вероятность успешного завершения проекта
Итак, напишем общее описание игрового процесса Хотя это описание не затра
гивает все аспекты игры, оно дает достаточно четкое представление о том, с чего начинать работу

Установка Pygame 227
Каждый игрок управляет кораблем, который находится в середине нижнего края экрана�
Игрок перемещает корабль вправо и влево клавишами управления или курсором; клавиша
«пробел» используется для стрельбы� В начале игры флот пришельцев находится в верхней
части экрана и постепенно опускается вниз, также смещаясь в сторону� Игрок выстрелами
уничтожает пришельцев� Если ему удается сбить всех пришельцев, появляется новый флот,
который движется быстрее предыдущего� Если пришелец сталкивается с кораблем игрока или
доходит до нижнего края экрана, игрок теряет корабль� Если игрок теряет все три корабля,
игра заканчивается�
В первой фазе разработки мы создадим корабль, котор ый может двигаться вправо
и влево Корабль должен стрелять из пушки, когда игрок нажимае т клавишу «про
бел» Когда это поведение будет реализовано, мы сможем заняться пришельцами и доработкой игрового процесса
Установка Pygame
Прежде чем браться за программирование, установите пакет ae Ниже описан процесс установки в iԡ, и Micrst is
Если вы используете th в системе iԡ или ес ли вы работаете в ,
для установки ae используется pip — программа, управляющая загрузкой
и установкой пакетов th Процедура установки пакетов с использованием pip
описана ниже
Если вы используете th Ć в системе iԡ или если вы работаете в is,
для установки ae программа pip вам не понадобится Вместо этого перейдите
к разделу «Установка ae в iԡ» (с ) или «Установка ae в is» (с ) ПРИМЕЧАНИЕ
Далее приводятся инструкции по установке pip во всех системах, потому что эта программа пона-
добится вам для визуализации данных и веб-приложений� Инструкции также доступны по адресу
https://www�nostarch�com/pythoncrashcourse/� Если у вас возникнут проблемы с инструкциями, при-веденными ниже, попробуйте загрузить инструкции с сайта — возможно, они сработают� Установка пакетов Python с использованием pip
В последних версиях th pip устанавливается автоматически, поэтому сначала
проверьте, присутствует ли эта программа в вашей системе В th программа pip иногда называется pip3
Проверка pip в Linux и OS X Откройте терминальное окно и введите следующую команду $ pip --version
 pip 7.0.3 from /usr/local/lib/python3.5/dist-packages (python 3.5)
$

228 Глава 12 • Стреляющий корабль
Если в вашей системе установлена только одна версия th и вы получили при
мерно такой результат, переходите к разделу «Установка ae в iԡ» (с )
или «Установка ae в » (с ) Если вы получите сообщение об ошиб ке,
попробуйте ввести имя pip3 вместо pip Если ни одна версия не установлена в вашей
системе, обратитесь к разделу «Установка pip» (c )
Если в вашей системе установлено несколько версий th, проверьте, что про грамма pip связана с используемой версией — например, th ҆  Если про
грамма pip связана с правильной версией, переходите к разделу «Установка ae
в iԡ» (с ) или «Установка ae в » (с ) Если версия неправиль
ная, попробуйте ввести имя pip3 вместо pip Если ни одна команда не работает для
вашей версии th, обратитесь к разделу «Установка pip» (c )
Проверка pip в Windows Откройте окно командной строки и введите следующую команду $ python -m pip --version
 pip 7.0.3 from C:\Python35\lib\site-packages (python 3.5)
$
Если в вашей системе установлена только одна версия th, и вы получили
примерно такой результат, переходите к разделу «Установка ae в is»
(с ) Если вы получите сообщение об ошибке, попробуйте ввести имя pip3 вме
сто pip Если ни одна версия не установлена в вашей системе, обратитесь к разделу
«Установка pip» (с )
Если в вашей системе установлено несколько версий th, проверьте, что программа pip связана с используемой версией, например th ҆  Если про
грамма pip связана с правильной версией, переходите к разделу «Установка ae
в is» (с ) Если версия неправильная, попробуйте ввести имя pip3 вместо
pip Если ни одна команда не работает для вашей версии th, обратитесь к сле
дующему разделу «Установка pip»
Установка pip
Чтобы установить pip, обратитесь по адресу ... Со
храните файл, если вам будет предложено Если код get-pip�py появится в браузере,
скопируйте код в текстовый редактор и сохраните в файле с именем get-pip�py
После того как программа get-pip�py будет сохранена на вашем компьютере, ее не
обходимо будет запустить с административными привилегиями, потому что pip
будет устанавливать новые пакеты в вашей системе ПРИМЕЧАНИЕ
Если вы не нашли программу get-pip�py, обратитесь по адресу https://pip�pypa�io/, щелкните
на ссылке Installation на левой панели, а затем в разделе Install pip перейдите по ссылке для за-
грузки get-pip�py�

Установка Pygame 229
Установка pip в Linux и OS X
Чтобы запустить get-pip�py с административными привилегиями, введите следу
ющую команду
$ sudo python get-pip.py
ПРИМЕЧАНИЕ
Если терминальный сеанс был запущен командой python3, используйте команду sudo python3 get-
pip�py�
После выполнения программы введите команду pip --version (или pip3 --version ),
чтобы убедиться в том, что программа pip была установлена правильно
Установка pip в WindowsЧтобы запустить get-pip�py, введите следующую команду
$ python get-pip.py
Если для запуска th в терминале использовалась другая команда, проследите
за тем, чтобы программа get-pip�py запускалась этой же командой — например,
python3 get-pip�py или C:\Python35\python get-pip�py
После выполнения программы введите команду python -m pip --version , чтобы
убедиться в том, что программа pip была установлена правильно
Установка Pygame в Linux
Если вы используете th Ć, установите ae при помощи менеджера паке
тов Откройте терминальное окно и введите следующую команду, которая загрузит и установит ae в вашей системе
$ sudo apt-get install python-pygame
Проверьте правильность установки в терминальном сеансе для этого введите следующую команду $ python
>>> import pygame
>>>
Если никаких дополнительных сообщений нет, значит, импортирование ae
прошло успешно, и вы можете переходить к разделу «Создание проекта игры» на с Æ
Если вы используете th , процесс состоит из двух шагов установки библио тек, от которых зависит ae, и загрузкиустановки ae
Чтобы установить библиотеки, необходимые ae, вв едите следующую команду
(если в вашей системе используется другая команда, например ɀth҆, замените
python3-dev на python3�5-dev )
$ sudo apt-get install python3-dev mercurial
$ sudo apt-get install libsdl-image1.2-dev libsdl2-dev libsdl-ttf2.0-dev

230 Глава 12 • Стреляющий корабль
Эти команды установят библиотеки, необходимые для успешного запуска игры
ie ƒasi Если вы хотите включить расширенную функциональность ae
(например, возможность добавления звуков), добавьте следующие библиотеки $ sudo apt-get install libsdl-mixer1.2-dev libportmidi-dev
$ sudo apt-get install libswscale-dev libsmpeg-dev libavformat-dev libavcode-dev
$ sudo apt-get install python-numpy
Теперь установите ae следующей командой (используйте pip3, если эта ко
манда соответствует вашей системе)
$ pip install --user hg+http://bitbucket.org/pygame/pygame
После небольшой паузы программа сообщает, какие библиотеки были найдены
Нажмите Eter, даже если некоторые библиотеки отсутствуют Вы увидите со
общение об успешной установке ae
Чтобы проверить правильность установки, откройте терминальный сеанс и попро
буйте импортировать ae
$ python3
>>> import pygame
>>>
Если импортирование прошло нормально, переходите к разделу «Создание про
екта игры» на с Æ
Установка Pygame в OS X
Для установки некоторых пакетов, от которых зависит ae, вам понадобится
менеджер пакетов ere Если в вашей системе он еще не установлен, обращайтесь к приложению А за инструкциями
Чтобы установить библиотеки, от которых зависит ae, введите следующую команду
$ brew install hg sdl sdl_image sdl_ttf
Команда устанавливает библиотеки, необходимые для ie ƒasi В процессе
установки каждой библиотеки на экране должна выводиться соответствующая информация
Если вы хотите включить расширенную функциональность (например, возмож
ность добавления звуков), добавьте еще две библиотеки
$ brew install sdl_mixer portmidi
Установите ae следующей командой (используйте pip вместо pip3, если вы
используете th Ć)
$ pip3 install --user hg+http://bitbucket.org/pygame/pygame
Чтобы проверить правильность установки, откройте терминальный сеанс и по
пробуйте импортировать ae (используйте python вместо python3, если вы
используете th Ć)

Создание проекта игры 231
$ python3
>>> import pygame
>>>
Если импортирование прошло нормально, переходите к разделу «Создание про
екта игры»
Установка Pygame в Windows
Проект ae размещен на сайте совместного использования кода itcet
Чтобы установить ae для вашей версии is, найдите на странице
. программу установки для is,
соответствующую вашей версии th Если вы не нашли подходящую программу
установки на сайте itcet, попробуйте поискать по адресу ...

Когда подходящий файл будет загружен, запустите программу установки, если это файл с расширением �exe
Если файл имеет суффикс �whl, скопируйте его в каталог проекта Откройте окно
командной строки, перейдите в папку, в которую был скопирован установочный пакет, и воспользуйтесь программой pip для запуска установки
> python -m pip install --user pygame-1.9.2a0-cp35-none-win32.whl
Создание проекта игры
Построение игры начнется с создания пустого окна ae, в котором позднее
будут отображаться игровые элементы — прежде всего корабль и пришельцы
Также игра должна реагировать на действия пользователя, назначать цвет фона и загружать изображение корабля
Создание окна Pygame и обработка ввода
Начнем с создания пустого окна ae Ниже приведена базовая структура игры, написанной с использованием ae alien_invasion.py import sys import pygamedef run_game(): # Инициализирует игру и создает объект экрана.
 pygame.init()
 screen = pygame.display.set_mode((1200, 800))
pygame.display.set_caption("Alien Invasion") # Запуск основного цикла игры.

232 Глава 12 • Стреляющий корабль
 while True:
# Отслеживание событий клавиатуры и мыши.
 for event in pygame.event.get():
 if event.type == pygame.QUIT:
sys.exit() # Отображение последнего прорисованного экрана.
 pygame.display.flip()
run_game()
Программа начинается с импортирования модуля sys и pygame Модуль pygame
содержит функциональность, необходимую для создания игры, а модуль sys
завершает игру по команде игрока
Игра ie ƒasi начинается с определения функции run_game() Строка pygame.
init()  инициализирует настройки, необходимые ae для нормальной ра
боты В точке  вызов pygame.display.set_mode() создает отображаемую область
screen , на которой прорисовываются все графические элементы игры Аргумент
(1200, 800) представляет собой кортеж, определяющий размеры игрового окна
Передавая эти размеры pygame.display.set_mode() , мы создаем игровое окно с ши
риной пикселов и высотой пикселов (Вы можете изменить эти значения в соответствии с размерами своего монитора)
Объект screen называется поверхностью (srace) Поверхность в ae пред
ставляет часть экрана, на которой отображается игровой элемент Каждый элемент
в игре (например, пришелец или корабль игрока) представлен поверхностью
Поверхность, возвращаемая display.set_mode(), представляет все игровое окно
При активизации игрового цикла анимации эта поверхность автоматически перерисовывается при каждом проходе цикла
Процессом игры управляет цикл while  , который содержит цикл событий и код,
управляющий обновлениями экрана Событием называется действие, выполняе
мое пользователем во время игры (например, нажатие клавиши или перемещение
мыши) Чтобы наша программа реагировала на события, мы напишем цикл со
бытий для прослушивания событий и выполнения соответствующей операции
в зависимости от типа произошедшего события Этим циклом событий является цикл for в точке 
Чтобы получить доступ к событиям, обнаруженным ae, мы используем метод
pygame.event.get() При любом событии клавиатуры или мыши отрабатывает
цикл for В этом цикле мы пишем серию команд if для обнаружения и обработки
конкретных событий Например, когда игрок щелкает на кнопке закрытия игрового
окна, программа обнаруживает событие pygame.QUIT, и программа вызывает метод
sys.exit() для выхода из игры 
Вызов pygame.display.flip()  приказывает ae отобразить последний от
рисованный экран В данном случае при каждом выполнении цикл а while будет
отображаться пустой экран со стиранием старого экра на, так что виден будет только
новый экран При перемещении игровых элементов вызов pygame.display.flip()
будет постоянно обновлять экран, отображая игровые элементы в новых позици
ях и скрывая старые изображения таким образом создается иллюзия плавного движения

Создание проекта игры 233
Последняя строка в этой базовой структуре вызывает метод run_game(), который
инициализирует игру и запускает основной цикл Запустите этот код, и вы увидите пустое окно ae Назначение цвета фона
ae по умолчанию создает черный экран, но это бан ально Выберем другой
цвет фона
alien_invasion.py
... def run_game():...pygame.display.set_caption("Alien Invasion")
# Назначение цвета фона.
 bg_color = (230, 230, 230)
# Запуск основного цикла игры. while True: # Отслеживание событий клавиатуры и мыши. ...
# При каждом проходе цикла перерисовывается экран.
 screen.fill(bg_color)
# Отображение последнего прорисованного экрана. pygame.display.flip() run_game()
Сначала программа создает цвет фона и сохраняет его в переменной bg_color 
Цвет достаточно задать только один раз, поэтому его значение определяется до вхо
да в основной цикл while
Цвета в ae задаются в схеме ٦ тройками интенсивности красной, зеленой
и синей составляющих цвета Значение каждой составляющей лежит в диапазоне
от до ʆ Цветовое значение (, , ) соответствует красному цвету, (, ,
) — зеленому и (, , ) — синему Разные сочетания составляющих ٦ позво
ляют создать до миллионов цветов В цветовом значении (, , ) красная,
синяя и зеленая составляющие смешиваются в равных долях, давая светлосерый цвет фона
В точке  экран заполняется цветом фона Для этого вызываетс я метод screen.
fill() , получающий всего один аргумент цвет фона
Создание класса Settings
Каждый раз, когда в нашу игру добавляется новая функциональность, также в нее
обычно добавляются новые настройки (параметры конфигурации) Вместо того
чтобы добавлять настройки в коде, мы напишем модуль с именем settings этот

234 Глава 12 • Стреляющий корабль
модуль содержит класс с именем Settings для хранения всех настроек Такое
решение позволит передавать один объект вместо множества отдельных настро
ек Кроме того, оно упрощает вызовы функций и упрощает изменение внешнего
вида игры с ростом проекта Чтобы внести изменения в игру, достаточно будет
изменить некоторые значения в settings�py вместо того, чтобы искать разные на
стройки в файлах Исходная версия класса Settings выглядит так
settings.py class Settings(): """Класс для хранения всех настроек игры Alien Invasion.""" def __init__(self): """Инициализирует настройки игры.""" # Параметры экрана self.screen_width = 1200 self.screen_height = 800 self.bg_color = (230, 230, 230)
Чтобы создать экземпляр Settings и использовать его для обращения к настройкам,
внесите изменения в alien_invasion�py
alien_invasion.py
... import pygame
from settings import Settings
def run_game(): # Инициализирует pygame, settings и объект экрана.pygame.init()  ai_settings = Settings()
 screen = pygame.display.set_mode(
(ai_settings.screen_width, ai_settings.screen_height))
pygame.display.set_caption("Alien Invasion") # Запуск основного цикла игры. while True:...# При каждом проходе цикла перерисовывается экран.
 screen.fill(ai_settings.bg_color)

# Отображение последнего прорисованного экрана. pygame.display.flip() run_game()
Класс Settings импортируется в основной файл программы, после чего программа
создает экземпляр Settings и сохраняет его в ai_settings после вызова pygame.
init()  При создании экрана  используются атрибуты screen_width и screen_
height объекта ai_settings , после чего объект ai_settings также используется для
получения цвета фона при заполнении экрана 

Добавление изображения корабля 235
Добавление изображения корабля
А теперь добавим в игру космический корабль, которым управляет игрок Чтобы
вывести его на экран, мы загрузим изображение, после чего воспользуемся методом ae blit() для вывода изображения
Выбирая графику для своих игр, обязательно обращайте внимание на условия ли
цензирования Самый безопасный и дешевый начальный вариант — использование бесплатной графики с таких сайтов, как .
В игре можно использовать практически любые графические форматы, но проще
всего использовать файлы в формате �bmp, потому что этот формат ae загру
жает по умолчанию И хотя ae можно настроить для других типов файлов,
некоторые типы зависят от установки на компьютере определенных графических
библиотек (Большинство изображений, которые вы найдете, имеют формат �jpg,
�png или �gif, но их можно преобразовать в формат �bmp при помощи таких про
грамм, как htsh, M или ait)
Обратите особое внимание на цвет фона вашего изображения Попробуйте найти
файл с прозрачным фоном, который можно заменить любым цветом фона в гра
фическом редакторе Чтобы ваша игра хорошо смотрелась, цвет фона изображения
должен соответствовать цвету фона игры Также можно подобрать цвет фона игры под цвет фона изображения
В игре ie ƒasi используется файл ship�bmp (рис Ć), который можно загру
зить в числе ресурсов книги по адресу ..
Цвет фона файла соответствует настройкам, используемым в проекте Создайте
в главном каталоге проекта ( alien_invasion) каталог с именем images Сохраните файл
ship�bmp в каталоге images
Рис. 12.1. Корабль для игры Alien Invasion

236 Глава 12 • Стреляющий корабль
Создание класса Ship
После того как изображение корабля выбрано, его необходимо вывести на экран
Для работы с кораблем мы напишем модуль ship, содержащий класс Ship Этот
класс реализует б ульшую часть поведения корабля
ship.py import pygame class Ship(): def __init__(self, screen): """Инициализирует корабль и задает его начальную позицию.""" self.screen = screen # Загрузка изображения корабля и получение прямоугольника.
 self.image = pygame.image.load('images/ship.bmp')
 self.rect = self.image.get_rect()
 self.screen_rect = screen.get_rect()
# Каждый новый корабль появляется у нижнего края экрана.
 self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
 def blitme(self):
"""Рисует корабль в текущей позиции.""" self.screen.blit(self.image, self.rect)
Сначала программа импортирует модуль pygame Метод __init__() класса Ship по
лучает два параметра ссылку self и объект screen, на котором выводится корабль
Загрузка изображения выполняется вызовом pygame.image.load()  Функция
возвращает поверхность, представляющую корабль полученный объект сохраня ется в self.image
После того как изображение будет загружено, метод get_rect() используется
для получения атрибута rect поверхности  Один из факторов эффективности
ae заключается в том, что программист может выполнять опера ции с игровы
ми элементами как с прямоугольниками даже в том случае, если они имеют другую
форму Операции с прямоугольниками эффективны, потому что прямоугольник —
простая геометрическая фигура Обычно этот подход работает достато чно хорошо
и игроки не замечают, что программа не отслеживает точную геометрическую форму каждого игрового элемента
При работе с объектом rect для вас доступны координаты x и y верхней, нижней,
левой и правой сторон, а также центра Присваивая любые из этих значений, вы задаете текущую позицию прямоугольника
Местонахождение центра игрового элемента определяет ся атрибутами center,
centerx или centery прямоугольника Стороны определяются атрибутами top,
bottom , left и right Для изменения горизонтального или вертикального рас
положения прямоугольника достаточно задать атрибуты x и y, содержащие
координаты левого верхнего угла Эти атрибуты избавляют вас от вычислений,
которые раньше разработчикам игр приходилось выполнять вручную, притом достаточно часто

Добавление изображения корабля 237
ПРИМЕЧАНИЕ
В Pygame начало координат (0, 0) находится в левом верхнем углу экрана, а оси направлены сверху
вниз и слева направо� На экране с размерами 1200 на 800 начало координат располагается в левом верхнем углу, а правый нижний угол имеет координаты (1200, 800)�
Корабль будет расположен в середине нижней стороны экрана Для этого мы сна
чала сохраняем прямоугольник экрана в self.screen_rect  , а затем присваиваем
self.rect.centerx (координата центра корабля) значение атрибута centerx пря
моугольника экрана  Атрибуту self.rect.bottom (координата низа корабля)
присваивается значение атрибута bottom прямоугольника экрана ae исполь
зует эти атрибуты rect для позиционирования изображения, чтобы корабль был
выровнен по центру, а его нижний край совпадал с нижним краем экрана
В точке  определяется метод blitme(), который выводит изображение на экран
в позиции, заданной self.rect
Вывод корабля на экран
Изменим программу alien_invasion�py, чтобы в ней создавался корабль (экземпляр
Ship ) и вызывался метод blitme() класса Ship
alien_invasion.py
... from settings import Settings
from ship import Ship
def run_game(): ...pygame.display.set_caption("Alien Invasion")
# Создание корабля.
 ship = Ship(screen)
# Start the main loop for the game. while True:...# При каждом проходе цикла перерисовывается экран.screen.fill(ai_settings.bg_color)
 ship.blitme()

# Отображение последнего прорисованного экрана. pygame.display.flip() run_game()
После создания экрана программа импортирует класс Ship и создает его экземпляр
(с именем ship) Это должно происходить до начала основного цикла while  ,
чтобы при каждом проходе цикла не создавался новый экземпляр корабля Чтобы
перерисовать корабль на экране, мы вызываем ship.blitme() после заполнения
фона, так что корабль выводится поверх фона 
Если вы запустите alien_invasion�py сейчас, вы увидите пустой игровой экран, в цен
тре нижней стороны которого находится корабль (рис Ć)

238 Глава 12 • Стреляющий корабль
Рис. 12.2. Корабль в середине нижней стороны экрана
Рефакторинг: модуль game_functions
В больших проектах перед добавлением нового кода часто проводится рефакто
ринг уже написанного кода Рефакторинг упрощает структуру существующего
кода и дальнейшее развитие проекта В этом разделе мы создадим новый модуль
game_functions для хранения функций, обеспечивающих работу игры Модуль
game_functions предотвратит чрезмерное разрастание alien_invasion�py и сделает
логику alien_invasion�py более простой и понятной
Функция check_events()
Начнем с перемещения кода управления событиями в отдельную функцию check_
events() Тем самым вы упростите run_game() и изолируете цикл управления
событиями от остального кода Изоляция цикла событий позволит организовать
управление событиями отдельно от других аспектов игры (например, обновления экрана) Поместим check_events() в отдельный модуль с именем game_functions
game_functions.py import sys import pygamedef check_events():

Рефакторинг: модуль game_functions 239
"""Обрабатывает нажатия клавиш и события мыши."""
for event in pygame.event.get(): if event.type == pygame.QUIT:sys.exit()
Этот модуль импортирует модули sys и pygame , используемые в цикле обработки
событий На данный момент эта функция не получает параметров, а ее тело копи руется из цикла событий в alien_invasion�py
Теперь изменим код alien_invasion�py, чтобы он импортировал модуль game_
functions , и мы заменим цикл событий вызовом check_events()
alien_invasion.py
import pygame from settings import Settings from ship import Ship
import game_functions as gf
def run_game(): ...# Запуск основного цикла игры.while True:
gf.check_events()
# При каждом проходе цикла перерисовывается экран. ...
Импортировать модуль sys прямо в главный файл в программы уже не нужно, по
тому что он сейчас используется только в модуле game_functions Импортируемому
модулю game_functions для удобства присваивается псевдоним gf
Функция update_screen()
Для дальнейшего упрощения run_game() выделим код обновления экрана в от
дельную функцию update_screen() в game_functions�py
game_functions.py
... def check_events(): ...
def update_screen(ai_settings, screen, ship): """Обновляет изображения на экране и отображает новый экран."""
# При каждом проходе цикла перерисовывается экран. screen.fill(ai_settings.bg_color)ship.blitme() # Отображение последнего прорисованного экрана. pygame.display.flip()
Новая функция update_screen() получает три параметра ai_settings, screen
и ship Теперь необходимо заменить цикл while из alien_invasion�py вызовом update_
sc reen()

240 Глава 12 • Стреляющий корабль
alien_invasion.py
... # Запуск основного цикла игры. while True:gf.check_events()
gf.update_screen(ai_settings, screen, ship)
run_game()
Эти две функции упрощают цикл while и процесс дальнейшей разработки Б уль
шая часть работы будет выполняться не в run_game(), а в модуле game_functions
Так как мы решили начать работу с кодом c одного файла, мы не стали вводить мо
дуль game_functions с самого начала Эта последовательность дает представление
о реальном процессе разработки сначала вы пишете свой код в самом простом виде, а потом подвергаете его рефакторингу по мере роста сложности проекта
Теперь, когда мы изменили структуру кода и упростили его расширение, можно переходить к динамическим аспектам игры
УПРАЖНЕНИЯ
12-1� Синее небо: создайте окно Pygame с синим фоном�
12-2� Игровой персонаж: найдите изображение игрового персонажа, который вам нравится,
в формате �bmp (или преобразуйте существующее изображение)� Создайте класс, который
рисует персонажа в центре экрана, и приведите цвет фона изображения в соответствие с цветом фона экрана (или наоборот)�
Управление кораблем
Реализуем возможность перемещения корабля по горизонтали Для этого мы на
пишем код, реагирующий на нажатие клавиш → или ← Начнем с движения впра
во, а потом применим те же принципы к движению влево Заодно вы научитесь управлять перемещением изображений на экране Обработка нажатия клавиши
Каждый раз, когда пользователь нажимает клавишу, это нажатие регистрируется
в ae как событие Каждое событие идентифицируется методом pygame.event.
get() , поэтому в функции check_events() необходимо указать, какие события
должны отслеживаться Каждое нажатие клавиши регистрируется как событие KEYDOWN
При обнаружении события KEYDOWN необходимо проверить, была ли нажата кла
виша, инициирующая некоторое игровое событие Например, при нажатии кла
виши → значение rect.centerx корабля увеличивается для перемещения корабля
вправо
game_functions.py def check_events(ship):
"""Обрабатывает нажатия клавиш и события мыши."""

Управление кораблем 241
for event in pygame.event.get(): if event.type == pygame.QUIT:sys.exit()
 elif event.type == pygame.KEYDOWN:
 if event.key == pygame.K_RIGHT:
# Переместить корабль вправо.
 ship.rect.centerx += 1
Функции check_events() передается параметр ship, потому что корабль должен
двигаться вправо при нажатии клавиши → Внутри check_events() в цикл событий
добавляется блок elif для выполнения кода при обнаружении события KEYDOWN 
Чтобы проверить, является ли нажатая клавиша клавишей → (pygame.K_RIGHT ),
мы читаем атрибут event.key  Если нажата клавиша →, корабль перемещается
вправо, для чего значение ship.rect.centerx увеличивается на 
Вызов check_events() в alien_invasion�py необходимо изменить, чтобы в аргументе
передавался объект ship
alien_invasion.py
# Запуск основного цикла игры. while True:
gf.check_events(ship)gf.update_screen(ai_settings, screen, ship)
Если запустить программу alien_invasion�py сейчас, вы увидите, что корабль переме
щается вправо на пиксел при каждом нажатии клавиши → Неплохо для начала,
но это не лучший способ управления кораблем Чтобы управление было более удобным, следует реализовать возможность непрерывного перемещения Непрерывное перемещение
Если игрок удерживает клавишу →, корабль должен двигаться вправо до тех пор,
пока клавиша не будет отпущена Чтобы узнать, когда клавиша → будет отпущена,
наша игра отслеживает событие pygame.KEYUP таким образом, реализация непре
рывного движения будет основана на отслеживании событий KEYDOWN и KEYUP в со
четании с флагом moving_right
В неподвижном состоянии корабля флаг moving_right равен False При нажатии
клавиши → флагу присваивается значение True, а когда клавиша будет отпущена,
флаг возвращается в состояние False
Класс Ship управляет всеми атрибутами корабля, и мы добавим в него атрибут
с именем moving_right и метод update() для проверки состояния флага moving_
right Метод update() изменяет позицию корабля, если флаг содержит значение
True Этот метод будет вызываться каждый раз, когда вы хотите обновить позицию
корабля Ниже приведены изменения в классе Ship
ship.py
class Ship():

242 Глава 12 • Стреляющий корабль
def __init__(self, screen): ...# Каждый новый корабль появляется у нижнего края экрана.self.rect.centerx = self.screen_rect.centerxself.rect.bottom = self.screen_rect.bottom
# Флаг перемещения
 self.moving_right = False
 def update(self):
"""Обновляет позицию корабля с учетом флага.""" if self.moving_right: self.rect.centerx += 1 def blitme(self): ...
Мы добавляем атрибут self.moving_right в методе __init__() и инициализируем
его значением False  Затем вызывается метод update(), который перемещает
корабль вправо, если флаг равен True 
Теперь внесем изменения в check_events(), чтобы при нажатии клавиши → moving_
right присваивалось значение True, а при ее отпускании — False
game_functions.py
def check_events(ship): """Обрабатывает нажатия клавиш и события мыши."""for event in pygame.event.get():...elif event.type == pygame.KEYDOWN:if event.key == pygame.K_RIGHT:
 ship.moving_right = True
 elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT: ship.moving_right = False
В точке  изменяется реакция игры при нажатии клавиши → вместо непосред
ственного изменения позиции корабля программа просто присваивает moving_right
значение True В точке  добавляется новый блок elif, реагирующий на события
KEYUP Когда игрок отпускает клавишу → (K_RIGHT ), moving_right присваивается
значение False
Остается изменить цикл while в alien_invasion�py , чтобы при каждом проходе цикла
вызывался метод update() корабля
alien_invasion.py
# Запуск основного цикла игры. while True:gf.check_events(ship)
ship.update()gf.update_screen(ai_settings, screen, ship)
Позиция корабля будет обновляться после проверки событий клавиатуры, но
перед обновлением экрана Таким образом, позиция корабля обновляется в от

Управление кораблем 243
вет на действия пользователя и будет использоваться при перерисовке корабля на экране
Если запустить alien_invasion�py и удерживать клавишу →, корабль непрерывно
двигается вправо, пока клавиша не будет отпущена Перемещение влево и вправо
Теперь, когда мы реализовали непрерывное движение вправо, добавить движе
ние влево относительно несложно Для этого нужно снова изменить класс Ship
и функцию check_events() Ниже приведены необходимые изменения в __init__()
и update() в классе Ship
ship.py
def __init__(self, screen): ...
# Флаги перемещенияself.moving_right = False self.moving_left = False
def update(self): """Обновляет позицию корабля с учетом флагов."""if self.moving_right: self.rect.centerx += 1
if self.moving_left: self.rect.centerx -= 1
В методе __init__() добавляется флаг self.moving_left В update() используются
два отдельных блока if вместо elif, чтобы при нажатии обеих клавиш со стрелками
атрибут rect.centerx сначала увеличивался, а потом уменьшался В результате
корабль остается на месте Если бы для движения влево использовался блок elif,
то клавиша → всегда имела бы приоритет Такая реализация повышает точность
перемещения при переключении направления, когда игрок может ненадолго удер
живать нажатыми обе клавиши В check_events() необходимо внести два изменения
game_functions.py
def check_events(ship): """Обрабатывает нажатия клавиш и события мыши."""for event in pygame.event.get():...elif event.type == pygame.KEYDOWN:if event.key == pygame.K_RIGHT:ship.moving_right = True
elif event.key == pygame.K_LEFT: ship.moving_left = True
elif event.type == pygame.KEYUP: if event.key == pygame.K_RIGHT:ship.moving_right = False
elif event.key == pygame.K_LEFT: ship.moving_left = Fals e

244 Глава 12 • Стреляющий корабль
Если событие KEYDOWN происходит для события K_LEFT, то moving_left присваи
вается True Если событие KEYUP происходит для события K_LEFT, то moving_left
присваивается False Здесь возможно использовать блоки elif, потому что каждое
событие связано только с одной клавишей Если же игрок нажимает обе клавиши одновременно, то программа обнаруживает два разных события
Если вы запустите alien_invasion�py, то увидите, что корабль может непрерывно
двигаться влево и вправо Если же нажать обе клавиши, корабль останавливается
Следующий шаг — доработка движения корабля Внесем изменения в скорость
и ограничим величину перемещения, чтобы корабль не выходил за края экрана Регулировка скорости корабля
В настоящий момент корабль смещается на один пиксел за каждый проход цикла while , но для повышения точности управления скоростью можно добавить в к ласс
Settings атрибут ship_speed_factor Этот атрибут определяет величину смещения
корабля при каждом проходе цикла Новый атрибут settings�py выглядит так
settings.py
class Settings(): """Класс для хранения всех настроек игры Alien Invasion.""" def __init__(self): ...
# Настройки корабля
Переменной ship_speed_factor присваивается значение Æʆ При перемещении ко
рабля его позиция изменяется на , пиксела вместо Æ Дробные значения скорости
позволят лучше управлять скоростью корабля при последующем повышении темпа
игры Однако атрибуты прямоугольников (такие, как centerx) принимают только
целочисленные значения, поэтому в Ship необходимо внести ряд изменений
ship.py
class Ship():  def __init__(self, ai_settings, screen): """Инициализирует корабль и задает его начальную позицию.""" self.screen = screen
 self.ai_settings = ai_settings ... # Каждый новый корабль появляется у нижнего края экрана ...
# Сохранение вещественной координаты центра корабля.
 self.center = float(self.rect.centerx)

# Флаги перемещения self.moving_right = Falseself.moving_left = False

Управление кораблем 245

def update(self):
"""Обновляет позицию корабля с учетом флагов."""
# Обновляется атрибут center, не rect.if self.moving_right:  self.center += self.ai_settings.ship_speed_factor if self.moving_left: self.center -= self.ai_settings.ship_speed_factor # Обновление атрибута rect на основании self.center.
 self.rect.centerx = self.center
def blitme(self): ...
В точке  в список параметров __init__() добавляется параметр ai_settings, что
бы для корабля была доступна величина его скорости Затем параметр ai_settings
преобразуется в атрибут для использования в update()  Так как позиция корабля
изменяется с нецелым приращением пикселов, она должна храниться в перемен
ной, способной хранить дробные значения Формально атрибутам rect можно
присвоить дробные значения, но rect сохранит только целую часть этого значения
Для точного хранения позиции корабля определяется новый атрибут self.center,
способный хранить дробные значения  Функция float() используется для пре
образования значения self.rect.centerx в вещественный формат и сохранения
этого значения в self.center
После изменения позиции корабля в update() значение self.center изменяется
на величину, хранящуюся в ai_settings.ship_speed_factor  После обновле
ния self.center новое значение используется для обновления атрибута self.
rect.centerx , управляющего позицией корабля  В self.rect.centerx будет
сохранена только целая часть self.center, но для отображения корабля этого
достаточно
Значение ai_settings должно передаваться в аргументе при создании экземпляра
Ship в alien_invasion�py
alien_invasion.py
... def run_game():...# Создание корабля.
ship = Ship(ai_settings, screen)...
Теперь с любым значением ship_speed_factor, бульшим , корабль будет двигаться
быстрее Эта возможность ускорит реакцию корабля на действия игрока, а также позволит нам изменить темп игры с течением времени Ограничение перемещений
Если удерживать какуюнибудь клавишу со стрелкой достаточно долго, корабль
выйдет за край экрана Давайте сделаем так, чтобы корабль останавливался при до
стижении края экрана Задача решается изменением метода update() в классе Ship

246 Глава 12 • Стреляющий корабль
ship.py
def update(self): """Обновляет позицию корабля с учетом флагов.""" # Обновляется атрибут center, не rect.
 if self.moving_right and self.rect.right < self.screen_rect.right: self.center += self.ai_settings.ship_speed_factor  if self.moving_left and self.rect.left > 0: self.center -= self.ai_settings.ship_speed_factor # Обновление атрибута rect на основании self.center self.rect.centerx = self.center
Этот код проверяет позицию корабля перед изменением значения self.center
Выражение self.rect.right возвращает координату правого края прямо
угольника корабля Если это значение меньше значения, возвращаемого self.
screen_rect.right , значит, корабль еще не достиг правого края экрана  То же
относится и к левому краю если координата левой стороны прямоугольника
больше , значит, корабль еще не достиг левого края экрана  Проверка гаранти
рует, что корабль будет оставаться в пределах экрана, перед изменением значения self.center
Если вы запустите alien_invasion�py сейчас, то движение корабля будет останавли
ваться у края экрана
Рефакторинг check_events()
В ходе разработки функция check_events() будет становиться все длиннее, поэтому
мы выделим из check_events() еще две функции для обработки событий KEYDOWN
и для обработки событий KEYUP
game_functions.py def check_keydown_events(event, ship): """Реагирует на нажатие клавиш."""
if event.key == pygame.K_RIGHT: ship.moving_right = Trueelif event.key == pygame.K_LEFT:ship.moving_left = True
def check_keyup_events(event, ship): """Реагирует на отпускание клавиш."""
if event.key == pygame.K_RIGHT: ship.moving_right = False elif event.key == pygame.K_LEFT:ship.moving_left = False def check_events(ship): """Обрабатывает нажатия клавиш и события мыши."""for event in pygame.event.get():if event.type == pygame.QUIT:sys.exit()elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ship)

В двух словах 247
elif event.type == pygame.KEYUP: check_keyup_events(event, ship)
В программе появились две новые функции check_keydown_events() и check_
keyup_events() Каждая функция получает параметр event и параметр ship Тела
двух функций скопированы из check_events(), а старый код заменен вызовами
новых функций Новая структура кода упрощает функцию check_events() и об
легчает последующее программирование реакции на действия игрока В двух словах
В следующем разделе мы реализуем стрельбу, для чего нам потребуется но
вый файл с именем bullet�py и изменения в некоторых уже имеющихся файлах
В настоящее время программа состоит из четырех файлов с разными классами,
функциями и методами Чтобы вы четко представляли себе структуру проекта,
кратко проанализируем каждый из этих файлов перед добавлением новой функ
циональности
alien_invasion�py
Главный файл программы alien_invasion�py создает ряд важных объектов, исполь
зуемых ходе игры настройки хранятся в ai_settings, основная поверхность для
вывода изображения хранится в screen, а экземпляр ship тоже создается в этом
файле Также в alien_invasion�py содержится главный цикл игры — цикл while с вы
зовами check_events() , ship.update() и update_screen()
Файл alien_invasion�py — единственный файл, который должен запускаться дл я игры
в ie ƒasi Все остальные файлы — settings�py, game_functions�py , ship�py — со
держат код, который импортируется (прямо или косвенно) в этот файл
settings�py Файл settings�py содержит класс Settings Этот класс содержит только метод
__init__() , инициализирующий атрибуты, которые управляют внешним видом
и скоростью игры
game_functions�py
Файл game_functions�py содержит набор функций, выполняющих основную
работу в игре Функция check_events() обнаруживает события, представ
ляющие интерес для игры (например, нажатия и отпускания клавиш), и об
рабатывает все эти типы событий при помощи вспомогательных функций check_keydown_events() и check_keyup_events() Пока эти функции управляют
только движением корабля Модуль game_functions также содержит функцию
update_screen() , которая перерисовывает экран при каждом проходе основного
цикла

248 Глава 12 • Стреляющий корабль
ship�py Файл ship�py содержит класс Ship В этом классе определен метод __init__(),
метод update() для управления позицией корабля и метод blitme() для вывода
изображения корабля на экран Изображение корабля хранится в файле ship�bmp,
который находится в папке images
УПРАЖНЕНИЯ
12-3� Ракета: создайте игру, у которой в исходном состоянии в центре экрана находится
ракета� Игрок может перемещать ракету вверх, вниз, вправо и влево четырьмя клавишами со стрелками� Проследите за тем, чтобы ракета не выходила за края экрана�
12-4� Клавиши: создайте файл Pygame, который создает пустой экран� В цикле событий
выводите значение атрибута event�key при обнаружении события pygame�KEYDOWN� За-
пустите программу, нажимайте различные клавиши и понаблюдайте за реакцией Pygame�
Стрельба
А теперь добавим в игру возможность стрельбы Мы напишем код, который выпу
скает пулю (маленький прямоугольник) при нажатии игроком клавиши «пробел» Пули летят вертикально вверх, пока не исчезнут у верхнего края экрана Добавление настроек
Сначала добавим в settings�py новые настройки для значений, управляющих по
ведением класса Bullet Эти настройки добавляются в конец метода __init__()
settings.py def __init__(self): ... # Параметры пули self.bullet_speed_factor = 1 self.bullet_width = 3 self.bullet_height = 15 self.bullet_color = 60, 60, 60
Эти настройки создают темносерые пули с шириной пиксела и высотой пик
селов Пули двигаются немного медленнее, чем корабль
Создание класса Bullet
Теперь создадим файл bullet�py для хранения класса Bullet Первая часть файла
bullet�py выглядит так
bullet.py import pygame from pygame.sprite import Sprite class Bullet(Sprite):

Стрельба 249
"""Класс для управления пулями, выпущенными кораблем.""" def __init__(self, ai_settings, screen, ship): """Создает объект пули в текущей позиции корабля.""" super(Bullet, self).__init__() self.screen = screen # Создание пули в позиции (0,0) и назначение правильной позиции.
 self.rect = pygame.Rect(0, 0, ai_settings.bullet_width,
ai_settings.bullet_height)
 self.rect.centerx = ship.rect.centerx
 self.rect.top = ship.rect.top
# Позиция пули хранится в вещественном формате.
 self.y = float(self.rect.y)
 self.color = ai_settings.bullet_color
self.speed_factor = ai_settings.bullet_speed_factor
Класс Bullet наследует от класса Sprite, импортируемого из модуля pygame.sprite
Работая со спрайтами (srite), разработчик группирует связанные элементы
в своей игре и выполняет операцию со всеми сгруппированными элементами одно
временно Чтобы создать экземпляр пули, методу __init__() необходимо передать
экземпляры ai_settings, screen и ship , а вызов super() необходим для правильной
реализации наследования от Sprite
ПРИМЕЧАНИЕ
Вызов super(Bullet, self)�__init__() использует синтаксис Python 2�7� В Python 3 этот синтаксис тоже
работает, хотя вызов также можно записать в более простой форме super()�__init__()�
В точке  создается атрибут rect пули Пуля не создается на основе готового
изображения, поэтому прямоугольник приходится строить «с нуля» при по
мощи класса pygame.Rect() При создании экземпляра этого класса необходимо
задать координаты левого верхнего угла прямоугольника, его ширину и высоту
Прямоугольник инициализируется в точке (, ), но в следующих двух строках
он перемещается в нужное место, так как позиция пули зависит от позиции
корабля Ширина и высота пули определяются значениями, хранящимися в ai_settings
В точке  атрибуту centerx пули присваивается значение rect.centerx корабля
Пуля должна появляться у верхнего края корабля, поэтому верхний край пули
совмещается с верхним краем прямоугольника корабля для имитации «выстрела» из корабля 
Координата пули хранится в вещественной форме для внесения более точных
изменений в скорость пули  В точке  настройки цвета и скорости пули сохра
няются в self.color и self.speed_factor
А вот как выглядит вторая часть bullet�py, update() и draw_bullet()
bullet.py def update(self): """Перемещает пулю вверх по экрану."""

250 Глава 12 • Стреляющий корабль
# Обновление позиции пули в вещественном формате.
 self.y -= self.speed_factor
# Обновление позиции прямоугольника.
 self.rect.y = self.y
def draw_bullet(self): """Вывод пули на экран."""
 pygame.draw.rect(self.screen, self.color, self.rect)
Метод update() управляет позицией пули Когда происходит выстрел, пуля двига
ется вверх по экрану, что соответствует уменьшению координаты следовательно,
для обновления позиции пули следует вычесть величину, хранящуюся в self.
speed_factor , из self.y  Затем значение self.y используется для изменения
значения self.rect.y  Атрибут speed_factor позволяет увеличить скорость пуль
по ходу игры или при изменении ее поведения Координата пули после выстрела
не изменяется, поэтому пуля летит вертикально по прямой линии
Для вывода пули на экран вызывается функция draw_bullet() Она заполняет часть
экрана, определяемую прямоугольником пули, цветом из self.color 
Группировка пуль
Класс Bullet и все необходимые настройки готовы можно переходить к на
писанию кода, который будет выпускать пулю каждый раз, когда игрок на
жимает клавишу «пробел» Сначала мы создадим в alien_invasion�py группу для
хранения всех летящих пуль, чтобы программа могла управлять их полетом
Эта группа будет представлена экземпляром класса pygame.sprite.Group, ко
торый напоминает список с расширенной функциональностью, которая может
быть полезна при построении игр Мы воспользуемся группой для прорисовки
пуль на экране при каждом проходе основного цикла и обновления текущей позиции каждой пули
alien_invasion.py
import pygamefrom pygame.sprite import Group... def run_game(): ...# Создание корабля.ship = Ship(ai_settings, screen)
# Создание группы для хранения пуль.
 bullets = Group()
# Запуск основного цикла игры. while True:
gf.check_events(ai_settings, screen, ship, bullets) ship.update()
 bullets.update()
gf.update_screen(ai_settings, screen, ship, bullets)
run_game()

Стрельба 251
Класс Group импортируется из pygame.sprite В точке  создается экземпляр Group
с именем bullets Эта группа создается за пределами цикла while, чтобы новая
группа пуль не создавалась при каждом проходе цикла ПРИМЕЧАНИЕ
Если группа будет создаваться в цикле, в результате программа создает тысячи групп, и скорость
игры упадет до минимума� Если ваша игра со временем начинает заметно «тормозить», вниматель-
но проверьте, что происходит в основном цикле while� Объект bullets передается методам check_events() и update_screen() В check_
events() он используется при обработке клавиши «пробел», а в update_screen()
необходимо перерисовать выводимые на экран пули Вызов update() для группы  приводит к автоматическому вызову update() для
каждого спрайта в группе Строка bullets.update() вызывает bullet.update() для
каждой пули, включенной в группу bullets
Обработка выстрелов
В файле game_functions�py необходимо внести изменения в метод check_keydown_
events() , чтобы при нажатии клавиши «пробел» происходил выстрел Изменять
check_keyup_events() не нужно, потому что при отпускании клавиши ничего
не происходит Также необходимо изменить update_screen() и вывести каждую
пулю на экран перед вызовом flip() Ниже представлены соответствующие
изменения в game_functions�py
game_functions.py ... from bullet import Bullet
 def check_keydown_events(event, ai_settings, screen, ship, bullets):
...  elif event.key == pygame.K_SPACE:
# Создание новой пули и включение ее в группу bullets. new_bullet = Bullet(ai_settings, screen, ship) bullets.add(new_bullet)
...
 def check_events(ai_settings, screen, ship, bullets): """Обрабатывает нажатия клавиш и события мыши.""" for event in pygame.event.get():...elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bullets)...
 def update_screen(ai_settings, screen, ship, bullets): ... # Все пули выводятся позади изображений корабля и пришельцев.
 for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme() ...

252 Глава 12 • Стреляющий корабль
Рис. 12.3. Экран игры после серии выстрелов
Группа bullets передается check_keydown_events()  Когда игрок нажимает про
бел, создается новая пуля (экземпляр Bullet с именем new_bullet ), которая добав
ляется в группу bullets  методом add() код bullets.add(new_bullet) сохраняет
новую пулю в группе bullets
Группу bullets необходимо добавить в число параметров в определении
check_events()  , а также передать в аргументе при вызове check_keydown_
events()
Параметр bullets передается функции update_screen()  , которая рисует пули
на экране Метод bullets.sprites() возвращает список всех спрайтов в группе
bullets Чтобы нарисовать все выпущенные пули на экране, программа перебирает
спрайты в bullets и вызывает для каждого draw_bullet() 
Если запустить alien_invasion�py сейчас, вы сможете двигать корабль влево и вправо
и выпускать сколько угодно пуль Пули перемещаются вверх по экрану и исчезают
при достижении верхнего края (рис Ć) Размер, цвет и скорость пуль можно из
менить при помощи настроек в settings�py
Удаление старых пуль
На данный момент пули исчезают по достижении верхнего края, но только потому,
что ae не может нарисовать их выше края экрана На самом деле пули про
должают существовать их координата продолжает уменьшаться И это создает
проблему, потому что пули продолжают потреблять память и вычислительные мощности

Стрельба 253
От старых пуль необходимо избавиться, иначе игра замедлится изза большого
объема лишней работы Для этого необходимо определить момент, когда атрибут bottom прямоугольника пули достигнет , — это означает, что пуля вышла за верх
ний край экрана
alien_invasion.py
# Запуск основного цикла игры. while True: gf.check_events(ai_settings, screen, ship, bullets)ship.update() bullets.update()
# Удаление пуль, вышедших за край экрана.
 for bullet in bullets.copy():
 if bullet.rect.bottom <= 0:
 bullets.remove(bullet)
 print(len(bullets))
gf.update_screen(ai_settings, screen, ship, bullets)
Удалять элементы из списка или группы в цикле for не следует, поэтому переби
рать нужно копию группы Метод copy() используется для создания цикла for  ,
в котором возможно изменять содержимое bullets Программа проверяет каждую
пулю и определяет, вышла ли она за верхний край экрана  Если пуля пересекла
границу, она удаляется из bullets  В точке  добавляется команда print, которая
сообщает, сколько пуль сейчас существует в игре по выведенному значению можно
убедиться в том, что пули действительно были удалены
Если код работает правильно, вы можете понаблюдать за выводом на терминале
и убедиться в том, что количество пуль уменьшается до после того, как очередной
залп уходит за верхний край экрана После того как вы запустите игру и убедитесь
в том, что пули правильно удаляются из группы, удалите команду print Если ко
манда останется в программе, она существенно замедлит игру, потому что вывод
на терминал занимает больше времени, чем отображение графики в игровом окне Ограничение количества пуль
Многие игры«стрелялки» ограничивают количество пуль, одновременно находя
щихся на экране, чтобы у игроков появился стимул стрелять более метко То же самое будет сделано и в игре ie ƒasi Сначала сохраним максимально допустимое количество пуль в settings�py
settings.py
# Параметры пули self.bullet_width = 3self.bullet_height = 15self.bullet_color = 60, 60, 60
self.bullets_allowed = 3
В любой момент времени на экране может находиться не более трех пуль Эта на
стройка будет использоваться в game_functions�py для проверки количества суще
ствующих пуль перед созданием новой пули в check_keydown_events()

254 Глава 12 • Стреляющий корабль
game_functions.py
def check_keydown_events(event, ai_settings, screen, ship, bullets): ...elif event.key == pygame.K_SPACE:# Создание новой пули и включение ее в группу bullets.
if len(bullets) < ai_settings.bullets_allowed:new_bullet = Bullet(ai_settings, screen, ship) bullets.add(new_bullet)
При нажатии клавиши «пробел» программа проверяет длину bullets Если
значение len(bullets) меньше трех, создается новая пуля Но, если на экране
уже находятся три активные пули, при нажатии пробел а ничего не происходит
Если вы запустите игру сейчас, вы сможете выпускать пули только группами по три
Создание функции update_bullets()
Мы хотим, чтобы главный файл программы alien_invasion�py был как можно более
простым, поэтому после написания и проверки кода управления пулями этот
код можно переместить в модуль game_functions Мы создадим новую функцию
update_bullets() и добавим ее в конец game_functions�py
game_functions.py def update_bullets(bullets): """Обновляет позиции пуль и уничтожает старые пули.""" # Обновление позиций пуль.
bullets.update() # Удаление пуль, вышедших за край экрана. for bullet in bullets.copy():if bullet.rect.bottom <= 0:bullets.remove(bullet)
Код update_bullets() вырезается и вставляется из alien_invasion�py единственным
необходимым параметром функции является группа bullets
Цикл while в alien_invasion�py снова выглядит просто
alien_invasion.py
# Запуск основного цикла игры. while True:
 gf.check_events(ai_settings, screen, ship, bullets)  ship.update()  gf.update_bullets(bullets)
 gf.update_screen(ai_settings, screen, ship, bullets)
В результате преобразования основной цикл содержит минимум кода, чтобы
можно было легко прочитать имена функций и понять, что происходит в игре
Основной цикл проверяет ввод, полученный от игрока , а затем обновляет пози
цию корабля  и всех выпущенных пуль  Затем обновленные позиции игровых
элементов используются для вывода нового экрана в точке 

Итоги 255
Создание функции fire_bullet()
Переместим код стрельбы в отдельную функцию, чтобы выстрел выполнялся всего одной строкой кода, а блок elif в check_keydown_events() оставался простым
game_functions.py
def check_keydown_events(event, ai_settings, screen, ship, bullets): """Реагирует на нажатия клавиш."""...elif event.key == pygame.K_SPACE:
fire_bullet(ai_settings, screen, ship, bullets) def fire_bullet(ai_settings, screen, ship, bullets): """Выпускает пулю, если максимум еще не достигнут."""
# Создание новой пули и включение ее в группу bullets. if len(bullets) < ai_settings.bullets_allowed:new_bullet = Bullet(ai_settings, screen, ship)bullets.add(new_bullet)
Функция fire_bullet() просто содержит код, который использовался для выстрела
при нажатии клавиши «пробел» вызов fire_bullet() добавляется в check_keydown_
events() при нажатии клавиши «пробел»
Запустите alien_invasion�py еще раз и убедитесь в том, что стрельба проходит без
ошибок
УПРАЖНЕНИЯ
12-5� Боковая стрельба: напишите игру, в которой корабль размещается у левого края
экрана, а игрок может перемещать корабль вверх и вниз� При нажатии клавиши «пробел»
корабль стреляет, и пуля двигается вправо по экрану� Проследите за тем, чтобы пули уда-лялись при выходе за край экрана�
Итоги
В этой главе вы научились составлять план игры, а также усвоили базовую струк
туру игры, написанной с использованием ae Вы узнали, как задать цвет фона
и как сохранить настройки в отдельном классе, чтобы они были доступны для всех
частей игры Вы научились выводить изображения на экран и управлять перемеще
нием игровых элементов Также вы узнали, как создавать элементы, двигающиеся
самостоятельно (например, пули, летящие по экрану), и как удалять объекты, ко
торые стали лишними Также в этой главе рассматривалась методика регулярного рефакторинга кода для упрощения текущей разработки
В главе в игру ie ƒasi будут добавлены пришельцы К концу главы
игрок сможет сбивать корабли пришельцев — конечно, если они не доберутся до него первыми

13 Осторожно, пришельцы!
В этой главе в игру ie ƒasi будут добавлены пришельцы Сначала мы до
бавим одного пришельца у верхнего края экрана, а потом сгенерируем целый
флот Пришельцы будут перемещаться в сторону и вниз при этом пришельцы,
в которых попадают пули, исчезают с экрана Наконец, мы ограничим количе
ство кораблей у игрока, так что при гибели последнего корабля игра завершается
В этой главе вы узнаете больше о ae и о ведении крупного проекта Вы
также научитесь обнаруживать коллизии (столкновения) игровых объектов,
например пуль и пришельцев Обнаружение коллизий помогае т определять
взаимодействия между элементами игры например, ограничить перемещение
персонажа областью между стенами лабиринта или организовать передачу мяча
между двумя персонажами Работа будет продолжаться на основе плана, к кото
рому мы будем время от времени возвращаться, чтобы не отклоняться от цели во время написания кода
Итак, прежде чем браться за новый код для добавления флота пришельцев на экран, рассмотрим проект и обновим план Анализ проекта
Приступая к новой фазе разработки крупного проекта, всегда полезно вернуться
к исходному плану и уточнить, чего же вы хотите добиться в том коде, который собираетесь написать В этой главе мы

‰ Проанализируем код и определим, нужно ли провести рефакторинг перед реализацией новых возможностей

‰ Добавим в левом верхнем углу экрана одного пришельца, отделив его от краев экрана интервалами

‰ По величине интервалов вокруг первого пришельца и общим размерам экрана
вычислим, сколько пришельцев поместится на экране Для создания пришель

цев, заполняющих верхнюю часть экрана, будет написан цикл

‰ Организуем перемещение флота пришельцев в сторону и вниз, пока весь флот
не будет уничтожен, пока пришелец не столкнется с кораблем игрока или пока
пришелец не достигнет земли Если весь флот будет уничтожен, программа
создает новый флот Если пришелец сталкивается с кораблем или с землей, программа уничтожает корабль и создает новый флот

Создание пришельца 257

‰ Ограничим количество кораблей, которые могут использоваться игроком, и за

вершим игру в конце последней попытки
Этот план будет уточняться по мере реализации новых возможностей, но для на чала и этого достаточно
Также проводите анализ кода, когда вы начинаете работу над новой серией воз
можностей проекта Так как с каждой новой фазой проект обычно становится
более сложным, лучше всего заняться расчисткой излишне громоздкого или не
эффективного кода И хотя сейчас особой расчистки не потребуется, потому что
мы уже проводили промежуточный рефакторинг, необходимость использовать
мышь для закрытия игры каждый раз, когда потребуется протестировать новую
функцию, раздражает Добавим возможность быстрого завершения игры при нажатии клавиши
game_functions.py
def check_keydown_events(event, ai_settings, screen, ship, bullets): ...
elif event.key == pygame.K_q: sys.exit()
В check_keydown_events() добавляется новый блок, который завершает игру при
нажатии клавиши Это довольно безопасное изменение, потому что клавиша
находится достаточно далеко от клавиш со стрелками и пробела, так что вероят
ность случайного нажатия и завершения игры невелика Теперь при тестировании игру можно закрыть клавишей , не прибегая к использованию мыши Создание пришельца
Размещение одного пришельца на экране мало чем отличается от размещения
корабля Поведением каждого пришельца будет управлять класс с именем Alien,
который по своей структуре очень похож на класс Ship Для простоты мы сно
ва воспользуемся готовыми графическими изображениями Вы можете найти
Рис. 13.1. Пришелец, который будет использоваться для создания флота

258 Глава 13 • Осторожно, пришельцы!
собственное изображение пришельца или использовать изображение на рис ҆,
доступное в ресурсах книги по адресу ..
Это изображение имеет серый фон, совпадающий с цветом фона экрана Не забудьте сохранить выбранный файл в каталоге images
Создание класса Alien Теперь можно написать класс Alien
alien.py import pygame from pygame.sprite import Sprite class Alien(Sprite): """Класс, представляющий одного пришельца.""" def __init__(self, ai_settings, screen): """Инициализирует пришельца и задает его начальную позицию.""" super(Alien, self).__init__() self.screen = screen self.ai_settings = ai_settings # Загрузка изображения пришельца и назначение атрибута rect. self.image = pygame.image.load('images/alien.bmp') self.rect = self.image.get_rect() # Каждый новый пришелец появляется в левом верхнем углу экрана.
 self.rect.x = self.rect.width
self.rect.y = self.rect.height # Сохранение точной позиции пришельца. self.x = float(self.rect.x) def blitme(self): """Выводит пришельца в текущем положении.""" self.screen.blit(self.image, self.rect)
В основном этот класс похож на класс Ship (если не считать размещения пришель
ца) Изначально каждый пришелец размещается в левом верхнем углу экрана, при
этом слева от него добавляется интервал, равный ширине пришельца, а над ним — интервал, равный высоте 
Создание экземпляра Alien Создадим экземпляр Alien в alien_invasion�py
alien_invasion.py ...
from ship import Shipfrom alien import Alienimport game_functions as gf

Создание пришельца 259
def run_game(): ...
# Создание пришельца. alien = Alien(ai_settings, screen)
# Запуск основного цикла игры. while True:gf.check_events(ai_settings, screen, ship, bullets)ship.update()gf.update_bullets(bullets)
gf.update_screen(ai_settings, screen, ship, alien, bullets)
run_game()
Программа импортирует новый класс Alien и создает экземпляр Alien непосред
ственно перед входом в основной цикл while Так как позиция пришельца еще
не успела измениться, ничего нового в цикле не добавляется изменения вносятся только в вызов update_screen() , которому передается экземпляр alien
Отображение пришельца на экране
Рис. 13.2. Появился первый пришелец
Чтобы пришелец появился на экране, программа вызывает его метод blitme()
в update_screen()
game_functions.py def update_screen(ai_settings, screen, ship, alien, bullets):
...

260 Глава 13 • Осторожно, пришельцы!
# Все пули выводятся позади изображений корабля и пришельцев. for bullet in bullets:bullet.draw_bullet()ship.blitme()
alien.blitme()
# Отображение последнего прорисованного экрана. pygame.display.flip()
Пришелец выводится после прорисовки корабля и пуль, так что пришельцы будут
находиться на верхнем «слое» экрана На рис ҆ изображен первый пришелец
После того как первый пришелец появится на экране, мы напишем код для вывода всего флота Построение флота
Чтобы нарисовать флот пришельцев, необходимо вычисли ть, сколько пришельцев
поместится в одном ряду и сколько рядов поместится по высоте Сначала мы вы
числим горизонтальные интервалы между пришельцами и создадим ряд затем будет вычислен вертикальный интервал и создан весь флот Вычисление количества пришельцев в одном ряду
Чтобы определить, сколько пришельцев помещается в одном ряду, сначала вы
числим доступное горизонтальное пространство Ширина экрана хранится в ai_
settings.screen_width , но с обеих сторон экрана необходимо зарезервирова ть
пустые интервалы Определим их равными ширине одного пришельца Так как
ширина уменьшается на величину двух интервалов, доступное пространство равно ширине экрана за вычетом удвоенной ширины пришельца
available_space_x = ai_settings.screen_width — (2 * alien_width)
Также необходимо зарезервировать интервалы между пришельцами они будут со
ставлять одну ширину пришельца Пространство, необходимое для вывода одного
пришельца, равно его удвоенной ширине одна ширина для самого пришельца и еще
одна для пустого интервала справа Чтобы определить количество пришельцев на экране, разделим доступное пространство на удвоенную ширину пришельца
number_aliens_x = available_space_x / (2 * alien_width)
Эти вычисления будут включены в программу при создании флота ПРИМЕЧАНИЕ
У вычислений в программировании есть одна замечательная особенность: не обязательно быть
полностью уверенным в правильности формулы, когда вы ее пишете� Вы можете опробовать
формулу на практике и посмотреть, что из этого получится� В худшем случае получится экран,
до отказа забитый пришельцами, — или наоборот, пустой� В этом случае вы пересмотрите формулу на основании полученных результатов�

Построение флота 261
Создание ряда
Чтобы создать один ряд пришельцев, сначала создадим в alien_invasion�py пустую
группу с именем aliens для хранения всех пришельцев, а затем вызовем функцию
в game_functions�py для создания флота
alien_invasion.py
import pygame from pygame.sprite import Groupfrom settings import Settingsfrom ship import Shipimport game_functions as gf def run_game(): ...
# Создание корабля, группы пуль и группы пришельцев.ship = Ship(ai_settings, screen) bullets = Group()
 aliens = Group()
# Создание флота пришельцев.
 gf.create_fleet(ai_settings, screen, aliens)
# Запуск основного цикла игры. while True:...
 gf.update_screen(ai_settings, screen, ship, aliens,
bullets)
run_game()
Так как пришельцы уже не создаются напрямую в alien_invasion�py, импортировать
класс Alien в этот файл не обязательно
Создайте пустую группу для хранения всех пришельцев в игре  Затем создайте
новую функцию create_fleet()  , которую мы вскоре вызовем, и передайте ей
ai_settings , объект screen и пустую группу aliens Затем измените вызов update_
screen() , чтобы предоставить функции доступ к группе пришельцев 
Также необходимо внести изменения в update_screen()
game_functions.py def update_screen(ai_settings, screen, ship, aliens, bullets):
... ship.blitme()
aliens.draw(screen)
# Отображение последнего прорисованного экрана. pygame.display.flip()
Когда вы вызываете метод draw() для группы, ae автоматически выводит
каждый элемент группы в позиции, определяемой его атрибутом rect В дан
ном случае вызов aliens.draw(screen) рисует каждого пришельца в группе
на экране

262 Глава 13 • Осторожно, пришельцы!
Создание флота
Теперь можно перейти к созданию флота Ниже приведена новая функция create_
fleet(), которую мы поместим в конец game_functions�py Также необходимо
импортировать класс Alien, не забудьте добавить команду import в начало файла
game_functions.py
... from bullet import Bullet
from alien import Alien...
def create_fleet(ai_settings, screen, aliens): """Создает флот пришельцев.""" # Создание пришельца и вычисление количества пришельцев в ряду. # Интервал между соседними пришельцами равен одной ширине пришельца.
 alien = Alien(ai_settings, screen)
 alien_width = alien.rect.width
 available_space_x = ai_settings.screen_width - 2 * alien_width
 number_aliens_x = int(available_space_x / (2 * alien_width))
# Создание первого ряда пришельцев.
 for alien_number in range(number_aliens_x):
# Создание пришельца и размещение его в ряду.
 alien = Alien(ai_settings, screen)
alien.x = alien_width + 2 * alien_width * alien_number alien.rect.x = alien.x aliens.add(alien)
Б ульшая часть этого кода уже была описана ранее Для размещения пришельцев
необходимо знать ширину и высоту одного пришельца, и мы создаем его в точке 
перед выполнением вычислений Этот пришелец не войд ет во флот, поэтому он не
включается в группу aliens В точке  ширина пришельца определяется по его
атрибуту rect, а полученное значение сохраняется в alien_width, чтобы избежать
лишних обращений к атрибуту rect В точке  вычисляется горизонтальное про
странство и количество пришельцев, которые в нем поместятся
По сравнению с исходными формулами всего одно изменение мы используем
int() , чтобы вычисленное количество пришельцев  было целым, — вопервых,
неясно, что делать с неполным пришельцем, а вовторых, функция range() должна
получать целое число Функция int() отсекает дробную часть числа, фактически
выполняя округление в меньшую сторону (И это правильно лучше оставить лиш
нее свободное место в каждом ряду, чем забивать ряды до отказа)
Затем создается цикл от до количества создаваемых пришельцев  В теле цикла
создается новый пришелец, после чего задается его координата для размещения
его в ряду  Каждый пришелец сдвигается вправо на одну ширину от левого поля
Затем ширина пришельца умножается на , чтобы учесть полное пространство,