Программирование на Python 3. Подробное руководство [2009] Саммерфилд

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



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

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

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

МАРК САММЕРФИЛД
ПРОГРАММИРОВАНИЕ НА
ПОДРОБНОЕ
РУКОВОДСТВО
Издательство «СимволПилюс»
(812) 3245353, (495) 9458100
www.symbol.ru
Óðîâåíü ïîäãîòîâêè ÷èòàòåëåé: ñðåäíèé
Êàòåãîðèÿ:
ïðîãðàììèðîâàíèå / Pytho
n
Марк Саммерфилд
дипломированный специалсист
в области информатики, обсладаю
щий многолетним опытосм работы
в индустрии производства ПО, но
в первую очередь – практсикую
щий программист. Почтис три года
он работал менеджером остдела до
кументирования в компании Troll
tech, руководил техническисм жур
налом «Qt Quarterly» этой компас
нии . (В настоящее время Trolltech
является подразделенисем Qt Soft
ware в компании Nokia.)
Марк является автором кнсиги
«Rapid GUI Programming with Py
thon and Qt: The Definitive Guide
to PyQt Programming» (Addison
Wesley, 2008), а также соавтором
«C++ GUI Programming with Qt 4»
(AddisonWesley, 2006). Марк владеет собственной ксон
салтинговой и тренинговосй компа
нией Qtrac Ltd., где работает в ка
честве независимого автосра, ре
дактора, преподавателяс и консуль
танта, специализируясь нса C++, Qt,
Python и PyQt.
Третья версия языка Python сдселала его еще более мощ
ным, удобным, логичным и вырасзительным. Книга «Про
граммирование на Python 3» написсана одним из ведущих
специалистов по этому ясзыку, обладающим многослетним
опытом работы с ним. Издсание содержит все необхосди
мое для практическогос освоения языка: написанияс любых
программ с использованисем как стандартной библисотеки,
так и сторонних библиотсек для языка Python 3, а такжес
создания собственных библсиотечных модулей.
Автор начинает с описания кслючевых элементов Py
thon, знание которых необходсимо в качестве базовых пос
нятий. Затем обсуждаютсяс более сложные темы, посданные
так, чтобы читатель мосг постепенно наращивать ссвой опыт.
В книге рассматриваются:
• Разработка ПО на языке Pythoсn с использованием про
цедурной, объектноорисентированной и функционасль
ной парадигм
• Создание собственных пакестов и модулей
• Запись и чтение двоичных си текстовых файлов, а такжсе
файлов в формате XML, включсая возможность дополнис
тельного сжатия, произсвольного доступа и парсиснга
• Использование возможносстей сложных типов данных,
коллекций, управляющисх структур и функций
• Распределение вычислитсельной нагрузки между нсесколь
кими процессами и потосками выполнения
• Создание приложений дляс работы с базами данных SQL
и с файлами DBM, в которых инфсормация хранится
в виде пар ключзначениес
• Использование миниязыска и модуля регулярныхс вы
ражений в языке Python
• Создание удобных, эффективных прилсожений с гра
фическим интерфейсом
• Передовые приемы просграммирования, включая гсене
раторы, декораторы фунскций и классов, менеджерсы
контекста, дескрипторыс, абстрактные базовые клсассы,
метаклассы и многое друсгое
Книга может служить какс учебником, так и справочсни
ком. Текст сопровождается мносгочисленными примерамис,
доступными на специальнсом сайте издания. Весь кодс при
меров был протестировсан с окончательным релизсом Py
thon 3 в ОС Windows, Linux и Mac OS X.
,6%1
  
Python 3

Mark Summerfield
Python 3
Programming in
A Complete Introduction
to the Python Language

Марк Саммерфилд
на Python 3
СанктПетербург – Москва
2009
Подробное руководство
Программирование

Серия «High tech»
Марк Саммерфилд
Программирование на Python 3
Подробное руководство
Перевод А. Киселева
Главный редакторА. Галунов
Зав. редакциейН. Макарова
Выпускающий редакторП. Щеголев
РедакторЮ. Бочина
КорректорС. Николаева
ВерсткаД. Орлова
Саммерфилд М.
Программирование на Python 3. Подробное руководство. – Пер. с англ. – СПб.:
СимволПлюс, 2009. – 608 с., ил.
ISBN: 9785932861615
Третья версия языка Python сделала его еще более мощным, удобным, логич
ным и выразительным. Книга «Программирование на Python 3» написана од
ним из ведущих специалистов по этому языку, обладающим многолетним
опытом работы с ним. Издание содержит все необходимое для практического
освоения языка: написания любых программ с использованием как стандарт
ной библиотеки, так и сторонних библиотек для языка Python 3, а также со
здания собственных библиотечных модулей.
Автор начинает с описания ключевых элементов Python, знание которых необ
ходимо в качестве базовых понятий. Затем обсуждаются более сложные темы,
поданные так, чтобы читатель мог постепенно наращивать свой опыт: распре
деление вычислительной нагрузки между несколькими процессами и потока
ми, использование сложных типов данных, управляющих структур и функ
ций, создание приложений для работы с базами данных SQL и с файлами DBM.
Книга может служить как учебником, так и справочником. Текст сопровожда
ется многочисленными примерами, доступными на специальном сайте изда
ния. Весь код примеров был протестирован с окончательным релизом Python 3
в ОС Windows, Linux и Mac OS X.
ISBN: 9785932861615
ISBN: 9780137129294 (англ)
© Издательство СимволПлюс, 2009
Authorized translation of the English edition © 2009 Pearson Education, Inc. This
translation is published and sold by permission of Pearson Education, Inc., the owner
of all rights to publish and sell the same.
Все права на данное издание защищены Законодательством РФ, включая право на полное или час
тичное воспроизведение в любой форме. Все товарные знаки или зарегистрированные товарные зна
ки, упоминаемые в настоящем издании, являются собственностью соответствующих фирм.
Издательство«СимволПлюс».199034,СанктПетербург,16линия,7,
тел. (812) 3245353, www.symbol.ru. Лицензия ЛП N 000054 от 25.12.98.
Налоговая льгота – общероссийский классификатор продукции
ОК 00593, том 2; 953000 – книги и брошюры.
Подписано в печать 29.04.2009. Формат 70х100
1/1 6 . Печать офсетная.
Объем 38 печ. л. Тираж 1500 экз. Заказ №
Отпечатано с готовых диапозитивов в ГУП «Типография «Наука»
199034, СанктПетербург, 9 линия, 12.

Памяти Франко Рабайотти
(Franco Rabaiotti)
1961–2001

Оглавление
Введение. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1. Быстрое введение в процедурное программирование . . . . . . . . . 21
Создание и запуск программ на языке Python
. . . . . . . . . . . . . . . . . . . . . 22
«Золотой запас» Python
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Составляющая №1: Типы данных
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Составляющая №2: ссылки на объекты
. . . . . . . . . . . . . . . . . . . . . . . . 29
Составляющая №3: коллекции данных
. . . . . . . . . . . . . . . . . . . . . . . . 32
Составляющая №4: логические операции
. . . . . . . . . . . . . . . . . . . . . . 36
Составляющая №5: инструкции управления
потоком выполнения
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Составляющая №6: арифметические операторы
. . . . . . . . . . . . . . . . . 45
Составляющая №7: ввод/вывод
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Составляющая №8: создание и вызов функций
. . . . . . . . . . . . . . . . . 52
Примеры
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
bigdigits.py
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
generate_grid.py
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
В заключение
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Упражнения
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
2. Типы данных . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Идентификаторы и ключевые слова
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Целочисленные типы
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
Целые числа
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
Логические значения
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Тип чисел с плавающей точкой
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
Числа с плавающей точкой
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
Комплексные числа
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
Числа типа Decimal
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
Строки
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Сравнение строк
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Получение срезов строк
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Операторы и методы строк
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

8 Оглавление
Форматирование строк с помощью метода str.format() . . . . . . . . . 100
Кодировки символов
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
Примеры
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
quadratic.py
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
csv2html.py
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
В заключение
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
Упражнения
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
3. Типы коллекций . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
Последовательности
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
Кортежи
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
Именованные кортежи
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
Списки
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
Множества
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
Тип set
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
Тип frozenset
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
Отображения
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
Словари
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
Словари со значениями по умолчанию
. . . . . . . . . . . . . . . . . . . . . . . 161
Обход в цикле и копирование коллекций
. . . . . . . . . . . . . . . . . . . . . . . . 163
Итераторы, функции и операторы для работы
с итерируемыми объектами
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
Копирование коллекций
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
Примеры
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
generate_usernames.py
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
statistics.py
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
В заключение
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
Упражнения
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
4. Управляющие структуры и функции . . . . . . . . . . . . . . . . . . . . . . . . . 188
Управляющие структуры
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
Условное ветвление
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
Циклы
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
Обработка исключений
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
Перехват и возбуждение исключений
. . . . . . . . . . . . . . . . . . . . . . . . . 193
Собственные исключения
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
Собственные функции
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
Имена и строки документирования
. . . . . . . . . . . . . . . . . . . . . . . . . . . 207
Распаковывание аргументов и параметров
. . . . . . . . . . . . . . . . . . . . 210
Доступ к переменным в глобальной области видимости
. . . . . . . . . 213
Лямбдафункции
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
Утверждения
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
Пример: make_html_skeleton.py
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218

Оглавление 9
В заключение . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
Упражнения
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
5. Модули . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
Модули и пакеты
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
Пакеты
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
Собственные модули
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
Обзор стандартной библиотеки языка Python
. . . . . . . . . . . . . . . . . . . . 248
Обработка строк
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
Работа с аргументами командной строки
. . . . . . . . . . . . . . . . . . . . . . 250
Математические вычисления и числа
. . . . . . . . . . . . . . . . . . . . . . . . . 252
Время и дата
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
Алгоритмы и типы коллекций
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
Форматы файлов, кодировки и сохранение данных
. . . . . . . . . . . . . 256
Работа с файлами, каталогами и процессами
. . . . . . . . . . . . . . . . . . 260
Работа с сетями и Интернетом
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
XML
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
Прочие модули
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
В заключение
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
Упражнение
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
6. Объектноориентированное программирование. . . . . . . . . . . . . 273
Объектноориентированный подход
. . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
Объектноориентированные концепции и терминология
. . . . . . . . 275
Собственные классы
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
Атрибуты и методы
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
Наследование и полиморфизм
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
Использование свойств для управления
доступом к атрибутам
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
Создание полных и полностью интегрированных
типов данных
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
Собственные классы коллекций
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
Создание классов, включающих коллекции
. . . . . . . . . . . . . . . . . . . 306
Создание классов коллекций посредством агрегирования
. . . . . . . 314
Создание классов коллекций посредством наследования
. . . . . . . . 321
В заключение
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
Упражнения
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
7. Работа с файлами . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
Запись и чтение двоичных данных
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
Консервирование с возможным сжатием
. . . . . . . . . . . . . . . . . . . . . 341
Неформатированные двоичные данные
свозможнымсжатием
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348

10 Оглавление
Запись и синтаксический анализ текстовых файлов . . . . . . . . . . . . . . 356
Запись текста
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
Синтаксический анализ текста
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
Синтаксический анализ текста с помощью
регулярных выражений
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
Запись и синтаксический анализ файлов XML
. . . . . . . . . . . . . . . . . . . 364
Деревья элементов
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
DOM (Document Object Model – объектная
модель документа)
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
Запись файла XML вручную
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
Синтаксический анализ файлов XML с помощью SAX
(Simple API for XML – упрощенный API для XML)
. . . . . . . . . . . . . 373
Произвольный доступ к двоичным данным в файлах
. . . . . . . . . . . . . . 376
Универсальный класс BinaryRecordFile
. . . . . . . . . . . . . . . . . . . . . . . 377
Пример: классы в модуле BikeStock
. . . . . . . . . . . . . . . . . . . . . . . . . . 386
В заключение
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
Упражнения
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
8. Усовершенствованные приемы программирования . . . . . . . . . . 394
Улучшенные приемы процедурного программирования
. . . . . . . . . . . 395
Ветвление с использованием словарей
. . . . . . . . . . . . . . . . . . . . . . . . 395
Выражениягенераторы и функциигенераторы
. . . . . . . . . . . . . . . 397
Динамическое выполнение программного кода
и динамическое импортирование
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400
Локальные и рекурсивные функции
. . . . . . . . . . . . . . . . . . . . . . . . . 409
Декораторы функций и методов
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414
Аннотации функций
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
Улучшенные приемы объектноориентированного
программирования
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421
Управление доступом к атрибутам
. . . . . . . . . . . . . . . . . . . . . . . . . . . 422
Функторы
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426
Менеджеры контекста
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 428
Дескрипторы
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432
Декораторы классов
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438
Абстрактные базовые классы
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 441
Множественное наследование
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
Метаклассы
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452
Функциональное программирование
. . . . . . . . . . . . . . . . . . . . . . . . . . . 457
Частично подготовленные функции
. . . . . . . . . . . . . . . . . . . . . . . . . . 460
Пример: Valid.py
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
В заключение
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464
Упражнения
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465

Оглавление 11
9. Процессы и потоки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
Делегирование работы процессам
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468
Делегирование работы потокам выполнения
. . . . . . . . . . . . . . . . . . . . . 473
Пример: многопоточная программа поиска слова
. . . . . . . . . . . . . . 475
Пример: многопоточная программа поиска
дубликатов файлов
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479
В заключение
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 484
Упражнения
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486
10. Сети. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 488
Клиент TCP
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490
Сервер TCP
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496
В заключение
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504
Упражнения
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505
11. Программирование приложений баз данных . . . . . . . . . . . . . . . . 508
Базы данных DBM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509
Базы данных SQL
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 513
В заключение
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 521
Упражнение
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 522
12. Регулярные выражения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 524
Язык регулярных выражений в Python
. . . . . . . . . . . . . . . . . . . . . . . . . 525
Символы и классы символов
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525
Квантификаторы
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 527
Группировка и сохранение
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 530
Проверки и флаги
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 533
Модуль для работы с регулярными выражениями
. . . . . . . . . . . . . . . . 538
В заключение
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 549
Упражнения
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550
13. Введение в программирование
графического интерфейса. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
552
Программы в виде диалога
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556
Программы с главным окном
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 563
Создание главного окна
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 564
Создание собственного диалога
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 576
В заключение
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579
Упражнения
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579
Эпилог . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 582
Алфавитный указатель. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 584

Введение
Язык Python является, пожалуй, самым простым в изучении и самым
приятным в использовании из языков программирования, получив
ших широкое распространение. Программный код на языке Python
легко читать и писать, и, будучи лаконичным, он не выглядит зага
дочным. Python – очень выразительный язык, позволяющий уместить
приложение в меньшее количество строк, чем на это потребовалось бы
в других языках, таких как C++ или Java.
Python является кроссплатформенным языком: обычно одна и та же
программа на языке Python может запускаться и в Windows, и в UNIX
подобных системах, таких как Linux, BSD и Mac OS, для чего достаточ
но просто скопировать файл или файлы, составляющие программу, на
нужный компьютер; при этом даже не потребуется выполнять «сбор
ку», или компилирование программы. Конечно, можно написать на
языке Python программу, которая будет использовать некоторые ха
рактерные особенности конкретной операционной системы, но такая
необходимость возникает крайне редко, т. к. практически вся стан
дартная библиотека языка Python и большинство библиотек сторон
них производителей обеспечивают полную кроссплатформенность.
Одним из основных преимуществ языка Python является наличие пол
ной стандартной библиотеки, позволяющей обеспечить загрузку фай
ла из Интернета, распаковку архива или создание вебсервера посред
ством написания нескольких строк программного кода. В дополнение
к ней существуют тысячи дополнительных библиотек сторонних про
изводителей, среди которых одни обеспечивают более сложные и более
мощные возможности, чем стандартная – например, библиотека для
организации сетевых взаимодействий Twisted и библиотека для реше
ния вычислительных задач NumPy; а другие предоставляют функцио
нальность, которая слишком узконаправленно специализирована,
чтобы ее можно было включить в стандартную библиотеку – напри
мер, пакет моделирования SimPy. Большинство сторонних библиотек
можно найти на сайте Python Package Index (каталог пакетов Python):
pypi.python.org/pypi.
Python может использоваться для программирования в процедурном,
в объектноориентированном и, в меньшей степени, в функциональ
ном стиле программирования, хотя в глубине души Python – объект
ноориентированный язык программирования. Эта книга покажет,

14 Введение
как писать процедурные и объектноориентированные программы,
а также расскажет об особенностях функционального программирова
ния на языке Python.
Цель этой книги – показать вам, как писать программы на языке Py
thon в стиле Python 3, а после прочтения – служить хорошим справоч
ником по этому языку. Несмотря на то, что версия Python 3 является
эволюционным, а не революционным усовершенствованием Python 2,
тем не менее в Python 3 некоторые прежние приемы программирова
ния стали неприменимы или в них отпала необходимость. При этом
появились некоторые новые приемы, позволяющие использовать пре
имущества особенностей новой версии. Язык Python 3 более соверше
нен, чем Python 2 – он основан на опыте многих лет использования
этого языка и привносит множество новых особенностей (и ликвиди
рует недостатки Python 2), делая его еще более приятным в использо
вании, более удобным, более простым и более последовательным.
Цель книги – научить языку Python, при этом в книге используются
многие стандартные библиотеки, но далеко не все. Впрочем, это не
проблема, так как после прочтения этой книги вы будете обладать объ
емом знаний, достаточным, чтобы суметь воспользоваться любыми
стандартными или сторонними библиотеками языка Python и даже
создавать свои собственные библиотечные модули.
Как предполагается, книга будет полезна различным группам читате
лей, включая тех, для кого программирование – это хобби, а также
студентов, научных работников, инженеров и всех тех, для кого про
граммирование является подручным средством в их работе, и, конеч
но же, – профессиональных программистов. Чтобы быть полезной та
кому широкому кругу читателей – не быть скучной для хорошо подго
товленных специалистов и одновременно оставаться доступной для
менее подготовленных читателей, книга предполагает наличие у чита
теля некоторого опыта программирования (на любом языке). В частно
сти, предполагается наличие основных представлений о типах данных
(таких как числа и строки), коллекциях данных (таких как множест
ва и списки), управляющих структурах (таких как инструкции if
иwhile) и функциях. Кроме того, некоторые примеры предполагают
знание основ языка разметки HTML, а некоторые, более специализи
рованные главы предполагают наличие базовых знаний по обсуждае
мым темам, например, глава о базах данных предполагает знакомство
с языком SQL.
Книга структурирована таким образом, чтобы вы могли быстро дви
гаться вперед. К концу первой главы вы уже сможете писать неболь
шие, но полезные программы на языке Python. Каждая последующая
глава вводит новые темы и часто расширяет и углубляет темы, введен
ные в предыдущих главах. Это означает, что если вы читаете главы по
следовательно, вы сможете остановиться в любой момент и написать
законченную программу на основе знаний, полученных к этому мо

Введение 15
менту, после чего продолжить чтение и узнать о существовании более
совершенных и более сложных приемов. По этой причине знакомство
с некоторыми темами происходит в одной главе, а более глубокое их
исследование – в последующих главах.
При изучении нового языка программирования обычно возникают две
основные проблемы. Первая заключается в том, что иногда для объяс
нения одной концепции необходимо знать другую концепцию, кото
рая, в свою очередь, прямо или косвенно опирается на первую. Вторая
проблема состоит в том, что некоторые читатели изначально могут ни
чего не знать о данном языке программирования, и изза этого бывает
очень трудно подыскать интересные или полезные примеры. В этой
книге предпринята попытка решить обе проблемы, вопервых, предпо
ложив наличие у читателя некоторого опыта программирования, и, во
вторых, представив в главе 1 «золотой запас» языка Python – восемь
основных составляющих языка, знания которых вполне достаточно,
чтобы начать самостоятельно писать программы. Вследствие принято
го подхода некоторые примеры в первых главах отличаются опреде
ленной долей искусственности, так как в них используется только то,
о чем до этого уже говорилось в книге. От главы к главе этот эффект
уменьшается и полностью исчезает к главе 7; все последующие приме
ры полностью написаны в характерном для языка Python 3 стиле.
Книга опирается на практический подход в обучении и предлагает вам
самостоятельно опробовать примеры и упражнения, чтобы приобрести
практический опыт. Везде, где только возможно, в качестве примеров
приводятся небольшие законченные программы, способные решать
вполне реальные задачи. Все примеры и решения упражнений можно
найти в Интернете по адресу www.qtrac.eu/py3book.html – каждый из
них протестирован с использованием Python 3 в операционных систе
мах Windows, Linux и Mac OS X.
Структура книги
В главе 1 будут представлены 8 составляющих языка Python, знания
которых будет вполне достаточно для написания законченных про
грамм. Здесь также описываются некоторые среды программирования
на языке Python и приводятся два примера маленьких программ, в ка
ждой из которых используются восемь составляющих языка Python,
описанные ранее в этой главе.
В главах со 2 по 5 вводятся средства процедурного программирования
на языке Python, включая базовые типы данных и коллекции данных,
а также множество полезных встроенных функций и структур управ
ления наряду с описанием простейших приемов обработки текстовых
файлов. В главе 5 рассказывается, как создавать собственные модули
и пакеты, и проводится общий обзор стандартной библиотеки Python,
чтобы вы получили представление о том, что может предложить Python

16 Введение
«из коробки», и могли избежать необходимости заново изобретать ко
лесо.
Глава 6 представляет собой полное введение в объектноориентирован
ное программирование на языке Python. Все сведения о процедурном
программировании, которые были получены в предыдущих главах,
попрежнему применимы, потому что объектноориентированное про
граммирование имеет процедурную основу: например, в объектно
ориентированном программировании используются те же самые типы
данных, коллекции данных и управляющие структуры.
Глава 7 охватывает темы записи в файлы и чтения из файлов. Для дво
ичных файлов среди всего прочего рассматривается применение опе
раций сжатия и произвольного доступа к содержимому файлов, а для
текстовых файлов – синтаксический анализ вручную и с применением
регулярных выражений. Кроме того, в этой главе будет показано, как
писать и читать файлы в формате XML, включая использование эле
ментов деревьев, DOM (Document Object Model – объектная модель до
кумента) и SAX (Simple API for XML – простой прикладной программ
ный интерфейс для работы с XML).
В главе 8 повторно рассматривается материал, представленный в неко
торых предыдущих главах, и исследуются многие современные осо
бенности языка Python в таких областях, как типы данных, коллек
ции данных, управляющие структуры, функции и объектноориенти
рованное программирование. В этой главе также будет представлено
множество новых функций, классов и усовершенствованных приемов,
включая функциональное программирование – материал, который бу
дет и полезен, и необходим.
В остальной части книги рассматриваются более сложные темы. В гла
ве 9 демонстрируются приемы распределения рабочей нагрузки меж
ду несколькими процессами и потоками выполнения. Глава 10 расска
зывает, как создавать клиентсерверные приложения, используя стан
дартную поддержку сетевых взаимодействий языка Python. Глава 11
охватывает вопросы программирования приложений для работы с ба
зами данных (как с простыми файлами «DBM», хранящими пары
ключзначение, так и с базами данных SQL). Глава 12 описывает и де
монстрирует применение миниязыка регулярных выражений в Py
thon и охватывает модуль регулярных выражений, а в главе 13 рас
сматриваются вопросы программирования GUI (Graphical User Inter
face – графический интерфейс пользователя).
Большинство глав в книге достаточно объемны, объединяя взаимосвя
занные сведения в одном месте для упрощения их поиска в будущем.
Кроме того, главы разбиты на разделы, подразделы, а иногда и под
подразделы, что позволяет читать их в том темпе, который устроит вас
больше всего, – например, по одному разделу или подразделу за раз.

Введение 17
Получение и установка Python 3
Если в вашем распоряжении имеется современная операционная сис
тема Mac или другая UNIXподобная система, то вполне возможно,
что Python 3 у вас уже установлен. Проверить это можно, введя коман
ду python
–V (обратите внимание, что символ V вводится в верхнем реги
стре) в консоли (Terminal.app – в Mac OS X). Если будет выведен номер
версии 3, значит Python 3 у вас уже имеется и выполнять его установ
ку не требуется, в противном случае вам следует продолжить чтение
этого раздела.
Для операционных систем Windows и Mac OS X существуют простые
в использовании установочные пакеты с графическим интерфейсом,
которые проведут вас через все этапы процесса установки. Эти пакеты
доступны по адресу www.python.org/download. Для Windows по ука
занному адресу имеется три различных инсталлятора; загружайте
тот, который подписан «Windows installer», если заранее вам точно не
известно, что ваша машина оснащена процессором AMD64 или Itani
um; в противном случае загружайте версию для своего процессора.
Как только установочный пакет будет загружен, просто запустите его
и следуйте инструкциям, появляющимся на экране.
Для Linux, BSD и других версий UNIX самый простой способ устано
вить Python состоит в том, чтобы воспользоваться имеющейся систе
мой управления пакетами. В большинстве случаев Python поставляет
ся в виде отдельных пакетов. Например, для Fedora Python поставляет
ся в виде пакета python, а IDLE (простая среда разработки) – в виде па
кета pythontools; при этом будьте внимательны: эти пакеты содержат
Python 3, только если ваша версия Fedora достаточно свежая (вер
сии 10 или выше). Аналогично в дистрибутивах на основе Debian, таких
как Ubuntu, эти пакеты называются python3 и idle3 соответственно.
Если для вашей операционной системы пакеты с Python 3 отсутству
ют, то вам потребуется загрузить исходные тексты по адресу www.py
thon.org/download и собрать Python 3 из исходных текстов. Загрузите
тарболл с исходными текстами и распакуйте его либо командой tar
xvfz Python3.0.tgz, если тарболл сжат архиватором gzip, либо коман
дой tar xvfj Python3.0.tar.bz2, если тарболл сжат архиватором bzip2.
Конфигурирование и сборка выполняются стандартным способом.
Сначала перейдите во вновь созданный каталог Python3.0 с распако
ванными исходными текстами и запустите команду ./configure. (Если
вы хотите выполнить установку в свой локальный каталог, можно оп
ределить значение параметра
––prefix.) Затем запустите команду make.
Вполне возможно, что в конце сборки вы получите сообщение о том,
что не все модули удалось собрать. Обычно это означает, что у вас от
сутствуют необходимые для этого библиотеки или заголовочные фай
лы. Например, если не удалось собрать модуль readline, воспользуй
тесь системой управления пакетами и установите соответствующую

18 Введение
библиотеку для разработки –readlinedevel в системах на базе Fedora
или readlinedev в системах на базе Debian. (К сожалению, названия
пакетов не всегда настолько очевидны.) После установки недостаю
щих пакетов запустите команды ./configure и make еще раз.
После успешной сборки можно попробовать запустить команду make
test, чтобы убедиться, что все в порядке, хотя это не обязательно и к то
му же может занять продолжительное время.
Если вы использовали параметр
––prefix, то, чтобы выполнить локаль
ную установку, просто запустите команду make install. Вероятно, бу
дет желательно добавить символическую ссылку на выполняемый
файл python (например, ln
–s
~/local/python3/bin/python3.0
~/bin/python3,
если предполагать, что использовался параметр
––prefix=$HOME/local/
python3 и каталог $HOME/bin присутствует в переменной окружения
PATH). Возможно, также будет удобно добавить символическую ссылку
на IDLE (например, ln
–s
~/local/python3/bin/idle
~/bin/idle3, если ис
ходить из тех же предположений, что и выше).
Если вы не использовали параметр
––prefix и обладаете правами поль
зователя root, зарегистрируйтесь в системе как root и выполните ко
манду make install. В системах с настроенной программой sudo, таких
как Ubuntu, выполните команду sudo make install. Если в системе был
установлен Python 2, файл /usr/bin/python не изменится и Python 3 бу
дет доступен как python3, точно также среда разработки IDLE для Py
thon 3 будет доступна как idle3.
Благодарности
Мои первые слова благодарности я хотел бы принести техническим ре
цензентам книги, начиная с Жасмин Бланшетт (Jasmin Blanchette),
программиста, в соавторстве с которой я участвовал в создании двух
книг о C++/Qt. Ее рекомендации о том, как расположить содержимое
книги по главам, критические замечания по всем примерам и тща
тельное чтение текста в значительной степени способствовали улучше
нию книги.
Джордж Брэндл (Georg Brandl) – ведущий разработчик Python и ответ
ственный за создание документации к новому комплекту инструмен
тов Python. Джордж обнаружил множество мелких ошибок и упорно,
очень терпеливо объяснял их, пока все они не были поняты и устране
ны. Он также внес множество усовершенствований в примеры.
Фил Томпсон (Phil Thompson) – эксперт по языку Python и создатель
PyQt, одной из лучших библиотек создания графического интерфейса
для языка Python. Проницательность и отзывы Фила позволили про
яснить и исправить множество неточностей.
Трентон Шульц (Trenton Schulz) – старший инженерпрограммист
подразделения Qt Software в компании Nokia (которое ранее было са

Введение 19
мостоятельной компанией Trolltech), бывший ценным рецензентом
всех моих предыдущих книг, вновь пришел мне на помощь. Внима
тельное отношение Трентона и множество внесенных им предложений
помогли выявить ряд неувязок и способствовали значительному улуч
шению этой книги.
В дополнение к вышеупомянутым рецензентам, каждый из которых
прочитал книгу целиком, я хотел бы поблагодарить Дэвида Бодди (Da
vid Boddie), старшего технического писателя подразделения Qt Soft
ware в компании Nokia, опытного практика языка Python, который
прочитал некоторые части книги и дал ценные рекомендации по ним.
Спасибо также Гвидо ван Россуму (Guido van Rossum), создателю язы
ка Python, а также обширному сообществу пользователей Python, при
ложившим невероятные усилия, чтобы сделать язык Python и в осо
бенности, его библиотеки такими полезными и удобными в использо
вании.
Как всегда, спасибо Джеффу Кингстону (Jeff Kingston), создателю
языка верстки Lout, который я использую уже более десяти лет.
Особое спасибо моему редактору Дебре Вильямс Коли (Debra Williams
Cauley) за ее поддержку и за то, что максимально обеспечивала плав
ность протекания всего процесса работы над книгой. Спасибо также
Анне Попик (Anna Popick), которая так здорово справлялась с управ
лением производственным процессом, и корректору Одри Дойл (Aud
rey Doyle), прекрасно выполнившему свою работу.
И напоследок, но не в последнюю очередь, я хочу поблагодарить мою
супругу Андреа (Andrea) за то, что терпела мои пробуждения в 4 часа
утра, когда часто появлялись новые идеи и вспоминались неточности
в программном коде, требующие немедленной проверки и исправле
ния, а также за ее любовь, верность и поддержку.

1
Быстрое введение
в процедурное программирование
В этой главе приводится объем информации, достаточный, чтобы на
чать писать программы на языке Python. Мы настоятельно рекоменду
ем установить Python, если вы еще не сделали этого, чтобы иметь воз
можность получить практический опыт для закрепления изучаемого
материала. (Во введении описывается, как получить и установить Py
thon во всех основных платформах, подробности на стр. 17.)
Первый раздел этой главы показывает, как создавать и запускать про
граммы на языке Python. Для написания программного кода вы може
те использовать любой простой текстовый редактор по своему усмотре
нию, при этом среда разработки IDLE, обсуждаемая в этом разделе,
предоставляет не только редактор программного кода, но и дополни
тельные функциональные возможности, включая средства отладки
и проведения экспериментов с программным кодом Python.
Во втором разделе будут представлены восемь составляющих языка
Python, знания которых вполне достаточно для создания программ,
имеющих практическую ценность. Все эти составляющие будут рас
смотрены глубже в последующих главах и по мере продвижения впе
ред будут дополняться остальными особенностями языка Python, что
бы к концу книги весь язык оказался полностью охваченным, а вы
были в состоянии использовать в своих программах все, что он пред
лагает.
В заключительной части главы представлены две короткие програм
мы, в которых используется подмножество особенностей языка Python,
введенных во втором разделе, благодаря чему вы сможете получить
представление о программировании на языке Python.
•Создание и запуск программ
на языке Python
•«Золотой запас» Python

22 Глава 1. Быстрое введение в процедурное программирование
Создание и запуск программ на языке Python
Программный код на языке Python можно записать с по
мощью любого простого текстового редактора, который
способен загружать и сохранять текст либо в кодировке
ASCII, либо UTF8. По умолчанию предполагается, что
файлы с программным кодом на языке Python сохраня
ются в кодировке UTF8, надмножестве кодировки
ASCII, с помощью которой можно представить практи
чески любой символ любого национального алфавита.
Файлы с программным кодом на языке Python обычно
имеют расширение .py, хотя в некоторых UNIXподоб
ных системах (таких как Linux и Mac OS X) некоторые
приложения на языке Python не имеют расширения,
а программы на языке Python с графическим интерфей
сом, в частности в Windows и Mac OS X, обычно имеют
расширение .pyw. В этой книге все время будет использо
ваться расширение .py для обозначения консольных про
грамм и модулей Python и расширение .pyw – для про
грамм с графическим интерфейсом. Все примеры, пред
ставленные в книге, не требуют изменений для запуска
в любой из платформ, поддерживаемых Python 3.
Ради того чтобы удостовериться, что установка выполнена корректно,
и чтобы продемонстрировать классический первый пример, создадим
файл с именем hello.py в простом текстовом редакторе (в Windows для
этих целей вполне подойдет «Блокнот» (Notepad), а кроме того вскоре
будет рассказано о более удобном редакторе) со следующим содер
жимым:
#!/usr/bin/env python3
print("Hello", "World!")
Первая строка – это комментарий. В языке Python комментарии начи
наются с символа # и продолжаются до конца строки. (Через минуту
мы объясним назначение этого странного комментария.) Вторая стро
ка – пустая. Python игнорирует пустые строки, но их бывает резонно
вставлять, разбивая протяженные блоки программного кода на более
мелкие части, более удобные для восприятия. Третья строка – это про
граммный код на языке Python. В данном случае вызывается функция
print(), которой передается два аргумента, оба – типа str (string, или
строка, то есть последовательность символов).
Все инструкции, которые встретятся в файле с расширением .py, вы
полняются последовательно, строка за строкой, начиная с первой
строки. В этом заключается отличие от других языков программиро
вания, таких как C++ или Java, в которых имеются функции или ме
тоды со специальными именами, которые запускаются в первую оче
Кодировки
символов,
стр. 112

Создание и запуск программ на языке Python 23
редь. Порядок выполнения инструкций может быть изменен, в чем
можно будет убедиться при обсуждении управляющих структур язы
ка Python в следующем разделе.
Будем считать, что пользователи Windows сохраняют файлы с про
граммным кодом Python в каталоге C:\py3eg, а пользователи UNIX (то
есть UNIX, Linux и Mac OS X) – в каталоге $HOME/py3eg. Сохраните
файл hello.py в каталоге py3eg и закройте текстовый редактор.
Теперь, когда у нас имеется программа, ее можно запустить. Програм
мы на языке Python выполняются интерпретатором Python, и, как
правило, делается это в окне консоли. В операционной системе Win
dows консоль может называться «Командная строка» (Console), или «DOS
Prompt», или «MS DOS Prompt» или иметь похожее название, и обычно
доступна в меню «Пуск→Все программы→Стандартные» (Start→All Programs→
Accessories). В Mac OS X консоль представлена программой Terminal.app
(по умолчанию находится в меню Applications/Utilities), которую можно
отыскать с помощью инструмента Finder. В других UNIXподобных
системах можно использовать программу xterm или консоль, предос
тавляемую используемым оконным окружением, например, konsole
или gnometerminal.
Запустите консоль и в операционной системе Windows введите сле
дующие команды (предполагается, что Python был установлен в ката
лог по умолчанию) – вывод консоли показан жирным шрифтом, а вводимые
вами команды – обычным моноширинным:
C:\>cd c:\py3eg
C:\py3eg\>C:\Python30\python.exe hello.py
Поскольку в команде cd (change directory – изменить каталог) указан
абсолютный путь, не имеет значения, в каком каталоге вы находились
перед этим.
Пользователи UNIX должны ввести следующие команды (предполага
ется, что путь к Python 3 включен в переменную окружения PATH):
1
$ cd $HOME/py3eg
$ python3 hello.py
В обоих случаях вывод программы должен быть одним и тем же:
Hello World!
Обратите внимание: если не указано иное, Python ведет себя в Mac OS X
точно так же, как и в других системах UNIX. В действительности под
общим термином «UNIX» мы подразумеваем Linux, BSD, Mac OS X
и большинство других версий UNIX и UNIXподобных систем.
1 Внешний вид строки приглашения к вводу в системах UNIX может отли
чаться от $, как показано здесь, но для нас это не имеет никакого значения.

24 Глава 1. Быстрое введение в процедурное программирование
Несмотря на то, что в данной программе имеется всего
одна выполняемая инструкция, запуская ее, мы можем
сделать некоторые выводы о функции print(). С одной
стороны, функция print() является встроенной частью
языка Python – чтобы воспользоваться ею нам не при
шлось «импортировать» или «подключать» какиелибо
библиотеки. Кроме того, при выводе она отделяет аргу
менты одним пробелом, а после вывода последнего аргу
мента выполняет переход на новую строку. Это поведе
ние по умолчанию, которое можно изменить, в чем вы
убедитесь позднее. Другое ценное замечание о функции
print() заключается в том, что она может принимать
столько аргументов, сколько потребуется.
Ввод последовательности команд для выполнения программ на языке
Python может быстро надоесть. К счастью, и в Windows, и в UNIX су
ществуют более удобные способы. Предположим, что мы уже нахо
димся в каталоге py3eg, тогда в Windows достаточно будет ввести ко
манду:
C:\py3eg\>hello.py
Windows использует свой реестр соответствия программ расширениям
файлов и при вводе имен файлов с расширением .py автоматически вы
зывает интерпретатор Python.
Если в консоли будет выведено:
('Hello', 'World!')
то это означает, что в системе установлен Python 2, который вызывает
ся вместо Python 3. Один из вариантов следующих действий – пере
ключить ассоциацию расширения .py с Python 2 на Python 3. Другое
решение (менее удобное, но более безопасное) – включить каталог
с Python 3 в путь поиска (предполагается, что он был установлен в ка
талог по умолчанию) и всякий раз явно вызывать его:
C:\py3eg\>path=c:\python30;%path%
C:\py3eg\>python hello.py
Возможно, было бы удобнее создать единственный пакетный файл
py3.bat с единственной строкой path=c:\python30;%path% и сохранить его
в каталоге C:\Windows. После этого при запуске консоли для выполне
ния программ Python 3 достаточно будет выполнять команду py3.bat.
При желании можно настроить автоматический запуск файла py3.bat.
Для этого следует изменить свойства консоли: отыщите консоль в ме
ню Пуск (Start), щелкните на ярлыке правой кнопкой мыши и выберите
в контекстном меню пункт Свойства (Properties), затем во вкладке Ярлык
(Shortcut) в конец строки в поле Объект (Ta r g e t) добавьте « /u /k c:\win
dows\py3.bat» (обратите внимание на пробелы перед, между и после
Функция print() ,
стр. 212

Создание и запуск программ на языке Python 25
параметров «/u» и «/k» и убедитесь, что указанная строка добавлена
после «cmd.exe»).
В UNIX сначала файл следует сделать выполняемым, после чего его
можно будет запускать, просто введя его имя:
$ chmod +x hello.py
$ ./hello.py
Конечно, команду chmod придется запускать всего один раз, после этого
достаточно будет просто вводить имя программы ./hello.py, чтобы за
пустить ее.
Когда в UNIX программа запускается в консоли, она читает первые
два байта.
1 Если это последовательность ASCIIсимволов #!, команд
ная оболочка предполагает, что файл должен выполняться интерпре
татором, а первая строка файла определяет, какой интерпретатор дол
жен использоваться. Данная строка называется строкой shebang (вы
полняется командной оболочкой) и всегда должна быть первой стро
кой в файле.
Строка shebang обычно записывается в одной из двух форм:
#!/usr/bin/python3
или:
#!/usr/bin/env python3
В первом случае она определяет используемый интерпретатор. Вторая
форма может потребоваться для программ на языке Python, запускае
мых вебсервером, хотя абсолютный путь в каждом конкретном слу
чае может отличаться от того, что показан здесь. Во втором случае бу
дет использован первый интерпретатор python3, найденный в теку
щем окружении. Вторая форма является более универсальной, потому
что допускает, что интерпретатор Python 3 может находиться не в ка
талоге /usr/bin (то есть он может находиться, например, в каталоге
/usr/local/bin или может быть установлен в каталоге $HOME). Строка
shebang не требуется (хотя и не мешает) в операционной системе Win
dows; все примеры в этой книге имеют строку shebang во второй ее
форме, хотя она может быть и не приведена.
Обратите внимание: когда мы говорим о системах UNIX, предполага
ется, что выполняемый файл Python 3 (или символическая ссылка на
него) находится в пути поиска PATH и имеет имя python3. Если это не
так, вам потребуется изменять строки shebang в примерах, подставив
туда корректное имя файла (или корректное имя и путь, если вы пред
1 Взаимодействие между пользователем и консолью обслуживается програм
мой «командной оболочки». Нас не интересуют различия, существующие
между консолью и командной оболочкой, поэтому эти термины мы будем
считать взаимозаменяемыми.

26 Глава 1. Быстрое введение в процедурное программирование
почтете использовать первую форму), или создать символическую
ссылку с именем python3 на выполняемый файл Python 3, поместив ее
в один из каталогов, находящийся в пути поиска PATH.
Многие мощные текстовые редакторы, такие как Vim
и Emacs, обладают встроенной поддержкой редактирова
ния программ на языке Python. Обычно эта поддержка
выражается в предоставлении подсветки синтаксиса
и в корректном оформлении отступов в строках. В каче
стве альтернативы можно использовать IDLE – среду
программирования на языке Python. В Windows и Mac
OS X IDLE устанавливается по умолчанию, а в UNIX час
то предоставляется в виде отдельного пакета, как уже
говорилось во введении.
Как показано на снимке с экрана, что приводится на рис. 1.1, среда
IDLE имеет весьма непритязательный внешний вид, напоминающий
времена Motif в UNIX и Windows 95. Это обусловлено тем, что для соз
дания графического интерфейса среды используется библиотека Tkin
ter (описывается в главе 13), а не одна из современных и мощных биб
лиотек, таких как PyGtk, PyQt или wxPython. Причины выбора Tkin
ter корнями уходят в прошлое и в значительной степени обусловлены
либеральностью лицензии и тем фактом, что библиотека Tkinter на
много меньше других библиотек создания графического интерфейса.
Положительная сторона этого выбора – IDLE входит в стандартную
поставку Python и очень проста в изучении и использовании.
Получение
и установка
Python,
стр. 17
Рис. 1.1. Командная оболочка Python в IDLE

«Золотой запас» Python 27
Среда IDLE обеспечивает три ключевые возможности: ввод выраже
ний и программного кода на языке Python с получением результатов
прямо в командной оболочке Python; предоставляет редактор про
граммного кода с подсветкой синтаксиса языка Python и поддержкой
функции оформления отступов и отладчик, который может использо
ваться в режиме пошагового выполнения программного кода, облег
чая поиск и устранение ошибок. Командная оболочка Python особенно
удобна при опробовании простых алгоритмов, фрагментов программ
ного кода и регулярных выражений и может использоваться как очень
мощный и гибкий калькулятор.
Для языка Python существуют и другие среды разработки, но мы реко
мендуем использовать IDLE – по крайней мере на начальном этапе.
При желании для создания программ вы можете использовать простой
текстовый редактор, а отладку выполнять посредством инструкций
print().
Интерпретатор Python можно запускать самостоятельно, не указывая
ему программу на языке Python. В этом случае интерпретатор запус
кается в интерактивном режиме. В этом режиме можно вводить инст
рукции языка Python и получать те же результаты, что и в командной
оболочке Python среды IDLE, при этом будет выводиться все та же
строка приглашения к вводу >>>. Но пользоваться IDLE гораздо про
ще, поэтому мы рекомендуем применять ее для проведения экспери
ментов с фрагментами программного кода. Короткие интерактивные
примеры, которые приводятся в книге, могут вводиться как в интер
претаторе Python, работающем в интерактивном режиме, так и в ко
мандной оболочке Python, в среде IDLE.
Теперь мы знаем, как создавать и запускать программы на языке Py
thon, но совершенно очевидно, что мы далеко не уедем, зная всего одну
функцию. В следующем разделе мы существенно расширим наши по
знания о языке Python. Они позволят нам писать пусть и короткие, но
уже полезные программы на языке Python, как те, что приводятся
в последнем разделе.
«Золотой запас» Python
В этом разделе мы узнаем о восьми ключевых составляющих языка
Python, а в следующем разделе увидим, как используются эти состав
ляющие на примере пары маленьких, но практичных программ. Обсу
ждение описываемых здесь тем ведется не только в этой главе, поэтому,
если вы почувствуете, что информации не хватает или чтото выглядит
слишком громоздко, воспользуйтесь ссылками вперед, содержанием
или предметным указателем; практически всегда обнаружится, что Py
thon предоставляет нужную вам особенность, к тому же в более краткой
и выразительной форме, чем показано здесь, и, кроме того, обнаружит
ся еще много чего вокруг.

28 Глава 1. Быстрое введение в процедурное программирование
Составляющая №1: типы данных
Одна из фундаментальных особенностей любого языка программиро
вания заключается в способности представлять элементы данных.
Язык Python предоставляет несколько встроенных типов данных, но
пока интерес для нас представляют только два из них. В языке Python
для представления целых чисел (положительных и отрицательных)
используется тип int, а для представления строк (последовательностей
символов Юникода) используется тип str. Ниже приводятся несколь
ко примеров литералов целых чисел и строк:
973
210624583337114373395836055367340864637790190801098222508621955072
0
"Infinitely Demanding"
'Simon Critchley'
'positively αβγ€÷
©'
''
Между прочим, второе число в этом примере – это число 2 217 – размер
целых чисел в языке Python ограничивается только объемом памяти,
имеющейся в компьютере, а не фиксированным числом байтов. Стро
ки могут ограничиваться кавычками или апострофами при условии,
что с обоих концов используются однотипные кавычки, а поскольку
для представления строк Python использует Юникод, строки могут со
держать не только символы из набора ASCII, как показано в предпо
следней строке примера. Пустые строки – это просто кавычки, внутри
которых ничего нет.
Для доступа к элементам последовательностей, таким как символы
в строках, в языке Python используются квадратные скобки ([]). На
пример, находясь в командной оболочке Python (когда интерпретатор
запущен в интерактивном режиме или в среде IDLE) мы можем ввести
следующее – вывод командной оболочки Python выделен жирным шриф
том, а то, что вводится с клавиатуры – обычным моноширинным шрифтом:
>>> "Hard Times"[5]
'T'
>>> "giraffe"[0]
'g'
Традиционно в командной оболочке Python строка приглашения к вво
ду имеет вид >>>, но ее можно изменить. Квадратные скобки могут ис
пользоваться для доступа к элементам любых типов данных, являю
щихся последовательностями, таких как строки и списки. Такая непро
тиворечивость синтаксиса – одно из оснований красоты языка Python.
Обратите внимание: индексы в языке Python начинаются с 0.
В Python тип str и элементарные числовые типы, такие как int, явля
ются неизменяемыми, то есть однажды установив значение, его уже
нельзя будет изменить. На первый взгляд, такое ограничение кажется

«Золотой запас» Python 29
странным, но на практике это не влечет за собой никаких проблем.
Единственная причина, по которой об этом было упомянуто здесь, за
ключается в том, что имея возможность с помощью квадратных ско
бок извлекать отдельные символы, мы не имеем возможности изме
нять их. (Обратите внимание: в языке Python под символом понимает
ся строка, имеющая длину, равную 1.)
Для преобразования элемента данных из одного типа в другой мы мо
жем использовать конструкцию datatype(item). Например:
>>> int("45")
45
>>> str(912)
'912'
Преобразование int() терпимо относится к начальным и конечным про
белам, поэтому оператор int("45") также будет работать. Преобразова
ние str() может применяться практически к любым типам данных. Мы
легко можем наделять поддержкой преобразований str(), int() идру
гих преобразований свои собственные типы данных, если в этом име
ется какойто смысл; это будет показано в главе 6. Если преобразова
ние терпит неудачу, возбуждается исключение – мы коротко затронем
тему обработки исключений, когда будем рассматривать составляю
щую №5, а полное обсуждение исключений приводится в главе 4.
Строки и целые числа подробно будут обсуждаться в главе 2 наряду
с другими встроенными типами данных и некоторыми другими типа
ми из стандартной библиотеки Python. В этой главе также будут рас
сматриваться операции, применимые к неизменяемым последователь
ностям, таким как строки.
Составляющая №2: ссылки на объекты
Теперь, зная о существовании некоторых типов данных,
нам необходимо рассмотреть переменные, которые хра
нят эти данные. В языке Python нет переменных как
таковых – вместо них используются ссылки на объекты.
Когда речь заходит о неизменяемых объектах, таких как
int или str, между переменной и ссылкой на объект нет
никакой разницы. Однако различия начинают прояв
ляться, когда дело доходит до изменяемых объектов, но
эти различия редко имеют практическое значение. Мы
будем использовать термины переменная и ссылка на
объект как взаимозаменяемые.
Взгляните на следующие крошечные примеры, а затем мы обсудим их
подробнее.
x = "blue"
y = "green"
z = x
Поверхно
стное
и глубокое
копирование,
стр. 173

30 Глава 1. Быстрое введение в процедурное программирование
Синтаксис выглядит очень просто: objectReference = value. Нет ника
кой необходимости в предварительном объявлении или определении
типов значений. Когда интерпретатор Python выполняет первую инст
рукцию, он создает объект типа str с текстом "blue", а затем создает
ссылку на объект с именем x, которая ссылается на объект типа str.
Практически можно сказать, что «переменной x была присвоена стро
ка 'blue'». Вторая инструкция похожа на первую. Третья инструкция
создает новую ссылку на объект с именем z и записывает в нее ссылку
на тот же самый объект, на который указывает ссылка x (в данном слу
чае это объект типа str с текстом "blue").
Оператор = – это не оператор присваивания значения переменной, как
в некоторых других языках программирования. Оператор = связывает
ссылку на объект с объектом, находящимся в памяти. Если ссылка на
объект уже существует, ее легко можно связать с другим объектом,
указав этот объект справа от оператора =. Если ссылка на объект еще
не существует, она будет создана оператором =.
Продолжим пример со ссылками x, y и z и выполним перепривязку.
Как уже упоминалось ранее, с символа # начинаются комментарии,
которые продолжаются до конца строки:
print(x, y, z) # выведет: blue green blue
z = y
print(x, y, z) # выведет: blue green green
x = z
print(x, y, z) # выведет: green green green
После выполнения четвертой инструкции (x = z) все три ссылки на
объекты будут ссылаться на один и тот же объект типа str. Поскольку
теперь не осталось ни одной ссылки, которая ссылалась бы на строку
"blue", Python сможет утилизировать ее.
На рис. 1.2 схематически изображены взаимоотношения между объ
ектами и ссылками на объекты.
a=7 a 7 Кружочками изображены ссылки на объекты.
Прямоугольниками – объекты в памяти.
a=7
b=a a 7
b a 7 a=7
b=a
a = "Liberty"
b "Liberty"
Рис. 1.2. Объекты и ссылки на объекты

«Золотой запас» Python 31
Имена, используемые для идентификации ссылок на
объекты (называются идентификаторами), имеют оп
ределенные ограничения. В частности, они не могут сов
падать с ключевыми словами языка Python и должны
начинаться с алфавитного символа или с символа под
черкивания, за которым следует ноль или более алфа
витных символов, символов подчеркивания или цифр.
Ограничений на длину имен не накладывается, а алфа
витные и цифровые символы – это те символы, что опре
деляются Юникодом, включая, но не ограничиваясь
цифрами и символами ASCII («a», «b», …, «z», «A», «B»,
…, «Z», «0», «1», …, «9»). Идентификаторы в языке Py
thon чувствительны к регистру символов, то есть имена
LIMIT, Limit и limit – это три разных имени. Дополни
тельные подробности по этому вопросу и ряд необычных
примеров приводятся в главе 2.
В языке Python используется динамический контроль типов, то есть
ссылки на объекты в любой момент могут повторно привязываться
к различным объектам (которые могут относиться к данным различ
ных типов). В языках со строгим контролем типов (таких как C++
и Java) разрешается выполнять только те операции, которые допусти
мы для данных, участвующих в операции. В языке Python также име
ется подобное ограничение, но в данном случае это не называется стро
гим типизированием, потому что допустимость операции может изме
ниться, например, когда ссылка на объект будет повторно связана
с объектом другого типа. Например:
route = 866
print(route, type(route)) # выведет: 866
route = "North"
print(route, type(route)) # выведет: North
Здесь была создана новая ссылка на объект с именем route и связана
с новым значением 866 типа int. С этого момента мы можем использо
вать оператор / применительно к route, потому что деление – это до
пустимая операция для целых чисел. После этого мы вновь воспользо
вались ссылкой route и связали ее с новым объектом типа str, имею
щим значение «North», а объект типа int был утилизирован сборщи
ком мусора, так как не осталось ни одной ссылки, которая ссылалась
бы на него. С этого момента применение оператора / будет вызывать
ошибку TypeError, так как деление не является допустимой операцией
для строк.
Функция type() возвращает тип данных (который также
называется «классом») для указанного элемента. Эта
функция может быть очень полезной на этапе тестиро
вания и отладки, но в готовом программном коде, как
Идентифи
каторы
иключевые
слова, стр. 68
Функция isin
stance() ,
стр. 284

32 Глава 1. Быстрое введение в процедурное программирование
правило, не используется, так как существует более удобная альтерна
тива, в чем вы убедитесь в главе 6.
При экспериментировании с программным кодом на языке Python в ин
терактивной оболочке интерпретатора или в командной оболочке
Python – такой, как предоставляется средой IDLE, достаточно просто
ввести имя ссылки на объект, чтобы интерпретатор вывел значение
связанного с ней объекта. Например:
>>> x = "blue"
>>> y = "green"
>>> z = x
>>> x
'blue'
>>> x, y, z
('blue', 'green', 'blue')
Это намного удобнее, чем постоянно вызывать функцию print(), но эта
особенность работает только при использовании интерпретатора Py
thon в интерактивном режиме – в любых создаваемых программах
и модулях для вывода значений следует использовать функцию print()
или подобные ей. Обратите внимание, что в последнем случае Python
вывел значения в круглых скобках, разделив их запятыми – так обо
значается тип данных tuple (кортеж), то есть упорядоченная, неизме
няемая последовательность объектов. О кортежах мы поговорим в сле
дующем разделе.
Составляющая №3: коллекции данных
Часто бывает удобно хранить целую коллекцию элементов данных.
В языке Python для этого имеется несколько типов коллекций, способ
ных хранить элементы данных, включая ассоциативные массивы
и множества. Но в этом разделе мы рассмотрим только два типа кол
лекций: tuple (кортежи) и list (списки). Кортежи и списки в языке Py
thon могут использоваться для хранения произвольного числа элемен
тов данных любых типов. Кортежи относятся к разряду неизменяе
мых объектов, поэтому после создания кортеж нельзя изменить. Спи
ски относятся к разряду изменяемых объектов, поэтому мы легко
можем вставлять и удалять элементы списка по своему желанию.
Кортежи создаются с помощью запятых (,), как показано ниже:
>>> "Denmark", "Norway", "Sweden"
('Denmark', 'Norway', 'Sweden')
>>> "one",
('one',)
При выводе кортежа интерпретатор Python заключает
его в круглые скобки. Многие программисты имитируют
такое поведение и заключают литералы кортежей в круг
лые скобки. Если создается кортеж с одним элементом, Создание
ивызов
функций,
стр. 52

«Золотой запас» Python 33
то даже при наличии круглых скобок мы обязаны использовать запя
тую, например: (1,). Пустой кортеж создается с помощью пустых
круглых скобок (). Запятая также используется для отделения аргу
ментов при вызове функции, поэтому, если в качестве аргумента тре
буется передать литерал кортежа, мы должны заключать его в круг
лые скобки, чтобы избежать неоднозначности.
Ниже приводятся несколько примеров списков:
[1, 4, 9, 16, 25, 36, 49]
['alpha', 'bravo', 'charlie', 'delta', 'echo']
['zebra', 49, 879, 'aardvark', 200]
[]
Как показано здесь, списки могут создаваться с помощью квадратных
скобок ([]), но позднее мы познакомимся с другими способами созда
ния списков. Четвертый список в примере – это пустой список.
Списки и кортежи хранят не сами элементы данных, а ссылки на объ
екты. При создании списков и кортежей (а также при добавлении но
вых элементов в списки) создаются копии указанных ссылок на объек
ты. В случае значенийлитералов, таких как целые числа и строки,
в памяти создаются и инициализируются объекты соответствующих
типов, затем создаются ссылки, указывающие на эти объекты, и эти
ссылки помещаются в список или в кортеж.
Как и все остальное в языке Python, коллекции данных –
это объекты: благодаря этому имеется возможность
вкладывать одни объектыколлекции в другие, напри
мер, без лишних формальностей можно создать список
списков. В некоторых ситуациях тот факт, что кортежи
и списки, а также большинство коллекций других ти
пов, хранят ссылки на объекты, а не сами объекты, име
ет большое значение – об этом будет рассказываться
в главе 3 (начиная со стр. 136).
В процедурном программировании мы вызываем функции и часто пе
редаем им данные в виде аргументов. Например, мы уже познакоми
лись с функцией print(). Другая часто используемая функция в языке
Python – это функция len(), которая в качестве аргумента принимает
единственный элемент данных и возвращает его «длину» в виде значе
ния типа int. Ниже приводятся несколько примеров вызова функции
len() – в этом примере мы не стали выделять вывод интерпретатора
жирным шрифтом, полагая, что вы уже сами сможете отличить, что
вводится с клавиатуры, а что выводится интерпретатором:
>>> len(("one",))
1
>>> len([3, 5, 1, 2, "pause", 5])
6
>>> len("automatically")
13
Поверхно
стное
и глубокое
копирование,
стр. 173

34 Глава 1. Быстрое введение в процедурное программирование
Кортежи, списки и строки имеют «размер», то есть это
типы данных, которые обладают категорией размера,
и элементы данных таких типов могут передаваться
функции len(). (Если функции len() передать элемент,
тип которого не предполагает такого понятия, как раз
мер, будет возбуждено исключение.)
Все элементы данных в языке Python являются объектами (называе
мых также экземплярами) определенных типов данных (называемых
также классами). Мы будем использовать термины тип данных
икласс как взаимозаменяемые. Одно из основных отличий между объ
ектом и простым элементом данных в некоторых других языках про
граммирования (например, встроенные числовые типы в C++ или Java)
состоит в том, что объект может обладать методами. В сущности, ме
тод – это обычная функция, которая вызывается в контексте конкрет
ного объекта. Например, тип list имеет метод append(), с помощью ко
торого можно добавить новый объект в список, как показано ниже:
>>> x = ["zebra", 49, 879, "aardvark", 200]
>>> x.append("more")
>>> x
['zebra', 49, 879, 'aardvark', 200, 'more']
Объект x знает, что принадлежит к типу list (все объекты в языке Py
thon знают, к какому типу они принадлежат), поэтому нам не требует
ся явно указывать тип данных. Первым аргументом методу append()
передается сам объект x – делается это интерпретатором Python авто
матически, в порядке реализованной в нем поддержки методов.
Метод append() изменяет первоначальный список. Это возможно благо
даря тому, что списки относятся к категории изменяемых объектов.
Потенциально это более эффективно, чем создание нового списка,
включающего первоначальные элементы и дополнительный элемент
с последующим связыванием ссылки на объект с новым списком, осо
бенно для очень длинных списков.
В процедурном языке то же самое могло бы быть достигнуто с исполь
зованием метода append(), как показано ниже (что является совершен
но допустимым вариантом в языке Python):
>>> list.append(x, "extra")
>>> x
['zebra', 49, 879, 'aardvark', 200, 'more', 'extra']
Здесь указываются тип данных и метод этого типа данных, а в качест
ве первого аргумента передается элемент данных того типа, метод ко
торого вызывается, за которым следуют дополнительные параметры.
(С точки зрения наследования между этими двумя подходами сущест
вует малозаметное семантическое отличие; на практике наиболее часто
используется первая форма. О наследовании рассказывается в главе 6.)
Понятие
«размер»,
стр. 443

«Золотой запас» Python 35
Если вы еще не знакомы с объектноориентированным программиро
ванием, на первый взгляд такая форма вызова функций может пока
заться немного странной. Пока вам достаточно будет знать, что обыч
ные функции в языке Python вызываются так: functionName(argiments),
а методы вызываются так: objectName.methodName(arguments). (Об объ
ектноориентированном программировании рассказывается в главе 6.)
Оператор точки («оператор доступа к атрибуту») используется для дос
тупа к атрибутам объектов. До сих пор мы видели только одну разно
видность атрибутов – методы, но атрибут может быть объектом любого
типа. Поскольку атрибут может быть объектом, имеющим свои атри
буты, которые также могут быть объектами, обладающими атрибута
ми и т. д., мы можем использовать столько операторов точки, сколько
потребуется для доступа к необходимому атрибуту.
Тип list имеет множество других методов, включая insert(), который
используется для вставки элемента в позицию с определенным индек
сом, и remove(), который удаляет элемент с указанным индексом. Как
уже упоминалось ранее, счет индексов в языке Python начинается с 0.
Ранее мы уже видели, что существует возможность извлечь из строки
символ, используя оператор квадратных скобок, и отметили, что этот
оператор может использоваться с любыми последовательностями. Спи
ски – это последовательности, поэтому мы можем выполнять следую
щие операции:
>>> x
['zebra', 49, 879, 'aardvark', 200, 'more', 'extra']
>>> x[0]
'zebra'
>>> x[4]
200
Кортежи также являются последовательностями, поэтому, если бы
ссылка x указывала на кортеж, мы могли бы извлекать элементы с по
мощью квадратных скобок точно так же, как это было сделано со ссыл
кой x, представляющей список. Но так как списки являются изменяе
мыми объектами (в отличие от строк и кортежей, которые являются
неизменяемыми объектами), мы можем использовать оператор квад
ратных скобок еще и для изменения элементов списка. Например:
>>> x[1] = "forty nine"
>>> x
['zebra', 'forty nine', 879, 'aardvark', 200, 'more', 'extra']
Если указать индекс, выходящий за пределы списка, будет возбужде
но исключение – обработка исключений вкратце будет рассматривать
ся в разделе с описанием составляющей №5, а полный охват этой темы
дается в главе 4.
Мы уже несколько раз использовали термин последовательность, по
лагаясь на неформальное понимание его смысла, и продолжим его

36 Глава 1. Быстрое введение в процедурное программирование
использовать в том же духе. Однако язык Python дает точное опреде
ление последовательностей и какие особенности должны ими поддер
живаться, точно так же он точно определяет, какие особенности долж
ны поддерживаться объектами, имеющими размер, и так далее для
других категорий, которым могут принадлежать типы данных, но об
этом будет рассказываться в главе 8.
Списки, кортежи и другие типы коллекций, встроенные в язык Py
thon, описываются в главе 3.
Составляющая №4: логические операции
Одна из фундаментальных особенностей любого языка программиро
вания заключается в поддержке логических операций. Язык Python
предоставляет четыре набора логических операций, и здесь мы рас
смотрим основные принципы их использования.
Оператор идентичности
Поскольку все переменные в языке Python фактически являются
ссылками, иногда возникает необходимость определить, не ссылаются
ли две или более ссылок на один и тот же объект. Оператор is – это
двухместный оператор, который возвращает True, если ссылка слева
указывает на тот же самый объект, что и ссылка справа. Ниже приво
дятся несколько примеров:
>>> a = ["Retention", 3, None]
>>> b = ["Retention", 3, None]
>>> a is b
False
>>> b = a
>>> a is b
True
Обратите внимание, что обычно не имеет смысла использовать опера
тор is для сравнения типов данных int, str и некоторых других, так
как обычно в этом случае бывает необходимо сравнить их значения.
Фактически результаты сравнения данных с помощью оператора is
могут приводить в замешательство, как это видно из предыдущего
примера, где a и b первоначально имеют одинаковые значениясписки,
однако сами списки хранятся в виде отдельных объектов типа list,
и поэтому в первом случае оператор is вернул False.
Одно из преимуществ операции проверки идентичности заключается
в высокой скорости ее выполнения. Это обусловлено тем, что сравни
ваются ссылки на объекты, а не сами объекты. Оператор is сравнивает
только адреса памяти, в которых располагаются объекты – если адре
са равны, следовательно, ссылки указывают на один и тот же объект.

«Золотой запас» Python 37
Чаще всего оператор is используется для сравнения элемента данных
со встроенным пустым объектом None, который часто применяется,
чтобы показать, что значение «неизвестно» или «не существует»:
>>> a = "Something"
>>> b = None
>>> a is not None, b is None
(True, True)
Для выполнения проверки на неидентичность, используется оператор
is not.
Назначение оператора проверки идентичности состоит в том, чтобы
узнать, ссылаются ли две ссылки на один и тот же объект, или прове
рить, не ссылается ли ссылка на объект None. Если требуется сравнить
значения объектов, мы должны использовать операторы сравнения.
Операторы сравнения
Язык Python предоставляет стандартный набор двухместных операто
ров сравнения с предсказуемой семантикой: < меньше чем, <= меньше
либо равно, == равно, != не равно, >= больше либо равно и > больше чем.
Эти операторы сравнивают значения объектов, то есть объекты, на ко
торые указывают ссылки, участвующие в сравнении. Ниже приводят
ся примеры, набранные в командной оболочке Python:
>>> a = 2
>>> b = 6
>>> a == b
False
>>> a < b
True
>>> a <= b, a != b, a >= b, a > b
(True, True, False, False)
Для целых чисел они действуют, как и следовало ожидать. Точно так
же корректно выполняется сравнение строк:
>>> a = "many paths"
>>> b = "many paths"
>>> a is b
False
>>> a == b
True
Хотя a и b ссылаются на разные объекты (с различными
идентификаторами), тем не менее они имеют одинаковые
значения, поэтому результат сравнения говорит о том,
что они равны. Однако не следует забывать, что в языке
Python для представления строк используется кодиров
ка Юникод, поэтому сравнение строк, содержащих сим Сравнение
строк, стр. 88

38 Глава 1. Быстрое введение в процедурное программирование
волы, не входящие в множество ASCIIсимволов, может оказаться де
лом более сложным, чем кажется на первый взгляд – подробнее эта
проблема будет рассматриваться в главе 2.
Одна из интересных особенностей операторов сравнения в языке Python
заключается в том, что они могут объединяться в цепочки. Например:
>>> a = 9
>>> 0 <= a <= 10
True
Это отличный способ убедиться, что некоторый элемент данных нахо
дится в пределах диапазона, вместо того чтобы выполнять два отдель
ных сравнения, результаты которых объединяются логическим опера
тором and, как это принято во многих других языках программирова
ния. Кроме того, у этого подхода имеется дополнительное преимуще
ство, так как оценка значения элемента данных производится только
один раз (поскольку в выражении он появляется всего один раз), что
может иметь большое значение, особенно когда вычисление значения
элемента данных является достаточно дорогостоящей операцией или
когда обращение к элементу данных имеет побочные эффекты.
Благодаря «строгости» механизма динамического контроля типов
в языке Python попытка сравнения несовместимых значений вызыва
ет исключение. Например:
>>> "three" < 4
Traceback (most recent call last):
(Трассировочная информация (самый последний вызов внизу)
...
TypeError: unorderable types: str() < int()
(TypeError: несопоставимые типы: str() < int())
В случае, когда возбуждается необрабатываемое исключение, интер
претатор Python выводит диагностическую информацию и текст сооб
щения об ошибке. Для простоты мы опустили диагностическую ин
формацию здесь, заменив ее многоточием.
1 То же самое исключение
TypeError возникнет, если записать "3" < 4, потому что Python никогда
не пытается строить предположения о наших намерениях – правиль
ный подход заключается в том, чтобы выполнить явное преобразова
ние, например: int("3") < 4, или использовать сопоставимые типы, то
есть оба значения должны быть либо целыми числами, либо строками.
1 Диагностическая информация (иногда называется обратной трассировкой
(backtrace)) – это список всех вызовов функций, которые были произведе
ны к моменту возбуждения необрабатываемого исключения, в обратном
порядке.

«Золотой запас» Python 39
Язык Python позволяет нам легко создавать свои собст
венные, легко интегрируемые типы данных, благодаря
чему, например, мы могли бы создать свой собственный
числовой тип, который смог бы участвовать в операциях
сравнения со встроенным типом int и другими встроен
ными или нашими собственными числовыми типами, но
не со строками или другими нечисловыми типами.
Оператор членства
Для типов данных, являющихся последовательностями или коллек
циями, таких как строки, списки и кортежи, мы можем выполнить
проверку членства с помощью оператора in, а проверку обратного ут
верждения – с помощью оператора not in. Например:
>>> p = (4, "frog", 9, 33, 9, 2)
>>> 2 in p
True
>>> "dog" not in p
True
Применительно к спискам и кортежам оператор in выполняет линей
ный поиск, который может оказаться очень медленным для огромных
коллекций (десятки тысяч элементов или больше). С другой стороны,
со словарями или со множествами оператор in работает очень быстро –
оба этих типа данных рассматриваются в главе 3. Ниже демонстриру
ется, как оператор in может использоваться со строками:
>>> phrase = "Peace is no longer permitted during Winterval"
>>> "v" in phrase
True
>>> "ring" in phrase
True
Применительно к строкам оператор in удобно использовать для провер
ки вхождения в строку подстроки произвольной длины. (Как отмеча
лось ранее, символ – это всего лишь строка, имеющая длину, равную 1.)
Логические операторы
Язык Python предоставляет три логических оператора: and, or и not.
Операторы and и or вычисляются по короткой схеме и возвращают опе
ранд, определяющий результат, – они не возвращают значение типа
Boolean (если операнды не являются значениями типа Boolean). Взгля
ните, что это означает на практике:
>>> five = 5
>>> two = 2
>>> zero = 0
>>> five and two
2
Альтерна
тивный тип
FuzzyBool ,
стр. 300

40 Глава 1. Быстрое введение в процедурное программирование
>>> two and five
5
>>> five and zero
0
Если выражение участвует в логическом контексте, результат оцени
вается как значение типа Boolean, поэтому предыдущие выражения
могли бы рассматриваться, как имеющие значения True, True и False,
например, в контексте инструкции if.
>>> nought = 0
>>> five or two
5
>>> two or five
2
>>> zero or five
5
>>> zero or nought
0
Оператор or напоминает оператор and – здесь в булевом контексте были
бы получены значения True, True, True и False.
Унарный оператор not оценивает свой операнд в булевом контексте
и всегда возвращает значение типа Boolean, поэтому, продолжая пре
дыдущий пример, выражение not (zero or nought) вернет значение
True, а выражение not two – False.
Составляющая №5: инструкции управления
потоком выполнения
Мы упоминали ранее, что все инструкции, встречающиеся в файле
срасширением .py, выполняются по очереди, строка за строкой. Поря
док движения потока управления может изменяться вызовами функ
ций и методов или структурами управления, такими как условные
операторы или операторы циклов. Поток управления также отклоня
ется, когда возбуждаются исключения.
В этом подразделе мы рассмотрим условный оператор if, а также опе
раторы циклов while и for, отложив рассмотрение функций до раздела
с описанием составляющей №8, а рассмотрение методов – до главы 6.
Мы также коротко коснемся вопросов обработки исключений, кото
рые подробнее будут обсуждаться в главе 4. Но для начала мы опреде
лим пару терминов.
Булево (Boolean) выражение – это выражение, которое может оцени
ваться как булево (Boolean) значение (True или False). В языке Python
выражение оценивается как False, если это предопределенная кон
станта False, специальный объект None, пустая последовательность или
коллекция (например, пустая строка, список или кортеж), или число
вой элемент данных со значением 0. Все остальное оценивается как

«Золотой запас» Python 41
True. При создании своих собственных типов данных (как будет рас
сказываться в главе 6) мы сами сможем определять, какое значение
возвращать в булевом контексте.
В языке Python используется понятие блока программного кода –
suite
1, представляющего собой последовательность одной или более
инструкций. Так как некоторые синтаксические конструкции языка
Python требуют наличия блока кода, Python предоставляет ключевое
слово pass, которое представляет собой инструкцию, не делающую
ровным счетом ничего, и которая может использоваться везде, где тре
буется блок кода (или когда мы хотим указать на наличие особого слу
чая), но никаких действий выполнять не требуется.
Инструкция if
В общем случае инструкция if имеет следующий синтаксис: 2
if boolean_expression1:
suite1
elif boolean_expression2:
suite2
...
elif boolean_expressionN:
suiteN
else:
else_suite
В инструкции может быть ноль или более предложений elif, а заклю
чительное предложение else является необязательным. Если необхо
димо принять в учет какойто особый случай, не требующий обработ
ки, мы можем использовать ключевое слово pass в качестве блока кода
соответствующей ветки.
Первое, что бросается в глаза программистам, использовавшим язык
C++ или Java, – это отсутствие круглых и фигурных скобок. Еще одна
особенность, на которую следует обратить внимание, – это двоеточие,
которое является частью синтаксиса и о котором легко забыть на пер
вых порах. Двоеточия используются с предложениями else, elif и прак
тически везде, где вслед за предложением должен следовать блок кода.
В отличие от большинства других языков программирования, отсту
пы в языке Python используются для обозначения блочной структу
ры. Некоторым программистам не нравится эта черта, особенно до то
го, пока они не испытают ее на практике, а некоторые весьма эмоцио
нально высказываются по этому поводу. Однако чтобы привыкнуть
к ней, требуется всего несколько дней, а спустя несколько недель или
1 Во многих других языках программирования такой блок программного ко
да называется составным оператором.– Прим. перев.
2 В этой книге многоточия (…) приводятся взамен строк, которые не пока
заны.

42 Глава 1. Быстрое введение в процедурное программирование
месяцев программный код без скобок начинает восприниматься как
более удобочитаемый и менее захламленный, чем программный код со
скобками.
Так как блоки кода оформляются посредством отступов, естественно,
возникает вопрос, какие отступы использовать. Руководства по оформ
лению программного кода на языке Python рекомендуют использовать
четыре пробела на каждый уровень и использовать только пробелы
(а не символы табуляции). Большинство современных текстовых ре
дакторов могут быть настроены на автоматическую обработку отступов
(редактор среды IDLE, конечно же, предусматривает такую возмож
ность, как и большинство других редакторов, поддерживающих язык
программирования Python). Интерпретатор Python прекрасно будет
справляться с любыми отступами, содержащими любое число пробе
лов или символов табуляции или смесь из тех и других – главное, что
бы оформление отступов выполнялось непротиворечивым образом.
В этой книге мы следуем официальным рекомендациям Python.
Ниже приводится пример простой инструкции if:
if x:
print("x is nonzero")
В данном случае, если условное выражение (x) оценивается как True,
блок кода (вызов функции print()) будет выполнен.
if lines < 1000:
print("small")
elif lines < 10000:
print("medium")
else:
print("large")
Это немного более сложная инструкция if, которая выводит оценку,
описывающую значение переменной lines.
Инструкция while
Инструкция while используется для выполнения своего блока кода
ноль или более раз, причем число раз зависит от булева выражения
в инструкции while. Ниже приводится синтаксис этой инструкции:
while boolean_expression:
suite
В действительности полный синтаксис цикла while– более сложный,
чем показано здесь, так как дополнительно поддерживаются инструк
ции break и continue, а также предложение else, о чем подробно будет
рассказываться в главе 4. Инструкция break передает управление за
пределы самого внутреннего цикла, в котором она встречается, то есть
прерывает выполнение цикла. Инструкция continue передает управле
ние в начало цикла. Как правило, инструкции break и continue исполь

«Золотой запас» Python 43
зуются внутри инструкций if, чтобы в зависимости от складываю
щихся условий изменять поведение цикла.
while True:
item = get_next_item()
if not item:
break
process_item(item)
Этот цикл while имеет вполне типичную структуру и выполняется до
тех пор, пока не будут исчерпаны элементы для обработки. (Предпола
гается, что функции get_next_item() и process_item() – это наши собст
венные функции, определенные гдето в другом месте.) В этом приме
ре блок кода инструкции while содержит инструкцию if, имеющую,
как ей и положено, свой собственный блок кода – в данном случае со
стоящий из единственной инструкции break.
Инструкция for … in
Инструкция цикла for в языке Python повторно использует ключевое
слово in (которое в других контекстах играет роль оператора проверки
членства) и имеет следующий синтаксис:
for variable in iterable:
suite
Точно так же, как и в случае с циклом while, инструкция for поддержи
вает инструкции break и continue, а также необязательное предложение
else. Переменная variable поочередно указывает на каждый объект
вобъекте iterable. В качестве iterable может использоваться любой
тип данных, допускающий выполнение итераций по нему, включая
строки (где итерации выполняются по символам строки), списки, кор
тежи и другие типы коллекций языка Python.
for country in ["Denmark", "Finland", "Norway", "Sweden"]:
print(country)
Здесь мы использовали весьма упрощенный подход к выводу списка
стран. На практике то же самое обычно делается с помощью перемен
ных:
countries = ["Denmark", "Finland", "Norway", "Sweden"]
for country in countries:
print(country)
На самом деле весь список (или кортеж) можно вывести
единственным вызовом функции print(), например,
print(countries), но часто предпочтительнее выводить
содержимое коллекций в цикле for (или в генераторах
списков, о которых будет рассказываться позже), чтобы
иметь полный контроль над форматированием. Генераторы
списков,
стр. 142

44 Глава 1. Быстрое введение в процедурное программирование
for letter in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
if letter in "AEIOU":
print(letter, "is a vowel")
else:
print(letter, "is a consonant")
В первой строке этого фрагмента мы использовали ключевое слово in,
как часть инструкции for, и переменную letter, последовательно при
нимающую значения "A", "B" и так далее до "Z" на каждом проходе
цикла. Во второй строке мы снова использовали ключевое слово in, но
на этот раз в качестве оператора проверки членства. Обратите также
внимание на то, что в этом примере присутствуют вложенные блоки
кода. Блок кода цикла for представлен инструкцией if ...else, а обе
ветки – и if, и else – имеют свои собственные блоки кода.
Основы обработки исключений
Многие функции и методы в языке Python в случае появления ошибок
или наступления других важных событий возбуждают исключения.
Исключение – это объект, такой же как любые другие объекты в язы
ке Python, который при преобразовании в строку (то есть при выводе
на экран) воспроизводится как строка с текстом сообщения. Ниже по
казана простейшая форма обработки исключений:
try:
try_suite
except exception1 as variable1:
exception_suite1

except exceptionN as variableN:
exception_suiteN
Обратите внимание на то, что часть as variable является необязатель
ной – нас может волновать только сам факт возникновения исключе
ния, а текст сообщения может быть для нас неинтересен.
Полный синтаксис имеет более сложный вид, например, каждое пред
ложение except может обрабатывать несколько исключений, а кроме
того, можно использовать необязательное предложение else. Обо всем
этом подробно рассказывается в главе 4.
Эта конструкция работает следующим образом. Если инструкции
в блоке кода try выполняются без ошибок, блоки except просто пропус
каются. Если в блоке try будет возбуждено исключение, управление
немедленно будет передано блоку первого соответствующего предло
жения except – то есть любые инструкции в блоке try, расположенные
ниже инструкции, вызвавшей исключение, выполняться не будут.
В случае исключения и если указана часть as variable, внутри блока
обработки исключения переменная variable будет ссылаться на объект
исключения.

«Золотой запас» Python 45
Если исключение возникнет в блоке except или для возникшего исклю
чения не будет обнаружено соответствующего предложения except, то
Python начнет поиск блока except в следующем объемлющем контек
сте. Поиск подходящего обработчика исключения будет выполняться
в направлении наружу и вверх по стеку вызовов, пока не будет найде
но соответствие. Если соответствия найдено не будет, то программа бу
дет аварийно завершена с выводом сообщения об ошибке. В случае по
явления необработанного исключения интерпретатор Python выводит
диагностическую информацию и текст сообщения.
Например:
s = input("enter an integer: ")
try:
i = int(s)
print("valid integer entered:", i)
except ValueError as err:
print(err)
Если пользователь введет 3.5, будет выведено сообщение:
invalid literal for int() with base 10: '3.5'
(неверный литерал типа int() по основанию 10: '3.5')
Но если он введет 13, вывод будет иной:
valid integer entered: 13
В многих книгах считается, что обработка исключений – это тема по
вышенной сложности, и ее рассмотрение откладывается как можно
дальше. Но возбуждение и особенно обработка исключений – это фун
даментальный способ работы Python. поэтому мы затронули этот во
прос в самом начале. И, как будет показано позже, обработчики ис
ключений могут сделать программный код более удобочитаемым, от
деляя «исключительные» случаи от обработки, которая для нас пред
ставляет наибольший интерес.
Составляющая №6: арифметические операторы
Язык Python предоставляет полный комплект арифметических опера
торов, включая двухместные операторы, выполняющие четыре основ
ных арифметических действия: + (сложение),
– (вычитание), * (умно
жение) и / (деление). Кроме того, многие типы данных в языке Python
допускают использование комбинированных операторов присваива
ния (или, в соответствии с другой используемой системой терминов, –
операторов дополняющего присваивания), таких как += и *=. Операто
ры +,
– и * действуют так, как и следовало ожидать, когда в качестве
операндов выступают целые числа:
>>> 5 + 6
11
>>> 3  7

46 Глава 1. Быстрое введение в процедурное программирование
4
>>> 4 * 8
32
Обратите внимание, что оператор может использоваться как унарный
(оператор отрицания) и как двухместный оператор (вычитание), что
является вполне обычным для большинства языков программирова
ния. Язык Python начинает выделяться из общей массы других язы
ков, когда дело доходит до оператора деления:
>>> 12 / 3
4.0
>>> 3 / 2
1.5
Оператор деления возвращает значение с плавающей точ
кой, а не целое значение. Во многих других языках он
возвращает целое значение, отсекая дробную часть. Если
требуется получить целочисленный результат, его всегда
можно преобразовать с помощью int() (или использовать
оператор деления с усечением //, который будет рассмат
риваться позже).
>>> a = 5
>>> a
5
>>> a += 8
>>> a
13
На первый взгляд в предыдущих инструкциях нет ничего особенного,
особенно для тех, кто знаком с Cподобными языками программирова
ния. В таких языках программирования комбинированные операторы
присваивания являются сокращенной формой записи присваивания
результата операции, например, выражение a += 8 эквивалентно вы
ражению a = a + 8. Однако следует иметь в виду две важные тонкости:
одна имеет отношение только к языку Python и одна характерна для
комбинированных операторов присваивания во всех языках.
Первое, что важно запомнить, это то, что тип int является неизменяе
мым, то есть после присваивания значение типа int нельзя изменить.
Поэтому при выполнении комбинированного оператора присваивания
с неизменяемыми объектами в действительности создается новый объ
ект с результатом, а затем целевая ссылка на объект привязывается
к этому новому объекту. То есть когда в предыдущем случае интерпре
татор Python встретит инструкцию a+=8, он вычислит значение
a+8, сохранит результат в новом объекте типа int и запишет в a ссыл
ку на этот новый объект типа int. (Если после этого в программе не ос
танется ни одной ссылки, указывающей на первоначальный объект,
он будет утилизирован сборщиком мусора.) Рис. 1.3 иллюстрирует,
как это происходит.
Числовые
операторы
и функции,
стр. 74

«Золотой запас» Python 47
Вторая особенность заключается в том, что выражение a operator= b–
это не совсем то же самое, что выражение a = a operator b. Комбиниро
ванная версия ищет значение a всего один раз, поэтому потенциально
она выполняется быстрее. Кроме того, если a – это сложное выражение
(например, элемент списка с вычисляемым индексом, таким как
items[offset + index]), то комбинированная версия может оказаться
менее подверженной ошибкам в случае, когда выражение, вычисляю
щее индекс, потребуется изменить, потому что программисту придет
ся изменить всего одно выражение, а не два.
Язык Python перегружает (то есть позволяет использовать с данными
разных типов) операторы + и += для строк и для списков. Первый из
них выполняет операцию конкатенации, а второй – добавление для
строк и расширение (добавление другого списка в конец) для списков:
>>> name = "John"
>>> name + "Doe"
'JohnDoe'
>>> name += " Doe"
>>> name
'John Doe'
Подобно целым числам, строки являются неизменяемыми, поэтому,
когда используется оператор +=, создается новая строка, и ссылка, рас
положенная слева от оператора, связывается с новым объектом точно
так же, как это было описано выше для случая с объектами типа int.
Списки поддерживают аналогичный синтаксис, но за кулисами вы
полняются другие действия:
>>> seeds = ["sesame", "sunflower"]
>>> seeds += ["pumpkin"]
>>> seeds
['sesame', 'sunflower', 'pumpkin']
Поскольку списки относятся к категории изменяемых объектов, опе
ратор += модифицирует существующий объект списка, поэтому по
вторной привязки ссылки seeds не происходит. Рис. 1.4 демонстриру
ет, как это происходит.
i=73 i 73 i+=2 i 73
75
Рис. 1.3. Комбинированные операторы присваивания
с неизменяемыми объектами

48 Глава 1. Быстрое введение в процедурное программирование
Если синтаксис языка Python так хитроумно скрывает различия меж
ду изменяемыми и неизменяемыми типами данных, то зачем вообще
делаются такие различия? Причина главным образом заключается
в обеспечении высокой производительности. Действия с неизменяе
мыми типами потенциально имеют более эффективную реализацию
(так как они никогда не изменяются), чем действия с изменяемыми
типами. Кроме того, некоторые типы коллекций, такие как множест
ва, могут включать в себя только данные неизменяемых типов. С дру
гой стороны, изменяемые типы удобнее в использовании. Случаи, ко
гда различия между этими двумя категориями приобретают особую
важность, мы будем рассматривать, например, в главе 4 – при обсуж
дении аргументов функций, имеющих значения по умолчанию; в гла
ве 3 – при обсуждении списков, множеств и некоторых других типов
данных; и в главе 6, где будет показано, как создавать свои собствен
ные типы данных.
Правый операнд в операторе += для списков должен быть итерируе
мым объектом, в противном случае будет возбуждено исключение:
>>> seeds += 5
Traceback (most recent call last):
(Трассировочная информация (самый последний вызов внизу)
...
TypeError: 'int' object is not iterable
(TypeError: объект 'int' не является итерируемым)
Правильный способ расширения списка заключается в том, чтобы ис
пользовать итерируемый объект, например, список:
>>> seeds += [5]
>>> seeds
['sesame', 'sunflower', 'pumpkin', 5]
И, конечно же, сам итерируемый объект, за счет которого выполняет
ся расширение списка, может содержать более одного элемента:
>>> seeds += [9, 1, 5, "poppy"]
>>> seeds
['sesame', 'sunflower', 'pumpkin', 5, 9, 1, 5, 'poppy']
m = [5, 9] m 0 1
5 9
m+=[6] m 0 1 2
5 9 6
Рис. 1.4. Комбинированные операторы присваивания
с изменяемыми объектами

«Золотой запас» Python 49
Попытка добавить простую строку (например, "durian"), а не список,
содержащий ее ( ["durian"]), приведет к логичному, но, возможно, не
ожиданному результату:
>>> seeds = ["sesame", "sunflower", "pumpkin"]
>>> seeds += "durian"
>>> seeds
['sesame', 'sunflower', 'pumpkin', 'd', 'u', 'r', 'i', 'a', 'n']
Оператор += списков расширяет список, добавляя к нему каждый эле
мент итерируемого объекта, находящегося справа, а поскольку строка –
это итерируемый объект, то каждый символ строки будет добавлен
как отдельный элемент. При использовании метода append() его аргу
мент всегда добавляется в список как единый элемент.
Составляющая №7: ввод/вывод
Чтобы писать понастоящему полезные программы, нам необходимо
иметь возможность читать входные данные – например, с клавиатуры
или из файла и выводить результаты – либо на консоль, либо в файлы.
Мы уже использовали встроенную функцию print(), но ее рассмотре
ние отложим до главы 4. В этом подразделе мы сосредоточим свое вни
мание на консольном вводе/выводе, а для чтения и записи файлов бу
дем использовать механизм перенаправления командной оболочки.
В языке Python имеется встроенная функция input(), с помощью кото
рой можно читать ввод пользователя. Эта функция принимает необя
зательный строковый аргумент (который выводится в консоли как
строка приглашения к вводу) и ожидает, пока пользователь введет от
вет и завершит ввод клавишей Enter (или Return). Если пользователь не
введет никакой текст и просто нажмет клавишу Enter, функция input()
вернет пустую строку, в противном случае она возвращает строку, со
держащую ввод пользователя без символа завершения строки.
Ниже приводится первая «полезная» программа. В ней использовано
большинство из уже описанных составляющих, единственное новое
в ней – это вызов функции input():
print("Type integers, each followed by Enter; or just Enter to finish")
total = 0
count = 0
while True:
line = input("integer: ")
if line:
try:
number = int(line)
except ValueError as err:
print(err)
continue
total += number

50 Глава 1. Быстрое введение в процедурное программирование
count += 1
else:
break
if count:
print("count =", count, "total =", total, "mean =", total / count)
Эта программа (файл sum1.py в примерах к книге) состо
ит всего лишь из 17 строк выполняемого программного
кода. Ниже приводятся результаты типичного сеанса ра
боты с программой:
Type integers, each followed by Enter; or just Enter to finish
number: 12
number: 7
number: 1x
invalid literal for int() with base 10: '1x'
(неверный литерал типа int() по основанию 10: '1x')
number: 15
number: 5
number:
count = 4 total = 39 mean = 9.75
Несмотря на небольшие размеры, программа обладает достаточно вы
сокой устойчивостью к ошибкам. Если пользователь введет строку,
которая не может быть преобразована в целое число, проблема будет
обнаружена обработчиком исключений, который выведет соответст
вующее сообщение и передаст управление в начало цикла (инструкция
continues). А заключительная инструкция if предотвратит вывод ста
тистической информации, если пользователь в течение сеанса не ввел
ни одного числа, избежав тем самым выполнения операции деления
на ноль.
Работа с файлами подробно будет описана в главе 7, а пока мы можем
создавать файлы за счет простого перенаправления вывода функции
print() средствами командной оболочки. Например, команда
C:\>test.py > results.txt
приведет к тому, что весь вывод, производимый простой функцией
print(), которая вызывается в вымышленной программе test.py, будет
записан в файл results.txt. Этот прием одинаково хорошо работает как
в консоли Windows, так и в консоли UNIX. Если в системе Windows
уже установлена версия Python 2 и она используется по умолчанию, то
команда должна выглядеть так: C:\Python30\python.exe test.py > re
sults.txt; если путь к интерпретатору Python 3 в переменной окруже
ния PATH стоит первым (мы больше не будем напоминать об этом), то
команда должна выглядеть так: python test.py > results.txt. В систе
мах UNIX нам необходимо сделать программу выполняемой (chmod +x
test.py) и затем вызвать ее командой ./test.py, если каталог, в кото
ром она находится, не содержится в переменной окружения PATH.
Примеры
ккниге,
стр. 15

«Золотой запас» Python 51
Чтение данных можно реализовать за счет перенаправления файла
с данными на вход программы – по аналогии с перенаправлением вы
вода. Однако если использовать перенаправление ввода с программой
sum1.py, она завершится с ошибкой. Это обусловлено тем, что функ
ция input() возбуждает исключение, когда принимает символ EOF
(end of file – конец файла). Ниже приводится более устойчивая версия
(sum2.py), которая способна принимать ввод с клавиатуры или за счет
перенаправления файла:
print("Type integers, each followed by Enter; or ^D or ^Z to finish")
total = 0
count = 0
while True:
try:
line = input()
if line:
number = int(line)
total += number
count += 1
except ValueError as err:
print(err)
continue
except EOFError:
break
if count:
print("count =", count, "total =", total, "mean =", total / count)
Если выполнить команду sum2.py < data\sum2.dat (где sum2.dat – это
файл, содержащий список чисел, по одному числу в строке, и находя
щийся в каталоге data в примерах к книге), на консоль будет выведено:
Type integers, each followed by Enter; or ^D or ^Z to finish
count = 37 total = 1839 mean = 49.7027027027
Мы внесли в программу некоторые незначительные изменения, чтобы
она могла принимать ввод как в интерактивном режиме, так и из пере
направляемого файла. Вопервых, мы изменили признак завершения
программы с пустой строки на символ EOF (Ctrl+D – в UNIX, Ctrl+Z, En
ter – в Windows). Это сделало программу более устойчивой при работе
с файлами, содержащими пустые строки. Вовторых, мы больше не
выводим строку подсказки перед вводом каждого числа, поскольку
в этом нет никакого смысла при перенаправлении ввода. И втретьих,
мы используем единый блок try с двумя обработчиками исключений.
Обратите внимание: в случае ввода неправильного целого числа (с кла
виатуры или в случае «ошибочной» строки в перенаправляемом фай
ле) преобразование int() возбудит исключение ValueError и управление
немедленно будет передано соответствующему блоку except. Это озна

52 Глава 1. Быстрое введение в процедурное программирование
чает, что значения total и count не будут увеличены при встрече
с ошибочными данными, то есть именно то, что нам и требуется.
Точно так же мы могли бы использовать два отдельных блока try:
while True:
try:
line = input()
if line:
try:
number = int(line)
except ValueError as err:
print(err)
continue
total += number
count += 1
except EOFError:
break
Но мы предпочли сгруппировать обработку исключений в конце, что
бы не загромождать лишними конструкциями основной блок обра
ботки.
Составляющая №8: создание и вызов функций
Вполне возможно написать программу, пользуясь исключительно ти
пами данных и управляющими структурами, которые мы рассмотрели
в предыдущих подразделах. Однако очень часто бывает необходимо по
вторно выполнять одну и ту же обработку, но с некоторыми отличиями
в начальных значениях. Язык Python предоставляет возможность
оформления блоков программного кода в виде функций, параметризуе
мых аргументами, которые им передаются. Ниже приводится общий
синтаксис определения функции:
def functionName(arguments):
suite
Часть arguments не является обязательной; для передачи
нескольких аргументов их необходимо отделить друг от
друга запятыми. Любая функция в языке Python возвра
щает некоторое значение – по умолчанию это значение
None; если мы явно не возвращаем значение с помощью
инструкции return value, где value – это возвращаемое
значение. Возвращаемое значение может быть единст
венным значением или кортежем возвращаемых значе
ний. Возвращаемое значение может игнорироваться вы
зывающей программой, в этом случае оно просто унич
тожается.
Примечательно, что инструкция def действует как оператор присваи
вания. При выполнении инструкции def создаются новый объект Инструкцияreturn ,
стр. 205

«Золотой запас» Python 53
функция и ссылка на объект с указанным именем, которая указывает
на объектфункцию. Поскольку функции – это объекты, они могут со
храняться в виде элементов коллекций и передаваться в качестве аргу
ментов другим функциям, в чем вы сможете убедиться в последующих
главах.
Часто в интерактивных консольных приложениях возникает необхо
димость получить целое число от пользователя. Ниже приводится
функция, которая выполняет эту операцию:
def get_int(msg):
while True:
try:
i = int(input(msg))
return i
except ValueError as err:
print(err)
Эта функция принимает единственный аргумент, msg. Внутри цикла
while пользователю предлагается ввести целое число. Если он вводит
чтото неправильное, возбуждается исключение ValueError. В этом слу
чае выводится сообщение об ошибке, и цикл повторяется. После ввода
правильного целого числа оно возвращается вызывающей программе.
Ниже показано, как можно использовать эту функцию:
age = get_int("enter your age: ")
В этом примере единственный параметр является обязательным, пото
му что мы не предусматриваем значение по умолчанию для него.
В действительности язык Python предоставляет очень сложный и гиб
кий синтаксис для описания параметров функции, включая значения
аргументов по умолчанию, а также позиционные и именованные аргу
менты. Полный синтаксис объявления функций будет рассматривать
ся в главе 4.
Создавая собственные функции, мы можем удовлетворить любые на
ши потребности, но во многих случаях в этом нет необходимости, по
тому что в составе языка Python имеется масса встроенных функций
и намного больше функций содержится в модулях стандартной биб
лиотеки. Поэтому многое из того, что нам может потребоваться, уже
существует.
Модуль в языке Python – это обычный файл .py с программным кодом
на языке Python, содержащим определения функций и классов (не
стандартных типов данных) и некоторых переменных. Чтобы полу
чить доступ к функциональным возможностям модуля, его сначала
необходимо импортировать. Например:
import sys

54 Глава 1. Быстрое введение в процедурное программирование
Для импортирования модуля используется инструкция import, за ко
торой следует имя файла .py, но без расширения. 1 Как только модуль
будет импортирован, мы можем обращаться к его функциям, классам
или переменным, содержащимся в нем. Например:
print(sys.argv)
Модуль sys содержит переменную argv – список, первым элементом ко
торого является имя, под которым была вызвана программа, а вторым
и последующими элементами – аргументы командной строки. Две вы
шеприведенные строки составляют целую программу echoargs.py. Ес
ли вызвать эту программу командой echoargs.py
–v, она выведет ['echo
args.py', '
–v']. (В системах UNIX первым элементом может быть стро
ка './echoargs.py'.)
В общем случае синтаксис обращения к функции из мо
дуля выглядит так: moduleName.functionName(arguments).
Здесь используется оператор точки («доступ к атрибу
ту»), который был представлен в составляющей №3.
Стандартная библиотека содержит огромное число моду
лей, и многие из них мы будем использовать в этой кни
ге. Имена всех стандартных модулей состоят только из
символов нижнего регистра, поэтому для отличия своих
модулей при именовании модулей многие программи
сты используют прием чередования регистра символов
(например, MyModule).
В качестве примера приведем модуль random (в стандартной библиоте
ке хранится в файле random.py), содержащий множество полезных
функций:
import random
x = random.randint(1, 6)
y = random.choice(["apple", "banana", "cherry", "durian"])
После выполнения этих инструкций переменная x будет содержать це
лое число в диапазоне от 1 до 6 включительно, а переменная y – одну из
строк из списка, переданного функции random.choice().
Как правило, все инструкции import помещаются в нача
ло файлов .py, после строки shebang и после комментари
ев с описанием модуля. (Порядок описания модулей рас
сматривается в главе 5.) Мы рекомендуем сначала им
портировать модули из стандартной библиотеки, затем
модули сторонних разработчиков, а потом ваши собст
венные.
1 Модуль sys, некоторые другие модули и модули, реализованные на языке C,
не обязательно должны храниться в виде файлов с расширением .py, но ис
пользуются они точно так же.
Оператор
точки,
стр. 35
Shebang –
строка ( #!),
стр. 25

Примеры 55
Примеры
В предыдущем разделе мы узнали достаточно много о языке Python,
чтобы иметь возможность приступить к созданию своих программ.
В этом разделе мы рассмотрим две законченные программы, которые
используют только те возможности языка Python, которые были опи
саны выше. Примеры должны продемонстрировать уже доступные
возможности и помочь закрепить полученные знания.
В последующих главах мы узнаем значительно больше о языке Python
и о библиотеке, благодаря чему мы сможем писать более короткие
и более надежные программы, чем те, что представлены здесь. Но для
начала необходимо заложить фундамент, на котором будет строиться
остальное здание.
bigdigits.py
Первая программа, которую мы рассмотрим, очень короткая, хотя в ней
имеется несколько интересных особенностей, включая список спи
сков. Эта программа принимает число в виде аргумента командной
строки и выводит его на экран, используя «большие» цифры.
На серверах, где множество пользователей совместно пользуются вы
сокоскоростным принтером, обычной практикой считается, когда за
дание для печати каждого пользователя предваряется титульным лис
том, на котором с помощью описываемой методики выводится имя
пользователя и некоторая дополнительная идентификационная ин
формация.
Мы будем рассматривать программный код в три этапа: импортирова
ние, создание списков, хранящих данные, используемые программой,
и собственно обработка. Но для начала посмотрим пример вывода:
bigdigits.py 41072819
* * *** ***** *** *** * ****
** ** * * * * * * * ** * *
* * * * * * * * * * * * *
* * * * * * * *** * ****
****** * * * * * * * * *
* * * * * * * * * *
* *** *** * ***** *** *** *
Мы не показали здесь строку приглашения к вводу в консоли (или ве
дущую комбинацию ./ для пользователей UNIX) – к настоящему вре
мени мы считаем их наличие чемто подразумеваемым.
import sys
Поскольку нам потребуется читать аргумент командной строки (выво
димое число), нам необходимо будет обращаться к списку sys.argv, по
этому начнем с того, что импортируем модуль sys.

56 Глава 1. Быстрое введение в процедурное программирование
Каждая цифра будет представлена списком строк. Например, ниже
показано представление цифры 0:
Zero = [" *** ",
" * * ",
"* *",
"* *",
"* *",
" * * ",
" *** "]
Обратите внимание, что список строк Zero располагается
в нескольких строках. Обычно инструкции языка Py
thon занимают одну строку, но они могут занимать и не
сколько строк, если они представлены выражением
в скобках. Литералы списков, множеств, словарей, спи
сок аргументов функции или многострочные инструк
ции, в которых символ конца строки экранируется сим
волом обратного слеша (\) – во всех этих случаях инст
рукции могут занимать несколько строк, при этом отсту
пы во второй и последующих строках не учитываются.
Каждый список, представляющий цифру, содержит семь строк, все
одинаковой длины, причем эта длина может изменяться от цифры
к цифре. Списки с изображением остальных цифр строятся аналогич
ным образом, но они выведены несколько иначе – для компактности,
а не для ясности:
One = [" * ", "** ", " * ", " * ", " * ", " * ", "***"]
Two = [" *** ", "* *", "* * ", " * ", " * ", "* ", "*****"]
# ...
Nine = [" ****", "* *", "* *", " ****", " *", " *", " *"]
Последний элемент данных, который нам потребуется, – это список
всех списков цифр:
Digits = [Zero, One, Two, Three, Four, Five, Six, Seven, Eight, Nine]
Мы могли бы создать список списков Digits, не создавая промежуточ
ные переменные. Например:
Digits = [
[" *** ", " * * ", "* *", "* *", "* *",
" * * ", " *** "], # Zero
[" * ", "** ", " * ", " * ", " * ", " * ", "***"], # One
# ...
[" ****", "* *", "* *", " ****", " *", " *",
" *"] # Nine
]
Мы предпочли использовать отдельные переменные для каждого чис
ла для удобочитаемости и потому, что вариант на основе переменных
выглядит более аккуратным.
Ти п set ,
стр. 144
Ти п dict ,
стр. 151

Примеры 57
Далее приведена остальная часть программного кода, чтобы вы могли
ознакомиться с ней, прежде чем продолжить читать пояснения.
try:
digits = sys.argv[1]
row = 0
while row < 7:
line = ""
column = 0
while column < len(digits):
number = int(digits[column])
digit = Digits[number]
line += digit[row] + " "
column += 1
print(line)
row += 1
except IndexError:
print("usage: bigdigits.py ")
except ValueError as err:
print(err, "in", digits)
Программный код полностью обернут в конструкцию обработки ис
ключений, которая способна перехватывать два вида исключений, ко
гда чтото пойдет не так. Сначала мы пытаемся извлечь параметр ко
мандной строки. Индексация элементов в списке sys.argv начинается
с нуля, как и в любых других списках в языке Python. Элемент списка
с индексом 0 – это имя программы, под которым она была вызвана, по
этому данный список в любой программе имеет как минимум один
элемент. Если при вызове программы аргумент не был указан, то при
попытке обратиться ко второму элементу списка, состоящему из одно
го элемента, будет возбуждено исключение IndexError. В этом случае
управление немедленно будет передано соответствующему блоку обра
ботки исключений, где мы просто выводим информацию о порядке ис
пользования программы. После этого выполнение программы продол
жается за концом блока try, но так как за его пределами больше нет
программного кода, то программа просто завершается.
Если исключение IndexError не возбуждается, в строку
digits записывается аргумент командной строки, кото
рая, как мы надеемся, состоит из одних цифр. (Вспомни
те, как в описании составляющей №2 мы говорили, что
идентификаторы в языке Python чувствительны к реги
стру символов, поэтому digits иDigits – это разные иден
тификаторы.) Каждая большая цифра представлена се
мью строками, и чтобы правильно вывести число, мы
должны сначала вывести верхние строки всех цифр, за
тем следующие строки всех цифр и т. д., пока не будут
выведены все семь строк. Для обхода всех строк мы ис
пользуем цикл while. Вместо него можно было бы ис
пользовать цикл for row in (0, 1, 2, 3, 4, 5, 6):,
Функция range() ,
стр. 167

58 Глава 1. Быстрое введение в процедурное программирование
а позднее будет представлен более интересный способ, основанный на
использовании встроенной функции range().
Переменная line используется для сборки строки из отдельных строк
всех имеющихся цифр. Далее выполняется обход в цикле всех коло
нок, то есть всех цифр в аргументе командной строки. Мы извлекаем
каждый символ, используя выражение digits[column], и преобразуем
полученную цифру в целое число number. Если при преобразовании воз
никает ошибка, возбуждается исключение ValueError и управление не
медленно передается соответствующему обработчику ошибок. В этом
случае мы выводим сообщение об ошибке, и выполнение продолжает
ся за пределами блока try. Как уже отмечалось ранее, поскольку за его
пределами больше нет программного кода, то программа просто завер
шается.
В случае благополучного преобразования значение number использует
ся как индекс в списке Digits, из которого извлекается список строк
digit. Затем мы добавляем строку с индексом row из этого списка в соз
даваемую строку line и добавляем два пробела, чтобы отделить цифры
друг от друга.
Каждый раз, когда внутренний цикл while завершает работу, мы выво
дим собранную строку. Ключом к пониманию этой программы являет
ся фрагмент кода, где выполняется сборка строки line из строк отдель
ных цифр. Попробуйте запустить эту программу, чтобы получить
представление о том, как она работает. Мы еще вернемся к этой про
грамме в упражнениях, чтобы попытаться несколько улучшить вывод.
generate_grid.py
Часто бывает необходимо сгенерировать тестовые данные. Не сущест
вует какойлибо универсальной программы, которая делала бы это,
поскольку требования к тестовым данным могут изменяться очень
сильно. Язык Python часто используется для создания массивов тесто
вых данных, потому что программы на языке Python писать и моди
фицировать не составляет большого труда. В этом подразделе мы соз
дадим программу, которая будет генерировать матрицу из случайных
целых чисел, где пользователь сможет указать число строк и столбцов,
а также диапазон целых чисел. Для начала рассмотрим пример запус
ка программы:
generate_grid.py
rows: 4x
invalid literal for int() with base 10: '4x'
(ошибочный литерал типа int() по основанию 10: '4x')
rows: 4
columns: 7
minimum (or Enter for 0): 100
maximum (or Enter for 1000):
554 720 550 217 810 649 912

Примеры 59
24 908 742 65 74 724 825
711 968 824 505 741 55 723
180 60 794 173 487 4 35
Программа работает в интерактивном режиме, и при первой попытке
мы допустили ошибку при вводе числа строк. Программа ответила вы
водом сообщения об ошибке и затем попросила повторить ввод числа
строк. При запросе ввести максимальное число мы просто нажали кла
вишу Enter, чтобы использовать число по умолчанию.
Мы будем рассматривать программный код в четыре приема: импорт,
определение функции get_int() (более сложная версия, чем была пока
зана в составляющей №8), взаимодействие с пользователем, во время
которого принимаются вводимые числа, и собственно обработка.
import random
Чтобы получить доступ к функции random.randint(), нам
необходимо импортировать модуль random.
def get_int(msg, minimum, default):
while True:
try:
line = input(msg)
if not line and default is not None:
return default
i = int(line)
if i < minimum:
print("must be >=", minimum)
else:
return i
except ValueError as err:
print(err)
Функция требует три аргумента: строку приглашения к вводу, мини
мальное значение и значение по умолчанию. Если пользователь просто
нажимает клавишу Enter, у функции имеется две возможности. Если
аргумент default имеет значение None, то есть значение по умолчанию
не задано, то управление передается строке i = int(line). Попытка
преобразования терпит неудачу (потому что пустая строка не может
быть преобразована в целое число), и возбуждается исключение Value
Error. Но если аргумент default имеет значение, отличное от None, оно
возвращается вызывающей программе. В противном случае функция
попытается преобразовать в целое число текст, введенный пользовате
лем, и если преобразование будет выполнено благополучно, функция
проверит, чтобы введенное число было не меньше аргумента minimum.
Таким образом, функция всегда будет возвращать либо значение аргу
мента default (если пользователь просто нажмет клавишу Enter), либо
целое число, большее или равное значению аргумента minimum.
rows = get_int("rows: ", 1, None)
columns = get_int("columns: ", 1, None)
Функция random.
randint() ,
стр. 54

60 Глава 1. Быстрое введение в процедурное программирование
minimum = get_int("minimum (or Enter for 0): ", 1000000, 0)
default = 1000
if default < minimum:
default = 2 * minimum
maximum = get_int("maximum (or Enter for " + str(default) + "): ",
minimum, default)
Наша функция get_int() упрощает получение числа строк и столбцов,
а также минимальное и максимальное значения желаемого диапазона
случайных чисел. Для числа столбцов и строк мы передаем в аргумен
те default значение None, что означает отсутствие значения по умолча
нию, то есть пользователь должен ввести целое число. При запросе ми
нимального значения мы указали значение по умолчанию 0, а при за
просе максимального значения выбирается значение по умолчанию
1000, или в два раза больше минимального значения, если минималь
ное значение окажется больше или равно 1000.
Как уже отмечалось в предыдущем примере, список аргументов при
вызове функции может занимать любое число строк в исходном про
граммном коде, при этом величина отступов во второй и последующих
строках не будет иметь значения.
Получив от пользователя число строк и столбцов, а также желатель
ные минимальное и максимальное значения случайных чисел, можно
приступать к работе.
row = 0
while row < rows:
line = ""
column = 0
while column < columns:
i = random.randint(minimum, maximum)
s = str(i)
while len(s) < 10:
s = " " + s
line += s
column += 1
print(line)
row += 1
Для создания матрицы мы используем три цикла while. Внешний
цикл обрабатывает строки, средний – столбцы и внутренний – симво
лы. В среднем цикле мы получаем случайное число из указанного диа
пазона и преобразуем его в строку. Внутренний цикл while обеспечива
ет дополнение строки ведущими пробелами так, чтобы каждое число
было представлено строкой из 10 символов. Строка line используется
для накопления чисел в строке матрицы, которая выводится после то
го, как будут добавлены числа для всех колонок. На этом мы заверша
ем наш второй пример.

В заключение 61
Язык Python предоставляет весьма широкие возможно
сти по форматированию строк, а также обеспечивает
поддержку цикла for ... in. Поэтому в более реалистич
ных версиях bigdigits.py и generate_grid.py можно было
бы использовать циклы for ... in, а в generate_grid.py
можно было бы также использовать возможности языка
Python по форматированию строк вместо неуклюжего
приема дополнения пробелами. Но мы ограничили себя
восемью составляющими языка Python, представленны
ми в этой главе, которых оказалось вполне достаточно
для написания законченных и полезных программ. В ка
ждой последующей главе мы будем знакомиться с новы
ми особенностями языка Python, поэтому по мере про
движения вперед мы будем видеть (и обретать способ
ность писать) все более сложные программы.
В заключение
В этой главе мы узнали, как редактировать и запускать программы на
языке Python. и рассмотрели пару маленьких, но законченных про
грамм. Но большая часть главы была посвящена восьми составляю
щим «золотого запаса» языка Python, знания которых вполне доста
точно для создания настоящих программ.
Мы начали с двух основных типов данных в языке Python – int и str.
Целочисленные литералы записываются точно так же, как и во мно
гих других языках программирования. Строковые литералы записы
ваются либо с помощью кавычек, либо с помощью апострофов. Неваж
но, что будет использоваться – кавычки или апострофы, главное, что
бы с обоих концов литерала использовался один и тот же тип кавычек.
У нас имеется возможность выполнять преобразования между строка
ми и целыми числами, например, int("250") и str(125). Если преобра
зование строки в целое число терпит неудачу, возбуждается исключе
ние ValueError; с другой стороны, в строку может быть преобразовано
практически все, что угодно.
Строки – это последовательности, поэтому те функции и операции, ко
торые могут применяться к последовательностям, могут применяться
и к строкам. Например, обратиться к определенному символу можно
с помощью оператора доступа ([]), конкатенацию строк можно выпол
нить с помощью оператора +, а дополнение одной строки другой – с по
мощью оператора +=. Так как строки являются неизменными объекта
ми, операция дополнения строки за кулисами создает новую строку,
представляющую собой результат конкатенации заданных строк,
и перепривязывает ссылку на строковый объект, указанный слева от
знака =, на результирующую строку. Мы также имеем возможность
выполнять итерации по символам в строке, используя цикл for ... in,
Метод str.
format() ,
стр. 100

62 Глава 1. Быстрое введение в процедурное программирование
а чтобы определить количество символов в строке, можно использо
вать функцию len().
При использовании неизменяемых объектов, таких как строки, целые
числа и кортежи, мы можем писать программный код, как если бы
ссылки на объекты были переменными, то есть как если бы ссылка на
объект была самим объектом, на который она ссылается. Точно так же
можно поступать и в случае изменяемых объектов, только в этом слу
чае изменения в объекте будут затрагивать все ссылки, указывающие
на этот объект, – эта проблема будет описываться в главе 3.
В языке Python имеется несколько встроенных типов коллекций,
а также ряд типов коллекций имеется в стандартной библиотеке. Мы
познакомились с типами list и tuple, в частности, мы узнали, как соз
давать кортежи и списки из литералов, например, even = [2, 4, 6, 8].
Списки, как и все остальное в языке Python, являются объектами, по
этому мы можем пользоваться их методами, например, вызов метода
even.append(10) добавит дополнительный элемент в список. Подобно
строкам, списки и кортежи являются последовательностями, благода
ря чему можно выполнять итерации по их элементам, используя цикл
for ... in, и определять количество элементов в коллекции с помо
щью функции len(). Кроме того, имеется возможность извлекать из
списков или кортежей отдельные элементы, используя оператор дос
тупа к элементам ([]), объединять два списка или кортежа с помощью
оператора + и дополнять один список другим с помощью оператора +=.
Если потребуется добавить в конец списка единственный элемент,
можно использовать метод list.append() или оператор +=, которому
следует передать список, состоящий из одного элемента, например,
even += [12]. Поскольку списки являются изменяемыми объектами,
для изменения отдельных элементов можно использовать оператор [],
например, even[1] = 16.
Быстрые операторы is и not is удобно использовать, чтобы выяснить,
не ссылаются ли две ссылки на один и тот же объект, что очень удобно,
особенно когда выполняется проверка ссылки на уникальный встроен
ный объект None. В вашем распоряжении имеются все обычные опера
торы сравнения (<, <=, ==, !=, >=, >), но они могут использоваться только
с совместимыми типами данных и только с теми, которые поддержи
вают эти операции. Мы пока познакомились только с типами данных
int, str, list и tuple – все они поддерживают полный набор операторов
сравнения. Попытка сравнения несовместимых типов, например,
сравнение типа int с типом str или list, будет приводить к возбужде
нию исключения TypeError.
Язык Python поддерживает стандартные логические операторы and, or
и not. Выражения с участием операторов and
и or вычисляются по со
кращенной схеме и возвращают операнд, определяющий результат
выражения, причем результат не обязательно будет иметь тип Boolean

В заключение 63
(хотя и может быть приведен к типу Boolean). Оператор not всегда дает
в результате либо True, либо False.
У нас имеется возможность проверить вхождение элементов в последо
вательности, включая строки, списки и кортежи, используя операто
ры in и not in. Проверка на вхождение в списки и кортежи произво
дится с использованием медленного алгоритма линейного поиска,
а проверка вхождения в строки реализует потенциально более скоро
стной гибридный алгоритм, но производительность в этих случаях
редко является проблемой, за исключением случаев использования
очень длинных строк, списков и кортежей. В главе 3 мы познакомим
ся с такими типами коллекций, как ассоциативные массивы и множе
ства, для которых операция проверки на вхождение выполняется
очень быстро. Кроме того, с помощью функции type() можно опреде
лить тип объекта, на который указывает ссылка, но обычно эта функ
ция используется только для нужд тестирования и отладки.
Язык Python предоставляет несколько управляющих структур, вклю
чая условный оператор if … elif … else, цикл с предусловием while,
цикл по последовательности for ... in и конструкцию обработки ис
ключений try ... except. Имеется возможность преждевременно пре
рывать циклы while и for ... in с помощью инструкции break или пе
редавать управление в начало цикла с помощью инструкции continue.
В языке Python имеется поддержка обычных арифметических опера
торов, включая +,
–, * и /, единственная необычность состоит в том, что
оператор / всегда возвращает число с плавающей точкой, даже если
оба операнда являются целыми числами. (Целочисленное деление,
имеющееся во многих других языках программирования, реализова
но и в языке Python – в виде оператора //.) Кроме того, язык Python
предоставляет комбинированные операторы присваивания, такие как
+= и *=. Они за кулисами создают новые объекты, если слева находится
неизменяемый операнд. Как уже отмечалось ранее, арифметические
операторы перегружены для применения к операндам типов str и list.
Консольный ввод/вывод можно реализовать с помощью функций in
put() и print(), а благодаря возможности перенаправлять ввод/вывод
в файлы, мы можем использовать те же самые встроенные функции
для чтения и записи файлов.
В дополнение к богатому набору встроенных функциональных воз
можностей имеется обширная стандартная библиотека; модули стано
вятся доступными после импортирования их с помощью инструкции
import.
Одним из наиболее часто импортируемых модулей является модуль
sys, в котором имеется список sys.argv, хранящий аргументы команд
ной строки. Если в языке Python отсутствует какаялибо необходимая
нам функция, с помощью инструкции def мы легко можем создать
свою собственную функцию, действующую так, как нам нужно.

64 Глава 1. Быстрое введение в процедурное программирование
Используя функциональные возможности, описанные в этой главе,
уже можно писать короткие и полезные программы на языке Python.
В следующей главе мы больше узнаем о типах данных в языке Python,
более подробно рассмотрим типы int и str, а также познакомимся с не
которыми совершенно новыми типами данных. В главе 3 мы больше
узнаем о кортежах, списках, а также о некоторых других типах кол
лекций в языке Python. Затем в главе 4 мы более подробно рассмотрим
управляющие структуры языка Python и узнаем, как создавать свои
функции, позволяющие избежать дублирования программного кода
и способствующие многократному его использованию.
Упражнения
Цель упражнений, которые приводятся здесь и будут
приводиться на протяжении всей книги, состоит в том,
чтобы стимулировать вас на проведение экспериментов
с языком Python и помочь получить практический опыт
с одновременным закреплением пройденного материала.
В примерах и упражнениях рассматриваются проблемы
числовой обработки и обработки текста, что может инте
ресовать самую широкую аудиторию, а кроме того, раз
меры упражнений настолько невелики, что при их реше
нии вам придется главным образом думать и учиться,
а не просто вводить программный код. Решение для ка
ждого упражнения можно найти в примерах книги.
1. Было бы довольно интересно написать версию программы bigdig
its.py, которая для рисования цифр использовала бы не символ *,
а соответствующие цифровые символы. Например:
bigdigits_ans.py 719428306
77777 1 9999 4 222 888 333 000 666
7 11 9 9 44 2 2 8 8 3 3 0 0 6
7 1 9 9 4 4 2 2 8 8 3 0 0 6
7 1 9999 4 4 2 888 33 0 0 6666
7 1 9 444444 2 8 8 3 0 0 6 6
7 1 9 4 2 8 8 3 3 0 0 6 6
7 111 9 4 22222 888 333 000 666
Эта задача может быть решена двумя способами. Самый простой
способ заключается в том, чтобы просто заменить символы * в спи
сках. Но этот путь не слишком гибкий, и хотелось бы, чтобы вы по
шли другим путем. Попробуйте изменить программный код так,
чтобы вместо добавления в строку за один проход целых строк (dig
it[row]), вырезанных из изображений цифр, в строку добавлялись
бы символ за символом, и при встрече символа * он заменялся бы
соответствующим цифровым символом.
Примеры
ккниге,
стр. 15

Упражнения 65
Сделать это можно, скопировав исходный программный код из big
digits.py и изменив пять строк. Это упражнение не столько слож
ное, сколько с подвохом. Решение приводится в файле bigdigits_
ans.py.
2. Среда разработки IDLE может использоваться как мощный кальку
лятор, но иногда бывает удобно иметь калькулятор, специализиро
ванный для решения определенного круга задач. Напишите про
грамму, которая в цикле while предлагала бы пользователю ввести
число, постепенно накапливая список введенных чисел. Затем, ко
гда пользователь завершит работу с программой (простым нажати
ем клавиши Enter), она выводила бы числа, введенные пользовате
лем, количество введенных чисел, их сумму, наименьшее и наи
большее число и среднее значение (сумма / количество). Ниже при
водится пример сеанса работы с программой:
average1_ans.py
enter a number or Enter to finish: 5
enter a number or Enter to finish: 4
enter a number or Enter to finish: 1
enter a number or Enter to finish: 8
enter a number or Enter to finish: 5
enter a number or Enter to finish: 2
enter a number or Enter to finish:
numbers: [5, 4, 1, 8, 5, 2]
count = 6 sum = 25 lowest = 1 highest = 8 mean = 4.16666666667
Решение этого упражнения потребует примерно четыре строки для
инициализации необходимых переменных (пустой список – это
просто литерал []) и не более 15 строк для цикла while, включая об
работку ошибок. Для вывода результатов в конце потребуется всего
пара строк, поэтому вся программа, включая пустые строки для
лучшей читаемости, должна уместиться примерно в 25 строк.
3. В некоторых ситуациях нам может потребоваться сгенерировать
тестовый текст, который пригодится, например, при разработке ди
зайна вебсайта, когда действительное содержимое еще отсутству
ет, или при разработке программы составления отчетов. Напишите
программу, которая создавала бы жуткие поэмы (способные посра
мить поэзию Вогона (Vogon)).
Создайте списки слов, например, артиклей («the»,
«a» и других), имен существительных («cat», «dog»,
«man», «woman»), глаголов («sang», «ran», «jumped»)
и наречий («loudly», «quietly», «well», «badly»). За
тем выполните пять циклов и на каждой итерации
с помощью функции random.choice() выберите ар
тикль, существительное, глагол и наречие. С помо
щью функции random.randint() выберите одну из двух
структур предложений: артикль, существительное,
Функции random.
randint()
и random.
choice() ,
стр. 54

66 Глава 1. Быстрое введение в процедурное программирование
глагол и наречие, или артикль, существительное и глагол, –
и выведите предложение. Ниже приводится пример запуска такой
программы:
awfulpoetry1_ans.py
her man heard politely
his boy sang
another woman hoped
her girl sang slowly
the cat heard loudly
Для решения этого упражнения вам потребуется импортировать
модуль random. Списки могут занимать порядка 4–10 строк, в зави
симости от того, как много слов вы подберете для каждого из них,
и сам цикл будет занимать не более 10 строк, поэтому вся програм
ма, включая пустые строки для лучшей читаемости, должна уме
ститься примерно в 20 строк. Решение приводится в файле awfulpo
etry1_ans.py.
4. Чтобы сделать поэтическую программу более универсальной, до
бавьте в нее программный код, дающий пользователю возможность
определить количество выводимых строк (от 1 до 10 включитель
но), передавая число в виде аргумента командной строки. Если про
грамма вызывается без аргумента, она должна по умолчанию выво
дить пять строк, как и раньше. Для решения этого упражнения вам
потребуется изменить главный цикл (это может быть цикл while).
Не забывайте, что операторы сравнения в языке Python могут объ
единяться в цепочки, поэтому здесь вам не потребуется использо
вать логический оператор and при проверке вхождения аргумента
командной строки в заданный диапазон. Функциональность про
граммы может быть расширена за счет приблизительно десяти
строк программного кода. Решение приводится в файле awfulpoet
ry2_ans.py.
5. В программе из упражнения 2 было бы неплохо реализовать нахож
дение не только среднего значения, но и медианы, но для этого при
дется отсортировать список. Сортировка списков в языке Python
легко осуществляется с помощью метода list.sort(), но мы еще не
рассматривали этот метод, поэтому вам не следует использовать
его. Дополните программу вычисления среднего значения про
граммным кодом, который сортировал бы список чисел. Эффектив
ность не имеет значения, поэтому используйте самый простой спо
соб сортировки, какой только придет вам на ум. Отсортировав спи
сок, можно будет найти и медиану, которая будет являться значе
нием элемента в середине, если список содержит нечетное число
элементов, и средним значением от двух средних элементов, если
список содержит четное число элементов. Найдите медиану и выве
дите ее вместе с остальной информацией.

Упражнения 67
Решение этого упражнения может оказаться не совсем простым де
лом, особенно для неопытных программистов. Даже если у вас име
ется опыт работы с языком Python, вы все равно можете столкнуть
ся с трудностями, так как вы ограничены только тем кругом воз
можностей, которые мы рассмотрели в этой главе. Реализация сор
тировки займет примерно дюжину строк, и вычисление медианы
(нельзя использовать оператор деления по модулю, так как он еще
не рассматривался) еще четыре строки. Решение приводится в фай
ле average2_ans.py.

2
Типы данных
В этой главе мы приступаем к более подробному изучению языка Py
thon. Для начала мы обсудим правила создания имен, которые мы да
ем ссылкам на объекты, и познакомимся со списком ключевых слов
языка Python. Затем мы рассмотрим наиболее важные типы данных,
исключая коллекции, которые будут рассматриваться в главе 3. Типы
данных считаются встроенными за исключением тех, что определены
в стандартной библиотеке. Единственное отличие встроенных типов
данных от библиотечных состоит в том, что, прежде чем воспользо
ваться последними, нам необходимо импортировать соответствующие
модули и мы должны квалифицировать имена типов именами моду
лей, в которых они определяются. Более подробно об импортировании
мы поговорим в главе 5.
Идентификаторы и ключевые слова
Создавая элемент данных, мы можем либо присвоить его
переменной, либо вставить в коллекцию. (Как уже отме
чалось в предыдущей главе, когда в языке Python вы
полняется операция присваивания, в действительности
происходит связывание ссылки на объект с объектом
в памяти, который хранит данные.) Имена, которые да
ются ссылкам на объекты, называются идентификато
рами, или просто именами.
Допустимый идентификатор в языке Python – это последовательность
символов произвольной длины, содержащей «начальный символ»
и ноль или более «символов продолжения». Такой идентификатор дол
жен следовать определенным правилам и соглашениям. Ссылки
на объекты,
стр. 29
•Идентификаторы и ключевые слова
•Целочисленные типы
•Числа с плавающей точкой
•Строки

Идентификаторы и ключевые слова 69
Первое правило касается начального символа и символов продолже
ния. Начальным символом может быть любой символ, который в ко
дировке Юникод рассматривается как принадлежащий диапазону ал
фавитных символов ASCII («a», «b», …, «z», «A», «B», …, «Z»), символ
подчеркивания («_»), а также символы большинства национальных
(не английских) алфавитов. Каждый символ продолжения может быть
любым символом из тех, что пригодны в качестве начального символа,
а также любым непробельным символом, включая символы, которые
в кодировке Юникод считаются цифрами, такие как («0», «1», …,
«9»), и символ Каталана «·». Идентификаторы чувствительны к реги
стру, поэтому TAXRATE, Taxrate, TaxRate, taxRate и taxrate – это пять раз
ных идентификаторов.
Точный перечень символов, допустимых для использования в качестве
начального символа и символов продолжения, описывается в докумен
тации по языку Python (справочник «Language reference», раздел «Le
xical analysis», подраздел «Identifiers and keywords»
1) или в PEP 3131 2
(раздел «Supporting NonASCII Identifiers»).
Второе правило гласит, что идентификатор не должен совпадать с ка
кимлибо из ключевых слов языка Python, поэтому мы не можем ис
пользовать имена, которые приводятся в табл. 2.1.
Таблица 2.1. Ключевые слова языка Python
С многими из них мы уже встречались в предыдущей главе, хотя
11 ключевых слов – assert, class, del, finally, from, global, lambda, non
local, raise, with и yield мы еще не рассматривали.
Первое соглашение выглядит так: «Не использовать в качестве своих
идентификаторов любые предопределенные имена». Поэтому старай
1 http://docs.python.org/3.0/reference/lexical_analysis.html# identifiersand
keywords. – Прим. перев.
2 Аббревиатура «PEP» расшифровывается как Python Enhancement Proposal
(предложение по расширению Python). Если ктото желает изменить или
дополнить язык Python, и его стремление пользуется широкой поддерж
кой сообщества, он посылает PEP с подробным описанием своего предло
жения, чтобы его можно было рассмотреть в официальном порядке; в неко
торых случаях, как это произошло с PEP 3131, предложение принимается
и реализуется. Все предложения PEP можно найти на странице www.py
thon.org/dev/peps/. and continue except global lambda pass while
as def False if None raise with
assert del finally import nonlocal return yield
break elif for in not True
class else from is or try

70 Глава 2. Типы данных
тесь не использовать такие идентификаторы, как NotImplemented и El
lipsis, имена любых встроенных типов (таких как int, float, list, str
иtuple), а также имена любых встроенных функций или исключений.
Как определить, относится ли тот или иной идентификатор к этим ка
тегориям? В языке Python имеется встроенная функция dir(), которая
возвращает список атрибутов объекта. Если эта функция вызывается
без аргументов, она возвращает список встроенных атрибутов языка
Python. Например:
>>> dir()
['__builtins__', '__doc__', '__name__']
Атрибут __builtins__ в действительности является модулем, в котором
определены все встроенные атрибуты языка Python. Его можно ис
пользовать в качестве аргумента функции dir():
>>> dir(__builtins__)
['ArithmeticError', 'AssertionError', 'AttributeError',
...
'sum', 'super', 'tuple', 'type', 'vars', 'zip']
В списке присутствует более 130 имен, поэтому мы опустили значи
тельную их часть. Имена, начинающиеся с символов верхнего регист
ра, являются именами встроенных исключений. Остальные имена
представляют функции и типы данных.
Если запоминание или поиск идентификаторов, использования кото
рых следует избегать, кажется вам слишком утомительным, то можно
воспользоваться инструментом проверки программного кода на языке
Python, таким как PyLint (www.logilab.org/project/name/pylint). Этот
инструмент поможет вам также выявлять множество других фактиче
ских или потенциальных проблем в программах на языке Python.
Второе соглашение касается использования символа подчеркивания
(_). Не должны использоваться имена, начинающиеся и заканчиваю
щиеся двумя символами подчеркивания (такие как __lt__). В языке
Python определено множество различных специальных методов и пе
ременных с такими именами (и в случае специальных методов мы мо
жем заменять их, то есть создать свои версии этих методов), но мы не
должны вводить новые имена такого рода. Такие имена будут рас
сматриваться в главе 6. Имена, начинающиеся с одного или двух сим
волов подчеркивания (и не завершающиеся двумя символами подчер
кивания), в некоторых контекстах интерпретируются как специаль
ные. Мы продемонстрируем это в главе 5, когда будем использовать
имена, начинающиеся с одного символа подчеркивания, и в главе 6,
когда будем использовать имена, начинающиеся с двух символов под
черкивания.
Символ подчеркивания сам по себе может использоваться в качестве
идентификатора; внутри интерактивной оболочки интерпретатора или
в командной оболочке Python в переменной с именем _ сохраняется ре

Идентификаторы и ключевые слова 71
зультат последнего вычисленного выражения. Во время выполнения
обычной программы идентификатор _ отсутствует, если мы явно не оп
ределяем его в своем программном коде. Некоторые программисты
любят использовать _ в качестве идентификатора переменной цикла
вциклах for ... in, когда не требуется обращаться к элементам, по
которым выполняются итерации. Например:
for _ in (0, 1, 2, 3, 4, 5):
print("Hello")
Но имейте в виду, что те, кто пишет программы, кото
рые затем интернационализируются, часто используют
идентификатор _ в качестве имени функции перевода.
Делается это, чтобы вместо необходимости писать вся
кий раз gettext.gettext("Translate me") можно было пи
сать _("Translate me"). (Чтобы можно было выполнить
такой вызов, мы сначала должны импортировать модуль
gettext, чтобы получить доступ к функции gettext(), на
ходящейся в этом модуле).
Давайте рассмотрим примеры допустимых идентификаторов во фраг
менте программного кода, написанного программистом, говорящим
на испанском языке. Здесь предполагается, что была выполнена инст
рукция import math и гдето выше в программе определены переменные
radio и vieja_a
Ђrea:
π1 = math.pi
ε = 0.0000001
nueva_a
Ђrea = π * radio * radio
if abs(nueva_a
Ђrea  vieja_a Ђrea) < ε:
print("las a
Ђreas han convergido")
Мы использовали здесь модуль math, записали в эпсилон ( ε) очень ма
ленькое число с плавающей точкой и с помощью функции abs() нашли
абсолютное значение разницы площадей – обо всем об этом мы погово
рим ниже, в этой же главе. Здесь следует обратить внимание на то, что
мы можем использовать в идентификаторах национальные символы
и символы греческого алфавита. С той же легкостью мы могли бы ис
пользовать арабские, китайские, еврейские, японские и русские сим
волы и практически любые другие алфавитные символы, поддержи
ваемые кодировкой Юникод.
Самый простой способ проверить допустимость идентификатора состо
ит в том, чтобы попробовать присвоить ему некоторое значение в инте
рактивной оболочке интерпретатора Python или в командной оболочке
Python среды IDLE. Ниже приводятся несколько примеров:
>>> stretchfactor = 1
SyntaxError: can't assign to operator (...)
1 Это символ «пи» греческого алфавита (π). – Прим. перев.
Инструкцияimport ,
стр. 53, 230

72 Глава 2. Типы данных
(SyntaxError: невозможно выполнить присваивание оператору (...))
>>> 2miles = 2
SyntaxError: invalid syntax (...)
(SyntaxError: синтаксическая ошибка (...))
>>> str = 3 # Допустимо, но неправильно
>>> l'impЂ
ot31 = 4
SyntaxError: EOL while scanning singlequoted string (...)
(SyntaxError: встречен конец строки при анализе строки в апострофах (...))
>>> l_impЂ
ot31 = 5
>>>
Попытка использовать недопустимый идентификатор вызывает ис
ключение SyntaxError. В каждом конкретном случае изменяется часть
текста сообщения, которая окружена круглыми скобками, поэтому
эту часть мы заменили многоточием. В первом примере ошибка обу
словлена попыткой использовать символ «
–», который не является ал
фавитным символом Юникода, цифрой или символом подчеркивания.
Во втором случае ошибка произошла изза того, что начальный символ
не является алфавитным символом Юникода или символом подчерки
вания. Если идентификатор допустим, исключение не возникает, да
же если выбранный идентификатор совпадает с именем встроенного
типа данных, исключения или функции, поэтому третий пример при
сваивания не вызвал ошибку, хотя выбор такого идентификатора – оп
рометчивый шаг. В четвертом примере ошибка вызвана использовани
ем символа апострофа, который не является алфавитным символом
Юникода, цифрой или символом подчеркивания. Пятый пример –
пример подходящего варианта.
Целочисленные типы
В языке Python имеется два целочисленных типа, int и bool. 1 И целые
числа, и логические значения являются неизменяемыми объектами,
но благодаря присутствию в языке Python комбинированных операто
ров присваивания эта особенность практически незаметна. В логиче
ских выражениях число 0 и значение False представляют False, а лю
бое другое целое число и значение True представляют True. В числовых
выражениях значение True представляет 1, а False – 0. Это означает,
что можно записывать весьма странные выражения, например, выра
жение i+=True увеличит значение i на единицу. Естественно, более
правильным будет записывать подобные выражения как i+=1.
1 В стандартной библиотеке также определяется тип fractions.Fraction (ра
циональные числа неограниченной точности), который может пригодиться
при выполнении некоторых математических и научных вычислений.

Целочисленные типы 73
Целые числа
Размер целого числа ограничивается только объемом памяти компью
тера, поэтому легко можно создать и обрабатывать целое число, со
стоящее из тысяч цифр, правда, скорость работы с такими числами су
щественно медленнее, чем с числами, которые соответствуют машин
ному представлению.
Литералы целых чисел по умолчанию записываются в десятичной сис
теме счисления, но при желании можно использовать другие системы
счисления:
>>> 14600926 # десятичное число
14600926
>>> 0b110111101100101011011110 # двоичное число
14600926
>>> 0o67545336 # восьмеричное число
14600926
>>> 0xDECADE # шестнадцатеричное число
14600926
Двоичные числа записываются с префиксом 0b, восьмеричные – в пре
фиксом
0o1 и шестнадцатеричные – с префиксом 0x. В префиксах до
пускается использовать символы верхнего регистра.
При работе с целыми числами могут использоваться обычные матема
тические функции и операторы, как показано в табл. 2.2. Некоторые
из функциональных возможностей представлены встроенными функ
циями, такими как abs() (например, вызов abs(i) вернет абсолютное
значение целого числа i), а другие – операторами, применимыми к ти
пу int (например, выражение i + j вернет сумму целых чисел i и j).
Для всех двухместных арифметических операторов (+,
–, /, //, % и **)
имеются соответствующие комбинированные операторы присваива
ния (+=,
–=, /=, //=, %= и **=), где выражение x op= y является логиче
ским эквивалентом выражения x = x op y, когда в обычной ситуации
обращение к значению x не имеет побочных эффектов.
Объекты могут создаваться путем присваивания литералов перемен
ным, например, x = 17, или обращением к имени соответствующего
типа как к функции, например, x = int(17). Некоторые объекты (на
пример, типа decimal.Decimal) могут создаваться только посредством
использования их типов, так как они не имеют литерального представ
ления. Создание объекта посредством использования его типа может
быть выполнено одним из трех способов.
1 Пользователи языка C должны обратить внимание, что одного ведущего 0
недостаточно, чтобы определить восьмеричное число – в языке Python сле
дует использовать комбинацию 0o (0 и символ o).

74 Глава 2. Типы данных
Таблица 2.2. Арифметические операторы и функции
Первый вариант – вызов типа данных без аргументов. В этом случае
объект приобретает значение по умолчанию, например, выражение x =
int() создаст целое число 0. Любые встроенные типы могут вызывать
ся без аргументов.
Второй вариант – тип вызывается с единственным аргу
ментом. Если указан аргумент соответствующего типа,
будет создана поверхностная копия оригинального объек
та. (Поверхностное копирование рассматривается в гла
ве 3.) Если задан аргумент другого типа, будет предпри
нята попытка выполнить преобразование. Такой способ
использования описывается в табл. 2.3. Если аргумент
имеет тип, для которого поддерживается преобразова
ние в требуемый тип, и преобразование терпит неудачу,
возбуждается исключение ValueError, в противном слу
чае возвращается результат преобразования – объект
Синтаксис Описание
x + yСкладывает число x и число y
x  yВычитает число y из числа x
x * yУмножает x на y
x / yДелит x на y – результатом всегда является значение типа float
(или complex, если x или y является комплексным числом)
x // yДелит x на y, при этом усекает дробную часть, поэтому резуль
татом всегда является значение типа int, смотрите также функ
цию round()
x % yВозвращает модуль (остаток) от деления x на y
x ** yВозводит x в степень y, смотрите также функцию pow()
xИзменяет знак числа x, если оно не является нулем, если ноль –
ничего не происходит
+xНичего не делает, иногда используется для повышения удобо
читаемости программного кода
abs(x)Возвращает абсолютное значение x
divmod(x, y) Возвращает частное и остаток деления x на y
в виде кортежа двух значений типа int
pow(x, y) Возводит x в степень y; то же самое, что и оператор **
pow(x, y, z)Более быстрая альтернатива выражению (x ** y) % z
round(x, n) Возвращает значение типа int, соответствующее значению x ти
па float, округленному до ближайшего целого числа (или зна
чение типа float, округленное до nго знака после запятой, если
задан аргумент n)
Кортежи,
стр. 32, 130
Поверхно
стное
и глубокое
копирование,
стр. 173

Целочисленные типы 75
требуемого типа. Если тип аргумента не поддерживает
преобразование в требуемый тип, возбуждается исклю
чение TypeError. Встроенные типы float и str поддержи
вают возможность преобразования в целое число. Точно
так же возможно обеспечить преобразование в целое
число ваших собственных типов данных, как будет по
казано в главе 6.
Таблица 2.3. Функции преобразования целых чисел
Третий вариант – когда передается два или более аргументов; не все
типы поддерживают такую возможность, а для тех типов, что поддер
живают ее, типы аргументов и их назначение отличаются. В случае
типа int допускается передавать два аргумента, где первый аргумент –
это строка с представлением целого числа, а второй аргумент – число
основания системы счисления. Например, вызов int("A4", 16) создаст
десятичное значение 164. Этот вариант использования продемонстри
рован в табл. 2.3.
В табл. 2.4 перечислены битовые операторы. Все битовые операторы
(|, ^, &, << и >>) имеют соответствующие комбинированные операторы
присваивания (|=, ^=, &=, <<= и >>=), где выражение i op= j является ло
гическим эквивалентом выражения i = i op j в случае, когда обраще
ние к значению i не имеет побочных эффектов.
Если имеется необходимость хранить множество флагов, способных
иметь всего два состояния, можно использовать единственное целое
число и проверять значения отдельных его битов с помощью битовых
операторов. То же самое можно делать менее компактным, но более
удобным способом, воспользовавшись логическим типом.
Синтаксис Описание
bin(i)Возвращает двоичное представление целого числа i в виде
строки, например, bin(1980) == '0b11110111100'
hex(i) Возвращает шестнадцатеричное представление целого числа i
в виде строки, например, hex(1980) == '0x7bc'
int(x) Преобразует объект x в целое число; в случае ошибки во время
преобразования возбуждает исключение ValueError, а если тип
объекта x не поддерживает преобразование в целое число, воз
буждает исключение TypeError. Если x является числом с пла
вающей точкой, оно преобразуется в целое число путем усече
ния дробной части.
int(s, base)Преобразует строку s в целое число, в случае ошибки возбуж
дает исключение ValueError. Если задан необязательный аргу
мент base, он должен быть целым числом в диапазоне от 2 до 36
включительно.
oct(i) Возвращает восьмеричное представление целого числа i в виде
строки, например, oct(1980) == '0o3674'
Преобразо
вание типов,
стр. 295

76 Глава 2. Типы данных
Таблица 2.4. Битовые операторы, применимые к целым числам
Логические значения
Существует два встроенных логических объекта: True и False. Как
и все остальные типы данных в языке Python (встроенные, библиотеч
ные или ваши собственные), тип данных bool может вызываться как
функция – при вызове без аргументов возвращается значение False,
при вызове с аргументом типа bool возвращается копия аргумента,
а при вызове с любым другим аргументом предпринимается попытка
преобразовать указанный объект в тип bool. Все встроенные типы дан
ных и типы данных из стандартной библиотеки могут быть преобразо
ваны в тип bool, а добавить поддержку такого преобразования в свои
собственные типы данных не представляет никакой сложности. Ниже
приводится пара присваиваний логических значений и пара логиче
ских выражений:
>>> t = True
>>> f = False
>>> t and f
False
>>> t and True
True
Как уже отмечалось ранее, в языке Python имеется три
логических оператора: and, or и not. Выражения с уча
стием операторов and и or вычисляются в соответствии
с логикой сокращенных вычислений (shortcircuit logic),
и возвращается операнд, определяющий значение всего
выражения, тогда как результатом оператора not всегда
является либо True, либо False.
Программисты, использовавшие старые версии языка Python, иногда
вместо True и False используют числа 1 и 0 – такой прием срабатывает
практически всегда, но в новых программах, когда возникает необхо
Синтаксис Описание
i | jБитовая операция OR (ИЛИ) над целыми числами i и j; отрица
тельные числа представляются как двоичное дополнение
i ^ jБитовая операция XOR (исключающее ИЛИ) над целыми числа
ми i и j
i & jБитовая операция AND (И) над целыми числами i и j
i << jСдвигает значение i влево на j битов аналогично операции i *
(2 ** j) без проверки на переполнение
i >> jСдвигает значение i вправо на j битов аналогично операции i //
(2 ** j) без проверки на переполнение
~iИнвертирует биты числа i
Логические
операторы,
стр. 39

Тип чисел с плавающей точкой 77
димость в логическом значении, следует использовать встроенные ло
гические объекты.
Тип чисел с плавающей точкой
Язык Python предоставляет три типа значений с плавающей точкой:
встроенные типы float и complex и тип decimal.Decimal в стандартной
библиотеке. Все три типа данных относятся к категории неизменяе
мых. Тип float представляет числа с плавающей точкой двойной точ
ности, диапазон значений которых зависит от компилятора языка C
(или C# или Java), применявшегося для компиляции интерпретатора
Python. Числа этого типа имеют ограниченную точность и не могут на
дежно сравниваться на равенство значений. Числа типа float записы
ваются с десятичной точкой или в экспоненциальной форме записи,
например, 0.0, 4., 5.7,
–2.5, –2e9, 8.9e –4.
В машинном представлении числа с плавающей точкой хранятся как
двоичные числа. Это означает, что одни дробные значения могут быть
представлены точно (такие как 0.5), а другие – только приблизительно
(такие как 0.1 и 0.2). Кроме того, для представления используется
фиксированное число битов, поэтому существует ограничение на ко
личество цифр в представлении таких чисел. Ниже приводится пояс
няющий пример, полученный в IDLE:
>>> 0.0, 5.4, 2.5, 8.9e4
(0.0, 5.4000000000000004, 2.5, 0.00088999999999999995)
Проблема потери точности – это не проблема, свойственная только
языку Python; все языки программирования обнаруживают проблему
с точным представлением чисел с плавающей точкой.
Если вам действительно необходимо обеспечить высокую точность,
можно использовать числа типа decimal.Decimal. Эти числа обеспечива
ют уровень точности, который вы укажете (по умолчанию 28 знаков
после запятой), и могут точно представлять периодические числа, та
кие как 0.1
1, но скорость работы с такими числами существенно ниже,
чем с обычными числами типа float. Вследствие высокой точности
числа типа decimal.Decimal прекрасно подходят для производства фи
нансовых вычислений.
Смешанная арифметика поддерживается таким образом, что резуль
татом выражения с участием чисел типов int и float является число
типа float, а с участием типов float и complex результатом является
число типа complex. Поскольку числа типа decimal.Decimal имеют фик
сированную точность, они могут участвовать в выражениях только
1 В десятичной системе счисления число 0.1 не является периодической дро
бью, но в двоичной (то есть в машинном представлении) – это действитель
но периодическая дробь. – Прим. перев.

78 Глава 2. Типы данных
с другими числами decimal.Decimal и с числами типа int; результатом
таких выражений является число decimal.Decimal. В случае попытки
выполнить операцию над несовместимыми типами возбуждается ис
ключение TypeError.
Числа с плавающей точкой
Все числовые операторы и функции, представленные в табл. 2.2
(стр. 74), могут применяться к числам типа float, включая комбини
рованные операторы присваивания. Тип данных float может вызы
ваться как функция – без аргументов возвращается число 0.0, с аргу
ментом типа float возвращается копия аргумента, а с аргументом лю
бого другого типа предпринимается попытка выполнить преобразова
ние указанного объекта в тип float. При преобразовании строки
аргумент может содержать либо простую форму записи числа с деся
тичной точкой, либо экспоненциальное представление числа. При вы
полнении операций с числами типа float может возникнуть ситуация,
когда в результате получается значение NaN (not a number – не число)
или «бесконечность». К сожалению, поведение интерпретатора в та
ких ситуациях может отличаться в разных реализациях и зависит от
математической библиотеки системы.
Ниже приводится пример простой функции, выполняющей сравнение
чисел типа float на равенство в пределах машинной точности:
def equal_float(a, b):
return abs(a  b) <= sys.float_info.epsilon
Чтобы воспользоваться этой функцией, необходимо импортировать
модуль sys. Объект sys.float_info имеет множество атрибутов. Так,
sys.float_info.epsilon хранит минимально возможную разницу между
двумя числами с плавающей точкой. На одной из 32разрядных ма
шин автора книги это число чуть больше 0.000 000 000 000 000 2. (Ep
silon – это традиционное название чисел такого рода.) Тип float в язы
ке Python обеспечивает надежную точность до 17 значащих цифр.
Если ввести sys.float_info в среде IDLE, будут выведены все атрибуты
этого объекта, куда входят минимальное и максимальное значения
чисел с плавающей точкой, которые могут быть представлены маши
ной. А если ввести команду help(sys.float_info), будет выведена неко
торая информация об объекте sys.float_info.
Числа с плавающей точкой можно преобразовать в целые числа с по
мощью функции int(), которая возвращает целую часть и отбрасывает
дробную часть, или с помощью функции round(), которая учитывает
величину дробной части, или с помощью функций math.floor() и
math.ceil(), которые округляют вверх или вниз до ближайшего целого.
Метод float.is_integer() возвращает значение True, если дробная часть
числа равна 0. Представление дробной части числа можно получить
с помощью метода float.as_integer_ratio(). Например, пусть x = 2.75,

Тип чисел с плавающей точкой 79
тогда метод x.as_integer_ratio() вернет (11, 4). Преобразование целых
чисел в тип float можно выполнить с помощью функции float().
Числа с плавающей точкой также могут быть представлены в виде
строк в шестнадцатеричном формате с помощью метода float.hex().
Обратное преобразование может быть выполнено с помощью метода
float.fromhex().
1 Например:
s = 14.25.hex() # str s == '0x1.c800000000000p+3'
f = float.fromhex(s) # float f == 14.25
t = f.hex() # str t == '0x1.c800000000000p+3'
Экспонента отмечается с помощью символа p («power» – «степень»),
ане e, так как символ e представляет допустимую шестнадцатеричную
цифру.
В дополнение к встроенным функциональным возможностям работы
счислами типа float модуль math предоставляет множество функций,
которые приводятся в табл. 2.5. Ниже приводятся несколько фрагмен
тов программного кода, демонстрирующих, как можно использовать
функциональные возможности модуля:
>>> import math
>>> math.pi * (5 ** 2)
78.539816339744831
>>> math.hypot(5, 12)
13.0
>>> math.modf(13.732)
(0.73199999999999932, 13.0)
Функция math.hypot() вычисляет расстояние от начала координат до
точки (x, y) и дает тот же результат, что и выражение math.sqrt((x ** 2)
+ (y ** 2)).
Модуль math в значительной степени опирается на математическую
библиотеку, с которой был собран интерпретатор Python. Это означа
ет, что при некоторых условиях и в граничных случаях функции мо
дуля могут иметь различное поведение на различных платформах.
Таблица 2.5. Функции и константы модуля math
1 Примечание для программистов, использующих объектноориентирован
ный стиль: float.fromhex() – это метод класса. Синтаксис Описание
math.acos(x)Возвращает арккосинус x в радианах
math.acosh(x)Возвращает гиперболический арккосинус x в радианах
math.asin(x)Возвращает арксинус x в радианах
math.asinh(x)Возвращает гиперболический арксинус x в радианах
math.atan(x)Возвращает арктангенс x в радианах

80 Глава 2. Типы данных
Таблица 2.5 (продолжение)
Синтаксис Описание
math.atan2(y, x)Возвращает арктангенс y/x в радианах
math.atanh(x)Возвращает гиперболический арктангенс x в радианах
math.ceil(x)Возвращает
⎡x⎤, то есть наименьшее целое число типа int,
большее и равное x, например, math.ceil(5.4) == 6
math.copysign(x,y)Возвращает x со знаком числа y
math.cos(x)Возвращает косинус x в радианах
math.cosh(x)Возвращает гиперболический косинус x в радианах
math.degrees(r)Преобразует число r, типа float, из радианов в градусы
math.eКонстанта e, примерно равная значению
2.7182818284590451
math.exp(x)Возвращает e
x, то есть math.e ** x
math.fabs(x)Возвращает |x|, то есть абсолютное значение x в виде числа
типа float
math.factorial(x)Возвращает x!
math.floor(x)Возвращает
⎣x⎦, то есть наименьшее целое число типа int,
меньшее и равное x, например, math.floor(5.4) == 5
math.fmod(x, y)Выполняет деление по модулю (возвращает остаток) чис
ла x на число y; дает более точный результат, чем оператор
%, применительно к числам типа float
math.frexp(x)Возвращает кортеж из двух элементов
с мантиссой (в виде числа типа float) и экс
понентой (в виде числа типа int)
math.fsum(i)Возвращает сумму значений в итерируемом объекте i
ввиде числа типа float
math.hypot(x, y)Возвращает
math.isinf(x)Возвращает True, если значение x типа float является бес
конечностью (±inf(±∞))
math.isnan(x)Возвращает True, если значение x типа float не является
числом
math.ldexp(m, e)Возвращает m × 2
e – операция, обратная math.frexp()
math.log(x, b)Возвращает log
bx, аргумент b является необязательным
и по умолчанию имеет значение math.e
math.log10(x)Возвращает log
10x
math.log1p(x)Возвращает log
e(1+x); дает точные значения, даже когда
значение x близко к 0
math.modf(x)Возвращает дробную и целую часть числа x в виде двух
значений типа float
Кортежи,
стр. 32, 130
x2 y2 +

Тип чисел с плавающей точкой 81
Комплексные числа
Тип данных complex относится к категории неизменяемых и хранит па
ру значений типа float, одно из которых представляет действитель
ную часть комплексного числа, а другое – мнимую. Литералы ком
плексных чисел записываются как действительная и мнимая части,
объединенные знаком + или
–, а за мнимой частью числа следует сим
вол j. 1 Вот примеры нескольких комплексных чисел: 3.5+2j, 0.5j, 4+0j,
–1–3.7j. Обратите внимание, что если действительная часть числа рав
на 0, ее можно вообще опустить.
Отдельные части комплексного числа доступны в виде атрибутов real
и imag. Например:
>>> z = 89.5+2.125j
>>> z.real, z.imag
(89.5, 2.125)
За исключением //, %, divmod() и версии pow() с тремя аргументами все
остальные арифметические операторы и функции, перечисленные
в табл. 2.2 (стр. 74), могут использоваться для работы с комплексны
ми числами, так же как и соответствующие комбинированные опера
торы присваивания. Кроме того, значения типа complex имеют метод
conjugate(), который изменяет знак мнимой части. Например:
math.piКонстанта π, примерно равна 3.1415926535897931
math.pow(x, y)Возвращает x
y в виде числа типа float
math.radians(d)Преобразует число d, типа float, из градусов в радианы
math.sin(x)Возвращает синус x в радианах
math.sinh(x)Возвращает гиперболический синус x в радианах
math.sqrt(x)Возвращает
math.sum(i)Возвращает сумму значений в итерируемом объекте i
ввиде числа типа float
a
math.tan(x)Возвращает тангенс x в радианах
math.tanh(x)Возвращает гиперболический тангенс x в радианах
math.trunc(x)Возвращает целую часть числа x в виде значения типа int;
то же самое, что и int(x)
a Функции math.sum в модуле math нет; предполагаю, что эта функция в Py
thon 3.0 вытеснена функцией math.fsum.– Прим. перев.
1 В математике, чтобы показать , используется символ i, но в Python,
следуя инженерной традиции, используется символ j. Синтаксис Описание
x
– 1

82 Глава 2. Типы данных
>>> z.conjugate()
(89.52.125j)
>>> 34j.conjugate()
(3+4j)
Обратите внимание, что в этом примере был вызван метод для литера
ла комплексного числа. Вообще, язык Python позволяет вызывать ме
тоды любого литерала или обращаться к его атрибутам при условии,
что тип данных литерала имеет вызываемый метод или атрибут. Одна
ко это не относится к специальным методам, так как им всегда соот
ветствуют определенные операторы, которые и должны использовать
ся. Например, выражение 4j.real вернет 0.0, выражение 4j.imag вер
нет 4.0 и выражение 4j + 3+2j вернет 3+6j.
Тип данных complex может вызываться как функция – без аргументов
она вернет значение 0j, с аргументом типа complex она вернет копию
аргумента, а с аргументом любого другого типа она попытается преоб
разовать указанный объект в значение типа complex. При использова
нии для преобразования функция complex() принимает либо единст
венный строковый аргумент, либо одно или два значения типа float.
Если ей передается единственное значение типа float, возвращается
комплексное число с мнимой частью, равной 0j.
Функции в модуле math не работают с комплексными числами. Это сде
лано преднамеренно, чтобы гарантировать, что пользователи модуля
math будут получать исключения вместо получения комплексных чи
сел в некоторых случаях.
Если возникает необходимость использовать комплексные числа,
можно воспользоваться модулем cmath, который содержит комплекс
ные версии большинства тригонометрических и логарифмических
функций, присутствующих в модуле math, плюс ряд функций, специ
ально предназначенных для работы с комплексными числами, таких
как cmath.phase(), cmath.polar() и cmath.rect(), а также константы
cmath.pi и cmath.e, которые хранят те же самые значения типа float,
что и родственные им константы в модуле math.
Числа типа Decimal
Во многих приложениях недостаток точности, свойственный числам
типа float, не имеет существенного значения, и эта неточность окупа
ется скоростью вычислений. Но в некоторых случаях предпочтение
отдается точности, даже в обмен на снижение скорости работы. Мо
дуль decimal реализует неизменяемый числовой тип Decimal, который
представляет числа с задаваемой точностью. Вычисления с участием
таких чисел производятся значительно медленнее, чем в случае ис
пользования значений типа float, но насколько это важно, будет зави
сеть от приложения.

Тип чисел с плавающей точкой 83
Чтобы создать объект типа Decimal, необходимо импортировать модуль
decimal. Например:
>>> import decimal
>>> a = decimal.Decimal(9876)
>>> b = decimal.Decimal("54321.012345678987654321")
>>> a + b
Decimal('64197.012345678987654321')
Числа типа Decimal создаются с помощью функции decimal.Decimal().
Эта функция может принимать целочисленный или строковый аргу
мент, но не значение типа float, потому что числа типа float не всегда
имеют точное представление, а числа типа Decimal всегда представля
ются точно. Если в качестве аргумента используется строка, она мо
жет содержать изображение числа как в обычной десятичной форме
записи, так и в экспоненциальной. Кроме того, возможность явно оп
ределить точность представления числа decimal.Decimal означает, что
они надежно могут проверяться на равенство.
Все арифметические операторы и функции, перечисленные в табл. 2.2
(стр. 74), включая соответствующие им комбинированные операторы
присваивания, могут использоваться применительно к значениям ти
па decimal.Decimal, но с некоторыми ограничениями. Если слева от опе
ратора ** находится объект типа decimal.Decimal, то справа от операто
ра должно быть целое число. Точно так же, если первый аргумент
функции pow() имеет тип decimal.Decimal, то второй и необязательный
третий аргументы должны быть целыми числами.
Модули math и cmath не могут использоваться для работы с числами типа
decimal.Decimal, однако некоторые функции, присутствующие в модуле
math, реализованы как методы типа decimal.Decimal. Например, чтобы
вычислить e
x, где x имеет тип float, вызывается функция math.exp(x),
акогда x имеет тип decimal.Decimal, следует использовать метод x.exp().
Из обсуждения составляющей №3 (стр. 34) мы можем видеть, что
x.exp() – это фактически разновидность записи decimal.Decimal.exp(x).
Кроме того, тип данных decimal.Decimal предоставляет метод ln(), ко
торый вычисляет натуральные (по основанию e) логарифмы (точно так
же, как функция math.log() с одним аргументом), log10() и sqrt(),
а также множество других методов, адаптированных для обработки
значений типа decimal.Decimal.
Значения типа decimal.Decimal работают в пределах контекста, где
контекст – это коллекция параметров настройки, определяющих пове
дение чисел decimal.Decimal. Контекст определяет точность представ
ления (по умолчанию 28 десятичных знаков), методику округления
и некоторые другие особенности.
В некоторых ситуациях различия в точности представления между ти
пами float и decimal.Decimal становятся очевидными:

84 Глава 2. Типы данных
>>> 23 / 1.05
21.904761904761905
>>> print(23 / 1.05)
21.9047619048
>>> print(decimal.Decimal(23) / decimal.Decimal("1.05"))
21.90476190476190476190476190
>>> decimal.Decimal(23) / decimal.Decimal("1.05")
Decimal('21.90476190476190476190476190')
Хотя деление чисел decimal.Decimal выполняется с более высокой точ
ностью, чем деление чисел float, в данном случае (в 32битной систе
ме) различия обнаруживаются только в пятнадцатом знаке после деся
тичной точки. Во многих ситуациях такая неточность не имеет боль
шого значения, например, в этой книге, где во всех примерах с участи
ем чисел с плавающей точкой используются числа типа float.
С другой стороны, следует отметить, что последние два приведенных
примера впервые демонстрируют, что печать объекта вызывает неко
торое закулисное форматирование. Когда для вывода результата выра
жения decimal.Decimal(23)/decimal.Decimal("1.05") вызывается функция
print(), выводится «голое» число – вывод имеет строковую форму. Ес
ли же просто вводится выражение, то на экран выводится decimal.De
cimal – в данном случае вывод имеет репрезентативную форму. Все
объекты в языке Python имеют две формы вывода. Строковая форма
предназначена для вывода информации для человека. Репрезентатив
ная форма является представлением объекта, которое можно передать
интерпретатору Python (если это необходимо) и воспроизвести пред
ставляемый объект. Мы вернемся к этой теме в следующем разделе,
когда будем обсуждать строки, и еще раз – в главе 6, когда будем обсу
ждать вопросы реализации строковой и репрезентативной формы для
наших собственных типов данных.
В документе «Library Reference» модуль decimal описывается во всех
подробностях, которые выходят далеко за рамки этой книги. Кроме
того, там приводится множество примеров и список часто задаваемых
вопросов с ответами на них.
Строки
Строки в языке Python представлены неизменяемым ти
пом данных str, который хранит последовательность
символов Юникода. Тип данных str может вызываться
как функция для создания строковых объектов – без ар
гументов возвращается пустая строка; с аргументом, ко
торый не является строкой, возвращается строковое
представление аргумента; а в случае, когда аргумент яв
ляется строкой, возвращается его копия. Функция str()
может также использоваться как функция преобразова
ния. В этом случае первый аргумент должен быть стро Кодировки
символов,
стр. 112

Строки 85
кой или объектом, который можно преобразовать в строку, а, кроме
того, функции может быть передано до двух необязательных строко
вых аргументов, один из которых определяет используемую кодиров
ку, а второй определяет порядок обработки ошибок кодирования.
Ранее мы уже упоминали, что литералы строк создаются с использо
ванием кавычек или апострофов, при этом важно, чтобы с обоих кон
цов литерала использовались кавычки одного и того же типа. В допол
нение к этому мы можем использовать строки в тройных кавычках,
то есть строки, которые начинаются и заканчиваются тремя символа
ми кавычки (либо тремя кавычками, либо тремя апострофами). На
пример:
text = """Строки в тройных кавычках могут включать 'апострофы' и "кавычки"
без лишних формальностей. Мы можем даже экранировать символ перевода строки \,
благодаря чему данная конкретная строка будет занимать всего две строки."""
Если нам потребуется использовать кавычки в строке, это можно сде
лать без лишних формальностей – при условии, что они отличаются от
кавычек, ограничивающих строку; в противном случае символы ка
вычек или апострофов внутри строки следует экранировать:
a = "Здесь 'апострофы' можно не экранировать, а \"кавычки\" придется."
b = 'Здесь \'апострофы\' придется экранировать, а "кавычки" не обязательно.'
В языке Python символ перевода строки интерпретируется как завер
шающий символ инструкции, но не внутри круглых скобок (()), квад
ратных скобок ([]), фигурных скобок ({}) и строк в тройных кавычках.
Символы перевода строки могут без лишних формальностей использо
ваться в строках в тройных кавычках, и мы можем включать символы
перевода строки в любые строковые литералы с помощью экраниро
ванной последовательности \n. Все экранированные последовательно
сти, допустимые в языке Python, перечислены в табл. 2.6. В некото
рых ситуациях, например, при записи регулярных выражений, при
ходится создавать строки с большим количеством символов обратного
слеша. (Регулярные выражения будут темой обсуждения главы 12.)
Это может доставлять определенные неудобства, так как каждый та
кой символ придется экранировать:
import re
phone1 = re.compile("^((?:[(]\\d+[)])?\\s*\\d+(?:\\d+)?)$")
Решить эту проблему можно, используя «сырые» (raw) строки. Это
обычные строки в кавычках или в тройных кавычках, в которые перед
первой кавычкой добавлен символ r. Внутри таких строк все символы
интерпретируются как обычные символы, поэтому отпадает необходи
мость экранировать символы, которые в других типах строк имеют
специальное значение. Ниже приводится регулярное выражение для
номера телефона в виде «сырой» строки:
phone2 = re.compile(r"^((?:[(]\d+[)])?\s*\d+(?:\d+)?)$")

86 Глава 2. Типы данных
Таблица 2.6. Экранированные последовательности в языке Python
Если потребуется записать длинный строковый литерал, занимающий
две или более строк, но без использования тройных кавычек, то можно
использовать один из приемов, показанных ниже:
t = "Это не самый лучший способ объединения двух длинных строк, " + \
"потому что он основан на использовании неуклюжего экранирования"
s = ("Это отличный способ объединить две длинные строки, "
" потому что он основан на конкатенации строковых литералов.")
Обратите внимание, что во втором случае для создания единственного
выражения мы должны были использовать круглые скобки – без этих
скобок переменной s была бы присвоена только первая строка, а нали
чие второй строки вызвало бы исключение IndentationError. В руковод
стве «Idioms and AntiIdioms», которое можно отыскать в документа
ции к языку Python, рекомендуется вместо экранирования перевода
строки всегда использовать круглые скобки для объединения операто
ров, не умещающихся в одну строку; именно этой рекомендации мы
ибудем следовать.
Последовательность Значение
\перевод_строкиЭкранирует (то есть игнорирует) символ перевода строки
\\Символ обратного слеша (\)
\'Апостроф (')
\"Кавычка (")
\aСимвол ASCII «сигнал» (bell, BEL)
\bСимвол ASCII «забой» (backspace, BS)
\fСимвол ASCII «перевод формата» (formfeed, FF)
\nСимвол ASCII «перевод строки» (linefeed, LF)
\N{название}Символ Юникода с заданным названием
\oooСимвол с заданным восьмеричным кодом
\rСимвол ASCII «возврат каретки» (carriage return, CR)
\tСимвол ASCII «табуляция» (tab, TAB)
\uhhhhСимвол Юникода с указанным 16битовым шестнадца
теричным значением
\UhhhhhhhhСимвол Юникода с указанным 32битовым шестнадца
теричным значением
\vСимвол ASCII «вертикальная табуляция» (vertical tab,
VT)
\xhhСимвол с указанным 8битовым шестнадцатеричным
значением

Строки 87
Поскольку содержимое файлов .py по умолчанию представляет собой
текст в кодировке UTF8 Юникод, мы можем использовать в строко
вых литералах любые символы Юникода. Мы можем даже помещать
в строковые литералы символы Юникода, используя шестнадцатерич
ные экранированные последовательности или названия символов
Юникода, например:
>>> euros = "Ђ \N{euro sign} \u20AC \U000020AC"
>>> print(euros)
€ € € €
В данном случае мы не можем использовать обычную шестнадцатерич
ную экранированную последовательность, так как она ограничена дву
мя цифрами и не может представлять символы с кодом больше, чем
0xFF. Обратите внимание, что названия символов Юникода не чувстви
тельны к регистру и пробелы внутри них являются необязательными.
Для определения кода символа Юникода (целое число,
связанное с символом в кодировке Юникод) в строке,
можно использовать встроенную функцию ord(). Напри
мер:
>>> ord(euros[0])
8364
>>> hex(ord(euros[0]))
'0x20ac'
Точно так же можно преобразовать любое целое число, представляю
щее собой допустимый код некоторого символа Юникода, воспользо
вавшись функцией chr():
>>> s = "anarchists are " + chr(8734) + chr(0x23B7)
>>> s
'anarchists are ∞√'
>>> ascii(s)
"'anarchists are \u221e\u23b7'"
Если ввести s в среде IDLE, содержимое объекта будет
выведено в строковой форме; для строк это означает, что
содержимое выводится в кавычках. Если нам потребует
ся вывести только символы ASCII, мы можем воспользо
ваться встроенной функцией ascii(), которая возвраща
ет репрезентативную форму своего аргумента, используя
7битовые символы ASCII, где это возможно; в против
ном случае используется наиболее краткая экранирован
ная последовательность из возможных: \xhh, \uhhhh или
\Uhhhhhhhh. Ниже в этой главе будет показано, как полу
чить полный контроль над выводом строк.
Кодировки
символов,
стр. 112
Метод str.
format() ,
стр. 100

88 Глава 2. Типы данных
Сравнение строк
Строки поддерживают обычные операторы сравнения <, <=, ==, !=, >
и>=. Эти операторы выполняют побайтовое сравнение строк в памяти.
К сожалению, возникают две проблемы при сравнении, например,
строк в отсортированных списках. Обе проблемы проявляются во всех
языках программирования и не являются характерной особенностью
Python.
Первая проблема связана с тем, что символы Юникода
могут быть представлены двумя и более последователь
ностями байтов. Например, °
A (символ Юникода с кодом
0x00C5) в кодировке UTF8 может быть представлен тремя
различными способами: [0xE2, 0x84, 0xAB], [0xC3, 0x85]
и[0x41, 0xCC, 0x8A]. К счастью, мы можем решить эту
проблему. Если импортировать модуль unicodedata и вы
звать функцию unicodedata.normalize() со значением
«NFKD» в первом аргументе (эта аббревиатура опреде
ляет способ нормализации «Normalization Form Compa
tibility Decomposition» – нормализация в форме совмес
тимой декомпозиции), то, передав ей строку, содержа
щую символ °
A, представленный любой из допустимых
последовательностей байтов, мы получим строку с симво
лами в кодировке UTF8, где интересующий нас символ
всегда будет представлен последовательностью [0x41,
0xCC, 0x8A].
Вторая проблема заключается в том, что порядок сортировки некото
рых символов зависит от конкретного языка. Например, в шведском
языке при сортировке символ
..а следует после символа z, тогда как
в немецком языке символ ..а сортируется так, как если бы он был пред
ставлен последовательностью символов ae. Еще один пример: в анг
лийском языке символ
/o сортируется как символ o, а в датском и нор
вежском языках он следует после символа z. Со строками Юникода
связана масса проблем, которые становятся трудноразрешимыми, ко
гда одним и тем же приложением могут пользоваться люди разных на
циональностей (привыкшие к различным порядкам расположения
символов), когда строки содержат текст сразу на нескольких языках
(например, часть строки на испанском, а часть на английском), и осо
бенно, если учесть, что некоторые символы (такие как стрелки, деко
ративные и математические символы) не имеют определенного поряд
ка сортировки.
Будучи настоящим политиком, во избежание трудноуловимых оши
бок Python не делает никаких предположений. В смысле сравнения
строк это означает, что выполняется побайтовое сравнение строк в па
мяти. При таком подходе порядок сортировки определяется кодами
Юникода, что для английского языка дает сортировку в соответствии
с кодами ASCII. Перевод всех символов строк в нижний или в верхний
Кодировки
символов,
стр. 112

Строки 89
регистр обеспечит более естественный порядок сортировки для анг
лийского языка. Нормализация может потребоваться, только когда
текстовые строки поступают из внешних источников, таких как фай
лы или сетевые сокеты, но даже в этих случаях едва ли стоит приме
нять ее, если нет веских доказательств в ее необходимости. При этом
мы, конечно, можем настроить методы сортировки, как будет показа
но в главе 3. Проблема сортировки строк Юникода подробно рассмат
ривается в документе «Unicode Collation Algorithm» (unicode.org/re
ports/tr10).
Получение срезов строк
Из описания составляющей №3 нам известно, что от
дельные элементы последовательности, а, следователь
но, и отдельные символы в строках, могут извлекаться
с помощью оператора доступа к элементам ([]). В дейст
вительности этот оператор намного более универсальный
и может использоваться для извлечения не только одно
го символа, но и целых комбинаций (подпоследователь
ностей) элементов или символов, когда этот оператор ис
пользуется в контексте оператора извлечения среза.
Для начала мы рассмотрим возможность извлечения отдельных сим
волов. Нумерация позиций символов в строках начинается с 0 и про
должается до значений длины строки минус 1. Однако допускается ис
пользовать и отрицательные индексы – в этом случае отсчет начинает
ся с последнего символа и ведется в обратном направлении к первому
символу. На рис. 2.1 показано, как нумеруются позиции символов
в строке, если предположить, что было выполнено присваивание s =
"Light ray".
Отрицательные индексы удивительно удобны, особенно индекс
–1, ко
торый всегда соответствует последнему символу строки. Попытка об
ращения к индексу, находящемуся за пределами строки (или к любо
му индексу в пустой строке), будет вызывать исключение IndexError.
Оператор получения среза имеет три формы записи:
Составляю
щая №3,
стр. 32
s[9] s[8] s[7] s[6] s[5] s[4] s[3] s[2] s[1]
s[0] s[1] s[2] s[3] s[4] s[5] s[6] s[7] s[8]
Light ray
Рис. 2.1. Номера позиций символов в строке

90 Глава 2. Типы данных
seq[start]
seq[start:end]
seq[start:end:step]
Ссылка seq может представлять любую последовательность, такую как
список, строку или кортеж. Значения start, end и step должны быть
целыми числами (или переменными, хранящими целые числа). Мы
уже использовали первую форму записи оператора доступа к элемен
там: с ее помощью извлекается элемент последовательности с индек
сом start. Вторая форма записи извлекает подстроку, начиная с эле
мента с индексом start и заканчивая элементом с индексом end, не
включая его. Третью форму записи мы рассмотрим очень скоро.
При использовании второй формы записи (с одним двоеточием) мы мо
жем опустить любой из индексов. Если опустить начальный индекс,
по умолчанию будет использоваться значение 0. Если опустить конеч
ный индекс, по умолчанию будет использоваться значение len(seq).
Это означает, что если опустить оба индекса, например, s[:], это будет
равносильно выражению s[0:len(s)], и в результате будет извлечена,
то есть скопирована, последовательность целиком.
На рис. 2.2 приводятся некоторые примеры извлечения срезов из стро
ки s, которая получена в результате присваивания s = "The waxwork man".
Один из способов вставить подстроку в строку состоит в смешивании
операторов извлечения среза и операторов конкатенации. Например:
>>> s = s[:12] + "wo" + s[12:]
>>> s
'The waxwork woman'
Кроме того, поскольку текст «wo» присутствует в оригинальной стро
ке, тот же самый эффект можно было бы получить путем присваива
ния значения выражения s[:12] + s[7:9] + s[12:].
Оператор конкатенации + и добавления подстроки += не
особенно эффективны, когда в операции участвует мно
жество строк. Для объединения большого числа строк
обычно лучше использовать метод str.join(), с которым
мы познакомимся в следующем подразделе.
The
wa
xwo
rk ma
n
s[4:11] s[3:]
s[:7] s[7:]
Рис. 2.2. Извлечение срезов из последовательности
Операторы
иметоды
строк, стр. 92

Строки 91
Третья форма записи (с двумя двоеточиями) напоминает вторую фор
му, но в отличие от нее значение step определяет, с каким шагом следу
ет извлекать символы. Как и при использовании второй формы запи
си, мы можем опустить любой из индексов. Если опустить начальный
индекс, по умолчанию будет использоваться значение 0, при условии,
что задано неотрицательное значение step; в противном случае началь
ный индекс по умолчанию получит значение –1. Если опустить конеч
ный индекс, по умолчанию будет использоваться значение len(seq),
при условии, что задано неотрицательное значение step; в противном
случае конечный индекс по умолчанию получит значение индекса пе
ред началом строки. Мы не можем опустить значение step, и оно не мо
жет быть равно нулю – если задание шага не требуется, то следует ис
пользовать вторую форму записи (с одним двоеточием), в которой шаг
выбора элементов не указывается.
На рис. 2.3 приводится пара примеров извлечения разреженных сре
зов из строки s, которая получена в результате присваивания s = "he ate
camel food".
Здесь мы использовали значения по умолчанию для начального и ко
нечного индексов, то есть извлечение среза s[::
–2] начинается с по
следнего символа строки и извлекается каждый второй символ по на
правлению к началу строки. Аналогично извлечение среза s[::3] на
чинается с первого символа строки и извлекается каждый третий сим
вол по направлению к концу строки.
Существует возможность комбинировать индексы с размером шага,
как показано на рис. 2.4.
Операция извлечения элементов с определенным шагом часто приме
няется к последовательностям, отличным от строк, но один из ее вари
антов часто применяется к строкам:
>>> s, s[::1]
('The waxwork woman', 'namow krowxaw ehT')
Шаг –1 означает, что будет извлекаться каждый символ, от конца до
начала, то есть будет получена строка, в которой символы следуют в об
ратном порядке.
s[::3] == 'ha m o'
he a t e came l f ood
s[::2] == 'do ea t h'
Рис. 2.3. Извлечение разреженных срезов

92 Глава 2. Типы данных
Операторы и методы строк
Поскольку строки относятся к категории неизменяемых
последовательностей, все функциональные возможно
сти, применимые к неизменяемым последовательностям,
могут использоваться и со строками. Сюда входят опера
тор проверки на вхождение in, оператор конкатенации +,
оператор добавления в конец +=, оператор дублирования *
и комбинированный оператор присваивания с дублиро
ванием *=. Применение всех этих операторов в контексте
строк мы обсудим в этом подразделе, а также обсудим
большинство строковых методов. В табл. 2.7 приводится
перечень всех строковых методов за исключением двух
специализированных (str.maketrans() и str.translate()),
которые будут обсуждаться немного позже.
Так как строки являются последовательностями, они яв
ляются объектами, имеющими «размер», и поэтому мы
можем вызывать функцию len(), передавая ей строки
в качестве аргумента. Возвращаемая функцией длина
представляет собой количество символов в строке (ноль –
для пустых строк).
Мы уже знаем, что перегруженная версия оператора + для строк вы
полняет операцию конкатенации. В случаях, когда требуется объеди
нить множество строк, лучше использовать метод str.join(). Метод
принимает в качестве аргумента последовательность (то есть список
или кортеж строк) и объединяет их в единую строку, вставляя между
ними строку, относительно которой был вызван метод. Например:
>>> treatises = ["Arithmetica", "Conics", "Elements"]
>>> " ".join(treatises)
'Arithmetica Conics Elements'
>>> "<>".join(treatises)
'Arithmetica<>Conics<>Elements'
>>> "".join(treatises)
'ArithmeticaConicsElements'
Операторы
и функции,
применимые
к итерируе
мым объек
там, стр. 164
he a t e came l f ood
s[1:2:2] == s[:2:2] == 'do ea t'
s[0:5:3] == s[:5:3] == 'ha m'
Рис. 2.4. Извлечение срезов из последовательности с определенным шагом
Понятие
«размер»,
стр. 443

Строки 93
Первый пример является, пожалуй, наиболее типичным; он объединя
ет строки из списка, вставляя между ними единственный символ,
в данном случае – пробел. Третий пример представляет собой опера
цию конкатенации в чистом виде – благодаря тому что метод вызыва
ется относительно пустой строки, строки объединяются без добавле
ния чего бы то ни было между ними.
Таблица 2.7. Строковые методы
Синтаксис Описание
s.capitalize()Возвращает копию строки s с первым символом в верхнем
регистре; смотрите также метод str.title
s.center(width,
char)Возвращает копию строки s, отцентрированную в строке
сдлиной width. Недостающие символы по умолчанию запол
няются пробелами или символами в соответствии с необяза
тельным аргументом char (строка с длиной, равной 1); смот
рите также методы str.ljust(), str.rjust() и str.format()
s.count(t,
start, end)Возвращает число вхождений строки t в строку s (или в срез
строки s[start:end])
s.encode(
encoding,
err)Возвращает объект типа bytes, представ
ляющий строку в кодировке по умолчанию
или в кодировке, определяемой аргументом
encoding, с обработкой ошибок, определяе
мой необязательным аргументом err
s.endswith(x,
start, end)Возвращает True, если строка s (или срез строки s[start:end])
оканчивается подстрокой x или любой из строк, если x – кор
теж; в противном случае возвращает False. Смотрите также
метод str.startswith()
s.expandtabs(
size)Возвращает копию строки s, в которой символы табуляции
замещены пробелами с шагом 8 или в соответствии со значе
нием необязательного аргумента size
s.find(t,
start, end)Возвращает позицию самого первого (крайнего слева) вхож
дения подстроки
t в строку s (или в срез строки s[start:end]),
если подстрока t не найдена, возвращается –1. Для поиска
самого последнего (крайнего справа) вхождения следует ис
пользовать метод str.rfind(). Смотрите также метод str.in
dex()
s.format(...)Возвращает копию строки s, отформатиро
ванную в соответствии с заданными аргу
ментами. Этот метод и его аргументы рас
сматриваются в следующем подразделе
s.index(t,
start, end)Возвращает позицию самого первого (крайнего слева) вхож
дения подстроки t в строку s (или в срез строки s[start:end]);
если подстрока t не найдена, возбуждается исключение Va
lueError. Для поиска самого последнего (крайнего справа)
вхождения следует использовать метод str.rfind()
Тип данных bytes , стр. 344
Кодировки сим
волов, стр. 112
Метод str.
format() ,
стр. 100

94 Глава 2. Типы данных
Таблица 2.7 (продолжение)
Синтаксис Описание
s.isalnum()Возвращает True, если строка s не пустая и содержит только
алфавитноцифровые символы
s.isalpha()Возвращает True, если строка s не пустая и содержит только
алфавитные символы
s.isdecimal()Возвращает True, если строка s не пустая и содержит только
символы Юникода, обозначающие цифры десятичной систе
мы счисления
s.isdigit()Возвращает True, если строка s не пустая и содержит только
символы ASCII, обозначающие цифры десятичной системы
счисления
s.isidentifier()Возвращает True, если строка s не пустая
и является допустимым идентификатором
s.islower()Возвращает True, если строка s имеет хотя бы один символ,
который может быть представлен в нижнем регистре, и все
такие символы находятся в нижнем регистре; смотрите так
же метод str.isupper()
s.isnumeric()Возвращает True, если строка s не пустая и содержит только
символы Юникода, используемые для обозначения чисел
s.isprintable()Возвращает True, если строка s пустая или содержит только
печатаемые символы, включая пробел, но не символ перево
да строки
s.isspace()Возвращает True, если строка s не пустая и содержит только
пробельные символы
s.istitle()Возвращает True, если строка s не пустая и имеет формат за
головка; смотрите также метод str.title()
s.isupper()Возвращает True, если строка s имеет хотя бы один символ,
который может быть представлен в верхнем регистре, и все
такие символы находятся в верхнем регистре; смотрите так
же метод str.islower()
s.join(seq)Объединяет все элементы последовательности seq, вставляя
между ними строку s (которая может быть пустой строкой)
s.ljust(
width,
char)Возвращает копию строки
s, выровненной по левому краю,
в строке длиной width. Недостающие символы по умолчанию
заполняются пробелами или символами в соответствии с не
обязательным аргументом char (строка с длиной, равной 1).
Для выравнивания по правому краю используйте метод
str.rjust(), для выравнивания по центру – метод str.cen
ter(); смотрите также метод str.format()
s.lower()Возвращает копию строки s, в которой все символы приведе
ны к нижнему регистру; смотрите также метод str.upper()
Идентификато
ры и ключевые
слова, стр. 68

Строки 95
s.maketrans()Парный метод для str.translate(); подробности приводятся
в тексте
s.partition
(t)Возвращает кортеж из трех строк – часть строки s перед са
мым первым (крайним слева) вхождением подстроки t, t
ичасть строки s после подстроки t; если подстрока t в строке
s отсутствует, возвращаются строка s и две пустые строки.
Для деления строки по самому последнему (крайнему справа)
вхождению подстроки t, используйте метод str.rpartition()
s.replace
(t, u, n)Возвращает копию строки s, в которой каждое (но не более n,
если этот аргумент определен) вхождение подстроки t заме
щается подстрокой u
s.split(t, n)Возвращает список строк, выполняя разбиение строки s не
более чем n раз по подстроке t. Если число n не задано, раз
биение выполняется по всем найденным подстрокам t. Если
подстрока t не задана, разбиение выполняется по пробель
ным символам. Для выполнения разбиения строки, начиная
с правого края, используйте метод str.rsplit – этот метод
имеет смысл применять, когда задано число разбиений n, ко
торое меньше максимального числа возможных разбиений
s.splitlines
(f)Возвращает список строк, выполняя разбиение строки s по
символам перевода строки, удаляя их, если в аргументе f не
задано значение True
s.startswith
(x, start,
end)Возвращает True, если строка s (или срез строки s[start:end
])
начинается подстрокой x или любой из строк, если x – кор
теж; в противном случае возвращает False. Смотрите также
метод str.endswith()
s.strip(chars)Возвращает копию строки s, из которой удалены начальные
и завершающие пробельные символы (или символы, входя
щие в строку chars). Метод str.lstrip() выполняет удаление
только в начале строки, а метод str.rstrip() – только в конце
s.swapcase()Возвращает копию строки s, в которой все символы верхнего
регистра преобразованы в символы нижнего регистра, а все
символы нижнего регистра – в символы верхнего регистра;
смотрите также методы str.lower() и str.upper()
s.title()Возвращает копию строки s, в которой первые символы ка
ждого слова преобразованы в символы верхнего регистра,
а все остальные символы – в символы нижнего регистра;
смотрите также метод str.istitle()
s.translate()Парный метод для str.maketrans(); подробности приводятся
в тексте
s.upper()Возвращает копию строки s, в которой все символы приведе
ны к верхнему регистру; смотрите также метод str.lower()
s.zfill(w)Возвращает копию строки s, которая, если ее длина меньше
величины w, дополняется слева нулями до длины w Синтаксис Описание

96 Глава 2. Типы данных
Метод str.join() может также использоваться в комбинации со встро
енной функцией reversed(), которая переворачивает строку – напри
мер, "".join(reversed(s)), хотя тот же результат может быть получен
более кратким оператором извлечения разреженного среза – напри
мер, s[::
–1].
Оператор * обеспечивает возможность дублирования строки:
>>> s = "=" * 5
>>> print(s)
=====
>>> s *= 10
>>> print(s)
==================================================
Как показано в примере, мы можем также использовать комбиниро
ванный оператор присваивания с дублированием. 1
Когда оператор проверки на вхождение in применяется к строкам, он
возвращает True, если операнд слева является подстрокой операнда
справа или равен ему. Когда необходимо точно определить позицию
подстроки в строке, можно использовать два метода. Первый метод
str.index() возвращает позицию подстроки в строке или возбуждает
исключение ValueError, если подстрока не будет найдена. Второй метод
str.find() возвращает позицию подстроки в строке или
–1 в случае не
удачи. Оба метода принимают искомую подстроку в качестве первого
аргумента и могут принимать еще пару необязательных аргументов.
Второй аргумент определяет позицию начала поиска, а третий – пози
цию окончания поиска.
Какой из двух методов использовать – это лишь вопрос вкуса, хотя, ес
ли выполняется поиск нескольких вхождений одной и той же подстро
ки, программный код, использующий метод str.index(), выглядит бо
лее понятным, как показано ниже:
def extract_from_tag(tag, line): def extract_from_tag(tag, line):
opener = "<" + tag + ">" opener = "<" + tag + ">"
closer = "" closer = ""
try: i = line.find(opener)
i = line.index(opener) if i != 1:
start = i + len(opener) start = i + len(opener)
j = line.index(closer, start) j = line.find(closer, start)
return line[start:j] if j != 1:
except ValueError: return line[start:j]
return None return None
1 Строки также поддерживают оператор форматирования %. Этот оператор
считается устаревшим и поддерживается только для облегчения перевода
программ с версии Python 2 на версию Python 3. Он не используется ни
в одном из тех примеров, которые приводятся в книге.

Строки 97
Обе версии функции extract_from_tag() обладают одинаковым поведе
нием. Например, вызов extract_from_tag("red", "what a rose
this is") возвращает строку «rose». В версии слева, основанной на об
работке исключения, программный код, выполняющий поиск, отде
лен от программного кода, выполняющего обработку ошибок, а в вер
сии справа программный код обработки строки смешивается с про
граммным кодом обработки ошибок.
Все методы – str.count(), str.endswith(), str.find(), str.rfind(), str.in
dex(), str.rindex() и str.startswith()– принимают до двух необяза
тельных аргументов: начальную и конечную позиции. Ниже приво
дятся два примера эквивалентностей (предполагается, что s – строка):
s.count("m", 6) == s[6:].count("m")
s.count("m", 5, 3) == s[5:3].count("m")
Как видите, строковые методы, принимающие начальную и конечную
позиции, действуют со срезом строки, определяемым этими позициями.
Теперь взгляните на еще одну пару эквивалентных фрагментов, на
этот раз поясняющих действие метода str.partition():
i = s.rfind("/")
if i == 1:
result = s, "", ""
else:
result = s.rpartition("/") result = s[:i], s[i], s[i + 1:]
Фрагменты программного кода слева и справа не совсем эквивалент
ны, потому что фрагмент справа создает новую переменную i. Обрати
те внимание, что имеется возможность выполнять присваивание кор
тежей без лишних формальностей и что в обоих случаях выполняется
поиск самого последнего (крайнего справа) вхождения символа /. Если
предполагать, что s – это строка "/usr/local/bin/firefox", то оба фраг
мента вернут один и тот же результат: ('/usr/local/bin', '/', 'firefox').
Метод str.endswith() (и str.startswith()) может использоваться с един
ственным строковым аргументом, например s.startswith("From:"), или
с кортежем строк. Ниже приводится инструкция, в которой использу
ются методы str.endswith() и str.lower() для вывода имени файла, ес
ли он является файлом изображения в формате JPEG:
if filename.lower().endswith((".jpg", ".jpeg")):
print(filename, "is a JPEG image")
Методы семейства is*, такие как isalpha() и isspace(), возвращают
True, если строка, в контексте которой они вызываются, имеет по
меньшей мере один символ, и все символы в строке соответствуют оп
ределенному критерию. Например:
>>> "917.5".isdigit(), "".isdigit(), "2".isdigit(), "203".isdigit()
(False, False, False, True)

98 Глава 2. Типы данных
Методы семейства is* работают с символами Юникода, поэтому вызов
str.isdigit() для строк "\N{circled digit two}03" и "
➁03" в обоих случа
ях вернет True. По этой причине, когда метод isdigit() возвращает
True, нельзя утверждать, что строка может быть преобразована в целое
число.
Когда мы получаем строки из внешних источников (из других про
грамм, из файлов, через сетевые соединения или в результате взаимо
действия с пользователем), они могут содержать в начале или в конце
нежелательные пробельные символы. Удалить пробельные символы,
находящиеся в начале строки, можно с помощью метода str.lstrip(),
в конце строки – с помощью метода str.rstrip(), а с обоих концов –
спомощью метода str.strip(). Мы можем также передавать методам
семейства *strip строки, в этом случае они удалят каждое вхождение
каждого символа с соответствующего конца (или с обоих концов) стро
ки. Например:
>>> s = "\t no parking "
>>> s.lstrip(), s.rstrip(), s.strip()
('no parking ', '\t no parking', 'no parking')
>>> "<[unbracketed]>".strip("[](){}<>")
'unbracketed'
Мы можем также замещать подстроки в строках, ис
пользуя метод str.replace(). Этот метод принимает два
строковых аргумента и возвращает копию строки, в кон
тексте которой был вызван метод, где каждое вхождение
строки в первом аргументе замещено строкой во втором
аргументе. Если второй аргумент представляет собой
пустую строку, это приведет к удалению всех вхождений
строки в первом аргументе. Мы увидим примеры ис
пользования str.replace() и некоторых других строко
вых методов в примере csv2html.py в разделе «Примеры»
в конце этой главы.
Часто бывает необходимо разбить строку на список строк. Например,
у нас может иметься текстовый файл с данными, в котором одной стро
ке соответствует одна запись, а поля внутри записи отделяются друг от
друга звездочками. Реализовать такое разбиение можно с помощью
метода str.split(), передав ему строку, по которой выполняется раз
биение, в виде первого аргумента и максимальное число разбиений
в виде второго, необязательного аргумента. Если второй аргумент не
указан, выполняется столько разбиений, сколько потребуется. Ниже
приводится пример использования метода:
>>> record = "Leo Tolstoy*1828828*19101120"
>>> fields = record.split("*")
>>> fields
['Leo Tolstoy', '1828828', '19101120']
Пример csv2html.
py, стр. 119

Строки 99
Теперь мы можем с помощью метода str.split() выделить год рожде
ния и год смерти и определить, сколько лет прожил Лев Толстой
(плюсминус один год):
>>> born = fields[1].split("")
>>> born
['1828', '8', '28']
>>> died = fields[2].split("")
>>> print("lived about", int(died[0])  int(born[0]), "years")
lived about 82 years
Нам потребовалось использовать преобразование int(), чтобы преобра
зовать годы из строк в целые числа, но в остальном в этом фрагменте
нет ничего сложного. Мы могли бы получить годы непосредственно из
списка fields– например, year_born = int(fields[1].split("
–")[0]).
В табл. 2.7 остались неописанными два метода – str.maketrans()
иstr.translate(). Метод str.maketrans() используется для создания таб
лицы преобразований, которая отображает одни символы в другие.
Этот метод принимает один, два или три аргумента, но мы рассмотрим
только простейший случай использования (с двумя аргументами), ко
гда первый аргумент представляет строку, содержащую символы для
преобразования, а второй – строку с символами, в которые нужно пре
образовать. Оба аргумента должны иметь одинаковую длину. Метод
str.translate() принимает таблицу преобразований и возвращает ко
пию строки с символами, преобразованными в соответствии с табли
цей преобразований. Ниже приводится пример преобразования бен
гальских цифр в английские:
table = "".maketrans("\N{bengali digit zero}"
"\N{bengali digit one}\N{bengali digit two}"
"\N{bengali digit three}\N{bengali digit four}"
"\N{bengali digit five}\N{bengali digit six}"
"\N{bengali digit seven}\N{bengali digit eight}"
"\N{bengali digit nine}", "0123456789")
print("20749".translate(table)) # выведет: 20749
print("\N{bengali digit two}07\N{bengali digit four}"
"\N{bengali digit nine}".translate(table)) # выведет: 20749
Обратите внимание на то, как в этом примере использовался характер
ный для Python прием конкатенации строк в вызове метода str.maket
rans() и во втором вызове функции print(), что позволило нам располо
жить строки в нескольких строках программного кода, не используя
экранирование символов перевода строки и явную операцию конкате
нации.
Метод str.maketrans() был вызван в контексте пустой строки, потому
что совершенно неважно, в контексте какой строки он вызывается –
он просто обрабатывает свои аргументы и возвращает таблицу преоб

100 Глава 2. Типы данных
разований. 1 Методы str.maketrans() и str.translate() могут также ис
пользоваться для удаления символов, если в третьем аргументе методу
str.maketrans() передать строку с нежелательными символами. Для бо
лее сложных случаев преобразования можно было бы создать отдель
ные кодеки; за более подробной информацией о кодеках обращайтесь
к описанию модуля codec.
В языке Python имеется еще ряд библиотечных модулей, обеспечи
вающих дополнительные функциональные возможности при работе со
строками. Мы уже упоминали модуль unicodedata и в следующем под
разделе покажем, как им пользоваться. Из других модулей, на кото
рые следует обратить внимание, можно назвать difflib, который ис
пользуется для поиска различий между двумя файлами или строками,
класс io.StringIO в модуле io, который позволяет обращаться к стро
кам как к файлам, и модуль textwrap, который предоставляет средства
обертывания и заполнения строк. Существует также модуль string,
в котором имеется несколько полезных констант, таких как ascii_let
ters и ascii_lowercase. Примеры использования некоторых из этих мо
дулей будут приводиться в главе 5. Кроме того, язык Python обеспечи
вает превосходную поддержку регулярных выражений с помощью мо
дуля re – этой теме целиком посвящена глава 12.
Форматирование строк с помощью метода str.format()
Метод str.format() представляет собой очень мощное и гибкое средство
создания строк. Использование метода str.format() в простых случаях
не вызывает сложностей, но для более сложного форматирования нам
необходимо изучить синтаксис форматирования.
Метод str.format() возвращает новую строку, замещая поля в контек
стной строке соответствующими аргументами. Например:
>>> "The novel '{0}' was published in {1}".format("Hard Times", 1854)
"The novel 'Hard Times' was published in 1854"
Каждое замещаемое поле идентифицируется именем поля в фигурных
скобках. Если в качестве имени поля используется целое число, оно
определяет порядковый номер аргумента, переданного методу str.for
mat(). Поэтому в данном случае поле с именем 0 было замещено пер
вым аргументом, а поле с именем 1 – вторым аргументом.
Если бы нам потребовалось включить фигурные скобки в строку фор
мата, мы могли бы сделать это, дублируя их, как показано ниже:
>>> "{{{0}}} {1} ;}}".format("I'm in braces", "I'm not")
"{I'm in braces} I'm not ;}"
1 Примечание для программистов, использующих объектноориентирован
ный стиль: str.maketrans() – это метод класса.

Строки 101
Если попытаться объединить строку и число, интерпретатор Python
совершенно справедливо возбудит исключение TypeError. Но это легко
можно сделать с помощью метода str.format():
>>> "{0}{1}".format("The amount due is $", 200)
'The amount due is $200'
С помощью str.format() мы также легко можем объединять строки
(хотя для этой цели лучше подходит метод str.join()):
>>> x = "three"
>>> s ="{0} {1} {2}"
>>> s = s.format("The", x, "tops")
>>> s
'The three tops'
Здесь мы использовали несколько строковых переменных, тем не ме
нее в большинстве примеров с применением метода str.format() в этом
разделе мы будем использовать строковые литералы – исключительно
ради удобства; но вы должны помнить, что в любых примерах, где ис
пользуются строковые литералы, точно также можно было бы исполь
зовать и строковые переменные.
Замещаемые поля могут определять одним из следующих способов:
{field_name}
{field_name!conversion}
{field_name:format_specification}
{field_name!conversion:format_specification}
Следует заметить, что замещаемые поля могут содержать другие за
мещаемые поля. Вложенные замещаемые поля не могут иметь какое
либо форматирование – их назначение состоит в том, чтобы позволить
динамически определять параметры форматирования. Примеры ис
пользования вложенных полей будут представлены при подробном
изучении спецификаторов формата. А теперь приступим к изучению
каждой части замещаемого поля, начав с его имени.
Имена полей
Имя поля может быть либо целым числом, соответствующим одному
из аргументов метода str.format(), либо именем одного из именован
ных аргументов метода. Именованные аргументы мы будем рассмат
ривать в главе 4, но в них нет ничего сложного, поэтому для полноты
картины ниже приводится пара примеров:
>>> "{who} turned {age} this year".format(who="She", age=88)
'She turned 88 this year'
>>> "The {who} was {0} last week".format(12, who="boy")
'The boy was 12 last week'

102 Глава 2. Типы данных
В первом примере используются два именованных аргумента, who и age,
а во втором – один позиционный аргумент (единственный тип аргу
ментов, который мы использовали до сих пор) и один именованный ар
гумент. Обратите внимание, что в списке аргументов именованные ар
гументы всегда следуют после позиционных, и, конечно же, мы можем
использовать в строке формата любые аргументы и в любом порядке.
Имена полей могут ссылаться на коллекции, такие как списки. В по
добных случаях для идентификации требуемого элемента можно ис
пользовать индексы (но не срезы!):
>>> stock = ["paper", "envelopes", "notepads", "pens", "paper clips"]
>>> "We have {0[1]} and {0[2]} in stock".format(stock)
'We have envelopes and notepads in stock'
Поле с именем 0 ссылается на позиционный аргумент, поэтому полю
{0[1]} соответствует второй элемент в списке stock, а полю {0[2]} – тре
тий элемент в списке stock.
Позднее мы познакомимся со словарями в языке Python.
Они хранят данные в виде пар «ключзначение», а по
скольку они также могут использоваться в качестве ар
гументов метода str.format(), приведем короткий при
мер. Не волнуйтесь, если чтото покажется вам непонят
ным, – вы все поймете, как только прочтете главу 3.
>>> d = dict(animal="elephant", weight=12000)
>>> "The {0[animal]} weighs {0[weight]}kg".format(d)
'The elephant weighs 12000kg'
При обращении к элементам словаря используется ключ, точно так же
как используется целочисленный индекс при обращении к элементам
списков и кортежей.
Мы можем также обращаться к атрибутам объектов по их именам.
Предположим, что мы импортировали модули math и sys, тогда можно
будет выполнить такое форматирование:
>>> "math.pi=={0.pi} sys.maxunicode=={1.maxunicode}".format(math, sys)
'math.pi==3.14159265359 sys.maxunicode==65535'
Таким образом, синтаксис имен полей позволяет обращаться как к по
зиционным, так и к именованным аргументам, которые передаются
методу str.format(). Если в качестве аргументов передаются коллек
ции, такие как списки или словари, или объекты, имеющие атрибуты,
то имеется возможность обращаться к любой части коллекции, ис
пользуя нотацию [] или ., как это показано на рис. 2.5.
Преобразования
Когда мы обсуждали числа типа decimal.Decimal, мы отме
чали, что такие числа могут выводиться одним из двух
способов, например:
Ти п dict ,
стр. 151
Числа типа Decimal ,
стр. 82

Строки 103
>>> decimal.Decimal("3.4084")
Decimal('3.4084')
>>> print(decimal.Decimal("3.4084"))
3.4084
Первый способ отображения значения типа decimal.Deci
mal – это репрезентативная форма. Ее назначение состо
ит в том, чтобы предоставить строку, которая может
быть воспринята интерпретатором Python для воссозда
ния объекта, который она представляет. Программы на
языке Python могут прибегать к интерпретации отдель
ных фрагментов программного кода или целых про
грамм, поэтому в некоторых ситуациях такая возмож
ность может оказаться совсем нелишней. Не все объекты
могут быть представлены в форме, позволяющей выпол
нить их воспроизведение; в таких случаях они предо
ставляются в виде строки, заключенной в угловые скоб
ки. Например, репрезентативной формой модуля sys яв
ляется строка "".
Второй способ отображения значения типа decimal.Decimal – это стро
ковая форма. Эта форма предназначена для представления человеку,
поэтому основное ее назначение состоит в том, чтобы отображать ин
формацию в том виде, в каком она будет иметь определенный смысл
для человека. Если тип данных не имеет строковой формы представле
ния, но необходима именно строка, Python будет использовать репре
зентативную форму.
Встроенные типы данных языка Python знакомы с методом str.for
mat() и при передаче их в качестве аргументов этому методу возвраща
ют соответствующую строку для отображения. В том, чтобы добавить
вметод str.format() поддержку собственных типов данных, нет ничего
сложного, в этом вы сможете убедиться в главе 6. Кроме того, имеется Функция eval() ,
стр. 400
индекс позиционного аргумента
{0} {1[5]}
{2[capital]}{3.rate}
индекс ключ атрибут
{title} {color[12]}{point[y]} {book.isbn}
имя именованного аргумента
Рис. 2.5. Примеры спецификаторов формата в именах полей с примечаниями

104 Глава 2. Типы данных
возможность переопределять привычное поведение типов данных и,
по желанию, заставлять их возвращать строковую или репрезентатив
ную форму представления. Этого можно добиться путем добавления
в поля спецификаторов преобразования. В настоящее время существу
ет три таких спецификатора: s – для принудительного вывода строко
вой формы, r – для принудительного вывода репрезентативной формы
иa– для принудительного вывода репрезентативной формы, но с ис
пользованием только символов ASCII. Ниже приводится пример ис
пользования этих спецификаторов:
>>> "{0} {0!s} {0!r} {0!a}".format(decimal.Decimal("93.4"))
"93.4 93.4 Decimal('93.4') Decimal('93.4')"
В данном случае строковая форма объекта decimal.Decimal совпадает со
строкой, предоставляемой для метода str.format(), что является впол
не обычным делом. Кроме того, в данном конкретном примере нет ни
какой разницы между репрезентативной формой и репрезентативной
формой ASCII, поскольку в обоих случаях используются только сим
волы ASCII.
Ниже приводится еще один пример, но на сей раз в нем используется
строка, содержащая заголовок фильма « » и хранящаяся
в переменной movie. Если вывести строку как "{0}".format(movie), она
будет выведена без изменений, но если необходимо избежать вывода
символов, не входящих в набор ASCII, можно использовать либо вызов
ascii(movie), либо выводить ее как "{0!a}".format(movie)– в обоих случа
ях будет получена строка '\u7ffb\u8a33\u3067\u5931\u308f\u308c\u308b'.
К настоящему моменту мы знаем, как помещать значения перемен
ных в строку формата и как принудительно выбирать строковую или
репрезентативную форму представления. Теперь мы готовы перейти
к рассмотрению приемов форматирования самих значений.
Спецификаторы формата
Форматирование целых чисел, чисел с плавающей точкой и строк час
то бывает вполне удовлетворительным. Но если нам требуется более
тонкое управление форматированием, мы легко можем реализовать
его с помощью спецификаторов формата. Мы будем отдельно рассмат
ривать форматирование строк, целых чисел и чисел с плавающей точ
кой, чтобы было легче разобраться в деталях. Общий синтаксис, кото
рый в равной мере относится ко всем этим типам данных, показан на
рис. 2.6.
В случае строк мы можем управлять символомзаполнителем, вырав
ниванием внутри поля, а также минимальной и максимальной шири
ной поля. Спецификаторы формата для строк начинаются с символа
двоеточия (:), за которым следует пара необязательных символов –
символзаполнитель (который не может быть правой фигурной скоб
кой }) и символ выравнивания (< – по левому краю, ^ – по центру и > –

Строки 105
по правому краю). Далее следует необязательное число, определяющее
минимальную ширину поля вывода, и далее, при желании, через точ
ку можно указать максимальную ширину поля вывода.
Обратите внимание, что если указан символзаполнитель, то должно
быть указано и направление выравнивания. Мы опустили части спе
цификатора формата, определяющие знак и тип, потому что на строки
они не оказывают никакого влияния. Вполне безопасно (хотя и бес
смысленно) использовать одно только двоеточие без указания допол
нительных элементов спецификатора.
Рассмотрим несколько примеров:
>>> s = "The sword of truth"
>>> "{0}".format(s) # форматирование по умолчанию
'The sword of truth'
>>> "{0:25}".format(s) # минимальная ширина поля вывода 25
'The sword of truth '
>>> "{0:>25}".format(s) # выравнивание по правому краю, минимальная ширина 25
' The sword of truth'
>>> "{0:^25}".format(s) # выравнивание по центру, минимальная ширина 25
' The sword of truth '
>>> "{0:^25}".format(s) #  заполнитель, по центру, минимальная ширина 25
'The sword of truth'
>>> "{0:.<25}".format(s) # . заполнитель, по левому краю, минимальная ширина 25
'The sword of truth.......'
>>> "{0:.10}".format(s) # максимальная ширина поля вывода 10
'The sword '
В предпоследнем примере нам пришлось определить выравнивание по
левому краю (даже при том, что это – значение по умолчанию). В про
тивном случае спецификатор формата приобрел бы вид :.25 и просто
означал бы максимальную ширину поля вывода 25 символов.
:.точность тип ширина 0 выравнивание знак заполнитель#
intb, c,
d, n,
o, x,
X;
floatse, E,
f, g,
G, n,
% <
по левому
краю;
> по правому
краю;
^ по центру
= заполнять
нулями прост
ранство между
знаком числа
и первой зна
чащей цифрой Любой
символ,
кроме
}+
всегда вы
водить знак;
– знак выво
дится,
только когда
необходимо;
“ ”
пробел или
знак «–»
префикс для целых чисел
0b, 0o, or
0x
Дополнение чисел нулями Мини
мальная
ширина
поляМаксимальная
ширина поля
для строк;
количество
знаков после
запятой для
чисел с плава
ющей точкой
Рис. 2.6. Спецификатор формата в общем виде

106 Глава 2. Типы данных
Как уже отмечалось ранее, внутри спецификатора формата можно ис
пользовать замещаемые поля. Это делает возможным динамически оп
ределять формат вывода. Ниже приводится пример, демонстрирую
щий два способа определить максимальную ширину строки с помо
щью переменной maxwidth:
>>> maxwidth = 12
>>> "{0}".format(s[:maxwidth])
'The sword of'
>>> "{0:.{1}}".format(s, maxwidth)
'The sword of'
В первом случае используется обычная операция извлечения среза, во
втором – вложенное замещаемое поле.
Применительно к целым числам спецификаторы формата позволяют
управлять символомзаполнителем, выравниванием внутри поля вы
вода, отображением знака числа, минимальной шириной поля и осно
ванием системы счисления.
Спецификаторы формата для целых чисел начинаются с двоеточия,
после которого может следовать пара необязательных символов – сим
волзаполнитель (который не может быть символом закрывающей фи
гурной скобки }) и символ выравнивания (< – по левому краю, ^ – по
центру, > – по правому краю и = – указывающий на необходимость за
полнять пространство между знаком числа и первой значащей циф
рой). Далее следует необязательный символ знака числа: «+» – говорит
об обязательной необходимости вывода знака числа, «
–» – знак выво
дится только для отрицательных чисел, и пробел говорит о том, что
для положительных чисел вместо знака числа должен выводиться
пробел, а для отрицательных чисел – знак «
–». Далее следует значение
минимальной ширины поля, которому может предшествовать символ
«#» с обозначением системы счисления (двоичная, восьмеричная или
шестнадцатеричная) и символ «0» – в случае необходимости дополне
ния числа нулями слева. Если число должно выводиться в системе
счисления, отличной от десятичной, необходимо указать символ типа
системы счисления: «b» – для двоичной, «o» – для восьмеричной, «x» –
для шестнадцатеричной с символами в нижнем регистре и «X» – для
шестнадцатеричной с символами в верхнем регистре. Для полноты
картины следует заметить, что допускается использовать символ «d»,
обозначающий десятичную систему счисления. Существует еще два
символа типа: «c», который означает, что должен выводиться символ
Юникода, соответствующий целому числу, и «n» – когда необходимо
обеспечить вывод чисел с учетом региональных настроек.
Дополнение нулями слева можно реализовать двумя способами:
>>> "{0:0=12}".format(8749203) # 0  символзаполнитель, минимальная ширина 12
'000008749203'
>>> "{0:0=12}".format(8749203)# 0  символзаполнитель, минимальная ширина 12
'00008749203'

Строки 107
>>> "{0:012}".format(8749203) # дополнение 0 и минимальная ширина 12
'000008749203'
>>> "{0:012}".format(8749203) # дополнение 0 и минимальная ширина 12
'00008749203'
В первых двух примерах 0 определяется как символзаполнитель, кото
рым заполняется пространство между знаком числа и первой значащей
цифрой (=). Во вторых двух примерах определяется минимальная ши
рина поля 12 символов и признак необходимости дополнения нулями.
Ниже приводится несколько примеров управления выравниванием:
>>> "{0:*<15}".format(18340427) # * символзаполнитель, выравнивание
'18340427*******' # по левому краю, минимальная ширина 15
>>> "{0:*>15}".format(18340427) # * символзаполнитель, выравнивание
'*******18340427' # по правому краю, минимальная ширина 15
>>> "{0:*^15}".format(18340427) # * символзаполнитель, выравнивание
'***18340427****' # по центру, минимальная ширина 15
>>> "{0:*^15}".format(18340427) # * символзаполнитель, выравнивание
'***18340427***' # по центру, минимальная ширина 15
Ниже приводится несколько примеров управления выводом знака
числа:
>>> "[{0: }] [{1: }]".format(539802, 539802) # пробел или знак ""
'[ 539802] [539802]'
>>> "[{0:+}] [{1:+}]".format(539802, 539802) # знак выводится принудительно
'[+539802] [539802]'
>>> "[{0:}] [{1:}]".format(539802, 539802) # знак "" выводится только
'[539802] [539802]' # при необходимости
Далее следуют два примера использования символов управления типом:
>>> "{0:b} {0:o} {0:x} {0:X}".format(14613198)
'110111101111101011001110 67575316 deface DEFACE'
>>> "{0:#b} {0:#o} {0:#x} {0:#X}".format(14613198)
'0b110111101111101011001110 0o67575316 0xdeface 0XDEFACE'
Для целых чисел невозможно определить максимальную ширину по
ля вывода, потому что в противном случае это может повлечь необхо
димость отсечения значащих цифр числа и вывод числа, не имеющего
смысла.
Последний символ управления форматом вывода целых чисел (доступ
ный также для чисел с плавающей точкой) – это символ «n». Он имеет
то же действие, что и символ «d» в случае вывода целых чисел или сим
вол «g» в случае вывода чисел с плавающей точкой. Отличительной
особенностью символа «n» является то, что он учитывает региональ
ные настройки, то есть использует характерный для текущего региона
символразделитель целой и дробной части числа и разделитель разря
дов. Регион, используемый по умолчанию, называется «C», и для это
го региона в качестве разделителя целой и дробной части числа ис
пользуется точка, а в качестве разделителя разрядов – пустая строка.

108 Глава 2. Типы данных
Чтобы иметь возможность принимать во внимание региональные на
стройки пользователя, в начале программы в качестве двух первых
выполняемых инструкций можно добавить следующие две строки:
1
import locale
locale.setlocale(locale.LC_ALL, "")
Передавая пустую строку в качестве названия региона, мы тем самым
предлагаем интерпретатору попытаться автоматически определить ре
гион пользователя (например, путем определения значения перемен
ной окружения LANG) и перейти на использование региона «C» в случае
неудачи. Ниже приводятся несколько примеров, демонстрирующих
влияние различных региональных настроек на вывод целых и вещест
венных чисел:
x, y = (1234567890, 1234.56)
locale.setlocale(locale.LC_ALL, "C")
c = "{0:n} {1:n}".format(x, y) # c == "1234567890 1234.56"
locale.setlocale(locale.LC_ALL, "en_US.UTF8")
en = "{0:n} {1:n}".format(x, y) # en == "1,234,567,890 1,234.56"
locale.setlocale(locale.LC_ALL, "de_DE.UTF8")
de = "{0:n} {1:n}".format(x, y) # de == "1.234.567.890 1.234,56"
Символ «n» очень удобно использовать с целыми числами, но при вы
воде чисел с плавающей точкой он имеет ограниченное применение,
потому что большие вещественные числа выводятся в экспоненциаль
ной форме.
При выводе чисел с плавающей точкой спецификаторы формата дают
возможность управлять символомзаполнителем, выравниванием в пре
делах поля вывода, выводом знака числа, минимальной шириной поля,
числом знаков после десятичной точки и формой представления – про
стая, экспоненциальная или в виде процентов.
Для форматирования чисел с плавающей точкой используются те же
самые спецификаторы, что и для целых чисел, с двумя отличиями
в конце. После необязательного значения минимальной ширины поля
вывода можно указать число знаков после десятичной точки, добавив
символ точки и целое число. В самом конце мы можем указать символ
типа: «e» – для вывода числа в экспоненциальной форме, с символом
«e» в нижнем регистре; «E» – для вывода числа в экспоненциальной
форме, с символом «E» в верхнем регистре; «f» – для вывода числа
в стандартной форме, «g» – для вывода числа в «общей» форме, то есть
для небольших чисел действует как символ «f», а для очень больших –
как символ «e», и «G» – то же самое, что символ «g», только использу
1 В программах, имеющих несколько потоков выполнения, функцию loca
le.setlocale() лучше вызывать всего один раз, на этапе запуска програм
мы, и еще до того, как будут запущены дополнительные потоки, поскольку
эту функцию обычно небезопасно вызывать в многопоточном окружении.

Строки 109
ется формат либо «f», либо «E». Кроме того, допускается использовать
символ «%», при использовании которого выводимое число умножает
ся на 100 и для вывода применяется формат «f», с добавлением симво
ла «%» в конце числа.
Ниже приводятся несколько примеров вывода числа в экспоненциаль
ной и стандартной форме:
>>> amount = (10 ** 3) * math.pi
>>> "[{0:12.2e}] [{0:12.2f}]".format(amount)
'[ 3.14e+03] [ 3141.59]'
>>> "[{0:*>12.2e}] [{0:*>12.2f}]".format(amount)
'[****3.14e+03] [*****3141.59]'
>>> "[{0:*>+12.2e}] [{0:*>+12.2f}]".format(amount)
'[***+3.14e+03] [****+3141.59]'
В первом примере установлена минимальная ширина поля вывода
12 символов и 2 знака после десятичной точки. Второй пример постро
ен на основе первого и к нему добавлен вывод символазаполнителя
«*». При использовании символазаполнителя необходимо указывать
символ выравнивания, поэтому мы указали выравнивание по правому
краю (даже при том, что этот способ выравнивания используется по
умолчанию для чисел). Третий пример построен на основе двух преды
дущих, в нем добавлен символ «+» управления принудительным выво
дом знака числа.
К моменту написания этих строк в языке Python отсутствовали средст
ва прямого управления форматированием комплексных чисел. Одна
ко мы легко можем решить эту проблему, форматируя действитель
ную и мнимую части как отдельные числа с плавающей точкой. На
пример:
>>> "{0.real:.3f}{0.imag:+.3f}j".format(4.75917+1.2042j)
'4.759+1.204j'
>>> "{0.real:.3f}{0.imag:+.3f}j".format(4.759171.2042j)
'4.7591.204j'
Мы обращаемся к каждому атрибуту комплексного числа по отдельно
сти и форматируем их как числа с плавающей точкой с тремя знаками
после запятой. Кроме того, мы принудительно выводим знак мнимой
части, добавляя символ j.
Пример: print_unicode.py
В предыдущих подразделах мы детально исследовали спецификаторы
формата для метода str.format() и видели достаточно много фрагментов
программного кода, демонстрирующих аспекты их применения на
практике. В этом подподразделе мы рассмотрим небольшой, но доста
точно поучительный пример использования метода str.format(), в кото
ром мы увидим применение спецификаторов формата в реальном кон
тексте. В примере также используются некоторые строковые методы,

110 Глава 2. Типы данных
с которыми мы познакомились в предыдущем разделе, и вводится
в использование функция из модуля unicodedata. 1
Эта программа состоит всего из 25 строк выполняемого программного
кода. Она импортирует два модуля, sys и unicodedata, и определяет од
ну функцию – print_unicode_table(). Рассмотрение примера мы начнем
с запуска программы, чтобы увидеть, что она делает; затем мы рас
смотрим программный код в конце программы, где выполняется вся
фактическая работа; и в заключение рассмотрим функцию, определяе
мую в программе.
print_unicode.py spoked
decimal hex chr name
   
10018 2722 ✢ Four TeardropSpoked Asterisk
10019 2723 ✣ Four BalloonSpoked Asterisk
10020 2724 ✤ Heavy Four BalloonSpoked Asterisk
10021 2725 ✥ Four ClubSpoked Asterisk
10035 2733 ✳ Eight Spoked Asterisk
10043 273B ✽ TeardropSpoked Asterisk
10044 273C ✼ Open Centre TeardropSpoked Asterisk
10045 273D ✽✽ Heavy TeardropSpoked Asterisk
10051 2743 ❃ Heavy TeardropSpoked Pinwheel Asterisk
10057 2749 ❈ BalloonSpoked Asterisk
10058 274A ❊ Eight TeardropSpoked Propeller Asterisk
10059 274B ❋ Heavy Eight TeardropSpoked Propeller Asterisk
При запуске без аргументов программа выводит таблицу всех символов
Юникода, начиная с пробела и до символа с наибольшим возможным
кодом. При запуске с аргументом, как показано в примере, выводятся
только те строки таблицы, где в названии символов Юникода содер
жится значение строкиаргумента, переведенной в нижний регистр.
word = None
if len(sys.argv) > 1:
if sys.argv[1] in ("h", "help"):
print("usage: {0} [string]".format(sys.argv[0]))
word = 0
else:
word = sys.argv[1].lower()
1 Эта программа предполагает, что консоль настроена на ра
боту в кодировке UTF8. К сожалению, консоль в операцион
ной системе Windows имеет весьма ограниченную поддерж
ку UTF8, а консоль в системе Mac OS X по умолчанию ис
пользует кодировку Apple Roman. Чтобы обойти эти ограни
чения, в состав примеров к книге включен файл print_
unicode_uni.py – версия программы, выполняющая вывод
в файл, который затем может быть открыт в редакторе, та
ком как IDLE, поддерживающем кодировку UTF8. Глава 7, рабо
та с файлами,
стр. 334

Строки 111
if word != 0:
print_unicode_table(word)
После инструкций импортирования и определения функции print_uni
code_table() выполнение достигает программного кода, показанного
выше. Сначала предположим, что пользователь не указал в командной
строке искомое слово. Если аргумент командной строки присутствует
и это
–h или ––help, программа выводит информацию о порядке исполь
зования и устанавливает флаг word в значение 0, указывая тем самым,
что работа завершена. В противном случае в переменную word записы
вается копия аргумента, введенного пользователем, с преобразовани
ем всех символов в нижний регистр. Если значение word не равно 0,
программа выводит таблицу.
При выводе информации о порядке использования применяется спе
цификатор формата, который представляет собой простое имя форма
та, в данном случае – порядковый номер позиционного аргумента. Мы
могли бы записать эту строку, как показано ниже:
print("usage: {0[0]} [string]".format(sys.argv))
При таком подходе первый символ 0 соответствует порядковому номе
ру позиционного аргумента, а [0] – это индекс элемента внутри аргу
мента, и такой прием сработает, потому что sys.argv является списком.
def print_unicode_table(word):
print("decimal hex chr {0:^40}".format("name"))
print("   {0:<40}".format(""))
code = ord(" ")
end = sys.maxunicode
while code < end:
c = chr(code)
name = unicodedata.name(c, "*** unknown ***")
if word is None or word in name.lower():
print("{0:7} {0:5X} {0:^3c} {1}".format(
code, name.title()))
code += 1
Мы использовали пару пустых строк исключительно для улучшения
удобочитаемости. Первые две строки функции выводят строки заго
ловка. Первый вызов str.format() выводит текст «name», отцентриро
ванный в поле вывода, шириной 40 символов, а второй вызов выводит
пустую строку в поле шириной 40 символов, используя символ «
–»
в качестве символазаполнителя, с выравниванием по левому краю.
(Мы вынуждены указывать символ выравнивания, когда задается сим
волзаполнитель.) Как вариант, вторую строку функции можно было
записать, как показано ниже:
print("   {0}".format("" * 40))

112 Глава 2. Типы данных
Здесь мы использовали оператор дублирования строки (*), чтобы соз
дать необходимую строку, и просто вставили ее в строку формата.
В третьем случае можно было бы просто ввести 40 символов «
–» и ис
пользовать простой литерал строки.
Текущий код символа Юникода сохраняется в перемен
ной code, которая инициализируется кодом пробела
(0x20). В переменную end записывается максимально воз
можный код символа Юникода, который может прини
мать разные значения в зависимости от того, какая из
кодировок (UCS2 или UCS4) использовалась при ком
пиляции Python.
Внутри цикла while с помощью функции chr() мы получаем символ
Юникода, соответствующий числовому коду. Функция unicodedata.na
me() возвращает название заданного символа Юникода, во втором не
обязательном аргументе передается имя, которое будет использовано
в случае, когда имя символа не определено.
Если пользователь не указывает аргумент командной строки (word is
None) или аргумент был указан и он входит в состав копии имени сим
вола Юникода, в которой все символы приведены к нижнему регист
ру, то выводится соответствующая строка таблицы.
Мы передаем переменную code методу str.format() один раз, но в стро
ке формата она используется трижды. Первый раз – при выводе значе
ния code как целого числа в поле с шириной 7 символов (по умолчанию
в качестве символазаполнителя используется пробел, поэтому нет не
обходимости явно указывать его). Второй раз – при выводе значения
code как целого числа в шестнадцатеричном формате символами верх
него регистра в поле шириной 5 символов. И третий раз – при выводе
символа Юникода, соответствующего значению code, с помощью спе
цификатора формата «c», отцентрированного в поле с минимальной
шириной 3 символа. Обратите внимание, что нам не потребовалось
указывать тип «d» в первом спецификаторе формата, потому что он
подразумевается по умолчанию для целых чисел. Второй аргумент –
это имя символа Юникода, которое выводится с помощью метода
str.title(), в результате которого первый символ каждого слова пре
образуется к верхнему регистру, а остальные символы – к нижнему.
Теперь, когда мы познакомились с универсальным методом str.for
mat(), мы можем с успехом использовать его на протяжении остальной
части книги.
Кодировки символов
Так или иначе, но компьютеры могут хранить информацию только
в виде байтов, то есть в виде 8битовых значений в диапазоне от 0x00 до
0xFF. Каждый символ должен быть представлен некоторым образом
в терминах байтов. На заре развития вычислительной техники были
Кодировки
символов,
стр. 112

Строки 113
разработаны схемы кодирования символов, в которых каждому кон
кретному был поставлен в соответствие байт с конкретным значением.
Например, в кодировке ASCII символ A представлен байтом со значе
нием 0x41, B – 0x42 и т. д. В Западной Европе часто использовалась ко
дировка Latin1, ее первые 127 символов совпадали с 7битовой коди
ровкой ASCII, а остальные значения представляли символы с умляу
тами и другие символы, необходимые европейцам. За долгие годы поя
вилось множество кодировок, которые используются до сих пор.
К сожалению, наличие такого разнообразия кодировок оказалось
очень неудобным, особенно при разработке интернационализируемого
программного обеспечения. Одним из решений, которое было принято
практически повсеместно, стало применение кодировки Юникод. В ко
дировке Юникод каждому символу ставится в соответствие целое чис
ло, то есть его код, как и в кодировках, разработанных ранее, но Юни
код не ограничивается использованием одного байта на символ и пото
му способен обеспечить единую систему представления каждого сим
вола любого языка. А для обеспечения обратной совместимости первые
127 символов Юникода совпадают со 127 символами 7битовой коди
ровки ASCII.
Но как хранятся символы Юникода? В настоящее время определено
немногим более 1 миллиона символов Юникода, поэтому даже 32би
товых целых чисел со знаком более чем достаточно для представления
любого кода в кодировке Юникод. Таким образом, самый простой спо
соб хранения символов Юникода заключается в использовании после
довательностей 32битовых целых чисел, по одному целому числу на
символ. Такое представление очень удобно хранить в памяти, потому
что мы получаем массив 32битовых чисел, элементы которого имеют
однозначное соответствие с символами. Но тогда если текст в файлах
или передаваемый по сети в основном содержит символы 7битовой
кодировки ASCII, то три из четырех переданных байтов будут нулевы
ми (0x00). Чтобы избежать появления такого большого объема ненуж
ной информации, сама кодировка Юникод имеет несколько представ
лений.
В памяти символы Юникода хранятся либо в формате UCS2 (по сути,
16битовые целые беззнаковые числа), способном представить первые
65 535 кодов символов, или в формате UCS4 (32битовые целые чис
ла), способном представить все коды символов, которых к моменту на
писания этих строк было 1 114 111. Выбор того или иного формата
производится на этапе компиляции PyП122
thon. (Если значение sys.maxunicode равно 65 535, значит Python ком
пилировался с поддержкой формата UCS2).
При сохранении данных в виде файлов или при передаче по сети ис
пользуется более сложный формат представления. При использовании
Юникода коды символов могут кодироваться с помощью кодировки
UTF8, в которой первые 127 символов кодируются однобайтовыми

114 Глава 2. Типы данных
значениями, а остальные – двумя или более байтами. Кодировка UTF8
очень компактна для английского текста, и если в нем используются
только символы из 7битового набора ASCII, то файлы с текстом в ко
дировке UTF8 ничем не отличаются от файлов в кодировке ASCII.
Другая популярная кодировка – UTF16. В ней для кодирования зна
чительной части символов используются два байта и для остальных –
четыре байта. Она более компактна для некоторых азиатских языков,
чем UTF8, но, в отличие от нее, текст в кодировке UTF16 должен на
чинаться с признака, указывающего порядок следования байтов, что
бы при чтении кодов можно было определить, какой порядок следова
ния пар байтов используется – прямой (bigendian) или обратный (litt
leendian). Кроме того, попрежнему широко используются старые ко
дировки, такие как GB2312, ISO88595, Latin1.
Метод str.encode() возвращает последовательность байтов, фактиче
ски – объект типа bytes, о котором будет рассказываться в главе 7, за
кодированных в соответствии с кодировкой, заданной в качестве аргу
мента. С помощью этого метода можно глубже понять различия между
кодировками и понять, почему неправильные предположения о коди
ровке могут приводить к появлению ошибок:
>>> artist = "Tage °Ase Ђn"
>>> artist.encode("Latin1")
b'Tage \xc5s\xe9n'
>>> artist.encode("CP850")
b'Tage \x8fs\x82n'
>>> artist.encode("utf8")
b'Tage \xc3\x85s\xc3\xa9n'
>>> artist.encode("utf16")
b'\xff\xfeT\x00a\x00g\x00e\x00 \x00\xc5\x00s\x00\xe9\x00n\x00'
Символ «b» перед открывающей кавычкой указывает, что это не стро
ковый литерал, а литерал типа bytes. Для удобства при создании лите
ралов типа bytes мы можем смешивать печатаемые символы ASCII
с экранированными шестнадцатеричными значениями.
Мы не можем представить имя «Tage °
AseЂn» с помощью символов
ASCII, потому что в этом наборе отсутствует символ «°
A», как и любые
другие символы с умляутами, поэтому при попытке сделать это возбу
ждается исключение UnicodeEncodeError. Кодировка Latin1 (известная
так же, как ISO88591) использует для представления символов 8би
товые значения, и в ней присутствуют все символы, необходимые для
представления данного имени. С другой стороны, артисту Erno
ЂЂ Bа
nk
повезло меньше, так как символ «o
ЂЂ» отсутствует в наборе символов
Latin1. Конечно, оба имени благополучно могут быть представлены
в кодировке Юникод. Примечательно, что при использовании коди
ровки UTF16 первые два байта являются признаком порядка следова
ния байтов – они используются функцией декодирования, чтобы опре
делить, какой порядок следования используется, прямой или обрат
ный, и выполнить декодирование соответствующим образом.

Строки 115
Следует отметить пару важных особенностей, присущих методу str.en
code(). Первый аргумент (имя кодировки) не чувствителен к регистру
символов, а символы дефиса и подчеркивания в имени считаются эк
вивалентными, поэтому имена «usascii» и «US_ASCII» рассматрива
ются как одно и то же имя. Кроме того, для одной и той же кодировки
может иметься множество альтернативных названий: например, на
звания «latin», «latin1», «latin_1», «ISO88591», «CP819» и некото
рые другие обозначают кодировку «Latin1». Метод может также при
нимать второй необязательный аргумент, который сообщает, как сле
дует обрабатывать ошибки. Например, мы можем закодировать лю
бую строку в кодировке ASCII, передав во втором аргументе «ignore»
или «replace» – ценой потери данных, или без потерь, передав строку
«backslashreplace» – в этом случае символы, не входящие в набор
ASCII, будут представлены последовательностями \x, \u и \U. Напри
мер, вызов artist.encode("ascii", "ignore") вернет b'Tage sn', вызов art
ist.encode("ascii", "replace") вернет b'Tage ?s?n', а вызов artist.enco
de("ascii", "backslashreplace") вернет b'Tage \xc5s\xe9n'. (Точно так же
мы могли бы получить строку ASCIIсимволов с помощью вызова
"{0!a}".format(artist), который вернет 'Tage \xc5s\xe9n'.)
В дополнение к методу str.encode() имеется метод bytes.decode() (а так
же bytearray.decode()), который возвращает строку с байтами, декоди
рованными при помощи заданной кодировки. Например:
>>> print(b"Tage \xc3\x85s\xc3\xa9n".decode("utf8"))
Tage °Ase Ђn
>>> print(b"Tage \xc5s\xe9n".decode("latin1"))
Tage
°Ase Ђn
Различия между 8битовыми кодировками Latin1, CP850 (кодировка
IBM PC) и кодировкой UTF8 очевидно доказывают, что выбор коди
ровки наугад едва ли может считаться успешной стратегией. К сча
стью, кодировка UTF8 фактически уже стала стандартом для про
стых текстовых файлов, благодаря чему следующие поколения, веро
ятно, даже не узнают, что некогда существовали другие кодировки.
Для файлов с расширением .py используется кодировка UTF8, поэто
му Python всегда знает, какая кодировка используется для представ
ления строковых литералов. Это означает, что мы можем использо
вать в своих строках любые символы Юникода, поддерживаемые на
шим текстовым редактором.
1
Когда Python читает данные из внешних источников, например из сете
вых сокетов, он не может заранее знать, какая кодировка используется,
1 Вполне возможно использовать и другие кодировки. Подробности можно
найти в документе «Python Tutorial», в разделе «Source Code Encoding».
(http://docs.python.org/3.0/tutorial/interpreter.html#sourcecodeencoding.–
Прим. перев.)

116 Глава 2. Типы данных
поэтому он возвращает байты, которые мы можем декодировать нуж
ным образом. В отношении текстовых файлов Python использует более
дружественный подход, используя локальную кодировку, если мы не
указываем ее явно.
К счастью, некоторые форматы файлов явно определяют свою коди
ровку. Например, мы можем предположить, что XMLфайл использу
ет кодировку UTF8, если в нем отсутствует директива , явно
указывающая другую кодировку. Поэтому при чтении XMLфайлов
мы можем извлекать, скажем первые 1000 байтов, отыскивать опреде
ление кодировки и, если оно присутствует, декодировать содержимое
файла в соответствии с указанной кодировкой; в противном случае пе
реходить на использование кодировки UTF8. Такой прием должен
срабатывать для любых XMLфайлов или простых текстовых файлов,
в которых используется любая из однобайтовых кодировок, поддержи
ваемых Python, за исключением кодировок, основанных на EBCDIC
(CP424, CP500), и некоторых других (CP037, CP864, CP865, CP1026,
CP1140, HZ, SHIFTJIS2004, SHIFTJISX0213). К сожалению, такой
подход неприменим в случае использования многобайтовых кодиро
вок (таких как UTF16 и UTF32). В каталоге пакетов Python (Python
Package Index), pypi.python.org/pypi, имеется по крайней мере два па
кета, позволяющих автоматически определять кодировку файлов.
Примеры
В этом разделе мы будем использовать знания, полученные в этой
и в предыдущей главах, чтобы представить две маленькие, но закон
ченные программы, соединяющие в себе все, что мы узнали до сих пор.
Первая программа имеет некоторое отношение к математике, но она
очень короткая и занимает всего 35 строк. Вторая связана с обработ
кой текста и имеет более существенный объем – в ней определяется
семь функций и содержит она около 80 строк программного кода.
quadratic.py
Квадратные уравнения – это уравнения вида ax 2+bx+c=0, где a≠0,
описывающие параболу. Корни таких уравнений находятся по фор
муле
.
Часть формулы b
2–4ac называется дискриминантом – если это поло
жительная величина, уравнение имеет два действительных корня, ес
ли дискриминант равен нулю – уравнение имеет один действительный
корень, и в случае отрицательного значения уравнение имеет два ком
плексных корня. Мы напишем программу, которая будет принимать
x–bb 2–4ac ±
2a  =

Примеры 117
от пользователя коэффициенты a, b и c (коэффициенты b и c могут быть
равны нулю) и затем вычислять и выводить его корень или корни. 1
Для начала посмотрим, как работает программа, а потом перейдем
к изучению программного кода.
quadratic.py
ax2 + bx + c = 0
enter a: 2.5
enter b: 0
enter c: 7.25
2.5x
2 + 0.0x + 7.25 = 0 → x = 1.70293863659 or x = 1.70293863659
С коэффициентами 1.5, –3 и 6 программа выведет (некоторые цифры
обрезаны):
1.5x 2 + 3.0x + 6.0 = 0 → x = (1+1.7320508j) or x = (11.7320508j)
Вывод программы не так хорош, как хотелось бы – например, вместо
+
–3.0x лучше было бы выводить –3.0x, а коэффициенты, равные нулю, –
вообще не показывать. Вы получите шанс ликвидировать эти недос
татки при выполнении упражнений.
Теперь обратимся к программному коду, который начинается тремя
инструкциями import:
import cmath
import math
import sys
Нам необходимы обе математические библиотеки для работы с числа
ми типа float и complex, так как функции, вычисляющие квадратный
корень из вещественных и комплексных чисел, отличаются. Модуль
sys нам необходим, так как в нем определена константа sys.float_in
fo.epsilon, которая потребуется нам для сравнения вещественных чи
сел со значением 0.
Нам также необходима функция, которая будет получать от пользова
теля число с плавающей точкой:
def get_float(msg, allow_zero):
x = None
while x is None:
try:
x = float(input(msg))
1 Поскольку консоль в операционной системе Windows имеет весьма ограни
ченную поддержку UTF8, а консоль в системе Mac OS X по умолчанию ис
пользует кодировку Apple Roman, существует две проблемы с символами
2
и →, которые используются программой quadratic.py. Мы включили в при
меры файл quadratic_uni.py, который отображает корректные символы
в консоли Linux и использует их заменители (^2 и >) в других системах.

118 Глава 2. Типы данных
if not allow_zero and abs(x) < sys.float_info.epsilon:
print("zero is not allowed")
x = None
except ValueError as err:
print(err)
return x
Эта функция выполняет цикл, пока пользователь не введет допустимое
число с плавающей точкой (например, 0.5,
–9, 21, 4.92), и допускает
ввод значения 0, только если аргумент allow_zero имеет значение True.
Вслед за определением функции get_float() выполняется оставшаяся
часть программного кода. Мы разделим его на три части и начнем со
взаимодействия с пользователем:
print("ax\N{SUPERSCRIPT TWO} + bx + c = 0")
a = get_float("enter a: ", False)
b = get_float("enter b: ", True)
c = get_float("enter c: ", True)
Благодаря функции get_float() получить значения коэффициентов a,
b и c оказалось очень просто. Второй аргумент функции сообщает, ко
гда значение 0 является допустимым.
x1 = None
x2 = None
discriminant = (b ** 2)  (4 * a * c)
if discriminant == 0:
x1 = (b / (2 * a))
else:
if discriminant > 0:
root = math.sqrt(discriminant)
else: # discriminant < 0
root = cmath.sqrt(discriminant)
x1 = (b + root) / (2 * a)
x2 = (b  root) / (2 * a)
Программный код выглядит несколько иначе, чем формула, потому
что мы начали вычисления с определения значения дискриминанта.
Если дискриминант равен 0, мы знаем, что уравнение имеет единствен
ное действительное решение и можно сразу же вычислить его. В про
тивном случае мы вычисляем действительный или комплексный квад
ратный корень из дискриминанта и находим два корня уравнения.
equation = ("{0}x\N{SUPERSCRIPT TWO} + {1}x + {2} = 0"
" \N{RIGHTWARDS ARROW} x = {3}").format(a, b, c, x1)
if x2 is not None:
equation += " or x = {0}".format(x2)
print(equation)
Мы не использовали скольконибудь сложного форматирования, по
скольку форматирование, используемое по умолчанию для чисел с пла
вающей точкой в языке Python, прекрасно подходит для этого приме

Примеры 119
ра, но мы использовали некоторые имена Юникода для вывода пары
специальных символов.
csv2html.py
Часто бывает необходимо представить данные в формате HTML. В этом
подразделе мы разработаем программу, которая читает данные из
файла в простом формате CSV (Comma Separated Value – значения,
разделенные запятыми) и выводит таблицу HTML, содержащую эти
данные. В составе Python присутствует мощный и сложный модуль
для работы с форматом CSV и похожими на него – модуль csv, но здесь
мы будем выполнять всю обработку вручную.
В формате CSV каждая запись располагается на одной строке, а поля
внутри записи отделяются друг от друга запятыми. Каждое поле мо
жет быть либо строкой, либо числом. Строки должны окружаться апо
строфами или кавычками, а числа не должны окружаться кавычками,
если они не содержат запятые. Внутри строк допускается присутствие
запятых, и они не должны интерпретироваться как разделители по
лей. Мы будем исходить из предположения, что первая запись в файле
содержит имена полей. На выходе будет воспроизводиться таблица
в формате HTML с выравниванием текста по левому краю (по умолча
нию для HTML) и с выравниванием чисел по правому краю, по одной
строке на запись и по одной ячейке на поле.
Программа должна вывести открывающий тег таблицы HTML, затем
прочитать каждую строку данных и для каждой строки вывести соот
ветствующую строку таблицы HTML, а в завершение вывести закры
вающий тег таблицы HTML. Мы будем использовать светлозеленый
цвет фона для первой строки таблицы (где будут выводиться названия
полей), а при выводе строк с данными будем чередовать белый и свет
ложелтый цвет фона. Кроме того, нам необходимо правильно экрани
ровать специальные символы HTML («&», «<» и «>»), а строки немно
го сократить.
Ниже приводится маленький фрагмент файла с данными:
"COUNTRY","2000","2001",2002,2003,2004
"ANTIGUA AND BARBUDA",0,0,0,0,0
"ARGENTINA",37,35,33,36,39
"BAHAMAS, THE",1,1,1,1,1
"BAHRAIN",5,6,6,6,6
Предположим, что данные находятся в файле data/co2sample.csv и вы
полнена команда csv2html.py < data/co2sample.csv > co2sample.html,
тогда файл co2sample.html должен содержать примерно следующее:






120 Глава 2. Типы данных
...




...
Country20002001 20022003 2004
Argentina 3735 3336 39

Мы немного привели в порядок результаты работы программы и опус
тили некоторые строки, подставив вместо них многоточия. Мы ис
пользовали очень простую версию HTML – HTML 4 transitional, без
применения таблиц стилей. На рис. 2.7 показано, как выглядит полу
ченная таблица в вебброузере.
Теперь, когда мы увидели, как используется программа и что она дела
ет, можно приступать к изучению программного кода. Программа на
чинается с импортирования модуля sys – с этого момента мы не будем
больше показывать строки, выполняющие импортирование, если в них
не импортируется нечто необычное или они не требуют обсуждения.
Последняя инструкция в программе – это простой вызов функции:
main()
Хотя в языке Python не требуется явно указывать точку входа в про
грамму, как в некоторых других языках программирования, тем не
менее является распространенной практикой создание в программе на
языке Python функции с именем main(), которая вызывается для вы
полнения обработки. Поскольку функция не может вызываться до то
го, как она будет определена, мы должны вставлять вызов main() толь
ко после того, как данная функция будет определена. Порядок следо
вания функций в файле (то есть порядок, в котором они создаются) не
имеет значения.
В программе csv2html.py первой вызываемой функцией является функ
ция main(), которая в свою очередь вызывает функции print_start()
иprint_line(). Функция print_line() вызывает функции extract_fields()
и escape_html(). Структура программы показана на рис. 2.8.
Когда интерпретатор Python читает файл, он начинает делать это с са
мого начала. Поэтому сначала будет выполнен импорт, затем будет
Рис. 2.7. Таблица, произведенная программой csv2html.py, в броузере

Примеры 121
создана функция main(), а затем будут созданы остальные функции –
в том порядке, в каком они следуют в файле. Когда интерпретатор, на
конец, достигнет вызова main() в конце файла, все функции, которые
вызываются функцией main() (и все функции, которые вызываются
этими функциями), будут определены. Выполнение обработки, как
и следовало ожидать, начинается в точке вызова функции main().
Рассмотрим все функции по порядку, начиная с функции main().
def main():
maxwidth = 100
print_start()
count = 0
while True:
try:
line = input()
if count == 0:
color = "lightgreen"
elif count % 2:
color = "white"
else:
color = "lightyellow"
print_line(line, color, maxwidth)
count += 1
except EOFError:
break
print_end()
Переменная maxwidth используется для хранения числа символов в ячей
ке. Если поле больше, чем это число, часть строки отсекается и на ме
сто отброшенного текста добавляется многоточие. Программный код
вызывает вызывает вызывает
import sys
def
main():
def
print_start():
def
print_line():
def
extract_fields():
def
escape_html():
def
print_end():
main()
Рис. 2.8. Структура программы csv2html.py

122 Глава 2. Типы данных
функций print_start(), print_line() и print_end() будет приведен чуть
ниже. Цикл while выполняет обход всех входных строк – это могут
быть строки, вводимые пользователем с клавиатуры, но мы предпола
гаем, что данные будут перенаправлены из файла. Далее выбирается
цвет фона и вызывается функция print_line(), которая выводит стро
ку в виде строки таблицы в формате HTML.
def print_start():
print("")
def print_end():
print("
")
Мы могли бы не создавать эти две функции и просто вставить соответ
ствующие вызовы print() в функцию main(). Но мы предпочитаем вы
делять логику, так как это делает реализацию более гибкой, хотя
в этом маленьком примере гибкость не имеет большого значения.
def print_line(line, color, maxwidth):
print("".format(color))
fields = extract_fields(line)
for field in fields:
if not field:
print("")
else:
number = field.replace(",", "")
try:
x = float(number)
print("{0:d}".format(round(x)))
except ValueError:
field = field.title()
field = field.replace(" And ", " and ")
field = escape_html(field)
if len(field) <= maxwidth:
print("{0}".format(field))
else:
print("{0:.{1}} ...".format(field,
maxwidth))
print("")
Мы не можем использовать метод str.split(",") для разбиения каж
дой строки на поля, потому что запятые могут находиться внутри
строк в кавычках. Поэтому мы возложили эту обязанность на функ
цию extract_fields(). Получив список строк полей (в виде строк без ок
ружающих их кавычек), мы выполняем обход списка и создаем для
каждого поля ячейку таблицы.
Если поле пустое, мы выводим пустую ячейку. Если поле было заклю
чено в кавычки, это может быть строка или число в кавычках, содер
жащее символы запятой, например "1,566". Учитывая такую возмож
ность, мы создаем копию поля без запятых и пытаемся преобразовать
ее в число типа float. Если преобразование удалось, мы определяем

Примеры 123
выравнивание в ячейке по правому краю, а значение поля округляется
до ближайшего целого, которое и выводится. Если преобразование не
удалось, следовательно, поле содержит строку. В этом случае мы с по
мощью метода str.title() изменяем регистр символов и замещаем сло
во «And» на слово «and», устраняя побочный эффект действия метода
str.title(). Затем выполняется экранирование специальных символов
HTML и выводится либо поле целиком, либо первые maxwidth символов
с добавлением многоточия. Простейшей альтернативой использова
нию вложенного поля замены в строке формата является получение
среза строки, например:
print("{0} ...".format(field[:maxwidth]))
Еще одно преимущество такого подхода состоит в том, что он требует
меньшего объема ввода с клавиатуры.
def extract_fields(line):
fields = []
field = ""
quote = None
for c in line:
if c in "\"'":
if quote is None: # начало строки в кавычках
quote = c
elif quote == c: # конец строки в кавычках
quote = None
else:
field += c # другая кавычка внутри строки в кавычках
continue
if quote is None and c == ",": # end of a field
fields.append(field)
field = ""
else:
field += c # добавить символ в поле
if field:
fields.append(field) # добавить последнее поле в список
return fields
Эта функция читает символы из строки один за другим и накапливает
список полей, где каждое поле – это строка без окружающих ее кавы
чек. Функция способна обрабатывать поля, не заключенные в кавыч
ки, и поля, заключенные в кавычки или в апострофы, корректно обра
батывая запятые и кавычки (апострофы в строках, заключенных в ка
вычки, и кавычки в строках, заключенных в апострофы).
def escape_html(text):
text = text.replace("&", "&")
text = text.replace("<", "<")
text = text.replace(">", ">")
return text

124 Глава 2. Типы данных
Эта функция просто замещает каждый специальный символ HTML со
ответствующей ему сущностью языка HTML. В первую очередь, ко
нечно, мы должны заменить символ амперсанда и угловые скобки, хо
тя порядок не имеет никакого значения. В стандартной библиотеке
Python имеется более сложная версия этой функции – вы получите
возможность использовать ее в упражнениях и еще раз встретитесь
с ней в главе 7.
В заключение
Эта глава началась с демонстрации списка ключевых слов языка Py
thon и описания правил, применяемых к идентификаторам в языке
Python. Благодаря поддержке Юникода идентификаторы языка Py
thon не ограничены поднабором символов такого небольшого множе
ства, как ASCII или Latin1.
Также был описан тип данных int, который отличается от аналогич
ных типов во многих других языках программирования тем, что не
имеет ограничений на размер. Размер целых чисел в языке Python ог
раничивается лишь объемом машинной памяти, и интерпретатор
вполне в состоянии работать с числами, состоящими из сотен цифр.
Все основные типы данных в языке Python относятся к категории не
изменяемых, но эта их особенность практически незаметна – за счет
того, что комбинированные операторы присваивания (+=, *=,
–=, /=
и другие) позволяют использовать достаточно естественный синтак
сис, хотя при этом интерпретатор Python создает новые объекты с ре
зультатами и выполняет повторную привязку к ним наших перемен
ных. Литералы целых чисел обычно записываются в виде десятичных
чисел, но также существует возможность записывать двоичные лите
ралы, используя префикс 0b, восьмеричные литералы, используя пре
фикс 0o, и шестнадцатеричные литералы, используя префикс 0x.
Когда деление двух целых чисел выполняется с помощью оператора /,
результатом всегда будет число типа float. Это отличает Python от
многих других языков программирования, но позволяет избежать не
которых трудноуловимых ошибок, которые могут возникнуть изза
усечения дробной части в результате. (Если необходимо выполнить це
лочисленное деление, следует использовать оператор //.)
В языке Python имеется тип данных bool, который может иметь одно
из двух значений – True или False. В языке Python имеется три логиче
ских оператора: and, or и not, два из которых (and и or) опираются на ло
гику сокращенных вычислений.
Имеется три разновидности чисел с плавающей точкой: float, complex
иdecimal.Decimal. Наиболее часто используется тип float – он пред
ставляет числа с плавающей точкой двойной точности, чьи точные ха
рактеристики зависят от библиотек C, C# или Java, на основе которых
была выполнена компиляция Python. Комплексные числа представле

В заключение 125
ны парой чисел типа float, одно из которых хранит действительную
часть комплексного числа, а второе – мнимую. Тип decimal.Decimal реа
лизован модулем decimal. По умолчанию эти числа имеют точность
представления 28 десятичных знаков, однако точность может быть
увеличена или уменьшена в зависимости от потребностей.
Все три типа чисел с плавающей точкой могут использоваться в ком
бинации с типичными арифметическими операторами и функциями.
В дополнение к этому модуль math предоставляет разнообразные триго
нометрические, гиперболические и логарифмические функции, кото
рые могут использоваться с числами типа float, а модуль cmath предос
тавляет аналогичное множество функций для работы с числами типа
complex.
Большая часть главы посвящена строкам. Литералы строк в языке Py
thon могут создаваться с помощью апострофов или кавычек, а если
возникает необходимость включить в строку символы перевода строки
или кавычки, можно использовать тройные кавычки. Для вставки
специальных символов могут использоваться различные экраниро
ванные последовательности, такие как табуляция (\t) и перевод стро
ки (\n), и символы Юникода, как с использованием шестнадцатерич
ных экранированных последовательностей, так и с использованием
названий символов Юникода. Несмотря на то, что строки поддержива
ют те же самые операторы сравнения, что и другие типы данных в язы
ке Python, мы отметили, что сортировка строк, содержащих неанг
лийские символы, может вызывать сложности.
Поскольку строки являются последовательностями, к ним может при
меняться оператор получения среза ([]), имеющий простой, но мощный
синтаксис. Строки могут также объединяться с помощью оператора +
и дублироваться с помощью оператора *; кроме того, можно использо
вать комбинированные операторы присваивания (+= и *=), хотя для
конкатенации строк предпочтительнее использовать метод str.join().
Строки имеют множество других методов, включая методы проверки
их содержимого (такие как str.isspace() и str.isalpha()), методы изме
нения регистра символов (такие как str.lower() и str.title()), методы
поиска (такие как str.find() и str.index()) и многие другие.
Поддержка строк в языке Python действительно находится на очень
высоком уровне, позволяя нам легко отыскивать, извлекать или срав
нивать как целые строки, так и их части, замещать символы или под
строки, разбивать строки на списки подстрок и объединять списки
строк в единую строку.
Пожалуй, самым универсальным строковым методом является метод
str.format(). Этот метод используется для создания строк путем заме
щения полей значениями переменных и посредством задания специ
фикаторов формата, точно определяющих характеристики каждого
поля, замещаемого некоторым значением. Синтаксис имен замещае
мых полей позволяет организовать доступ к позиционным или имено

126 Глава 2. Типы данных
ванным аргументам метода и использовать индексы, ключи или имена
атрибутов для доступа к элементам или атрибутам аргументов. Специ
фикаторы формата позволяют определять символзаполнитель, на
правление выравнивания и минимальную ширину поля вывода. Кроме
того, при форматировании чисел мы можем определять, как должен
выводиться знак числа, а для чисел с плавающей точкой указывать
число знаков после десятичной точки и выводить их в стандартном
или экспоненциальном представлении.
Мы также обсудили сложную проблему кодировок символов. По умол
чанию для файлов .py используется кодировка UTF8, благодаря чему
мы имеем возможность записывать комментарии, идентификаторы
и данные на любом языке человеческого общения. С помощью метода
str.encode() мы можем преобразовать строку в последовательность бай
тов, используя определенную кодировку, а с помощью метода bytes.de
code() выполнить обратное преобразование последовательности байтов
в строку, используя определенную кодировку. Широкое разнообразие
кодировок, находящихся в использовании, может доставлять массу
неудобств, но кодировка UTF8 быстро превращается в фактический
стандарт для простых текстовых файлов (и уже используется по умол
чанию для XMLфайлов), поэтому данная проблема должна потерять
свою остроту в ближайшие годы.
В дополнение к типам данных, рассматривавшимся в этой главе, Py
thon предоставляет еще два встроенных типа данных – bytes и bytearray,
оба они будут рассматриваться в главе 7. В языке Python имеется так
же несколько типов коллекций, часть которых является встроенными
типами, а часть реализована в стандартной библиотеке. Наиболее важ
ные типы коллекций языка Python будут рассматриваться в следую
щей главе.
Упражнения
1. Измените программу print_unicode.py так, чтобы пользователь мог
вводить в командной строке несколько разных слов и получать
только те строки из таблицы символов Юникода, в которых содер
жатся все слова, указанные пользователем. Это означает, что мы
сможем вводить такие команды:
print_unicode_ans.py greek symbol
Один из способов достижения поставленной цели состоит в том,
чтобы заменить переменную word (которая может хранить 0, None
или строку) списком words. Не забудьте изменить информацию о по
рядке использования. В результате изменений не более десяти
строк программного кода добавится и не более десяти строк изме
нится. Решение находится в файле print_unicode_ans.py (Пользова
тели Windows и кроссплатформенной версии программы должны

Упражнения 127
модифицировать файл print_inicode_uni.py, а решение находится
вфайле print_inicode_uni_ans.py.)
2. Измените программу quadratic.py так, чтобы она не выводила коэф
фициенты со значением 0.0, а отрицательные коэффициенты выво
дились бы как
–n, а не + –n. Для этого придется заменить последние
пять строк программы примерно пятнадцатью строками. Решение
находится в файле quadratic_ans.py. (Пользователи Windows
и кроссплатформенной версии программы должны модифицировать
файл quadratic_uni.py, а решение находится в файле quadratic_uni_
ans.py.)
3. Удалите функцию escape_html() из программы cvs2html.py и ис
пользуйте вместо нее функцию xml.sax.saxutils.escape() из модуля
xml.sax.saxutils. Для этого потребуется добавить одну новую строку
(с инструкцией import), удалить пять строк (с ненужной функцией)
и изменить одну строку (задействовать функцию xml.sax.saxutils.
escape() вместо escape_html()). Решение приводится в файле csv2
html1_ans.py.
4. Измените программу cvs2html.py еще раз и добавьте в нее новую
функцию с именем process_options(). Эта функция должна вызы
ваться из функции main() и возвращать кортеж с двумя значениями:
maxwidth (типа int) и format (типа str). При вызове функция
process_options() должна устанавливать maxwidth в значение по умол
чанию 100, а строку format – в значение по умолчанию ".0f", которое
будет использоваться как спецификатор формата при выводе чисел.
Если пользователь вводит в командной строке «
–h» или « ––help»,
должно выводиться сообщение о порядке использования и возвра
щаться кортеж (None, None). (В этом случае функция main() ничего
делать не должна.) В противном случае функция должна прочитать
аргументы командной строки и выполнить соответствующие при
сваивания. Например, устанавливать значение переменной maxwidth,
если задан аргумент «maxwidth=n», и точно так же устанавливать
значение переменной format, если задан аргумент «format=s». Ниже
приводится сеанс работы с программой, когда пользователь затребо
вал инструкцию о порядке работы:
csv2html2_ans.py h
usage:
csv2html.py [maxwidth=int] [format=str] < infile.csv > outfile.html
maxwidth is an optional integer; if specified, it sets the maximum
number of characters that can be output for string fields,
otherwise a default of 100 characters is used.
(maxwidth – необязательное целое число. Если задано, определяет
максимальное число символов для строковых полей. В противном случае
используется значение по умолчанию 100.)
format is the format to use for numbers; if not specified it
defaults to ".0f".

128 Глава 2. Типы данных
(format – формат вывода чисел. Если не задан, по умолчанию используется
формат ".0f".)
А ниже приводится пример командной строки, в которой установ
лены оба аргумента:
csv2html2_ans.py maxwidth=20 format=0.2f < mydata.csv > mydata.html
Не забудьте изменить функцию print_line() так, чтобы она исполь
зовала переменную format при выводе чисел – для этого вам придет
ся передавать функции дополнительный аргумент, добавить одну
строку и изменить еще одну строку. И это немного затронет функ
цию main(). Функция process_options() должна содержать порядка
двадцати пяти строк (включая девять строк с текстом сообщения
о порядке использования). Это упражнение может оказаться слож
ным для неопытных программистов.
В состав примеров входят два файла с тестовыми данными: data/
co2sample.csv и data/co2fromfossilfuels.csv. Решение приводится
вфайле csv2html2_ans.py. В главе 5 мы увидим, как для обработки
аргументов командной строки можно использовать модуль optparse.

3
Типы коллекций
В предыдущей главе мы познакомились с наиболее важными фунда
ментальными типами данных языка Python. В этой главе мы расши
рим свои возможности, узнав, как объединять элементы данных вме
сте, используя типы коллекций языка Python. В этой главе мы рас
смотрим кортежи и списки, а также познакомимся с новыми типами
коллекций, включая словари и множества, и детально изучим их.
1
В дополнение к коллекциям мы также узнаем, как создавать элементы
данных, вмещающие в себя другие элементы данных (подобно струк
турам в языках C и C++ и записям в языке Pascal). Такие элементы
в случае необходимости могут интерпретироваться как единое целое,
и при этом сохраняется возможность прямого доступа к отдельным эле
ментам, хранящимся в них. Естественно, ничто не мешает вставлять
такие агрегатные элементы в коллекции, как любые другие элементы.
Наличие коллекций элементов данных существенно упрощает выпол
нение операций, которые должны применяться к элементам, а также
упрощает обработку коллекций элементов при чтении их из файлов.
В этой главе мы рассмотрим основные приемы работы с файлами лишь
в том объеме, который нам потребуется, отложив описание основных
подробностей (включая обработку ошибок) до главы 7.
После знакомства с отдельными типами коллекций мы посмотрим,
как можно организовать обход коллекций в цикле, поскольку в языке
Python для итераций через любые коллекции используются одни и те
же синтаксические конструкции. Кроме этого, мы исследуем пробле
мы и приемы копирования коллекций.
1 Определение того, что является последовательностью, множеством или
отображением, в этой главе дается не с формальной, а с практической точ
ки зрения. Более формальные определения даются в главе 8. •
Последовательности
•Множества
•Отображения
•Обход в цикле и копирование
коллекций

130 Глава 3. Типы коллекций
Последовательности
Последовательности – это один из типов данных, поддерживающих
оператор проверки на вхождение (in), функцию определения размера
(len()), оператор извлечения срезов ([]) и возможность выполнения
итераций. В языке Python имеется пять встроенных типов последова
тельностей: bytearray, bytes, list, str и tuple – первые два будут описа
ны отдельно, в главе 7. Ряд дополнительных типов последовательно
стей реализован в стандартной библиотеке; наиболее примечательным
из них является тип collections.namedtuple. При выполнении итераций
все эти последовательности гарантируют строго определенный поря
док следования элементов.
Строки мы уже рассматривали в предыдущей главе,
а в этом разделе познакомимся с кортежами, именован
ными кортежами и списками.
Кортежи
Кортеж – это упорядоченная последовательность из ну
ля или более ссылок на объекты. Кортежи поддержива
ют тот же синтаксис получения срезов, что и строки. Это
упрощает извлечение элементов из кортежа. Подобно
строкам, кортежи относятся к категории неизменяемых
объектов, поэтому мы не можем замещать или удалять
какиелибо их элементы. Если нам необходимо иметь
возможность изменять упорядоченную последователь
ность, то вместо кортежей можно просто использовать
списки или, если в программе уже используется кортеж,
который нежелательно модифицировать, можно преоб
разовать кортеж в список с помощью функции преобра
зования list() и затем изменять полученный список.
Тип данных tuple может вызываться как функция tup
le() – без аргументов она возвращает пустой кортеж,
с аргументом типа tuple возвращает поверхностную ко
пию аргумента; в случае, если аргумент имеет другой
тип, выполняется попытка преобразовать его в объект
типа tuple. Эта функция принимает не более одного аргу
мента. Кроме того, кортежи могут создаваться без ис
пользования функции tuple(). Пустой кортеж создается
с помощью пары пустых круглых скобок (), а кортеж,
состоящий из одного или более элементов, может быть
создан с помощью запятых. Иногда кортежи приходится
заключать в круглые скобки, чтобы избежать синтакси
ческой неоднозначности. Например, чтобы передать
кортеж 1, 2, 3 в функцию, необходимо использовать та
кую форму записи: function((1, 2, 3)).
Строки,
стр. 84
Извлечение
срезов из
строк, стр. 89
Поверхно
стное
и глубокое
копирование,
стр. 173

Последовательности 131
На рис. 3.1 показан кортеж t = "venus", –28, "green", "21", 19.74 и ин
дексы элементов внутри кортежа. Строки индексируются точно так
же, но, если в строках каждой позиции соответствует единственный
символ, то в кортежах каждой позиции соответствует единственная
ссылка на объект.
Кортежи предоставляют всего два метода: t.count(x), который возвра
щает количество объектов x в кортеже t, и t.index(x), который возвра
щает индекс самого первого (слева) вхождения объекта x в кортеж t или
возбуждает исключение ValueError, если объект x отсутствует в корте
же. (Эти методы имеются также и у списков.)
Кроме того, кортежи могут использоваться с оператором + (конкатена
ции), * (дублирования) и [] (получения среза), а операторы in и not in
могут применяться для проверки на вхождение. Можно использовать
также комбинированные операторы присваивания += и *=. Несмотря
на то, что кортежи являются неизменяемыми объектами, при выпол
нении этих операторов интерпретатор Python создает за кулисами но
вый кортеж с результатом операции и присваивает ссылку на него объ
екту, расположенному слева от оператора, то есть используется тот же
самый прием, что и со строками. Кортежи могут сравниваться с помо
щью стандартных операторов сравнения (<, <=, ==, !=, >=, >), при этом
сравнивание производится поэлементно (и рекурсивно, при наличии
вложенных элементов, таких как кортежи в кортежах).
Рассмотрим несколько примеров получения срезов, начав с извлече
ния единственного элемента и группы элементов:
>>> hair = "black", "brown", "blonde", "red"
>>> hair[2]
'blonde'
>>> hair[3:] # то же, что и hair[1:]
('brown', 'blonde', 'red')
Эта операция выполняется точно так же, как и в случае со строками,
списками или любыми другими последовательностями.
>>> hair[:2], "gray", hair[2:]
(('black', 'brown'), 'gray', ('blonde', 'red'))
Здесь мы попытались создать новый кортеж из 5 элементов, но в ре
зультате получили кортеж с тремя элементами, содержащий два двух
t[5] t[4] t[3] t[2] t[1]
'venus' 28 'green' '21' 19.74
t[0] t[1] t[2] t[3] t[4]
Рис. 3.1. Позиции элементов в кортеже

132 Глава 3. Типы коллекций
элементных кортежа. Это произошло потому, что мы применили опе
ратор запятой к трем элементам (кортеж, строка и кортеж). Чтобы по
лучить единый кортеж со всеми этими элементами, необходимо вы
полнить конкатенацию кортежей:
>>> hair[:2] + ("gray",) + hair[2:]
('black', 'brown', 'gray', 'blonde', 'red')
Чтобы создать кортеж из одного элемента, необходимо поставить запя
тую, но если запятую просто добавить, будет получено исключение
TypeError (так как интерпретатор будет думать, что выполняется кон
катенация строки и кортежа), поэтому необходимо использовать запя
тую и круглые скобки.
В этой книге (начиная с этого момента) мы будем использовать опреде
ленный стиль записи кортежей. Когда кортеж будет стоять слева от
двухместного оператора или справа от одноместного, мы будем опус
кать круглые скобки. Во всех остальных случаях будут использовать
ся круглые скобки. Ниже приводятся несколько примеров:
a, b = (1, 2) # слева от двухместного оператора
del a, b # справа от одноместного оператора
def f(x):
return x, x ** 2 # справа от одноместного оператора
for x, y in ((1, 1), (2, 4), (3, 9)): # слева от двухместного оператора
print(x, y)
Совершенно необязательно следовать этому стилю записи – некоторые
программисты предпочитают всегда использовать круглые скобки, что
соответствует репрезентативной форме представления кортежей, одна
ко другие используют скобки, только когда это строго необходимо.
>>> eyes = ("brown", "hazel", "amber", "green", "blue", "gray")
>>> colors = (hair, eyes)
>>> colors[1][3:1]
('green', 'blue')
В следующем примере мы вложили друг в друга два кортежа. Коллек
ции допускают возможность вложения с любой глубиной вложенно
сти. Оператор извлечения срезов [] может применяться для доступа
к вложенным коллекциям столько раз, сколько это будет необходимо.
Например:
>>> things = (1, 7.5, ("pea", (5, "Xyz"), "queue"))
>>> things[2][1][1][2]
'z'
Рассмотрим этот пример по частям, начиная с выражения things[2],
которое дает нам третий элемент кортежа (не забывайте, что первый
элемент имеет индекс 0), который сам является кортежем ("pea", (5,
"Xyz"), "queue"). Выражение things[2][1] дает нам второй элемент кор

Последовательности 133
тежа things[2], который тоже является кортежем (5, "Xyz"). А выра
жение things[2][1][1] дает нам второй элемент этого кортежа, который
представляет строку "Xyz". Наконец, выражение things[2][1][1][2] да
ет нам третий элемент (символ) строки, то есть символ "z".
Кортежи могут хранить элементы любых типов, включая другие кол
лекции, такие как кортежи и списки, так как на самом деле кортежи
хранят ссылки на объекты. Использование сложных, вложенных
структур данных, таких, как показано ниже, легко может создавать
путаницу. Одно из решений этой проблемы состоит в том, чтобы да
вать значениям индексов осмысленные имена. Например:
>>> MANUFACTURER, MODEL, SEATING = (0, 1, 2)
>>> MINIMUM, MAXIMUM = (0, 1)
>>> aircraft = ("Airbus", "A320200", (100, 220))
>>> aircraft[SEATING][MAXIMUM]
220
Конечно, в таком виде программный код выглядит более осмыслен
ным, чем простое выражение aircraft[2][1], но при этом приходится
создавать большое число переменных, да и выглядит он несколько
уродливо. В следующем подразделе мы познакомимся с более привле
кательной альтернативой.
В первых двух строках вышеприведенного фрагмента мы выполнили
присваивание кортежам. Когда справа от оператора присваивания
указывается последовательность (в данном случае – это кортежи),
а слева указан кортеж, мы говорим, что последовательность справа
распаковывается). Операция распаковывания последовательностей
может использоваться для организации обмена значений между пере
менными, например:
a, b = (b, a)
Строго говоря, круглые скобки справа не являются обязательными,
но, как уже отмечалось выше, в этой книге мы используем стиль запи
си, когда скобки опускаются только в левом операнде двухместного
оператора и в правом операнде одноместного оператора и используют
ся во всех остальных случаях.
Мы уже сталкивались с примерами распаковывания последовательно
стей в контексте оператора цикла for ... in. Следующий пример при
водится только в качестве напоминания:
for x, y in ((3, 4), (5, 12), (28, 45)):
print(math.hypot(x, y))
Здесь выполняется обход кортежа, состоящего из двухэлементных
кортежей, каждый из которых распаковывается в переменные x и y.

134 Глава 3. Типы коллекций
Именованные кортежи
Именованные кортежи ведут себя точно так же, как и обычные корте
жи, и не уступают им в производительности. Отличаются они возмож
ностью ссылаться на элементы кортежа не только по числовому индек
су, но и по имени, что в свою очередь позволяет создавать сложные аг
регаты из элементов данных.
В модуле collections имеется функция namedtuple(). Эта функция ис
пользуется для создания собственных типов кортежей. Например:
Sale = collections.namedtuple("Sale",
"productid customerid date quantity price")
Первый аргумент функции collections.namedtuple() – это имя создавае
мого кортежа. Второй аргумент – это строка имен, разделенных пробе
лами, для каждого элемента, который будет присутствовать в этом
кортеже. Первый аргумент и имена во втором аргументе должны быть
допустимыми идентификаторами языка Python. Функция возвращает
класс (тип данных), который может использоваться для создания име
нованных кортежей. Так, в примере выше мы можем интерпретиро
вать имя Sale как имя любого другого класса (такого как tuple) в языке
Python и создавать объекты типа Sale.
1 Например:
sales = []
sales.append(Sale(432, 921, "20080914", 3, 7.99))
sales.append(Sale(419, 874, "20080915", 1, 18.49))
В этом примере мы создали список из двух элементов типа Sale, то есть
из двух именованных кортежей. Мы можем обращаться к элементам
таких кортежей по их индексам – например, обратиться к элементу
price в первом элементе списка sales можно с помощью выражения
sales[0][
–1] (вернет значение 7.99) – или по именам, которые делают
программный код более удобочитаемым:
total = 0
for sale in sales:
total += sale.quantity * sale.price
print("Total ${0:.2f}".format(total)) # выведет: Total $42.46
Очень часто простоту и удобство, которые предоставляют именован
ные кортежи, можно обратить на пользу делу. Например, ниже приво
дится версия примера «aircraft» из предыдущего подраздела (стр. 133),
имеющая более аккуратный вид:
>>> Aircraft = collections.namedtuple("Aircraft",
... "manufacturer model seating")
>>> Seating = collections.namedtuple("Seating", "minimum maximum")
1 Примечание для программистов, использующих объектноориентирован
ный стиль: каждый класс, созданный таким способом, будет являться под
классом класса tuple.

Последовательности 135
>>> aircraft = Aircraft("Airbus", "A320200", Seating(100, 220))
>>> aircraft.seating.maximum
220
Уже видно, что именованные кортежи могут быть очень удобны; кро
ме того, в главе 6 мы перейдем к изучению объектноориентированно
го программирования, где выйдем за пределы простых именованных
кортежей и узнаем, как создавать свои собственные типы данных, ко
торые могут не только хранить элементы данных, но и иметь собствен
ные методы.
Списки
Список – это упорядоченная последовательность из нуля
или более ссылок на объекты. Списки поддерживают тот
же синтаксис получения срезов, что и строки с кортежа
ми. Это упрощает извлечение элементов из списка. В от
личие от строк и кортежей списки относятся к катего
рии изменяемых объектов, поэтому мы можем замещать
или удалять любые их элементы. Кроме того, существу
ет возможность вставлять, замещать и удалять целые
срезы списков.
Тип данных list может вызываться как функция list() –
без аргументов она возвращает пустой список, с аргу
ментом типа list возвращает поверхностную копию ар
гумента; в случае, если аргумент имеет другой тип, вы
полняется попытка преобразовать его в объект типа list.
Эта функция принимает не более одного аргумента. Кро
ме того, списки могут создаваться без использования
функции list(). Пустой список создается с помощью па
ры пустых квадратных скобок [], а список, состоящий
из одного или более элементов, может быть создан с по
мощью последовательности элементов, разделенных за
пятыми, заключенной в квадратные скобки. Другой спо
соб создания списков заключается в использовании гене
раторов списков – эта тема будет рассматриваться ниже
в этом подразделе.
Поскольку все элементы списка в действительности являются ссылка
ми на объекты, списки, как и кортежи, могут хранить элементы лю
бых типов данных, включая коллекции, такие как списки и кортежи.
Списки могут сравниваться с помощью стандартных операторов срав
нения (<, <=, ==, !=, >=, >), при этом сравнивание производится поэле
ментно (и рекурсивно, при наличии вложенных элементов, таких как
списки или кортежи в списках).
В результате выполнения операции присваивания L = [
–17.5, "kilo", 49,
"V", ["ram", 5, "echo"], 7] мы получим список, как показано на рис. 3.2.
Извлечение
срезов из
строк, стр. 89
Поверхно
стное
и глубокое
копирование,
стр. 173
Генераторы
списков,
стр. 142

136 Глава 3. Типы коллекций
К спискам, таким как L, мы можем применять оператор извлечения
среза, повторяя его столько раз, сколько потребуется для доступа к эле
ментам в списке, как показано ниже:
L[0] == L[6] == 17.5
L[1] == L[5] == 'kilo'
L[1][0] == L[5][0] == 'k'
L[4][2] == L[4][1] == L[2][2] == L[2][1] == 'echo'
L[4][2][1] == L[4][2][3] == L[2][1][1] == L[2][1][3] == 'c'
Списки, как и кортежи, могут вкладываться друг в друга; допускают
выполнение итераций по их элементам и извлечение срезов. Все при
меры с кортежами, которые приводились в предыдущем подразделе,
будут работать точно так же, если вместо кортежей в них будут ис
пользованы списки. Списки поддерживают операторы проверки на
вхождение in и not in, оператор конкатенации +, оператор расширения
+= (то есть добавляет операнд справа в конец списка) и операторы дуб
лирования * и *=. Списки могут также использоваться в качестве аргу
ментов функции len() и в инструкции del, которая будет рассматри
ваться в этом подразделе и которая описывается во врезке «Удаление
элементов с помощью инструкции del» на стр. 139. Кроме того, списки
предоставляют методы, перечисленные в табл. 3.1.
Таблица 3.1. Методы списков
Синтаксис Описание
L.append (x) Добавляет элемент x в конец списка L
L.count(x) Возвращает число вхождений элемента x в список L
L.extend(m)
L += mДобавляет в конец списка L все элементы итерируемого объ
екта m; оператор += делает то же самое
L.index(x,
start,
end) Возвращает индекс самого первого (слева) вхождения элемен
та x в список L (или в срез start:end списка L), в противном слу
чае возбуждает исключение ValueError
L.insert(i, x) Вставляет элемент x в список L в позицию int i
L.pop()Удаляет самый последний элемент из списка L и возвращает
его в качестве результата
L.pop(i) Удаляет из списка L элемент с индексом int i и возвращает
его в качестве результата
L[6] L[5] L[4] L[3] L[2] L[1]
17.5 'kilo' 49 'V' ['ram', 5, 'echo'] 7
L[0] L[1] L[2] L[3] L[4] L[5]
Рис. 3.2. Позиции элементов в списке

Последовательности 137
Несмотря на то, что для доступа к элементам списка можно использо
вать оператор извлечения среза, тем не менее в некоторых ситуациях
бывает необходимо одновременно извлечь две или более частей списка.
Сделать это можно с помощью операции распаковывания последова
тельности. Любой итерируемый объект (списки, кортежи и другие) мо
жет быть распакован с помощью оператора распаковывания «звездоч
ка» (*). Когда слева от оператора присваивания указывается две или
более переменных, одна из которых предваряется символом *, каждой
переменной присваивается по одному элементу списка, а переменной
со звездочкой присваивается оставшаяся часть списка. Ниже приво
дится несколько примеров выполнения распаковывания списков:
>>> first, *rest = [9, 2, 4, 8, 7]
>>> first, rest
(9, [2, 4, 8, 7])
>>> first, *mid, last = "Charles Philip Arthur George Windsor".split()
>>> first, mid, last
('Charles', ['Philip', 'Arthur', 'George'], 'Windsor')
>>> *directories, executable = "/usr/local/bin/gvim".split("/")
>>> directories, executable
(['', 'usr', 'local', 'bin'], 'gvim')
Когда используется оператор распаковывания последовательности,
как в данном примере, выражение *rest и подобные ему называются
выражениями со звездочкой.
В языке Python имеется также похожее понятие аргументов со звез
дочкой. Например, допустим, что имеется следующая функция, при
нимающая три аргумента:
def product(a, b, c):
return a * b * c # здесь * – это оператор умножения
тогда мы можем вызывать эту функцию с тремя аргументами или ис
пользовать аргументы со звездочкой:
>>> product(2, 3, 5)
30
>>> L = [2, 3, 5]
>>> product(*L) L.remove(x) Удаляет самый первый (слева) найденный элемент x из спи
ска L или возбуждает исключение ValueError, если элемент x
не будет найден
L.reverse()Переставляет в памяти элементы списка в обратном порядке
L.sort(...)Сортирует список в памяти. Этот метод при
нимает те же необязательные аргументы key
иreverse, что и встроенная функция sorted() Синтаксис Описание
Функция sorted() ,
стр. 164, 170

138 Глава 3. Типы коллекций
30
>>> product(2, *L[1:])
30
В первом примере функция вызывается, как обычно, с тремя аргумен
тами. Во втором вызове использован аргумент со звездочкой; в этом
случае список из трех элементов распаковывается оператором *, так
что функция получает столько аргументов, сколько ей требуется. Того
же эффекта можно было бы добиться при использовании кортежа
с тремя элементами. В третьем вызове функции первый аргумент пере
дается традиционным способом, а другие два – посредством примене
ния операции распаковывания двухэлементного среза списка L. Функ
ции и передача аргументов полностью будут описываться в главе 4.
В программах всегда однозначно известно, является оператор * опера
тором умножения или оператором распаковывания последовательно
сти. Когда он появляется слева от оператора присваивания – это опера
тор распаковывания; когда он появляется гдето в другом месте (на
пример, в вызове функции) – это оператор распаковывания, если он
используется в одноместном операторе, и оператор умножения, если
он используется в двухместном операторе.
Мы уже знаем, что имеется возможность выполнять итерации по эле
ментам списка с помощью конструкции for item in L:. Если в цикле
потребуется изменять элементы списка, то можно использовать сле
дующий прием:
for i in range(len(L)):
L[i] = process(L[i])
Встроенная функция range() возвращает целочисленный
итератор. С одним целочисленным аргументом, n, итера
тор range() возвращает последовательность чисел 0, 1, …,
n – 1.
Этот прием можно использовать для увеличения всех элементов в спи
ске целых чисел. Например:
for i in range(len(numbers)):
numbers[i] += 1
Поскольку списки поддерживают возможность извлечения срезов,
в определенных случаях один и тот же эффект может быть достигнут
как с помощью оператора извлечения среза, так и с помощью одного
из методов списков. Например, предположим, что имеется список
woods = ["Cedar", "Yew", "Fir"]; дополнить такой список можно двумя
способами:
woods += ["Kauri", "Larch"] woods.extend(["Kauri", "Larch"])
В обоих случаях в результате будет получен список ['Cedar', 'Yew',
'Fir', 'Kauri', 'Larch'].
Функция range() ,
стр. 167

Последовательности 139
Удаление элементов с помощью инструкции del
Несмотря на то, что название инструкции del вызывает ассоциа
ции со словом delete (удалить), она не обязательно удаляет ка
киелибо данные. Когда инструкция del применяется к элементу
данных, который не является коллекцией, она разрывает связь
между ссылкой на объект и самим элементом данных и удаляет
ссылку на объект. Например:
>>> x = 8143 # создается ссылка на объект 'x'
и целое число 8143
>>> x
8143
>>> del x # удаляется ссылка на объект 'x',
число готово к утилизации
>>> x
Traceback (most recent call last):
...
NameError: name 'x' is not defined
(NameError: имя 'x' не определено)
Когда удаляется ссылка на объект, если не осталось других ссы
лок, указывающих на этот объект, то интерпретатор помечает
элемент данных, на который указывала ссылка, как готовый
к утилизации. Невозможно предсказать, когда произойдет ути
лизация и произойдет ли она вообще (это зависит от реализации
Python), поэтому, когда необходимо явно освободить память, де
лать это придется вручную. Язык Python предоставляет два ре
шения проблемы неопределенности. Одно из них состоит в ис
пользовании конструкции try ... finally, которая гарантирует
освобождение памяти, а другое заключается в использовании
инструкции with, с которой мы познакомимся в главе 8.
Когда инструкция del применяется к коллекциям, таким как
кортежи или списки, удаляется только ссылка на эти коллек
ции. Коллекция и ее элементы (а также элементы, которые сами
являются коллекциями для своих элементов, рекурсивно) поме
чаются как готовые к утилизации, если не осталось других ссы
лок, указывающих на эти коллекции.
Для изменяемых коллекций, таких как списки, инструкция del
может применяться к отдельным элементам или срезам – в лю
бом из этих случаев используется оператор извлечения среза [].
Если для удаления предназначен элемент или элементы коллек
ции и в программе не осталось ссылок, указывающих на эти эле
менты, они помечаются, как готовые к утилизации.

140 Глава 3. Типы коллекций
Отдельные элементы можно добавлять в конец списка с помощью ме
тода list.append(). Элементы могут вставляться в любую позицию
в списке с помощью метода list.insert() или посредством обращения
к срезу с нулевой длиной. Например, допустим, что имеется список
woods = ["Cedar", "Yew", "Fir", "Spruce"]; тогда вставить новый эле
мент в позицию с индексом 2 (то есть сделать этот элемент третьим эле
ментом списка) можно одним из двух способов:
woods[2:2] = ["Pine"] woods.insert(2, "Pine")
В обоих случаях в результате будет получен список ['Cedar', 'Yew',
'Pine', 'Fir', 'Spruce'].
Отдельные элементы списка можно изменять, выполняя присваива
ние определенной позиции в списке, например, woods = 'Redwood'. Пу
тем присваивания итерируемых объектов можно изменять целые срезы
в списке, например, woods[1:3] = ["Spruce", "Sugi", "Rimu"]. Срез и ите
рируемый объект не обязательно должны иметь одинаковую длину.
В любом случае элементы, попавшие в срез, будут удалены, а на их ме
сто будут вставлены элементы итерируемого объекта. Если длина ите
рируемого объекта короче замещаемого среза, список уменьшится,
а если длина итерируемого объекта больше замещаемого среза, то спи
сок увеличится.
Чтобы прояснить, что именно происходит в результате присваивания
итерируемого объекта срезу списка, рассмотрим еще один пример.
Представим, что имеется список L = ["A", "B", "C", "D", "E", "F"]
и что выполняется присваивание итерируемого объекта (в данном слу
чае – списка) срезу: L[2:5] = ["X", "Y"]. В первую очередь производит
ся удаление элементов среза, то есть за кулисами список принимает
вид ['A', 'B', 'F']. А затем все элементы итерируемого объекта встав
ляются в позицию первого элемента среза, и в результате получается
список ['A', 'B', 'X', 'Y', 'F'].
Существует еще ряд других способов удаления элементов списка. Что
бы удалить самый правый элемент списка, можно воспользоваться ме
тодом list.pop() без аргументов – удаленный элемент возвращается
в качестве результата. Аналогично можно использовать метод list.pop()
с целочисленным значением индекса – для удаления (и возвращения)
элемента с определенным индексом. Еще один способ удаления эле
мента заключается в использовании метода list.remove(), которому
передается удаляемый элемент. Также для удаления отдельных эле
ментов или целых срезов можно использовать инструкцию del– на
пример, del woods[4]. Кроме того, срезы могут удаляться путем при
сваивания пустого списка, так следующие два фрагмента являются эк
вивалентными:
woods[2:4] = [] del woods[2:4]
В левом фрагменте выполняется присваивание итерируемого объекта
(пустого списка) срезу, то есть здесь сначала удаляются элементы сре

Последовательности 141
за, а так как итерируемый объект, предназначенный для вставки,
пуст, вставка не производится.
Когда мы впервые обсуждали операцию извлечения раз
реженного среза, мы делали это в контексте строк, где
извлечение разреженных срезов выглядит не очень ин
тересно. Но в случае списков операция извлечения раз
реженного среза позволяет получить доступ к каждому
nму элементу, что может оказаться очень удобно. На
пример, предположим, что имеется список x = [1, 2, 3,
4, 5, 6, 7, 8, 9, 10] и необходимо все элементы с нечет
ными индексами (то есть x[1], x[3] и т. д.) установить
в значение 0. Получить доступ к каждому второму эле
менту можно, используя оператор среза с шагом, напри
мер, x[::2]. Но такой оператор даст доступ к элементам
с индексами 0, 2, 4 и т. д. Мы можем исправить положе
ние, указав начальный индекс, использовав выражение
x[1::2], которое возвращает срез, содержащий нужные
нам элементы. Чтобы установить все элементы среза
в значение 0, нам необходим список нулей, и этот список
должен содержать то же самое число нулей, сколько эле
ментов содержится в срезе.
Полное решение задачи выглядит так: x[1::2] = [0] * len(x[1::2]). Те
перь список x имеет следующий вид [1, 0, 3, 0, 5, 0, 7, 0, 9, 0]. Мы вос
пользовались оператором дублирования * для создания списка, содер
жащего необходимое число нулей, основываясь на длине (то есть коли
честве элементов) среза. Особенно интересно, что при присваивании
списка [0, 0, 0, 0, 0] разреженному срезу, интерпретатор корректно за
мещает первым нулем значение x[1], вторым нулем значение x[3] и т. д.
Списки могут упорядочиваться в обратном порядке и сор
тироваться, так же как и другие итерируемые объекты,
с помощью встроенных функций reversed() и sorted(),
описываемых в подразделе «Итераторы, функции и опе
раторы для работы с итерируемыми объектами»
(стр. 163). Списки также имеют эквивалентные методы
list.reverse() и list.sort(), которые работают непосред
ственно с самим списком (поэтому они ничего не возвра
щают); последний из них принимает те же необязатель
ные аргументы, что и функция sorted(). Для сортировки
списков строк без учета регистра символов используется
распространенный прием – например, список woods мож
но было бы отсортировать так: woods.sort(key=str.lower).
Аргумент key используется, чтобы определить функцию,
которая будет применяться к каждому элементу, и воз
вращать значение, участвующее в сравнении в процессе
сортировки. Как уже отмечалось в предыдущей главе,
Извлечение
срезов из
строк, стр. 89
Функция sorted() ,
стр. 164, 170

142 Глава 3. Типы коллекций
в разделе, где рассматривались вопросы сравнения строк (стр. 63), для
языков, отличных от английского, сортировка строк в подразумевае
мом людьми порядке может оказаться непростым делом.
Что касается вставки элементов, списки обеспечивают лучшую произ
водительность при добавлении или удалении элементов в конце списка
(list.append(), list.pop()). Падение производительности происходит,
когда приходится отыскивать элементы в списке, например, с помо
щью методов list.remove() или list.index(), а также при использова
нии оператора in проверки на вхождение. Когда необходимо обеспе
чить высокую скорость поиска или проверки на вхождение, возмож
но, более удачным выбором будут множества и словари (оба типа кол
лекций описываются ниже, в этой же главе). Однако и для списков
можно обеспечить высокую скорость поиска, если хранить их в отсор
тированном виде – в языке Python алгоритм сортировки особенно хо
рошо оптимизирован для случая сортировки частично отсортирован
ных списков – и отыскивать элементы методом дихотомии (реализо
ван в модуле bisect). (В главе 6 мы создадим свой класс списков, кото
рые хранятся в отсортированном виде.)
Генераторы списков
Небольшие списки часто создаются как литералы, но длинные списки
обычно создаются программным способом. Списки целых чисел могут
создаваться с помощью выражения list(range(n)); когда необходим
итератор целых чисел, достаточно функции range(); а для создания
списков других типов часто используется оператор цикла for ... in.
Предположим, например, что нам требуется получить список високос
ных годов в определенном диапазоне. Для начала мы могли бы исполь
зовать такой цикл:
leaps = []
for year in range(1900, 1940):
if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
leaps.append(year)
Когда функции range() передаются два целочисленных
аргумента n и m, итератор возвращает последователь
ность целых чисел n, n + 1, …, m
– 1.
Конечно, если диапазон известен заранее, можно было
бы использовать литерал списка, например, leaps = [1904,
1908, 1912, 1916, 1920, 1924, 1928, 1932, 1936].
Генератор списков – это выражение и цикл с дополнительным услови
ем, заключенное в квадратные скобки, в котором цикл используется
для создания элементов списка, а условие используется для исключе
ния нежелательных элементов. В простейшем виде генератор списков
записывается, как показано ниже:
[item for item in iterable]
Функция range() ,
стр. 167

Последовательности 143
Это выражение вернет список всех элементов объекта iterable и семан
тически ничем не отличается от выражения list(iterable). Интерес
ными генераторы списков делают две особенности – они могут исполь
зоваться как выражения и они допускают включение условной инст
рукции, вследствие чего мы получаем две типичные синтаксические
конструкции использования генераторов списков:
[expression for item in iterable]
[expression for item in iterable if condition]
Вторая форма записи эквивалентна циклу:
temp = []
for item in iterable:
if condition:
temp.append(expression)
Обычно выражение expression является либо самим элементом item,
либо некоторым выражением с его участием. Конечно, генератору
списков не требуется временная переменная temp[], которая необходи
ма в версии с циклом for ... in.
Теперь можно переписать программный код создания списка високос
ных годов с использованием генератора списка. Мы сделаем это в три
этапа. Сначала создадим список, содержащий все годы в указанном
диапазоне:
leaps = [y for y in range(1900, 1940)]
То же самое можно было бы сделать с помощью выражения leaps =
list(range(1900, 1940)). Теперь добавим простое условие, которое будет
оставлять в списке только каждый четвертый год:
leaps = [y for y in range(1900, 1940) if y % 4 == 0]
И, наконец, получаем окончательную версию:
leaps = [y for y in range(1900, 1940)
if (y % 4 == 0 and y % 100 != 0) or (y % 400 == 0)]
Использование генератора списков в данном случае позволило умень
шить объем программного кода с четырех строк до двух – не так мно
го, но в крупных проектах суммарная экономия может оказаться весь
ма существенной.
Так как генераторы списков воспроизводят списки, то есть итерируе
мые объекты, и сами генераторы списков используют итерируемые
объекты, имеется возможность вкладывать генераторы списков друг
в друга. Это эквивалентно вложению циклов for ... in. Например, ес
ли бы нам потребовалось сгенерировать список всех возможных кодов
одежды для разных полов, разных размеров и расцветок, но исключая
одежду для полных женщин, нужды и чаянья которых индустрия мо
ды нередко игнорирует, мы могли бы использовать вложенные циклы
for ... in, как показано ниже:

144 Глава 3. Типы коллекций
codes = []
for sex in "MF": # мужская (Male), женская (Female)
for size in "SMLX": # маленький, средний, большой, очень большой
if sex == "F" and size == "X":
continue
for color in "BGW": # черный (Black), серый (Gray), белый (White)
codes.append(sex + size + color)
Этот фрагмент воспроизводит список, содержащий 21 элемент –
['MSB', 'MSG', ..., 'FLW']. Тот же самый список можно создать парой
строк, если воспользоваться генераторами списков:
codes = [s + z + c for s in "MF" for z in "SMLX" for c in "BGW"
if not (s == "F" and z == "X")]
Здесь каждый элемент списка воспроизводится выражением s + z + c.
Кроме того, в генераторе списков несколько иначе построена логика
обхода нежелательной комбинации пол/размер – проверка выполня
ется в самом внутреннем цикле, тогда как в версии с циклами for ...
in эта проверка выполняется в среднем цикле. Любой генератор спи
сков можно переписать, используя один или более циклов for ... in.
Если сгенерированный список получается очень боль
шим, то, возможно, более эффективным было бы созда
вать очередные элементы списка по мере необходимо
сти, вместо того чтобы создавать сразу весь список. Эту
задачу можно решить с помощью выраженийгенерато
ров, которые будут обсуждаться в главе 8.
Множества
Тип set – это разновидность коллекций, которая поддерживает опера
тор проверки на вхождение in, функцию len() и относится к разряду
итерируемых объектов. Кроме того, множества предоставляют метод
set.isdisjoint() и поддерживают операторы сравнения и битовые опе
раторы (которые в контексте множеств используются для получения
объединения, пересечения и т. д.). В языке Python имеется два встро
енных типа множеств: изменяемый тип set и неизменяемый frozenset.
При переборе элементов множества элементы могут следовать в произ
вольном порядке.
В состав множеств могут включаться только хешируемые объекты. Хе
шируемые объекты – это объекты, имеющие специальный метод
__hash__(), на протяжении всего жизненного цикла объекта всегда воз
вращающий одно и то же значение, которые могут участвовать в опе
рациях сравнения на равенство посредством специального метода
__eq__(). (Специальные методы – это методы, имена которых начина
ются и оканчиваются двумя символами подчеркивания; они описыва
ются в главе 6.)
Выражения
генераторы,
стр. 397

Множества 145
Все встроенные неизменяемые типы данных, такие как float, frozen
set, int, str и tuple, являются хешируемыми объектами и могут добав
ляться во множества. Встроенные изменяемые типы данных, такие
как dict, list и set, не являются хешируемыми объектами, так как
значение хеша в каждом конкретном случае зависит от содержащихся
в объекте элементов, поэтому они не могут добавляться в множества.
Множества могут сравниваться между собой с использованием стан
дартных операторов сравнения (<, <=, ==, !=, >=, >). Обратите внимание:
операторы == и != имеют обычный смысл, и сравнение выполняется пу
тем поэлементного сравнения (или рекурсивно при наличии таких
вложенных элементов, как кортежи и фиксированные множества
(frozenset)), но остальные операторы сравнения выполняют сравнение
подмножеств и надмножеств, как вскоре будет показано.
Тип set
Тип set – это неупорядоченная коллекция из нуля или более ссылок на
объекты, указывающих на хешируемые объекты. Множества относят
ся к категории изменяемых типов, поэтому легко можно добавлять
и удалять их элементы, но, так как они являются неупорядоченными
коллекциями, к ним не применимо понятие индекса и не применима
операция извлечения среза. На рис. 3.3 иллюстрируется множество,
созданное следующим фрагментом программного кода:
S = {7, "veil", 0, 29, ("x", 11), "sun", frozenset({8, 4, 7}), 913}
Тип данных set может вызываться как функция set() –
без аргументов она возвращает пустое множество; с аргу
ментом типа set возвращает поверхностную копию аргу
мента; в случае, если аргумент имеет другой тип, выпол
няется попытка преобразовать его в объект типа set. Эта
функция принимает не более одного аргумента. Кроме то
го, непустые множества могут создаваться без использо
вания функции set(), а пустые множества могут созда
ваться только с помощью функции set() – их нельзя соз
дать с помощью пары пустых скобок.
1 Множество, со
стоящее из одного или более элементов, может быть
создано с помощью последовательности элементов, разде
ленных запятыми, заключенной в фигурные скобки.
Другой способ создания множеств заключается в исполь
зовании генераторов множеств – эта тема будет рассмат
риваться ниже в соответствующем подразделе. Множе
ства всегда содержат уникальные элементы – добавле
ние повторяющихся элементов возможно, но не имеет
1 Пустые фигурные скобки {} используются для создания пустого словаря,
как будет показано в следующем разделе.
Поверхно
стное
и глубокое
копирование,
стр. 173
Генераторы
множеств,
стр. 149

146 Глава 3. Типы коллекций
смысла. Например, следующие три множества являются эквивалент
ными: set("apple"), set("aple") и {'e', 'p', 'a', 'l'}. Благодаря этой
их особенности множества часто используются для устранения повто
ряющихся значений. Например, если предположить, что x – это спи
сок строк, то после выполнения инструкции x = list(set(x)) в списке
останутся только уникальные строки, причем располагаться они мо
гут в произвольном порядке.
('x', 11) 'sun'
29 'veil'
7
913
0
frozenset({8, 4, 7})
Рис. 3.3. Множество – это неупорядоченная коллекция
уникальных элементов
∪→


\→

set("pecan") | set("pie") == {'p', 'e', 'c', 'a', 'n', 'i'} # Объединение
set("pecan") & set("pie") == {'p', 'e'} # Пересечение
set("pecan")  set("pie") == {'c', 'a', 'n'} # Разность
pecan p i e can i
pecan p i e can
pecan p i e pe
pecan p i e pecan i
set("pecan") ^ set("pie") == {'c', 'a', 'n', 'i'} # Строгая дизъюнкция
(исключающее ИЛИ)
Рис. 3.4. Стандартные операторы множеств

Множества 147
Множества поддерживают встроенную функцию len() и быструю про
верку на вхождение с помощью операторов in и not in. Они также пре
доставляют типичный набор операторов, как показано на рис. 3.4.
Полный перечень методов и операторов, применимых к множествам,
приводится в табл. 3.2. Все методы семейства «update» (set.update(),
set.intersection_update() и т. д.) могут принимать в качестве аргумен
та любые итерируемые объекты, но эквивалентные им комбинирован
ные операторы присваивания (|=, &= и т. д.) требуют, чтобы оба операн
да были множествами.
Таблица 3.2. Методы и операторы множеств
Синтаксис Описание
s.add(x) Добавляет элементы x во множество s, если они от
сутствуют в s
s.clear()Удаляет все элементы из множества s
s.copy()Возвращает поверхностную ко
пию множества s
a
a Этот метод и соответствующий ему оператор (если таковой имеется) могут
также применять к фиксированным множествам. s.difference(t)
s  tВозвращает новое множество, включающее элемен
ты множества s, которые отсутствуют в множестве t
а
s.difference_update(t)
s = tУдаляет из множества s все элементы, присутствую
щие в множестве t
s.discard(x) Удаляет элемент x из множества s, если он присутст
вует в множестве s; смотрите также метод set.remove()
s.intersection(t)
s & tВозвращает новое множество, включающее элементы,
присутствующие одновременно в множествах s и t
а
s.intersection_update(t)
s &= tОставляет во множестве s пересечение множеств s и t
s.isdisjoint(t) Возвращает True, если множества s и t не имеют об
щих элементов
а
s.issubset(t)
s <= tВозвращает True, если множество s эквивалентно мно
жеству t или является его подмножеством; чтобы про
верить, является ли множество s только подмножест
вом множества t, следует использовать проверку s < t
а
s.issuperset(t)
s >= tВозвращает True, если множество s эквивалентно мно
жеству t или является его надмножеством; чтобы про
верить, является ли множество s только надмножест
вом множества t, следует использовать проверку s > t
а
Поверхностное
и глубокое копи
рование, стр. 173

148 Глава 3. Типы коллекций
Таблица 3.2 (продолжение)
Типичный случай использования множеств – когда необходимо орга
низовать быструю проверку на вхождение. Например, нам может по
требоваться выводить на экран инструкцию о порядке использования
программы, когда пользователь вводит аргументы «
–h» или « ––help»:
if len(sys.argv) == 1 or sys.argv[1] in {"h", "help"}:
Другой типичный случай использования множеств – когда необходи
мо избежать обработки повторяющихся элементов данных. Например,
предположим, что имеется итерируемый объект (такой как список),
содержащий IPадреса, извлеченные из файлов журнала вебсервера,
и необходимо выполнить некоторые действия, причем не более одного
раза для каждого отдельного IPадреса. Допустим, что IPадреса со
храняются в хешируемом и итерируемом объекте ips и что для каждо
го адреса должна вызываться функция process_ip(), которая уже опре
делена. Тогда следующие фрагменты программного кода сделают то,
что нам требуется, хотя и немного поразному:
seen = set()
for ip in ips:
if ip not in seen:
seen.add(ip) for ip in set(ips):
process_ip(ip) process_ip(ip) Синтаксис Описание
s.pop()Возвращает и удаляет случайный элемент множества
s или возбуждает исключение KeyError, если s – это
пустое множество
s.remove(x) Удаляет элемент x из множества s или возбуждает ис
ключение KeyError, если элемент x отсутствует в мно
жестве s; смотрите также метод set.discard()
s.symmetric_
difference(t)
s ^ tВозвращает новое множество, включающее все эле
менты, присутствующие в множествах s и t, за ис
ключением элементов, присутствующих в обоих мно
жествах одновременно
а
s.symmetric_
difference_update(t)
s ^= tВозвращает в множестве s результат строгой дизъ
юнкции множеств s и t а
s.union(t)
s | tВозвращает новое множество, включающее все эле
менты множества s и все элементы множества t, от
сутствующие в множестве s
а
s.update(t)
s |= tДобавляет во множество s все элементы множества t,
отсутствующие в множестве s

Множества 149
Во фрагменте слева, если очередной IPадрес еще не обрабатывался, он
добавляется во множество seen и обрабатывается, в противном случае
адрес пропускается. В фрагменте справа мы изначально имеем дело
только с уникальными IPадресами. Различие между этими фрагмен
тами заключается, вопервых, в том, что в левом фрагменте создается
множество seen, необходимость в котором отсутствует в правом фраг
менте, и, вовторых, в левом фрагменте IPадреса обрабатываются
в порядке, в каком они присутствуют в объекте ips, тогда как в правом
фрагменте они обрабатываются в произвольном порядке.
Фрагмент справа выглядит проще, но, если порядок обработки эле
ментов объекта ips имеет значение, придется использовать фрагмент
слева или изменить первую строку во фрагменте справа – например,
так: for ip in sorted(set(ips)):, если этого будет достаточно, чтобы по
лучить желаемый порядок следования. Теоретически фрагмент справа
может оказаться более медленным при большом количестве элементов
в объекте ips, поскольку в нем множество создается целиком, а не с оп
ределенным шагом.
Множества также могут использоваться для удаления требуемых эле
ментов. Например, если представить, что у нас имеется список имен
файлов и нам необходимо исключить из него файлы с инструкциями
по сборке (возможно, по той простой причине, что они генерируются
автоматически, а не создаются вручную), мы могли бы использовать
следующий прием:
filenames = set(filenames)
for makefile in {"MAKEFILE", "Makefile", "makefile"}:
filenames.discard(makefile)
Этот фрагмент удалит все makefile, присутствующие в списке, имена
которых следуют стандарту использования заглавных символов. Этот
фрагмент ничего не будет делать, если в списке отсутствуют искомые
файлы. То же самое может быть реализовано в одной строке программ
ного кода при помощи оператора получения разности множеств (
–):
filenames = set(filenames)  {"MAKEFILE", "Makefile", "makefile"}
Кроме того, мы могли бы удалить элементы с помощью метода set.re
move(), хотя этот метод возбуждает исключение KeyError, если удаляе
мый элемент отсутствует во множестве.
Генераторы множеств
В дополнение к возможности создавать множества с помощью функ
ции set() или литералов, существует возможность создавать множест
ва с помощью генераторов множеств. Генератор множества – это вы
ражение и цикл с необязательным условием, заключенные в фигур
ные скобки. Подобно генераторам списков, генераторы множеств под
держивают две формы записи:

150 Глава 3. Типы коллекций
{expression for item in iterable}
{expression for item in iterable if condition}
Мы могли бы использовать генераторы множеств для фильтрации не
желательных элементов (когда порядок следования элементов не име
ет значения), как показано ниже:
html = {x for x in files if x.lower().endswith((".htm", ".html"))}
Если предположить, что files – это список имен файлов, то данный ге
нератор множества создает множество html, в котором хранятся только
имена файлов с расширениями .htm и .html, независимо от регистра
символов.
Как и в случае с генераторами списков, в генераторах множеств ис
пользуются итерируемые объеты, которые в свою очередь могут быть
генераторами множеств (или генераторами любого другого типа), что
позволяет создавать весьма замысловатые генераторы множеств.
Тип frozenset
Фиксированное множество (frozenset) – это множество,
которое после создания невозможно изменить. Хотя при
этом мы, конечно, можем повторно связать переменную,
которая ссылалась на фиксированное множество, с чем
то другим. Фиксированные множества могут создавать
ся только в результате обращения к имени типа frozenset
как к функции. При вызове frozenset() без аргументов
возвращается пустое фиксированное множество; с аргу
ментом типа frozenset возвращается поверхностная ко
пия аргумента; если аргумент имеет другой тип, выпол
няется попытка преобразовать его в объект типа frozen
set. Эта функция принимает не более одного аргумента.
Поскольку фиксированные множества относятся к категории неизме
няемых объектов, они поддерживают только те методы и операторы,
которые воспроизводят результат, не оказывая воздействия на фикси
рованное множество или на множества, к которым они применяются.
В табл. 3.2 (на стр. 147) перечислены все методы множеств из которых
фиксированными множествами поддерживаются: frozenset.copy(),
frozenset.difference() (
–), frozenset.intersection() (&), frozenset.isdis
joint(), frozenset.issubset() (<= и < для выявления подмножеств), fro
zenset.issuperset() (>= и > для выявления надмножеств), frozen
set.union() (|) и frozenset.symmetric_difference() (^), – то есть все те, что
помечены в таблице знаком сноски
a.
Если двухместный оператор применяется ко множеству и фиксиро
ванному множеству, тип результата будет совпадать с типом операнда,
стоящего слева от оператора. То есть если предположить, что f – это
фиксированное множество, а s – это обычное множество, то выраже
ние f & s вернет объект типа frozenset, а выражение s & f – объект ти
Поверхно
стное
и глубокое
копирование,
стр. 173

Отображения 151
па set. В случае операторов == и != порядок операндов не имеет значе
ния, и выражение f == s вернет True, только если оба множества содер
жат одни и те же элементы.
Другое следствие неизменности фиксированных множеств заключает
ся в том, что они соответствуют критерию хеширования, предъявляе
мому к элементам множеств, и потому множества и фиксированные
множества могут содержать другие фиксированные множества.
В следующем разделе, а также в упражнениях в конце главы мы встре
тим множество примеров использования множеств.
Отображения
Отображениями называются типы данных, поддерживающие опера
тор проверки на вхождение (in), функцию len() и возможность обхода
элементов в цикле. Отображения – это коллекции пар элементов
«ключзначение», которые предоставляют методы доступа к элемен
там и их ключам и значениям. При выполнении итераций порядок
следования элементов отображений может быть произвольным. В язы
ке Python имеется два типа отображений: встроенный тип dict и тип
collections.defaultdict, определяемый в стандартной библиотеке. Мы
будем использовать термин словарь для ссылки на любой из этих ти
пов, когда различия между ними не будут иметь никакого значения.
В качестве ключей словарей могут использоваться толь
ко хешируемые объекты, поэтому в качестве ключей
словаря такие неизменяемые типы, как float, frozenset,
int, str иtuple, использовать допускается, а изменяемые
типы, такие как dict, list и set, – нет. С другой стороны,
каждому ключу соответствует некоторое значение, кото
рое может быть ссылкой на объект любого типа, вклю
чая числа, строки, списки, множества, словари, функ
ции и т. д.
Словари могут сравниваться с помощью стандартных операторов срав
нения (<, <=, ==, !=, >=, >), при этом сравнивание производится поэле
ментно (и рекурсивно, при наличии вложенных элементов, таких как
кортежи или словари в словарях). Пожалуй, единственными операто
рами сравнения, применение которых к словарям имеет смысл, явля
ются операторы == и !=.
Словари
Тип dict – это неупорядоченная коллекция из нуля или более пар
«ключзначение», в которых в качестве ключей могут использоваться
ссылки на хешируемые объекты, а в качестве значений – ссылки на
объекты любого типа. Словари относятся к категории изменяемых ти
пов, поэтому легко можно добавлять и удалять их элементы, но так
Хешируемые
объекты,
стр. 145

152 Глава 3. Типы коллекций
как они являются неупорядоченными коллекциями, к ним не приме
нимо понятие индекса и не применима операция извлечения среза.
Тип данных dict может вызываться как функция dict() –
без аргументов она возвращает пустой словарь; если
в качестве аргумента передается отображение, возвраща
ется словарь, основанный на этом отображении: напри
мер, с аргументом типа dict возвращается поверхностная
копия словаря. Существует возможность передавать
в качестве аргумента последовательности, если каждый
элемент последовательности в свою очередь является по
следовательностью из двух объектов, первый из которых
используется в качестве ключа, а второй – в качестве зна
чения. Как вариант, для создания словарей, в которых
ключи являются допустимыми идентификаторами язы
ка Python, можно использовать именованные аргумен
ты; тогда имена аргументов будут играть роль ключей, а
значения аргументов – роль значений ключей. Кроме то
го, словари могут создаваться с помощью фигурных ско
бок – пустые скобки {} создадут пустой словарь. Непус
тые фигурные скобки должны содержать один или более
элементов, разделенных запятыми, каждый из которых
состоит из ключа, символа двоеточия и значения. Еще
один способ создания словарей заключается в использо
вании генераторов словарей – эта тема будет рассматри
ваться ниже, в соответствующем подразделе.
Ниже приводятся несколько способов создания словарей – все они соз
дают один и тот же словарь:
d1 = dict({"id": 1948, "name": "Washer", "size": 3})
d2 = dict(id=1948, name="Washer", size=3)
d3 = dict([("id", 1948), ("name", "Washer"), ("size", 3)])
d4 = dict(zip(("id", "name", "size"), (1948, "Washer", 3)))
d5 = {"id": 1948, "name": "Washer", "size": 3}
Словарь d1 создается с помощью литерала словаря. Сло
варь d2 создается с помощью именованных аргументов.
Словари d3 и d4 создаются из последовательностей, а сло
варь d5 создается из литерала словаря. Встроенная функ
ция zip(), использованная при создании словаря d4, воз
вращает список кортежей, первый из которых содержит
первые элементы всех итерируемых аргументов функ
ции zip(), второй – вторые элементы и т. д. Синтаксис,
основанный на применении именованных аргументов
(использованный при создании словаря d2), обычно явля
ется наиболее компактным и удобным, но при этом клю
чами могут быть только допустимые идентификаторы.
Поверхно
стное
и глубокое
копирование,
стр. 173
Именованные
аргументы,
стр. 206
Генераторы
словарей,
стр. 160
Функция zip() ,
стр. 169

Отображения 153
На рис. 3.5 демонстрируется словарь, созданный следующим фрагмен
том программного кода:
d = {"root": 18, "blue": [75, "R", 2], 21: "venus", 14: None,
"mars": "rover", (4, 11): 18, 0: 45}
Ключи словарей являются уникальными, поэтому если в словарь до
бавляется пара «ключзначение» с ключом, который уже присутству
ет в словаре, в результате происходит замена значения существующе
го ключа новым значением. Для доступа к отдельным элементам ис
пользуются квадратные скобки: например, выражение d["root"] вер
нет 18, выражение d[21] вернет строку "venus", а выражение d[91]
применительно к словарю, изображенному на рис. 3.5, возбудит ис
ключение KeyError.
Квадратные скобки могут также использоваться для добавления и уда
ления элементов словаря. Чтобы добавить новый элемент, использует
ся оператор =, например, d["X"] = 59. Для удаления элементов исполь
зуется инструкция del, например, инструкция del d["mars"] удалит из
словаря элемент с ключом "mars" или возбудит исключение KeyError,
если элемент с указанным ключом отсутствует в словаре. Кроме того,
элементы могут удаляться (и возвращаться вызывающей программе)
методом dict.pop().
Словари поддерживают встроенную функцию len() и для ключей под
держивают возможность быстрой проверки на вхождение с помощью
операторов in и not in. В табл. 3.3 перечислены все методы словарей.
Так как словари содержат пары «ключзначение», у нас может воз
никнуть потребность обойти в цикле элементы словаря (ключ, значе
ние) по значениям или по ключам. Например, ниже приводятся два
эквивалентных способа обхода пар «ключзначение»:
for item in d.items(): for key, value in d.items():
print(item[0], item[1]) print(key, value)
14
None
'blue'
[75, 'R', 2]
(4, 11)
18
21
'venus'
'root'
18
'mars'
'rover'
0
45
Рис. 3.5. Словарь – это неупорядоченная коллекция элементов
(ключ, значение) с уникальными ключами

154 Глава 3. Типы коллекций
Таблица 3.3. Методы словарей
Обход значений в словаре выполняется похожим способом:
for value in d.values():
print(value)
Для обхода ключей в словаре можно использовать метод dict.keys()
или просто интерпретировать словарь как итерируемый объект и вы
полнить итерации по его ключам, как показано в следующих двух
фрагментах:
Синтаксис Описание
d.clear()Удаляет все элементы из словаря d
d.copy()Возвращает поверхностную копию словаря d
d.fromkeys(
s, v) Возвращает словарь типа dict, ключами которого являются
элементы последовательности s, а значениями либо None, либо
v, если аргумент v определен
d.get(k) Возвращает значение ключа k или None, если ключ k отсутствует
в словаре
d.get(k, v)Возвращает значение ключа k или v, если ключ k отсутствует
всловаре
d.items()Возвращает представление
a всех пар (ключ, значение) в словаре d
a Представления словарей можно трактовать и использовать как итерируе
мые объекты. Они обсуждаются ниже, в этом же разделе. d.keys()Возвращает представление
а всех ключей словаря d
d.pop(k) Возвращает значение ключа k и удаляет из словаря элемент
сключом k или возбуждает исключение KeyError, если ключ k
отсутствует в словаре
d.pop(k, v)Возвращает значение ключа k и удаляет из словаря элемент
сключом k или возвращает значение v, если ключ k отсутствует
в словаре
d.popitem()Возвращает и удаляет произвольную пару (ключ, значение) из
словаря d или возбуждает исключение KeyError, если словарь d
пуст
d.setdefault(
k, v) То же, что и dict.get() за исключением того, что, если ключ k
в словаре отсутствует, в словарь вставляется новый элемент
сключом k и со значением None или v, если аргумент v задан
d.update(a) Добавляет в словарь d пары (ключ, значение) из a, которые от
сутствуют в словаре d, а для каждого ключа, который уже при
сутствует в словаре d, выполняется замена соответствующим
значением из a; a может быть словарем, итерируемым объектом
с парами (ключ, значение) или именованными аргументами
d.values()Возвращает представление
а всех значений в словаре d
Поверхностное
и глубокое копи
рование, стр. 173

Отображения 155
for key in d: for key in d.keys():
print(key) print(key)
Если необходимо изменить значения в словаре, то можно выполнить
обход ключей словаря в цикле и изменить значения, используя опера
тор квадратных скобок. Например, ниже показано, как можно было
бы увеличить все значения в словаре d, если предполагать, что все зна
чения являются числами:
for key in d:
d[key] += 1
Методы dict.items(), dict.keys() и dict.values() возвращают представ
ления словарей. Представление словаря – это в действительности ите
рируемый объект, доступный только для чтения и хранящий элемен
ты, ключи или значения словаря в зависимости от того, какое пред
ставление было запрошено.
Вообще, мы легко можем интерпретировать представления как итери
руемые объекты. Однако между представлениями и обычными итери
руемыми объектами есть два различия. Одно из них заключается в том,
что если словарь, для которого было получено представление, изменя
ется, то представление будет отражать эти изменения. Другое отличие
состоит в том, что представления ключей и элементов поддерживают
некоторые операции, свойственные множествам. Допустим, у нас име
ются представление словаря v и множество или представление словаря
x; для этой пары поддерживаются следующие операции:
v & x # Пересечение
v | x # Объединение
v  x # Разность
v ^ x # Строгая дизъюнкция
Для проверки наличия некоторого определенного ключа в словаре мож
но использовать оператор проверки на вхождение in, например, x in d.
Чтобы выяснить, какие ключи из заданного множества присутствуют
в словаре, можно использовать оператор пересечения. Например:
d = {}.fromkeys ("ABCD", 3) # d == {'A': 3, 'B': 3, 'C': 3, 'D': 3}
s = set("ACX") # s == {'A', 'C', 'X'}
matches = d.keys() & s # matches == {'A', 'C'}
Обратите внимание, что в комментариях в этом фрагменте программ
ного кода мы указали ключи в алфавитном порядке – это сделано
лишь для простоты восприятия, поскольку словари и множества явля
ются неупорядоченными коллекциями.
Словари часто используются для хранения счетчиков уникальных эле
ментов. Один такой пример – подсчет числа вхождений каждого от
дельного слова в файле. Ниже приводится программный код закон
ченной программы (uniqwords1.py)), которая выводит в алфавитном

156 Глава 3. Типы коллекций
порядке список слов с количеством вхождений каждого из них во всех
файлах, перечисленных в командной строке:
import string
import sys
words = {}
strip = string.whitespace + string.punctuation + string.digits + "\"'"
for filename in sys.argv[1:]:
for line in open(filename):
for word in line.lower().split():
word = word.strip(strip)
if len(word) > 2:
words[word] = words.get (word, 0) + 1
for word in sorted(words):
print("'{0}' occurs {1} times".format(word, words[word]))
Программа начинается с создания пустого словаря с именем words, за
тем путем конкатенации некоторых полезных строк, объявленных
вмодуле string, создается строка, содержащая все символы, которые
мы будем игнорировать. Мы выполняем итерации по всем именам
файлов, полученным в виде аргументов командной строки, и по всем
строкам в каждом файле. Описание функции open() смотрите во врезке
«Чтение и запись текстовых файлов» (стр. 157). Мы не указываем ко
дировку символов (потому что не знаем, какой она будет в каждом
файле) и позволяем интерпретатору открывать каждый файл, исполь
зуя по умолчанию локальную кодировку. После приведения всех сим
волов строки к нижнему регистру она разбивается на слова, после это
го с обоих концов каждого слова удаляются нежелательные символы.
Если в получившемся слове осталось хотя бы три символа, мы выпол
няем обновление словаря.
Мы не можем использовать синтаксис words[word] += 1, потому что
в самый первый раз, когда слово word еще отсутствует в словаре, это
выражение будет возбуждать исключение KeyError – мы же не можем
увеличивать значение элемента, отсутствующего в словаре. Поэтому
мы используем иной подход. Мы вызываем метод dict.get() со значе
нием по умолчанию 0. Если слово уже присутствует в словаре, метод
dict.get() вернет существующее значение и это значение, увеличенное
на 1, будет записано как новое значение элемента. Если слово отсутст
вует в словаре, метод dict.get() вернет значение по умолчанию 0 и это
значение, увеличенное на 1 (то есть 1), будет записано как значение
нового элемента, ключом которого является строка word. Чтобы пояс
нить ситуацию, ниже приводятся два фрагмента программного кода,
выполняющего одно и то же, хотя код, использующий метод
dict.get(), имеет более высокую эффективность:
if word not in words:
words[word] = 0
words[word] = words.get(word, 0) + 1 words[word] += 1

Отображения 157
В следующем подразделе мы познакомимся со словарями, имеющими
значения по умолчанию, и рассмотрим альтернативное решение.
После того, как все встретившиеся слова будут накоплены в словаре,
выполняются итерации по его ключам (по словам) в алфавитном по
рядке и выводятся слова и число раз, которое они встречаются.
Использование метода dict.get() позволяет легко обновлять значения
в словаре, возвращая значения, если это отдельные элементы данных,
такие как числа или строки. Но как быть, если каждое значение само
по себе является коллекцией? Чтобы продемонстрировать, как обраба
тывать такие значения, ниже приводится программа, которая читает
Чтение и запись текстовых файлов
Файлы открываются с помощью встроенной функ
ции open(), которая возвращает «объект файла»
(типа io.TextIOWrapper для текстовых файлов).
Функция open() принимает один обязательный ар
гумент – имя файла, которое может включать путь
к файлу, и до шести необязательных аргументов,
два из которых коротко описываются здесь. Вто
рой аргумент определяет режим работы с файлом,
то есть он указывает, будет ли файл интерпретиро
ваться как текстовый или как двоичный, и для вы
полнения каких действий будет открыт файл – для
чтения, для записи, для дополнения в конец или
комбинации этих действий.
Для работы с текстовыми файлами Python исполь
зует кодировку символов, зависящую от платфор
мы. Поэтому, возможно, лучше будет указывать
кодировку с помощью аргумента encoding функции
open(), то есть обычный синтаксис, используемый
для открытия файлов, имеет следующий вид:
fin = open(filename, encoding="utf8") # для чтения текста
fout = open(filename, "w", encoding="utf8") # для записи текста
Поскольку по умолчанию функция open() использует режим
«для чтения», и для указания кодировки используется имено
ванный аргумент encoding, а не позиционный, то при открытии
файла для чтения можно опустить другие необязательные пози
ционные аргументы. Аналогично, при открытии файла для за
писи мы можем указывать только те аргументы, которые дейст
вительно необходимы. (Вопросы передачи аргументов подробно
рассматриваются в главе 4.)
Глава 7, рабо
та с файлами,
стр. 334
Kодировки
символов,
стр. 112

158 Глава 3. Типы коллекций
содержимое файлов HTML, имена которых указываются в командной
строке, и выводит список различных вебсайтов, ссылки на которые
присутствуют в файлах, и список файлов, в которых эти ссылки встре
чаются, ниже каждого вебсайта. По своей структуре программа
(external_sites.py) очень похожа на программу подсчета числа вхожде
ний отдельных слов, которую мы только что рассмотрели. Ниже при
водится основная часть программы:
sites = {}
for filename in sys.argv[1:]:
for line in open(filename):
i = 0
while True:
site = None
Как только файл будет открыт для чтения в текстовом режиме,
можно будет прочитать его целиком в одну строку, используя
метод объекта файла read(), или в список строк, используя метод
объекта файла readlines(). Типичный прием построчного чтения
содержимого файла основан на интерпретации объекта файла
как итератора:
for line in open(filename, encoding="utf8"):
process(line)
Этот прием работает, потому что объект файла допускает выпол
нение итераций по нему, как по последовательности, каждый
элемент которой представляет собой строку, содержащую от
дельную строку из файла. Строки, которые в этом случае полу
чает программа, содержат символы перевода строки \n.
Если в качестве режима указать «w», файл будет открыт в режи
ме «записи текста». Запись в файл может производиться с помо
щью метода объекта файла write(), который в качестве аргумен
та принимает единственную строку. Каждая записываемая стро
ка уже должна содержать символ перевода строки \n. При вы
полнении чтения и записи Python автоматически преобразует
символы \n в последовательность символов завершения строки,
характерную для той или иной платформы.
После окончания работы с объектом файла можно вызвать его
метод close() – это приведет к выталкиванию буферов вывода на
диск. В небольших программах на языке Python обычно не при
нято беспокоиться о вызове функции close(), поскольку Python
делает это автоматически, когда объект файла выходит из теку
щей области видимости. Если возникают какиелибо проблемы,
объект тут же сообщает о них возбуждением исключений.

Отображения 159
i = line.find("http://", i)
if i > 1:
i += len("http://")
for j in range(i, len(line)):
if not (line[j].isalnum() or line[j] in "."):
site = line[i:j].lower()
break
if site and "." in site:
sites.setdefault(site, set()).add(filename)
i = j
else:
break
Программа начинается с создания пустого словаря. Затем выполняются
итерации по списку файлов, перечисленных в командной строке, и по
строкам в каждом файле. Нам необходимо учесть тот факт, что в каж
дой строке может содержаться произвольное число ссылок на вебсай
ты, поэтому мы продолжаем вызывать метод str.find() в цикле, пока
поиск не завершится неудачей. Если обнаруживается строка «http://»,
мы увеличиваем значение переменной i (начальная позиция в строке)
на длину строки «http://» и затем просматриваем все последующие сим
волы, пока не будет найден символ, недопустимый в именах вебсайтов.
Если обнаружена ссылка на сайт (в качестве простой проверки мы убе
ждаемся, что она содержит символ точки), мы добавляем ее в словарь.
Мы не можем использовать выражение sites[site].add(filename), пото
му что в самый первый раз, когда сайт еще отсутствует в словаре, это
выражение будет возбуждать исключение KeyError – т. к. нельзя доба
вить новое значение к множеству, которое пока отсутствует в словаре.
Поэтому мы используем иной подход. Метод dict.setdefault() возвра
щает ссылку на элемент словаря с заданным ключом (первый аргу
мент). Если такой элемент отсутствует, метод создает новый элемент
с указанным ключом и устанавливает в качестве значения либо None,
либо указанное значение по умолчанию (второй аргумент). В данном
случае в качестве значения по умолчанию передается результат вызо
ва функции set(), то есть пустое множество. Поэтому вызов метода
dict.setdefault() всегда будет возвращать ссылку на значение, либо су
ществовавшее ранее, либо на вновь созданное. (Безусловно, если ука
занный ключ не является хешируемым значением, будет возбуждено
исключение TypeError.)
В данном примере возвращаемая ссылка всегда будет указывать на
множество (пустое множество при первом упоминании каждого кон
кретного ключа, то есть сайта), после чего мы добавляем имя файла,
где встречена ссылка на сайт, ко множеству имен файлов для данного
сайта. Использование множества гарантирует, что даже при наличии
в файле нескольких ссылок на один и тот же сайт имя файла будет за
писано всего один раз.

160 Глава 3. Типы коллекций
Чтобы прояснить, как функционирует метод dict.setdefault(), ниже
приводятся два эквивалентных фрагмента программного кода:
if site not in sites:
sites[site] = set()
sites.setdefault(site, set()).add(fname) sites[site].add(fname)
Для полноты картины ниже приводится остальная часть программы:
for site in sorted(sites):
print("{0} is referred to in:".format(site))
for filename in sorted(sites[site], key=str.lower):
print(" {0}".format(filename))
Под каждым вебсайтом выводится с отступом список
файлов, в которых встречается ссылка на этот вебсайт.
Вызов функции sorted() во внешнем цикле for ... in вы
полняет сортировку ключей словаря – всякий раз, когда
словарь используется в контексте, где требуется итери
руемый объект, используются его ключи. Если необхо
димо выполнить итерации по элементам (ключ, значе
ние) или по значениям, можно использовать методы
dict.items() или dict.values(). Внутренний цикл for ...
in выполняет итерации по отсортированному списку
имен файлов, присутствующих во множестве имен фай
лов для данного сайта.
Генераторы словарей
Генератор словарей – это выражение и цикл с необязательным усло
вием, заключенное в фигурные скобки, очень напоминающее генера
тор множеств. Подобно генераторам списков и множеств, генераторы
словарей поддерживают две формы записи:
{keyexpression: valueexpression for key, value in iterable}
{keyexpression: valueexpression for key, value in iterable if condition}
Ниже показано, как можно использовать генератор словарей для соз
дания словаря, в котором каждый ключ является именем файла в те
кущем каталоге, а каждое значение – это размер файла в байтах:
file_sizes = {name: os.path.getsize(name) for name in os.listdir(".")}
Функция os.listdir() из модуля os («operating system» –
операционная система) возвращает список файлов и ка
талогов в указанном каталоге, но при этом в список ни
когда не включаются специальные имена каталогов «.»
или «..». Функция os.path.getsize() возвращает размер
заданного файла в байтах. Чтобы отфильтровать катало
ги и другие элементы списка, не являющиеся файлами,
можно добавить дополнительное условие:
Функция sorted() ,
стр. 164, 170
Модули os
и os.path ,
стр. 261

Отображения 161
file_sizes = {name: os.path.getsize(name) for name in os.listdir(".")
if os.path.isfile(name)}
Функция os.path.isfile() из модуля os.path возвращает True, если ука
занный путь соответствует файлу, и False – в противном случае, то есть
для каталогов, ссылок и тому подобного.
Генераторы словарей могут также использоваться для создания инвер
тированных словарей. Например, пусть имеется словарь d, тогда мы
можем создать новый словарь, ключами которого будут значения сло
варя d, а значениями – ключи словаря d:
inverted_d = {v: k for k, v in d.items()}
Полученный словарь можно инвертировать обратно и получить перво
начальный словарь – при условии, что все значения в первоначальном
словаре были уникальными, однако инверсия будет терпеть неудачу,
с возбуждением исключения TypeError, если какоелибо значение ока
жется не хешируемым.
Точно так же, как и в случае с генераторами списков и множеств, в ка
честве итерируемого объекта в генераторах словарей могут использо
ваться другие генераторы, то есть это могут быть вложенные генерато
ры любого типа.
Словари со значениями по умолчанию
Словари со значениями по умолчанию – это обычные словари, они под
держивают те же самые методы и операторы, что и обычные словари. 1
Единственное, что отличает такие словари от обычных словарей, – это
способ обработки отсутствующих ключей, но во всех остальных отно
шениях они ничем не отличаются друг от друга.
При обращении к несуществующему («отсутствующему») ключу слова
ря возбуждается исключение KeyError. Это очень удобно, так как неред
ко для нас бывает желательно знать об отсутствии ключа, который, со
гласно нашим предположениям, может присутствовать. Но в некоторых
случаях бывает необходимо, чтобы в словаре присутствовали все ключи,
которые мы используем, даже если это означает, что элемент с задан
ным ключом добавляется в словарь в момент первого обращения к нему.
Например, допустим, что имеется словарь d, который не имеет элемен
та с ключом m, тогда выражение x = d[m] возбудит исключение KeyEr
ror. Если d – это словарь со значениями по умолчанию, созданный со
ответствующим способом, а элемент с ключом m принадлежит такому
словарю, то при обращении к нему будет возвращено соответствующее
значение, как и в случае с обычным словарем. Но если в словаре со
значениями по умолчанию отсутствует ключ m, то будет создан новый
1 Примечание для программистов, использующих объектноориентирован
ный стиль: defaultdict – это подкласс класса dict.

162 Глава 3. Типы коллекций
элемент словаря с ключом m и со значением по умолчанию, и будет воз
вращено значение этого, вновь созданного элемента.
Ранее мы написали небольшую программу, которая под
считывала количество вхождений каждого отдельного
слова в файлы, имена которых передавались в виде аргу
ментов командной строки. В этой программе создавался
словарь слов, как показано ниже:
words = {}
Каждый ключ в словаре words является словом, а значение – целым
числом, в котором хранится количество вхождений данного слова во
всех файлах. Ниже показано, как увеличивается значение счетчика,
соответствующего некоторому слову:
words[word] = words.get(word, 0) + 1
Мы вынуждены были использовать метод dict.get(), чтобы учесть слу
чай, когда слово встречается впервые (когда необходимо создать но
вый элемент со значением счетчика, равным 1), а также случаи, когда
слово встречается повторно (когда необходимо прибавить 1 к значе
нию счетчика для уже существующего слова).
При создании словаря со значениями по умолчанию мы можем опреде
лять фабричную функцию. Фабричная функция – это функция, кото
рая вызывается, чтобы получить объект определенного типа. Все
встроенные типы данных языка Python могут использоваться как фаб
ричные функции, например, тип данных str может вызываться как
функция str(), которая при вызове без аргументов возвращает пустой
строковый объект. Фабричная функция, передаваемая словарю со зна
чениями по умолчанию, используется для создания значений по умол
чанию для отсутствующих ключей.
Обратите внимание, что имя функции – это ссылка на объект функ
ции, поэтому, когда функция передается в качестве аргумента, пере
дается одно только имя функции. Когда вслед за именем функции за
писываются круглые скобки, они сообщают интерпретатору, что он
должен вызвать эту функцию.
Программа uniquewords2.py на одну строку длиннее, чем исходная
программа uniquewords1.py (import collections), а, кроме того, измени
лись строки создания и обновления словаря. Ниже показано, как соз
дается словарь со значениями по умолчанию:
words = collections.defaultdict (int)
Словарь со значениями по умолчанию words никогда не возбудит ис
ключение KeyError. Если будет необходимо выполнить выражение x =
words["xyz"] и в словаре будет отсутствовать элемент с ключом "xyz", то
при обращении к несуществующему ключу словарь со значениями по
умолчанию немедленно создаст новый элемент с ключом "xyz" и значе
Пример unique
words1.py ,
стр. 155

Обход в цикле и копирование коллекций 163
нием 0 (вызовом функции int()), и это значение будет присвоено пере
менной x.
words[word] += 1
Теперь мы можем отказаться от использования метода dict.get() и про
сто увеличивать значение элемента. Когда будет обнаружено самое
первое вхождение слова, будет создан новый элемент со значением 0
(к которому тут же будет прибавлено число 1), а при обнаружении ка
ждого последующего вхождения число 1 будет добавляться к текуще
му значению.
Мы закончили полный обзор всех встроенных типов коллекций языка
Python и пары типов коллекций из стандартной библиотеки. В сле
дующем разделе мы рассмотрим некоторые проблемы, общие для всех
типов коллекций.
Обход в цикле и копирование коллекций
После того как будет создана коллекция элементов данных, вполне ес
тественно возникает желание обойти все элементы, содержащиеся
в ней. В первом подразделе этого раздела мы познакомимся с итерато
рами языка Python, а также с операторами и функциями, применяе
мыми для работы с итераторами.
Еще одна часто выполняемая операция – копирование коллекций. Из
за того, что в языке Python повсеместно используются ссылки на объ
екты (ради повышения эффективности), существуют некоторые осо
бенности, связанные с копированием, поэтому во втором подразделе
этого раздела мы изучим принципы копирования коллекций и узна
ем, как добиться именно того, что нам нужно.
Итераторы, функции и операторы для работы
с итерируемыми объектами
Итерируемый тип данных – это такой тип, который мо
жет возвращать свои элементы по одному. Любой объ
ект, имеющий метод __iter__(), или любая последова
тельность (то есть объект, имеющий метод __getitem__(),
принимающий целочисленный аргумент со значением
от 0 и выше), является итерируемым и может предостав
лять итератор. Итератор – это объект, имеющий метод
__next__(), который при каждом вызове возвращает оче
редной элемент и возбуждает исключение StopIteration
после исчерпания всех элементов. В табл. 3.4 перечисле
ны операторы и функции, которые могут применяться
к итерируемым объектам. Специальный
метод
__iter __(),
стр. 319

164 Глава 3. Типы коллекций
Таблица 3.4. Общие функции и операторы для работы
с итерируемыми объектами
Синтаксис Описание
s + tВозвращает конкатенацию последовательностей s и t
s * nВозвращает конкатенацию из int n последовательностей s
x in iВозвращает True, если элемент x присутствует в итерируемом
объекте i, обратная проверка выполняется с помощью оператора
not in
all(i)Возвращает True, если все элементы итерируемого объекта i в ло
гическом контексте оцениваются как значение True
any(i) Возвращает True, если хотя бы один элемент итерируемого объек
та i в логическом контексте оценивается как значение True
enumerate
(i,start) Обычно используется в циклах for ... in, чтобы получить последо
вательность кортежей (index, item), где значения индексов начина
ют отсчитывать от 0 или от значения start; подробности в тексте
len(x) Возвращает «длину» объекта x. Если x – коллекция, то возвра
щаемое число представляет количество элементов. Если x – стро
ка, то возвращаемое число представляет количество символов
max(i, key) Возвращает наибольший элемент в итерируемом объекте i или
элемент с наибольшим значением key(item), если функция key оп
ределена
min(i, key) Возвращает наименьший элемент в итерируемом объекте i или
элемент с наименьшим значением key(item), если функция key
определена
range
(start,
stop,

step) Возвращает целочисленный итератор. С одним аргументом (stop)
итератор представляет последовательность целых чисел от 0 до
stop – 1, с двумя аргументами (start, stop) – последовательность
целых чисел от start до stop – 1, с тремя аргументами – последо
вательность целых чисел от start до stop – 1 c шагом step
reversed(i) Возвращает итератор, который будет возвращать элементы ите
ратора i в обратном порядке
sorted
(i, key,
reverse) Возвращает список элементов итератора i в отсортированном по
рядке; аргумент key используется для выполнения сортировки
DSU (Decorate, Sort, Undecorate – декорирование, сортировка,
обратное декорирование). Если аргумент reverse имеет значение
True, сортировка выполняется в обратном порядке
sum
(i, start) Возвращает сумму элементов итерируемого объекта i, плюс ар
гумент start (значение которого по умолчанию равно 0); объект i
не должен содержать строк
zip(i1,
..., iN) Возвращает итератор кортежей, используя итераторы от i1 до iN;
подробности в тексте

Обход в цикле и копирование коллекций 165
Порядок, в котором возвращаются элементы, зависит от итерируемого
объекта. В случае списков и кортежей элементы обычно возвращаются
в предопределенном порядке, начиная с первого элемента (находя
щегося в позиции с индексом 0), но другие итераторы возвращают
элементы в произвольном порядке – например, итераторы словарей
имножеств.
Встроенная функция iter() используется двумя совершенно различны
ми способами. Применяемая к коллекции или к последовательности,
она возвращает итератор для заданного объекта или возбуждает исклю
чение TypeError, если объект не является итерируемым. Такой способ
часто используется при работе с нестандартными типами коллекций
и крайне редко – в других контекстах. Во втором варианте использова
ния функции iter() ей передается вызываемый объект (функция или
метод) и специальное значение. В этом случае полученная функция или
метод вызывается на каждой итерации, а значение этой функции, если
оно не равно специальному значению, возвращается вызывающей про
грамме; в противном случае возбуждается исключение StopIteration.
Когда в программе используется цикл for item in iterable, интерпре
татор Python вызывает функцию iter(iterable), чтобы получить ите
ратор. После этого на каждой итерации вызывается метод __next__()
итератора, чтобы получить очередной элемент, а когда возбуждается
исключение StopIteration, оно перехватывается и цикл завершается.
Другой способ получить очередной элемент итератора состоит в том,
чтобы вызвать встроенную функцию next(). Ниже приводятся два экви
валентных фрагмента программного кода (оба они вычисляют произве
дение элементов списка), в одном из них используется цикл for ... in,
а во втором явно используется итератор:
product = 1
i = iter([1, 2, 4, 8])
while True:
try:
product = 1 product *= next(i)
for i in [1, 2, 4, 8]: except StopIteration:
product *= i break
print(product) # выведет: 64 print(product) # выведет: 64
Любой (конечный) итерируемый объект i может быть преобразован
в кортеж вызовом функции tuple(i) или в список – вызовом функции
list(i).
К итераторам могут применяться функции all() и any(),
и они часто используются в функциональном програм
мировании. Ниже приводится пара примеров, демонст
рирующих использование функций all(), any(), len(),
min(), max() и sum():
>>> x = [2, 9, 7, 4, 3]
>>> all(x), any(x), len(x), min(x), max(x), sum(x)
Функциональ
ное програм
мирование,
стр. 397

166 Глава 3. Типы коллекций
(True, True, 5, 4, 9, 13)
>>> x.append(0)
>>> all(x), any(x), len(x), min(x), max(x), sum(x)
(False, True, 6, 4, 9, 13)
Из всех этих маленьких функций наиболее часто, пожалуй, использу
ется функция len().
Функция enumerate() принимает итератор и возвращает объект пере
числения. Этот объект может рассматриваться как своего рода итера
тор. На каждой итерации он возвращает кортеж из двух элементов,
первый из которых – это номер итерации (по умолчанию нумерация
начинается с 0), а второй – следующий элемент итератора, который
был передан функции enumerate(). Давайте рассмотрим порядок ис
пользования функции enumerate() в контексте небольшой, но закон
ченной программы.
Программа grepword.py принимает в виде аргументов командной стро
ки слово и одно или более имен файлов. Она выводит имя файла, но
мер строки и саму строку, содержащую искомое слово.
1 Ниже приво
дится пример сеанса работы с программой:
grepword.py Dom data/forenames.txt
data/forenames.txt:615:Dominykas
data/forenames.txt:1435:Dominik
data/forenames.txt:1611:Domhnall
data/forenames.txt:3314:Dominic
Файлы с данными data/forenames.txt и data/surnames.txt содержат
несортированные списки имен, по одному имени в каждой строке.
Не считая инструкции импортирования модуля sys, программа зани
мает всего десять строк:
if len(sys.argv) < 3:
print("usage: grepword.py word infile1 [infile2 [... infileN]]")
sys.exit()
word = sys.argv[1]
for filename in sys.argv[2:]:
for lino, line in enumerate(open(filename), start=1):
if word in line:
print("{0}:{1}:{2:.40}".format(filename, lino,
line.rstrip()))
Программа начинается с проверки наличия хотя бы двух аргументов
командной строки. Если число аргументов меньше двух, программа вы
водит сообщение с инструкцией о порядке использования и завершает
работу. Функция sys.exit() немедленно завершает работу программы,
1 В главе 9 будут представлены еще две реализации этой программы – grep
wordp.py и grepwordt.py, которые распределяют работу по нескольким про
цессам и потокам выполнения.

Обход в цикле и копирование коллекций 167
закрывая любые открытые файлы. Она принимает необязательный ар
гумент типа int, который передается вызывающей командной оболочке.
Предполагается, что первым аргументом является сло
во, которое требуется отыскать, а другие аргументы –
это имена файлов, в которых требуется произвести по
иск. Мы преднамеренно вызываем функцию open(), не
указывая кодировку символов – пользователь может ис
пользовать в именах файлов шаблонные символы для
выбора группы файлов, каждый из которых может
иметь собственную кодировку символов, поэтому в дан
ном случае мы оставляем за интерпретатором право ис
пользовать платформозависимую кодировку.
Функция open() возвращает объект файла, открытого в текстовом ре
жиме, в котором этот объект может использоваться как итератор, воз
вращая по одной строке из файла в каждой итерации. Передав итератор
функции enumerate(), мы получаем итераторперечисление, который
в каждой итерации возвращает номер итерации (в переменной lino,
«line number» – «номер строки») и строку из файла. Если слово, ука
занное пользователем, присутствует в строке, программа выводит имя
файла, номер строки и первые 40 символов этой строки, из которой
удаляются завершающие пробельные символы (такие как \n). Функ
ция enumerate() принимает необязательный именованный аргумент
start, который по умолчанию имеет значение 0. Мы передаем в этом
аргументе значение 1, так как, в соответствии с общепринятыми согла
шениями, нумерация строк в текстовых файлах начинается с 1.
Как правило, на практике используется не итераторперечисление,
а итератор, возвращающий последовательные целые числа. Это имен
но то, что делает функция range(). Если нам необходим кортеж или
список целых чисел, мы можем преобразовать итератор, возвращае
мый функцией range(), воспользовавшись соответствующей функцией
преобразования, как показано ниже:
>>> list(range(5)), list(range(9, 14)), tuple(range(10, 11, 5))
([0, 1, 2, 3, 4], [9, 10, 11, 12, 13], (10, 5, 0, 5, 10))
Функция range() обычно используется в двух случаях: для создания
списков или кортежей целых чисел и в качестве счетчика в циклах for
... in. Например, следующие два эквивалентных примера преобразу
ют значения в списке в положительные числа:
i = 0
while i < len(x):
for i in range(len(x)): x[i] = abs(x[i])
x[i] = abs(x[i]) i += 1
В обоих случаях, если предположить, что x – это список значений [11,
–3, –12, 8, –1], то после выполнения фрагментов он превратится в спи
сок [11, 3, 12, 8, 1].
Врезка «Чте
ние и запись
текстовых
файлов»,
стр. 157

168 Глава 3. Типы коллекций
Благодаря тому, что существует возможность распаковывать итери
руемые объекты с помощью оператора *, мы можем распаковать ите
ратор, возвращаемый функцией range(). Например, если представить,
что у нас имеется функция calculate(), которая принимает четыре ар
гумента, ниже приводятся несколько способов вызова этой функции
с аргументами 1, 2, 3 и 4:
calculate(1, 2, 3, 4)
t = (1, 2, 3, 4)
calculate(*t)
calculate(*range(1, 5))
Во всех трех случаях функции передается четыре аргумента. Во вто
ром случае распаковывается кортеж из четырех элементов, а в треть
ем – распаковывается итератор, возвращаемый функцией range().
Теперь рассмотрим небольшую, но законченную программу, которая
соединяет в себе все, что мы узнали к настоящему моменту, и впервые
явно использует функцию записи в файл. Программа generate_test_
names1.py читает данные из файла с фамилиями и из файла именами,
создает два списка, а затем записывает 100 случайно образованных
имен в файл testnames1.txt.
В программе используется функция random.choice(), которая извлека
ет случайный элемент из последовательности, поэтому вполне воз
можно, что в окончательном списке одно и то же имя может появиться
несколько раз. Для начала рассмотрим функцию, возвращающую спи
сок имен, а затем перейдем к остальной части программы:
def get_forenames_and_surnames():
forenames = []
surnames = []
for names, filename in ((forenames, "data/forenames.txt"),
(surnames, "data/surnames.txt")):
for name in open(filename, encoding="utf8"):
names.append(name.rstrip())
return forenames, surnames
Во внешнем цикле for ... in выполняется обход двух
элементных кортежей, каждый из которых распаковы
вается в две переменные. Хотя списки могут быть чрез
вычайно длинными, возврат их из функции выполняет
ся очень эффективно, так как в языке Python использу
ются ссылки на объекты, поэтому фактически функция
возвращает всего лишь две ссылки на объекты.
Внутри программ на языке Python всегда следует использовать запись
путей к файлам в стиле операционной системы UNIX, поскольку
в этом случае можно не применять экранирование служебных симво
лов и такой прием одинаково хорошо работает на всех поддерживае
мых платформах (включая и Windows). Если у нас имеется строка
пути, например в переменной path, и нам необходимо вывести ее перед Распаковыва
ние кортежей,
стр. 133

Обход в цикле и копирование коллекций 169
пользователем, мы всегда можем импортировать модуль os и вызвать
метод path.replace("\", os.sep) для замены прямых слешей на символ
разделитель каталогов, используемый в текущей платформе.
forenames, surnames = get_forenames_and_surnames()
fh = open("testnames1.txt", "w", encoding="utf8")
for i in range(100):
line = "{0} {1}\n".format(random.choice(forenames),
random.choice(surnames))
fh.write(line)
Получив два списка, программа открывает выходной
файл для записи и сохраняет объект файла в переменной
fh («file handle» – дескриптор файла). После этого вы
полняется 100 циклов и на каждой итерации создается
строка, в конец которой добавляется символ перевода
строки, и эта строка записывается в файл. Мы не исполь
зуем переменную цикла i – она нужна исключительно
для того, чтобы удовлетворить требования синтаксиса
цикла for ... in. Предыдущий фрагмент программного
кода, функция get_forenames_and_surnames() и инструк
ция import образуют полную программу.
В программе generate_test_names1.py мы объединяли элементы из
двух отдельных списков в единую строку. Другой способ объединения
элементов двух или более списков (или других итерируемых объектов)
заключается в использовании функции zip(). Функция zip() принима
ет один или более итерируемых объектов и возвращает итератор, кото
рый в свою очередь возвращает кортежи. Первый кортеж включает
в себя первые элементы всех итерируемых объектов, второй кортеж –
вторые элементы и т. д., итерации прекращаются, как только содер
жимое любого из итерируемых объектов будет исчерпано. Например:
>>> for t in zip(range(4), range(0, 10, 2), range(1, 10, 2)):
... print(t)
(0, 0, 1)
(1, 2, 3)
(2, 4, 5)
(3, 6, 7)
Несмотря на то, что итераторы, возвращаемые вторым и третьим вызо
вами функции range(), могут вернуть по пять элементов, тем не менее
первый итератор может воспроизвести всего четыре элемента, тем са
мым ограничивая количество элементов, которые может вернуть функ
ция zip().
Ниже приводится модифицированная версия программы, генерирую
щей имена, но на этот раз под имя отводится 25 символов, а вслед за
каждым именем выводится случайный год. Программа называется
generate_test_names2.py и выводит результаты в файл testnames2.txt.
Мы не приводим здесь программный код функции get_forenames_and_
Врезка «Чте
ние и запись
текстовых
файлов»,
стр. 157

170 Глава 3. Типы коллекций
surnames() и вызов функции open(), так как они не изменились, за ис
ключением имени выходного файла.
limit = 100
years = list(range(1970, 2013)) * 3
for year, forename, surname in zip(
random.sample(years, limit),
random.sample(forenames, limit),
random.sample(surnames, limit)):
name = "{0} {1}".format(forename, surname)
fh.write("{0:.<25}.{1}\n".format(name, year))
Программа начинается с определения значения максимального числа
имен, которые могут быть сгенерированы. Затем создается список лет –
путем создания списка со значениями в диапазоне от 1970 до 2012
включительно, после чего этот список дублируется трижды, поэтому
в окончательном списке каждый год встречается три раза. Это необхо
димо потому, что функция random.sample() (используемая вместо ran
dom.choice()) принимает итерируемый объект и число элементов, кото
рые требуется воспроизвести – это число не может быть меньше, чем
число элементов, которые может вернуть итерируемый объект. Функ
ция random.sample() возвращает итератор, который воспроизводит ука
занное число элементов без повторений. Поэтому данная версия про
граммы всегда будет воспроизводить уникальные имена.
В цикле for ... in распаковывается каждый кортеж,
возвращаемый функцией zip(). Нам требуется ограни
чить длину каждого имени 25 символами, а для этого
сначала нужно создать строку с полным именем, а затем
вторым вызовом метода str.format() ограничить ее дли
ну. Каждое имя выравнивается по левому краю, а для
имен короче 25 символов производится дополнение стро
ки точками. Дополнительная точка гарантирует, что
имена, полностью занимающие поле вывода, все же бу
дут отделяться от года хотя бы одной точкой.
В завершение этого подраздела мы упомянем еще две функции, имею
щие отношение к итерируемым объектам – sorted() и reversed().
Функция sorted() возвращает отсортированный список элементов,
афункция reversed() просто возвращает итератор, который позволяет
выполнить обход элементов заданного итератора в обратном порядке.
Ниже приводится пример использования функции reversed():
>>> list(range(6))
[0, 1, 2, 3, 4, 5]
>>> list(reversed(range(6)))
[5, 4, 3, 2, 1, 0]
Функция sorted() – более сложная, как показано в примере ниже:
>>> x = []
>>> for t in zip(range(10, 0, 1), range(0, 10, 2), range(1, 10, 2)):
Распаковыва
ние кортежей,
стр. 133
Метод str.
format() ,
стр. 100

Обход в цикле и копирование коллекций 171
... x += t
>>> x
[10, 0, 1, 9, 2, 3, 8, 4, 5, 7, 6, 7, 6, 8, 9]
>>> sorted(x)
[10, 9, 8, 7, 6, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> sorted(x, reverse=True)
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 6, 7, 8, 9, 10]
>>> sorted(x, key=abs)
[0, 1, 2, 3, 4, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10]
В предыдущем фрагменте функция zip() возвращает кортежи, состоя
щие из трех элементов: (
–10, 0, 1), ( –9, 2, 3) и т. д. Оператор += допол
няет список, то есть добавляет каждый элемент заданной последова
тельности в конец списка.
Первый вызов функции sorted() возвращает копию списка, отсортиро
ванную в привычном порядке. Второй вызов возвращает копию спи
ска, отсортированную в обратном порядке. В последнем вызове функ
ции sorted() определена функция «key», к которой мы вернемся через
несколько мгновений.
Обратите внимание, что в языке Python функции являются самыми
обычными объектами, поэтому они могут передаваться другим функ
циям в виде аргументов и сохраняться в коллекциях без излишних
формальностей. Не забывайте, что имя функции – это ссылка на объ
ект функции, а круглые скобки, следующие за именем, сообщают ин
терпретатору Python о необходимости вызова этой функции.
Когда функции sorted() передается функция key (в данном случае –
функция abs()), она будет вызываться для каждого элемента списка
(каждый элемент будет передаваться функции в виде единственного
аргумента), чтобы создать «декорированный» список. Затем выполня
ется сортировка декорированного списка, после чего в качестве ре
зультата возвращается недекорированный список. Мы легко можем
использовать собственные функции в качестве аргумента key, в чем вы
вскоре убедитесь.
Например, мы можем выполнить сортировку без учета регистра симво
лов, передав в аргументе key метод str.lower(). Если представить, что
у нас имеется список ["Sloop", "Yawl", "Cutter", "schooner", "ketch"],
мы можем отсортировать его без учета регистра символов, используя
DSUсортировку (Decorate, Sort, Undecorate – декорирование, сорти
ровка, обратное декорирование), всего одной строкой программного
кода, передав нужную функцию в аргументе key или выполнив сорти
ровку явно, как показано в следующих двух эквивалентных фрагмен
тах программного кода:
temp = []
for item in x:
t e mp .a p pe nd (( i te m. lo w er () , it em ) )

172 Глава 3. Типы коллекций
x = []
for key, value in sorted(temp):
x = sorted(x, key=str.lower) x.append(value)
Оба фрагмента воспроизводят новый список: ["Cutter", "ketch", "schoo
ner", "Sloop", "Yawl"], хотя действия, которые они выполняют, не
идентичны, потому что во фрагменте справа создается промежуточ
ный список temp.
В языке Python реализован адаптивный алгоритм устойчивой сорти
ровки со слиянием, который отличается высокой скоростью и интел
лектуальностью и особенно хорошо подходит для сортировки частич
но отсортированных списков, что встречается достаточно часто.
1 Сло
во «адаптивный» в названии алгоритма означает, что алгоритм сорти
ровки адаптируется под определенные условия, например, учитывает
наличие частичной сортировки данных. Слово «устойчивый» означа
ет, что одинаковые элементы не перемещаются относительно друг дру
га (в конце концов, в этом нет никакой необходимости), и слова «сор
тировка со слиянием» – это общее название используемых алгоритмов
сортировки. Когда выполняется сортировка списка целых чисел,
строк или данных других простых типов, используется оператор
«меньше чем» (<). Интерпретатор Python может сортировать коллек
ции, содержащие другие коллекции, выполняя рекурсивный спуск на
произвольную глубину. Например:
>>> x = list(zip((1, 3, 1, 3), ("pram", "dorie", "kayak", "canoe")))
>>> x
[(1, 'pram'), (3, 'dorie'), (1, 'kayak'), (3, 'canoe')]
>>> sorted(x)
[(1, 'kayak'), (1, 'pram'), (3, 'canoe'), (3, 'dorie')]
Python отсортировал список кортежей, сравнив сначала первые эле
менты каждого кортежа, а если они были равны – вторые элементы.
В результате элементы были отсортированы на основе целых чисел,
с использованием строк для дополнительной сортировки. Мы можем
принудительно сначала отсортировать список по строкам, а дополни
тельную сортировку выполнить по целым числам, определив простую
функцию, которая будет использоваться в качестве аргумента key:
def swap(t):
return t[1], t[0]
Функция swap() принимает кортеж из двух элементов и возвращает но
вый кортеж из двух элементов, в котором элементы переставлены мес
тами. Представим, что функцию swap() мы ввели в среде IDLE, тогда
можно выполнить следующее:
1 Алгоритм был создан Тимом Петерсом (Tim Peters). Интересное описание
и обсуждение алгоритма можно найти в файле listsort.txt, который постав
ляется в составе исходных программных кодов Python.

Обход в цикле и копирование коллекций 173
>>> sorted(x, key=swap)
[(3, 'canoe'), (3, 'dorie'), (1, 'kayak'), (1, 'pram')]
Кроме того, списки могут быть отсортированы непосредственно, без
создания копии, с помощью метода list.sort(), который принимает те
же необязательные аргументы, что и функция sorted().
Сортировка может применяться только к коллекциям, все элементы
которых могут сравниваться друг с другом:
sorted([3, 8, 7.5, 0, 1.3]) # вернет: [7.5, 0, 1.3, 3, 8]
sorted([3, "spanner", 7.5, 0, 1.3]) # возбудит исключение TypeError
Хотя первый список содержит числа разных типов (int и float), тем не
менее эти типы могут сравниваться друг с другом, поэтому сортировка
к такому списку вполне применима. Но во втором списке содержится
строка, которую не имеет смысла сравнивать с числами, поэтому воз
буждается исключение TypeError. Если необходимо отсортировать спи
сок, содержащий целые числа, числа с плавающей точкой и строки,
представляющие числа, можно попробовать передать в аргументе key
функцию float():
sorted(["1.3", 7.5, "5", 4, "2.4", 1], key=float)
Это выражение вернет список [ –7.3, ' –2.4', 1, '1.3', 4, '5']. Обрати
те внимание, что значения в списке не изменились, то есть строки так
и остались строками. Если какаялибо строка не сможет быть преобра
зована в число (например, "spanner"), будет возбуждено исключение
TypeError.
Копирование коллекций
Поскольку в языке Python повсюду используются ссыл
ки на объекты, когда выполняется оператор присваива
ния (=), никакого копирования данных на самом деле не
происходит. Если справа от оператора находится лите
рал, например, строка или число, в операнд слева запи
сывается ссылка, которая указывает на объект в памя
ти, хранящий значение литерала. Если справа находит
ся ссылка на объект, в левый операнд записывается
ссылка, указывающая на тот же самый объект, на кото
рый ссылается правый операнд. Вследствие этого опера
ция присваивания обладает чрезвычайно высокой ско
ростью выполнения.
Когда выполняется присваивание крупной коллекции, такой как
длинный список, экономия времени становится более чем очевидной.
Например:
>>> songs = ["Because", "Boys", "Carol"]
>>> beatles = songs
Ссылки
на объекты,
стр. 29

174 Глава 3. Типы коллекций
>>> beatles, songs
(['Because', 'Boys', 'Carol'], ['Because', 'Boys', 'Carol'])
Здесь была создана новая ссылка на объект (beatles), и обе ссылки ука
зывают на один и тот же список – никакого копирования данных не
производилось.
Поскольку списки относятся к категории изменяемых объектов, мы
можем вносить в них изменения. Например:
>>> beatles[2] = "Cayenne"
>>> beatles, songs
(['Because', 'Boys', 'Cayenne'], ['Because', 'Boys', 'Cayenne'])
Изменения были внесены с использованием переменной beatles, но это
всего лишь ссылка, указывающая на тот же самый объект, что и ссыл
ка songs. Поэтому любые изменения, произведенные с использованием
одной ссылки, можно наблюдать с использованием другой ссылки.
Часто это именно то, что нам требуется, поскольку копирование круп
ных коллекций может оказаться дорогостоящей операцией. Кроме то
го, это также означает, что имеется возможность передавать списки
или другие изменяемые коллекции в виде аргументов функций, изме
нять эти коллекции в функциях и пребывать в уверенности, что изме
нения будут доступны после того, как функция вернет управление вы
зывающей программе.
Однако в некоторых ситуациях действительно бывает необходимо соз
дать отдельную копию коллекции (то есть создать другой изменяемый
объект). В случае последовательностей, когда выполняется оператор
извлечения среза, например, songs[:2], полученный срез – это всегда
независимая копия элементов. Поэтому скопировать последователь
ность целиком можно следующим способом:
>>> songs = ["Because", "Boys", "Carol"]
>>> beatles = songs[:]
>>> beatles[2] = "Cayenne"
>>> beatles, songs
(['Because', 'Boys', 'Cayenne'], ['Because', 'Boys', 'Carol'])
В случае словарей и множеств копирование можно выполнить с помо
щью методов dict.copy() и set.copy(). Кроме того, в модуле copy имеет
ся функция copy.copy(), которая возвращает копию заданного объек
та. Другой способ копирования встроенных типов коллекций заклю
чается в использовании имени типа как функции, которой в качестве
аргумента передается копируемая коллекция. Например:
copy_of_dict_d = dict(d)
copy_of_list_L = list(L)
copy_of_set_s = set(s)
Обратите внимание, что все эти приемы копирования создают поверх
ностные копии, то есть копируются только ссылки на объекты, но не

Примеры 175
сами объекты. Для неизменяемых типов данных, таких как числа
и строки, это равносильно копированию (за исключением более высо
кой эффективности), но для изменяемых типов данных, таких как
вложенные коллекции, это означает, что ссылки в оригинальной кол
лекции и в копии будут указывать на одни и те же объекты. Эту осо
бенность иллюстрирует следующий пример:
>>> x = [53, 68, ["A", "B", "C"]]
>>> y = x[:] # поверхностное копирование
>>> x, y
([53, 68, ['A', 'B', 'C']], [53, 68, ['A', 'B', 'C']])
>>> y[1] = 40
>>> x[2][0] = 'Q'
>>> x, y
([53, 68, ['Q', 'B', 'C']], [53, 40, ['Q', 'B', 'C']])
Когда выполняется поверхностное копирование списка x, копируется
ссылка на вложенный список ["A", "B", "C"]. Это означает, что третий
элемент в обоих списках, x и y, ссылается на один и тот же список, по
этому любые изменения, произведенные во вложенном списке, можно
наблюдать с помощью любой из ссылок, x или y. Если действительно
необходимо создать абсолютно независимую копию коллекции с про
извольной глубиной вложенности, необходимо выполнить глубокое
копирование:
>>> import copy
>>> x = [53, 68, ["A", "B", "C"]]
>>> y = copy.deepcopy(x)

>>> y[1] = 40
>>> x[2][0] = 'Q'
>>> x, y
([53, 68, ['Q', 'B', 'C']], [53, 40, ['A', 'B', 'C']])
Здесь списки x и y, а также элементы, которые они содержат, полно
стью независимы.
Обратите внимание: с этого момента мы будем использовать термины ко
пия и поверхностная копия как взаимозаменяемые, а когда будет под
разумеваться глубокое копирование, об этом будет упоминаться явно.
Примеры
Мы завершили обзор встроенных типов коллекций в языке Python,
а также двух типов, реализованных в стандартной библиотеке (collec
tions.namedtuple и collections.defaultdict). В языке Python имеется так
же тип коллекций collections.deque, двухсторонней очереди, и многие
другие типы коллекций, реализованные сторонними разработчиками
и доступные в каталоге пакетов Python Package Index, pypi.python.org/
pypi. А сейчас мы рассмотрим пару немного более длинных примеров,

176 Глава 3. Типы коллекций
в которых используется многое из того, о чем рассказывалось в этой
и в предыдущей главах.
Первая программа насчитывает примерно семьдесят строк и имеет от
ношение к обработке текстовой информации. Вторая программа содер
жит примерно девяносто строк и предназначена для выполнения мате
матических вычислений. Обе программы используют словари, спи
ски, именованные кортежи и множества и обе широко используют ме
тод str.format(), который был описан в предыдущей главе.
generate_usernames.py
Представьте, что мы выполняем настройку новой компьютерной сис
темы и нам необходимо сгенерировать имена пользователей для всех
служащих нашей компании. У нас имеется простой текстовый файл
(в кодировке UTF8), где каждая строка представляет собой запись из
полей, разделенных двоеточиями. Каждая запись соответствует одно
му сотруднику компании и содержит следующие поля: уникальный
идентификатор служащего, имя, отчество (это поле может быть пус
тым), фамилию и название отдела. Ниже в качестве примера приво
дятся несколько строк из файла data/users.txt:
1601:Albert:Lukas:Montgomery:Legal
3702:Albert:Lukas:Montgomery:Sales
4730:Nadelle::Landale:Warehousing
Программа должна читать данные из файла, который указан в ко
мандной строке, извлекать отдельные поля из каждой строки (записи)
и возвращать подходящие имена пользователей. Каждое имя пользо
вателя должно быть уникальным и должно создаваться на основе име
ни сотрудника. Результаты должны выводиться на консоль в тексто
вом виде, отсортированными в алфавитном порядке по фамилии и име
ни, например:
Name ID Username
  
Landale, Nadelle................ (4730) nlandale
Montgomery, Albert L............ (1601) almontgo
Montgomery, Albert L............ (3702) almontgo1
Каждая запись имеет точно пять полей, и хотя можно обращаться
к ним по числовым индексам, тем не менее мы будем использовать ос
мысленные имена, чтобы сделать программный код более понятным:
ID, FORENAME, MIDDLENAME, SURNAME, DEPARTMENT = range(5)
В языке Python общепринято использовать только символы верхнего
регистра для идентификаторов, которые будут играть роль констант.
Нам также необходимо создать тип именованного кортежа, где будут
храниться данные о текущем пользователе:

Примеры 177
User = collections.namedtuple("User",
"username forename middlename surname id")
Позднее, когда мы будем рассматривать оставшуюся часть программы,
мы увидим, как используется именованный кортеж User и константы.
Основная логика программы сосредоточена в функции main():
def main():
if len(sys.argv) == 1 or sys.argv[1] in {"h", "help"}:
print("usage: {0} file1 [file2 [... fileN]]".format(
sys.argv[0]))
sys.exit()
usernames = set()
users = {}
for filename in sys.argv[1:]:
for line in open(filename, encoding="utf8"):
line = line.rstrip()
if line:
user = process_line(line, usernames)
users[(user.surname.lower(), user.forename.lower(),
user.id)] = user
print_users(users)
Если пользователь не ввел в командной строке имя какогонибудь
файла или ввел параметр «
–h» или « ––help», то программа просто вы
водит текст сообщения с инструкцией о порядке использования и за
вершает работу.
Из каждой прочитанной строки удаляются любые завершающие про
бельные символы (такие как \n), и обработка строки продолжается,
только если она не пустая. Это означает, что если в данных содержатся
пустые строки, они будут просто проигнорированы.
Все сгенерированные имена пользователей сохраняются в множестве
usernames, чтобы гарантировать отсутствие повторяющихся имен поль
зователей. Сами данные сохраняются в словаре users. Информация
о каждом пользователе сохраняется в виде элемента словаря, ключом
которого является кортеж, содержащий фамилию сотрудника, его имя
и идентификатор, а значением – именованный кортеж типа User. Ис
пользование кортежа, содержащего фамилию сотрудника, его имя
и идентификатор, в качестве ключа обеспечивает возможность вызы
вать функцию sorted() для словаря и получать итерируемый объект,
в котором элементы будут упорядочены в требуемом нам порядке (то
есть фамилия, имя, идентификатор), избежав необходимости создавать
функцию, которую пришлось бы передавать в качестве аргумента key.
def process_line(line, usernames):
fields = line.split(":")
username = generate_username(fields, usernames)
user = User(username, fields[FORENAME], fields[MIDDLENAME],

178 Глава 3. Типы коллекций
fields[SURNAME], fields[ID])
return user
Поскольку все записи имеют очень простой формат, и мы уже удалили
из строки завершающие пробельные символы, извлечь отдельные по
ля можно простой разбивкой строки по двоеточиям. Мы передаем спи
сок полей и множество usernames в функцию generate_username() и затем
создаем экземпляр именованного кортежа User, который возвращается
вызывающей программе (функции main()), которая в свою очередь
вставляет информацию о пользователе в словарь users, готовый для
вывода на экран.
Если бы мы не создали соответствующие константы для хранения ин
дексов, мы могли бы использовать числовые индексы, как показано
ниже:
user = User(username, fields[1], fields[2], fields[3], fields[0])
Хотя такой программный код занимает меньше места, тем не менее
это не самое лучшее решение. Вопервых, человеку, который будет со
провождать такой программный код, непонятно, какое поле какую
информацию содержит, а, вовторых, такой программный код чувст
вителен к изменениям в формате файла с данными – если изменится
порядок или число полей в записи, этот программный код окажется
неработоспособен. При использовании констант в случае изменения
структуры записи нам достаточно будет изменить только значения
констант, и программа сохранит свою работоспособность.
def generate_username(fields, usernames):
username = ((fields[FORENAME][0] + fields[MIDDLENAME][:1] +
fields[SURNAME]).replace("", "").replace("'", ""))
username = original_name = username[:8].lower()
count = 1
while username in usernames:
username = "{0}{1}".format(original_name, count)
count += 1
usernames.add(username)
return username
При первой попытке имя пользователя создается путем конкатенации
первого символа имени, первого символа отчества и фамилии цели
ком, после чего из полученной строки удаляются дефисы и апострофы.
Выражение, извлекающее первый символ отчества, таит в себе одну
хитрость. Если просто использовать обращение fields[MIDDLENAME][0],
то в случае отсутствия отчества будет возбуждено исключение Index
Error. Но при использовании операции извлечения среза мы получаем
либо первый символ отчества, либо пустую строку.
Затем мы переводим все символы полученного имени пользователя
в нижний регистр и ограничиваем его длину восемью символами. Если
имя пользователя уже занято (то есть оно уже присутствует в множе

Примеры 179
стве usernames), предпринимается попытка добавить в конец имени
пользователя символ «1», если это имя пользователя тоже занято, то
гда предпринимается попытка добавить символ «2» и т. д., пока не бу
дет получено незанятое имя пользователя. После этого имя пользова
теля добавляется в множество usernames и возвращается вызывающей
программе.
def print_users(users):
namewidth = 32
usernamewidth = 9
print("{0:<{nw}} {1:^6} {2:{uw}}".format(
"Name", "ID", "Username", nw=namewidth, uw=usernamewidth))
print("{0:<{nw}} {0:<6} {0:<{uw}}".format(
"", nw=namewidth, uw=usernamewidth))
for key in sorted(users):
user = users[key]
initial = ""
if user.middlename:
initial = " " + user.middlename[0]
name = "{0.surname}, {0.forename}{1}".format(user, initial)
print("{0:.<{nw}} ({1.id:4}) {1.username:{uw}}".format(
name, user, nw=namewidth, uw=usernamewidth))
После обработки всех записей вызывается функция print_users(), ко
торой в качестве параметра передается словарь users.
Первая инструкция print() выводит заголовки столбцов.
Вторая инструкция print() выводит дефисы под каждым
из заголовков. В этой второй инструкции метод str.for
mat() используется довольно оригинальным образом.
Для вывода ему определяется строка "", то есть пустая
строка; в результате при выводе пустой строки мы полу
чаем строку из дефисов заданной ширины поля вывода.
Затем мы используем цикл for ... in для вывода информации о каж
дом пользователе, извлекая ключи из отсортированного словаря. Для
удобства мы создаем переменную user, чтобы не вводить каждый раз
users[key] в оставшейся части функции. В цикле сначала вызывается
метод str.format(), чтобы записать в переменную name фамилию со
трудника, имя и необязательный первый символ отчества. Обращение
к элементам в именованном кортеже user производится по их именам.
Собрав строку с именем пользователя, мы выводим информацию
о пользователе, ограничивая ширину каждого столбца (имя сотрудни
ка, идентификатор и имя пользователя) желаемыми значениями.
Полный программный код программы (который несколько отличается
от того, что мы только что рассмотрели, несколькими начальными
строками с комментариями и инструкциями импорта) находится в фай
ле generate_usernames.py. Структура программы – чтение данных из
Метод str.
format() ,
стр. 100

180 Глава 3. Типы коллекций
файла, обработка каждой записи, вывод результата – одна из наиболее
часто встречающихся, и она повторяется в следующем примере.
statistics.py
Предположим, что у нас имеется пакет файлов с данными, содержа
щих числовые результаты некоторой обработки, выполненной нами,
и нам необходимо вычислить некоторые основные статистические ха
рактеристики, которые дадут нам возможность составить общую кар
тину о полученных данных. В каждом файле находится обычный
текст (в кодировке ASCII), с одним или более числами в каждой строке
(разделенными пробельными символами).
Ниже приводится пример информации, которую нам необходимо по
лучить:
count = 183
mean = 130.56
median = 43.00
mode = [5.00, 7.00, 50.00]
std. dev. = 235.01
Здесь видно, что было прочитано 183 числа, из которых наиболее час
то встречаются числа 5, 7 и 50, и со стандартным отклонением по вы
борке 235.01.
Сами статистические характеристики хранятся в именованном корте
же Statistics:
Statistics = collections.namedtuple("Statistics",
"mean mode median std_dev")
Функция main() может служить схематическим отображением струк
туры программы:
def main():
if len(sys.argv) == 1 or sys.argv[1] in {"h", "help"}:
print("usage: {0} file1 [file2 [... fileN]]".format(
sys.argv[0]))
sys.exit()
numbers = []
frequencies = collections.defaultdict(int)
for filename in sys.argv[1:]:
read_data(filename, numbers, frequencies)
if numbers:
statistics = calculate_statistics(numbers, frequencies)
print_results(len(numbers), statistics)
else:
print("no numbers found")
Все числа из всех файлов сохраняются в списке numbers. Для нахожде
ния модальных («наиболее часто встречающихся») значений нам необ

Примеры 181
ходимо знать, сколько раз встречается каждое число, поэтому мы соз
даем словарь со значениями по умолчанию, используя функцию int()
в качестве фабричной функции, где будут накапливаться счетчики.
Затем выполняется обход списка файлов и производится чтение дан
ных из них. В качестве дополнительных аргументов мы передаем функ
ции read_data() список и словарь со значениями по умолчанию, чтобы
она могла обновлять их. После чтения всех данных мы исходим из
предположения, что некоторые числа были благополучно прочитаны,
и вызываем функцию calculate_statistics(). Она возвращает имено
ванный кортеж типа Statistics, который затем используется для вы
вода результатов.
def read_data(filename, numbers, frequencies):
for lino, line in enumerate(open(filename, encoding="ascii"),
start=1):
for x in line.split():
try:
number = float(x)
numbers.append(number)
frequencies[number] += 1
except ValueError as err:
print("{0}:{1}: skipping {2}: {3}".format(
filename, lino, x, err))
Каждая строка разбивается по пробельным символам, после чего про
изводится попытка преобразовать каждый элемент в число типа float.
Если преобразование удалось, следовательно, это либо целое число,
либо число с плавающей точкой в десятичной или в экспоненциальной
форме. Полученное число добавляется в список numbers и выполняется
обновление словаря frequencies со значениями по умолчанию. (Если
бы здесь использовался обычный словарь dict, программный код, вы
полняющий обновление словаря, мог бы выглядеть так: frequenci
es[number] = frequencies.get(number, 0) + 1.) Если преобразование потер
пело неудачу, выводится номер строки (счет строк в текстовых файлах
по традиции начинается с 1), текст, который программа пыталась пре
образовать в число, и сообщение об ошибке, соответствующее исклю
чению ValueError.
def calculate_statistics(numbers, frequencies):
mean = sum(numbers) / len(numbers)
mode = calculate_mode(frequencies, 3)
median = calculate_median(numbers)
std_dev = calculate_std_dev(numbers, mean)
return Statistics(mean, mode, median, std_dev)
Эта функция используется для сбора всех статистических характери
стик воедино. Поскольку среднее значение вычисляется очень просто,
мы делаем это прямо здесь. Вычислением других статистических ха
рактеристик занимаются отдельные функции, и в заключение данная

182 Глава 3. Типы коллекций
функция возвращает экземпляр именованного кортежа Statistics, со
держащий четыре вычисленные статистические характеристики.
def calculate_mode(frequencies, maximum_modes):
highest_frequency = max(frequencies.values())
mode = [number for number, frequency in frequencies.items()
if math.fabs(frequency  highest_frequency) <=
sys.float_info.epsilon]
if not (1 <= len(mode) <= maximum_modes):
mode = None
else:
mode.sort()
return mode
В выборке может существовать сразу несколько значений, встречаю
щихся наиболее часто, поэтому, помимо словаря frequencies, функции
передается максимально допустимое число модальных значений. (Эта
функция вызывается из calculate_statistics(), и при вызове задается
максимальное число модальных значений, равное трем.)
Функция max() используется для поиска наибольшего значения в сло
варе frequencies. Затем с помощью генератора списков создается спи
сок из значений, которые равны наивысшему значению. Поскольку
числа могут быть с плавающей точкой, мы сравниваем абсолютное
значение разницы (используя функцию math.fabs(), поскольку она
лучше подходит для случаев сравнения малых величин, близких к по
рогу точности представления числовых значений в компьютере, чем
abs()) с наименьшим значением, которое может быть представлено
компьютером.
Если число модальных значений равно 0 или больше максимального,
то в качестве модального значения возвращается None; в противном
случае возвращается сортированный список модальных значений.
def calculate_median(numbers):
numbers = sorted(numbers)
middle = len(numbers) // 2
median = numbers[middle]
if len(numbers) % 2 == 0:
median = (median + numbers[middle  1]) / 2
return median
Медиана («среднее значение») – это значение, находящееся в середине
упорядоченной выборки чисел, за исключением случая, когда в вы
борке присутствует четное число чисел, – тогда значение медианы оп
ределяется как среднее арифметическое значение двух чисел, находя
щихся в середине.
Функция вычисления медианы сначала выполняет сортировку чисел
по возрастанию. Затем посредством целочисленного деления опреде
ляется позиция середины выборки, откуда извлекается число и сохра
няется как значение медианы. Если выборка содержит четное число

Примеры 183
значений, то значение медианы определяется как среднее арифмети
ческое двух чисел, находящихся в середине.
def calculate_std_dev(numbers, mean):
total = 0
for number in numbers:
total += ((number  mean) ** 2)
variance = total / (len(numbers)  1)
return math.sqrt(variance)
Стандартное отклонение – это мера дисперсии; оно определяет, как
сильно отклоняются значения в выборке от среднего значения. Вычис
ление стандартного отклонения в этой функции выполняется по фор
муле
,
где x – очередное число,
–x – среднее значение, а n – количество чисел.
def print_results(count, statistics):
real = "9.2f"
if statistics.mode is None:
modeline = ""
elif len(statistics.mode) == 1:
modeline = "mode = {0:{fmt}}\n".format(
statistics.mode[0], fmt=real)
else:
modeline = ("mode = [" +
", ".join(["{0:.2f}".format(m)
for m in statistics.mode]) + "]\n")
print("""\
count = {0:6}
mean = {1.mean:{fmt}}
median = {1.median:{fmt}}
{2}\
std. dev. = {1.std_dev:{fmt}}""".format(
count, statistics, modeline, fmt=real))
Большая часть этой функции связана с форматировани
ем списка модальных значений в строку modeline. Если
модальные значения отсутствуют, то строка modeline во
обще не выводится. Если модальное значение единствен
ное, список модальных значений содержит единствен
ный элемент (mode[0]), который и выводится с той же
строкой форматирования, что используется при выводе
других статистических значений. Если имеется несколь
ко модальных значений, они выводятся как список, в ко
тором каждое значение форматируется отдельно. Дела
ется это с помощью генератора списков, который позвоΣ
xx+ () 2
n–1  =
Метод str.
format() ,
стр. 100

184 Глава 3. Типы коллекций
ляет получить список строк с модальными значениями; строки затем
объединяются в единую строку, где отделяются друг от друга запятой
с пробелом (", "). Последняя инструкция print() в самом конце полу
чилась очень простой благодаря использованию именованного корте
жа. Он позволяет обращаться к статистическим значениям в объекте
statistics, используя не числовые индексы, а их имена, а благодаря
строкам в тройных кавычках мы смогли отформатировать выводимый
текст наглядным способом.
В этой функции имеется одна особенность, о которой следует упомя
нуть отдельно. Строка с модальными значениями выводится с помо
щью элемента строки формата {2}, за которым следует символ обрат
ного слеша. Символ обратного слеша экранирует символ перевода
строки, поэтому если строка с модальными значениями пустая, то пус
тая строка выводиться не будет. Именно по этой причине мы вынужде
ны были добавить символ \n в конец строки modeline, если она не пус
тая.
В заключение
В этой главе мы рассмотрели все встроенные типы коллекций в языке
Python, а также пару типов коллекций из стандартной библиотеки.
Мы рассмотрели коллекциипоследовательности, tuple, collections.na
medtuple и list, поддерживающие, как и строки, возможность извлече
ния срезов. Также было рассмотрено использование оператора распа
ковывания последовательностей (*) и коротко было упомянуто исполь
зование аргументов со звездочками в вызовах функций. Мы также
рассмотрели типы множеств set и frozenset и типы отображений dict
иcollections.defaultdict.
Мы узнали, как использовать именованные кортежи из стандартной
библиотеки языка Python для создания своих собственных типов кор
тежей, доступ к элементам которых выполняется не только с помощью
числовых индексов, но и более удобным способом – с помощью имен.
Мы также увидели, как создавать «константы», используя для этого
переменные, идентификаторы которых состоят исключительно из
символов верхнего регистра.
При изучении списков мы увидели, что все, что применимо к корте
жам, в равной степени применимо и к спискам. А благодаря тому, что
списки относятся к категории изменяемых объектов, они обладают го
раздо более широкими функциональными возможностями, чем корте
жи. В число этих возможностей входят методы, изменяющие содер
жимое списка (например, list.pop()), а поддержка операций со среза
ми обеспечивает возможность вставки, замены и удаления срезов.
Списки идеально подходят для хранения последовательностей элемен
тов, особенно, когда необходим быстрый доступ к элементам по их ин
дексам.

В заключение 185
При обсуждении типов set и frozenset мы отметили, что они могут со
держать только элементы хешируемых типов данных. Множества обес
печивают быструю работу оператора проверки на вхождение и удобны
для фильтрации повторяющихся данных.
Словари отчасти напоминают множества, например, ключами слова
рей могут быть только уникальные значения хешируемых типов дан
ных, как и элементы множеств. Но, в отличие от множеств, словари
хранят пары ключзначение, в которых значениями могут быть дан
ные любых типов. При изучении словарей были охвачены методы
dict.get() и dict.setdefault(), а при описании словарей со значениями
по умолчанию были продемонстрированы альтернативы этим мето
дам. Подобно множествам, словари предоставляют очень эффектив
ный оператор проверки на вхождение и обеспечивают быстрый доступ
к элементам по ключу.
Списки, множества, словари – все они имеют собственные реализации
генераторов, которые могут использоваться для создания коллекций
этих типов из итерируемых объектов (которые в свою очередь также
могут быть генераторами), с наложением дополнительных условий,
если это необходимо. Функции range() и zip() часто используются для
создания коллекций; обе эти функции удобно использовать в циклах
for ... in и в генераторах.
Элементы изменяемых коллекций могут удаляться с помощью соот
ветствующих методов, таких как list.pop() и set.discard(), или с по
мощью инструкции del– например, инструкция del d[k] удалит из
словаря d элемент с ключом k.
В языке Python используются ссылки на объекты, что делает опера
цию присваивания чрезвычайно эффективной, но это также означает,
что при использовании оператора присваивания (=) сами объекты не
копируются. Мы рассмотрели различия между поверхностным и глу
боким копированием, а позднее увидели, как с помощью операции из
влечения среза L[:] можно создать поверхностную копию всего спи
ска, а с помощью метода dict.copy() создать поверхностную копию
словаря. Любой объект, допускающий возможность копирования, мо
жет быть скопирован с помощью функций из модуля copy, например,
функция copy.copy() выполняет поверхностное копирование, а функ
ция copy.deepcopy() выполняет глубокое копирование.
Мы познакомились с высокооптимизированной встроенной функцией
sorted(). Эта функция широко используется при программировании
на языке Python. В языке Python отсутствуют типы упорядоченных
коллекций, поэтому, когда необходимо выполнить итерации через
коллекции в определенном порядке, это можно реализовать с помо
щью функции sorted().
Встроенных типов коллекций – кортежей, списков, множеств, фикси
рованных множеств и словарей – вполне достаточно для решения лю

186 Глава 3. Типы коллекций
бого круга задач. Тем не менее в стандартной библиотеке имеется не
сколько дополнительных типов коллекций и значительное количество
типов, созданных сторонними разработчиками.
Часто возникает необходимость читать коллекции данных из файлов
или записывать содержимое коллекций в файлы. В этой главе, в ходе
очень краткого рассмотрения принципов работы с текстовыми файла
ми, основное наше внимание мы уделили чтению и записи текстовых
строк. Полное описание работы с файлами приводится в главе 7, а до
полнительные средства сохранения данных – в главе 11.
В следующей главе мы поближе познакомимся с управляющими кон
струкциями языка Python, среди которых будет представлена одна
конструкция, с которой мы еще не сталкивались. Кроме того, мы бо
лее подробно изучим тему обработки исключений и некоторые допол
нительные инструкции, такие как assert, с которыми мы еще не зна
комы. Помимо этого, мы рассмотрим порядок создания собственных
функций и в частности изучим чрезвычайно гибкий механизм работы
с аргументами, используемый в языке Python.
Упражнения
1. Модифицируйте программу external_sites.py и задействуйте в ней
словарь со значениями по умолчанию. Это легко сделать, добавив
одну дополнительную инструкцию import и изменив всего две стро
ки. Решение приводится в файле external_sites_ans.py.
2. Модифицируйте программу uniquewords2.py так, чтобы она выво
дила слова не в алфавитном порядке, а по частоте встречаемости.
Вам потребуется обойти элементы словаря и создать маленькую
функцию из двух строк, которая будет извлекать значение каждого
элемента, и передать ее в виде аргумента key функции sorted(). Кро
ме того, потребуется соответствующим образом изменить инструк
цию print(). Это несложно, но тут есть некоторый подвох. Решение
приводится в файле uniquewords_ans.py.
3. Модифицируйте программу generate_usernames.py так, чтобы в ка
ждой строке она выводила информацию о двух пользователях, ог
раничив длину имени 17 символами; через каждые 64 строки про
грамма должна выводить символ перевода формата и в начале каж
дой страницы она должна выводить заголовки столбцов. Ниже при
водится пример того, как должен выглядеть вывод программы:
Name ID Username Name ID Username
     
Aitkin, Shatha... (2370) saitkin Alderson, Nicole. (8429) nalderso
Allison, Karma... (8621) kallison Alwood, Kole E... (2095) kealwood
Annie, Neervana.. (2633) nannie Apperson, Lucyann (7282) leappers

Упражнения 187
Это достаточно сложно. Вам потребуется сохранить заголовки
столбцов в переменных, чтобы потом их можно было использовать
по мере необходимости, и изменить спецификаторы формата, что
бы обеспечить вывод более коротких имен. Один из способов обеспе
чить постраничный вывод заключается в том, чтобы сохранить все
выводимые строки в списке, а затем выполнить обход списка, ис
пользуя оператор извлечения среза с шагом для получения элемен
тов слева и справа и применяя функцию zip() для их объединения.
Решение приводится в файле generate_usernames_ans.py, а доста
точно большой объем исходных данных вы найдете в файле data/
users2.txt.

4
Управляющие структуры и функции
В первых двух разделах этой главы будут рассматриваться управляю
щие структуры языка Python, причем в первом разделе будут рас
сматриваться условные инструкции и циклы, а во втором – инструк
ции обработки исключительных ситуаций. Большая часть управляю
щих структур и основы обработки исключений уже рассматривались
в главе 1, но в этой главе они будут изучены более полно, включая до
полнительный синтаксис управляющих структур, а также порядок
возбуждения исключительных ситуаций и создание собственных ис
ключений.
Третий и самый большой раздел посвящен созданию собственных
функций, и здесь будет подробно рассматриваться чрезвычайно гиб
кий механизм работы с аргументами функций. Собственные функции
позволяют нам упаковывать параметризуемую функциональность
и уменьшать объем программного кода за счет оформления повторяю
щихся фрагментов в виде функций многократного использования.
(В следующей главе мы узнаем, как создавать собственные модули,
чтобы одни и те же функции можно было использовать в разных про
граммах.)
Управляющие структуры
В языке Python условное ветвление реализуется с помощью инструк
ции if, а циклическая обработка – с помощью инструкций while и for
... in. В языке Python имеется также такая конструкция, как услов
ное выражение – вариант инструкции if, аналог трехместного опера
тора (?:), имеющегося в Cподобных языках.
•Управляющие структуры
•Обработка исключений
•Собственные функции

Управляющие структуры 189
Условное ветвление
Как мы видели в главе 1, общий синтаксис инструкции условного
ветвления в языке Python имеет следующий вид:
if boolean_expression1:
suite1
elif boolean_expression2:
suite2
...
elif boolean_expressionN:
suiteN
else:
else_suite
Инструкция может содержать ноль или более предложений elif. За
ключительное предложение else также является необязательным. Ес
ли необходимо предусмотреть ветку для какогото особого случая, ко
торый не требует никакой обработки, в качестве блока кода этой ветки
можно использовать инструкцию pass (она ничего не делает и просто
является инструкциейзаполнителем, используемой там, где должна
находиться хотя бы одна инструкция).
В некоторых случаях можно сократить инструкцию if ... else до
единственного условного выражения. Ниже приводится синтаксис ус
ловных выражений:
expression1 if boolean_expression else expression2
Если логическое выражение boolean_expression возвращает значение
True, результатом всего условного выражения будет результат выраже
ния expression1, в противном случае – результат выражения expression2.
В практике программирования часто применяется такой прием, когда
в переменную сначала записывается значение по умолчанию, а затем
в случае необходимости оно изменяется, например, по требованию
пользователя или в результате выяснения типа платформы, на кото
рой выполняется программа. Ниже приводится типичная реализация
такого приема с использованием инструкции if:
offset = 20
if not sys.platform.startswith("win"):
offset = 10
Переменная sys.platform хранит название текущей платформы, напри
мер, «win32» или «linux2». Тот же результат можно получить с помо
щью условного выражения:
offset = 20 if sys.platform.startswith("win") else 10
В данном случае нет необходимости использовать круглые скобки, но
их использование поможет избежать малозаметных ловушек. Напри
мер, предположим, что нам необходимо записать в переменную width

190 Глава 4. Управляющие структуры и функции
значение 100 и прибавить к нему 10, если переменная margin имеет
значение True. Мы могли бы написать такое выражение:
width = 100 + 10 if margin else 0 # ОШИБКА!
Особенно неприятно, что эта строка программного кода работа
ет правильно, когда переменная margin имеет значение True, за
писывая значение 110 в переменную width. Но когда перемен
ная margin имеет значение False, в переменную width вместо 100
будет записано значение 0. Это происходит потому, что интер
претатор Python воспринимает выражение 100 + 10 как часть
expression1 условного выражения. Решить эту проблему можно
с помощью круглых скобок:
width = 100 + (10 if margin else 0)
Кроме того, круглые скобки делают программный код более понятным
для человека.
Условные выражения могут использоваться для видоизменения сооб
щений, выводимых для пользователя. Например, при выводе числа об
работанных файлов, вместо того чтобы печатать «0 file(s)», «1 file(s)»
1
или чтото подобное, можно было бы использовать пару условных вы
ражений:
print("{0} file{1}".format((count if count != 0 else "no"),
("s" if count != 1 else "")))
Эта инструкция будет выводить «no files», «1 file», «2 files» и т. д., что
придаст программе более профессиональный вид.
Циклы
В языке Python есть две инструкции циклов – while и for ... in, кото
рые имеют более сложный синтаксис, чем было показано в главе 1.
Циклы while
Ниже приводится полный синтаксис цикла while:
while boolean_expression:
while_suite
else:
else_suite
Предложение else является необязательным. До тех пор, пока выра
жение boolean_expression возвращает значение True, в цикле будет вы
полняться блок while_suite. Если выражение boolean_expression вернет
1 Имеется в виду склонение по числам, то есть вместо «1 файл(ов)», «5 фай
л(ов)» можно выводить более правильно: «1 файл», «5 файлов». Но в отли
чие от английского языка реализация правильного склонения по числам
в русском языке не уместится в два условных выражения. – Прим. перев.

Управляющие структуры 191
значение False, цикл завершится, и при наличии предложения else
будет выполнен блок else_suite. Если внутри блока while_suite выпол
няется инструкция continue, то управление немедленно передается
в начало цикла и выражение boolean_expression вычисляется снова.
Если цикл не завершается нормально, блок предложения else не вы
полняется.
Необязательное предложение else имеет несколько сбивающее с толку
название, поскольку оно выполняется во всех в случаях, когда цикл
нормально завершается. Если цикл завершается в результате выпол
нения инструкции break или return, когда цикл находится внутри
функции или метода, или в результате исключения, то блок else_suite
предложения else не выполняется. (При возникновении исключитель
ной ситуации интерпретатор Python пропускает предложение else
и пытается отыскать подходящий обработчик исключения, о чем будет
рассказываться в следующем разделе.) Плюсом такой реализации яв
ляется одинаковое поведение предложения else в циклах while, в цик
лах for ... in и в блоках try ... except.
Рассмотрим пример, демонстрирующий предложение else в действии.
Методы str.index() и list.index() возвращают индекс заданной под
строки или элемента или возбуждают исключение ValueError, если
подстрока или элемент не найдены. Метод str.find() делает то же са
мое, но в случае неудачи он не возбуждает исключение, а возвращает
значение –1. Для списков не существует эквивалентного метода, но
при желании мы могли бы создать такую функцию, использующую
цикл while:
def list_find(lst, target):
index = 0
while index < len(lst):
if lst[index] == target:
break
index += 1
else:
index = 1
return index
Эта функция просматривает список в поисках заданного элемента tar
get. Если искомый элемент будет найден, инструкция break завершит
цикл и вызывающей программе будет возвращен соответствующий
индекс. Если искомый элемент не будет найден, цикл достигнет конца
списка и завершится обычным способом. В случае нормального завер
шения цикла будет выполнен блок в предложении else, индекс полу
чит значение –1 и будет возвращен вызывающей программе.
Циклы for
Подобно циклу while, полный синтаксис цикла for ... in также вклю
чает необязательное предложение else:

192 Глава 4. Управляющие структуры и функции
for expression in iterable:
for_suite
else:
else_suite
В качестве выражения expression обычно используется либо единствен
ная переменная, либо последовательность переменных, как правило,
в форме кортежа. Если в качестве выражения expression используется
кортеж или список, каждый элемент итерируемого объекта iterable
распаковывается в элементы expression.
Если внутри блока for_suite встретится инструкция continue, управле
ние будет немедленно передано в начало цикла и будет начата новая ите
рация. Если цикл завершается по выполнении всех итераций и в цик
ле присутствует предложение else, выполняется блок else_suite. Если
выполнение цикла прерывается принудительно (инструкцией break
или return), управление немедленно передается первой инструкции,
следующей за циклом, а дополнительное предложение else при этом
пропускается. Точно так же, когда возбуждается исключение, интер
претатор Python пропускает предложение else и пытается отыскать
подходящий обработчик исключения (о чем будет рассказываться
в следующем разделе).
Ниже приводится версия функции list_find(), реализо
ванная на базе цикла for ... in, которая так же, как
иверсия на базе цикла while, демонстрирует предложе
ние else в действии:
def list_find(lst, target):
for index, x in enumerate(lst):
if x == target:
break
else:
index = 1
return index
Как видно из этого фрагмента, переменные, созданные в выражении
expression цикла for ... in, продолжают существовать после заверше
ния цикла. Как и любые локальные переменные, они прекращают свое
существование после выхода из области видимости, включающей их.
Обработка исключений
Об ошибках и исключительных ситуациях интерпретатор Python сооб
щает посредством возбуждения исключений, хотя в некоторых биб
лиотеках сторонних разработчиков еще используются устаревшие
приемы, такие как возврат «ошибочного» значения.
Функция enume
rate() ,
стр. 166

Обработка исключений 193
Перехват и возбуждение исключений
Перехватывать исключения можно с помощью блоков try ... except,
которые имеют следующий синтаксис:
try:
try_suite
except exception_group1 as variable1:
except_suite1

except exception_groupN as variableN:
except_suiteN
else:
else_suite
finally:
finally_suite
Эта конструкция должна содержать хотя бы один блок except, а блоки
else и finally являются необязательными. Блок else_suite выполняет
ся, только если блок try_suite завершается обычным способом, и не
выполняется в случае возникновения исключения. Если блок finally
присутствует, он выполняется всегда и в последнюю очередь.
Каждая группа exception_group в предложении except может быть един
ственным исключением или кортежем исключений в круглых скобках.
Часть as variable в каждой группе является необязательной. В случае
ее использования в переменную variable записывается ссылка на ис
ключение, которое возникло, благодаря этому к нему можно будет об
ратиться в блоке except_suite.
Если исключение возникнет во время выполнения блока try_suite, ин
терпретатор поочередно проверит каждое предложение except. Если
будет найдена соответствующая группа exception_group, будет выпол
нен соответствующий блок except_suite. Соответствующей считается
группа, в которой присутствует исключение того же типа, что и воз
никшее исключение, или возникшее исключение является подклас
сом
1 одного из исключений, перечисленных в группе.
Например, если при поиске по словарю возникнет исключение KeyEr
ror, первое предложение except, содержащее класс Exception, будет
считаться соответствующим, так как KeyError является (косвенно) под
классом Exception. Если ни одна из групп не содержит класс Exception
(в чем нет ничего необычного), но имеется группа с классом Lookup
1 Как будет показано в главе 6, в объектноориентированном программиро
вании обычно создаются иерархии классов, то есть один класс (тип дан
ных) наследует другой класс. В языке Python родоначальником любой
иерархии является класс object – все остальные классы прямо или косвен
но наследуют его. Подкласс – это класс, который наследует другой класс,
поэтому все классы в языке Python (за исключением класса object) являют
ся подклассами, так как все они так или иначе наследуют класс object.

194 Глава 4. Управляющие структуры и функции
Error, исключение KeyError будет соответствовать этой группе, потому
что класс KeyError является подклассом класса LookupError. И если нет
группы, в которой присутствовал бы класс Exception или LookupError,
но имеется группа, содержащая класс KeyError, эта группа будет счи
таться соответствующей. На рис.4.1 приводится фрагмент иерархии
классов исключений.
Ниже приводится пример неправильного использования:
try:
x = d[5]
except LookupError: # НЕВЕРНЫЙ ПОРЯДОК
print("Lookup error occurred")
except KeyError:
print("Invalid key used")
Если в словаре d не будет найден элемент с ключом 5, для нас
было бы желательно обработать исключение KeyError, а не бо
лее общее LookupError. Но в данном случае блок except с классом
KeyError никогда не будет выполняться. В случае возникнове
ния исключения KeyError соответствующим будет признан блок
except с классом LookupError, потому что LookupError является
базовым классом для KeyError, то есть класс LookupError нахо
дится выше класса KeyError в иерархии классов исключений.
Поэтому в случае использования нескольких блоков except не
обходимо всегда располагать их сверху вниз в порядке от более
специализированных (расположенных ниже в иерархии) к бо
лее общим (расположенных выше в иерархии).
try:
x = d[k / n]
except Exception: # ПЛОХАЯ ПРАКТИКА
print("Something happened")
object
BaseException
Exception
ArithmeticError EnvironmentError EOFError LookupError ValueError
IOError OSError IndexError KeyError
Рис. 4.1. Фрагмент иерархии классов исключений в языке Python

Обработка исключений 195
Обратите внимание, что обычно не принято указывать класс
Exception в предложении except, так как оно будет соответство
вать любому исключению и легко может скрыть логические
ошибки в программном коде. Возможно, в этом примере пред
полагалось перехватить исключение KeyError, но если n имеет
значение 0, то мы неумышленно перехватим и исключение
ZeroDivisionError.
Имеется также возможность записать предложение в форме
except:, то есть вообще без указания группы исключений. По
добный блок except будет перехватывать любые исключения,
включая те, что наследуют класс BaseException, но не наследую
щие класс Exception (они не показаны на рис. 4.1). Этот вариант
порождает те же проблемы, что и при использовании предло
жения except Exception, и даже еще хуже; такое предложение
никогда не должно использоваться.
Если интерпретатор Python не обнаружит ни одного соответствующего
предложения except, он начнет подъем вверх по стеку вызовов, пыта
ясь отыскать подходящий обработчик исключения. Если такой обра
ботчик не будет найден, программа завершит свою работу с выводом
диагностической информации и сообщения об ошибке.
Если исключение не возникло, будет выполнен необязательный блок
else, если таковой имеется. И в любом случае, то есть независимо от
того, возникло ли исключение или нет, и было ли оно обработано или
интерпретатору предстоит выполнить подъем по стеку вызовов, всегда
выполняется блок finally, если он присутствует. Если исключение не
возникло или было обработано одним из блоков except, блок finally бу
дет выполнен самым последним, но если для возникшего исключения
не было найдено соответствующего блока except, то сначала будет вы
полнен блок finally, и только потом интерпретатор передаст исключе
ние вверх по стеку вызовов. Такое гарантированное выполнение блока
finally может быть очень полезным, когда необходимо обеспечить
корректное освобождение ресурсов. На рис. 4.2 демонстрируется по
рядок выполнения типичной конструкции try ... except ... finally.
Ниже приводится окончательная версия функции list_find(), на этот
раз она использует механизм обработки исключения:
def list_find(lst, target):
try:
index = lst.index(target)
except ValueError:
index = 1
return index
Здесь мы использовали конструкцию try ... except для преобразова
ния исключения в возвращаемое значение. Аналогичный подход мож
но использовать для перехвата одних исключений и возбуждения дру
гих – с этим приемом мы познакомимся очень скоро.

196 Глава 4. Управляющие структуры и функции
Язык Python предоставляет возможность использовать более простую
конструкцию try ... finally, которая в некоторых ситуациях может
быть весьма удобна:
try:
try_suite
finally:
finally_suite
Неважно, что произойдет в блоке try_suite (кроме краха системы или
программы!), в любом случае блок finally_suite будет выполнен. Того
же эффекта, аналогичного использованию конструкции try ... finally,
можно достичь с помощью инструкции with и менеджера контекста
(о которых будет рассказываться в главе 8).
Очень часто конструкция try ... except ... finally используется для об
работки ошибок, возникающих при работе с файлами. Например, про
грамма noblanks.py принимает список имен файлов в виде аргументов
командной строки и для каждого из них воспроизводит другой файл
с тем же самым именем, но с расширением .nb, и с тем же содержи
мым, за исключением пустых строк. Ниже приводится функция read_
data() из этой программы:
def read_data(filename):
lines = []
fh = None
try:
fh = open(filename, encoding="utf8")
for line in fh:
if line.strip():
lines.append(line)
try:
# основные
действия
except
exception:
# обработка
исключения
finally:
# освобождение
ресурсов
# выполнение
продолжается
здесь
Необработанное исключение Обработанное исключение Нормальное выполнение
# исключение
передается вверх
по стеку вызовов
try:
# основные
действия
except
exception:
# обработка
исключения
finally:
# освобождение
ресурсов
# выполнение
продолжается
здесьtry:
# основные
действия
except
exception:
# обработка
исключения
finally:
# освобождение
ресурсов
Рис. 4.2. Порядок выполнения конструкции try … except … finally

Обработка исключений 197
except (IOError, OSError) as err:
print(err)
return []
finally:
if fh is not None:
fh.close()
return lines
Изначально функция записывает в переменную fh значение None, так
как вполне возможно, что вызов функции open() потерпит неудачу, то
гда переменной fh ничего не будет присвоено (и в ней останется значе
ние None) и будет возбуждено исключение. Если возникнет одно из ис
ключений, которые мы определили (IOError или OSError), обработчик
выведет сообщение об ошибке и вернет пустой список. Но, обратите
внимание, что прежде, чем функция действительно вернет управле
ние, будет выполнен блок finally и файл будет закрыт, если перед этим
он был благополучно открыт.
Обратите также внимание, что если возникнет ошибка, связанная
с кодировкой символов, файл все равно будет закрыт, хотя функция не
предусматривает обработку соответствующего исключения (Value
Error). Если это произойдет, интерпретатор сначала выполнит блок
finally, а затем передаст исключение вверх по стеку вызовов, при этом
возвращаемое значение будет отброшено, так как функция завершит
ся в результате необработанного исключения. А так как в данном при
мере нет соответствующего блока except, который обрабатывал бы
ошибки, связанные с кодировкой, программа завершит свою работу
с выводом диагностической информации.
Предложение except в данном примере можно было бы записать более
кратко:
except EnvironmentError as err:
print(err)
return []
Этот прием будет работать, так как EnvironmentError является базовым
классом как для класса IOError, так и для класса OSError.
В главе 8 демонстрируется более компактный способ, га
рантирующий закрытие файлов, не требующий наличия
блока finally.
Возбуждение исключений
Исключения представляют собой удобное средство управления пото
ком выполнения. Мы можем воспользоваться этим, используя либо
встроенные исключения, либо создавая свои собственные и возбуждая
нужные нам, когда это необходимо. Возбудить исключение можно од
ним из двух способов:
Менеджеры
контекста,
стр. 428

198 Глава 4. Управляющие структуры и функции
raise exception(args)
raise
В первом случае, то есть когда явно указывается возбуждаемое исклю
чение, оно должно быть либо встроенным, либо нашим собственным,
наследующим класс Exception. Если исключению в виде аргумента пе
редается некоторый текст, этот текст будет выведен на экран, если ис
ключение не будет обработано программой. Во втором случае, то есть
когда исключение не указывается, инструкция raise повторно возбу
дит текущее активное исключение, а в случае отсутствия активного
исключения будет возбуждено исключение TypeError.
Собственные исключения
Собственные исключения – это наши собственные типы данных (клас
сы). Создание классов будет рассматриваться в главе 6, но поскольку
создать простейший тип собственного исключения не составляет ника
кого труда, мы покажем, как это делается:
class exceptionName(baseException): pass
Базовым классом basException должен быть либо класс Exception, либо
один из его наследников.
Собственные исключения нередко используются, чтобы избежать глу
боко вложенных циклов. Например, допустим, что у нас имеется объ
ект table, хранящий записи (строки), каждая из которых состоит из
полей (столбцов), в каждом из которых может иметься несколько зна
чений (элементов). Тогда поиск определенного значения можно было
бы реализовать примерно так:
found = False
for row, record in enumerate(table):
for column, field in enumerate(record):
for index, item in enumerate(field):
if item == target:
found = True
break
if found:
break
if found:
break
if found:
print("found at ({0}, {1}, {2})".format(row, column, index))
else:
print("not found")
Эти 15 строк программного кода осложняет тот факт, что нам при
шлось предусмотреть прерывание каждого цикла в отдельности. Аль
тернативное решение заключается в использовании нестандартного
исключения:

Обработка исключений 199
class FoundException(Exception): pass
try:
for row, record in enumerate(table):
for column, field in enumerate(record):
for index, item in enumerate(field):
if item == target:
raise FoundException()
except FoundException:
print("found at ({0}, {1}, {2})".format(row, column, index))
else:
print("not found")
Этот прием позволил сократить программный код до десяти строк (или
до 11, если включить определение класса исключения) и придал коду
более удобочитаемый вид. Если искомый элемент будет найден, возбу
ждается наше собственное исключение и выполняется соответствую
щий блок except, при этом блок else не выполняется. Если искомый
элемент не будет найден, исключение не возбуждается и тогда в конце
выполняется блок else.
Рассмотрим еще один пример, демонстрирующий другие способы об
работки исключений. Все фрагменты взяты из программы check
tags.py, которая читает содержимое файлов HTML, имена которых пе
редаются в виде аргументов командной строки, и выполняет некото
рые простые проверки, чтобы убедиться, что все теги начинаются
ссимвола «<» и заканчиваются символом «>» и все сущности оформле
ны правильно. В программе определяются четыре нестандартных ис
ключения:
class InvalidEntityError(Exception): pass
class InvalidNumericEntityError(invalidEntityError): pass
class InvalidAlphaEntityError(invalidEntityError): pass
class InvalidTagContentError(Exception): pass
Второе и третье исключения наследуют первое; для чего это необходи
мо, мы увидим, когда будем обсуждать программный код, использую
щий эти исключение. Функция parse(), использующая эти исключе
ния, содержит более 70 строк программного кода, поэтому мы пока
жем только ту часть функции, которая имеет непосредственное отно
шение к обработке исключений.
fh = None
try:
fh = open(filename, encoding="utf8")
errors = False
for lino, line in enumerate(fh, start=1):
for column, c in enumerate(line, start=1):
try:
Этот фрагмент начинается вполне традиционно, записывая значение
None в переменную, которая впоследствии будет ссылаться на объект

200 Глава 4. Управляющие структуры и функции
файла, и помещая все действия с файлом в блок try. Программа читает
содержимое файла строку за строкой и каждую строку символ за сим
волом.
Примечательно, что здесь имеется два блока try – внешний использу
ется для обработки исключений, которые могут возникнуть при рабо
те с объектом файла, а внутренний – для обработки исключений, воз
никающих в ходе синтаксического анализа.
...
elif state == PARSING_ENTITY:
if c == ";":
if entity.startswith("#"):
if frozenset(entity[1:])  HEXDIGITS:
raise InvalidNumericEntityError()
elif not entity.isalpha():
raise InvalidAlphaEntityError()
...
Функция может находиться в нескольких состояниях, например, по
сле чтения символа амперсанда (&) она входит в состояние PARSING_EN
TITY и запоминает символы, расположенные между амперсандом и точ
кой с запятой (но не включая их), в строке entity.
Часть программного кода, которая показана здесь, обра
батывает случай, когда точка с запятой была обнаруже
на в процессе чтения сущности. Если это числовая сущ
ность (начинается комбинацией символов «&#», за кото
рой следуют шестнадцатеричные цифры и символ «;»,
например: «AC;»), мы преобразуем числовую часть
в множество и исключаем из него все шестнадцатерич
ные цифры – если после этого множество окажется не
пустым, следовательно, в числе был указан как мини
мум один ошибочный символ, и мы возбуждаем собст
венное исключение. Если это текстовая сущность (ком
бинация, начинающаяся с символа «&», за которым сле
дуют алфавитные символы и символ «;», например:
«©»), мы возбуждаем исключение, если будет обна
ружен неалфавитный символ.
...
except (invalidEntityError,
InvalidTagContentError) as err:
if isinstance(err, InvalidNumericEntityError):
error = "invalid numeric entity"
elif isinstance(err, InvalidAlphaEntityError):
error = "invalid alphabetic entity"
elif isinstance(err, InvalidTagContentError):
error = "invalid tag"
print("ERROR {0} in {1} on line {2} column {3}"
Ти п set ,
стр. 144

Обработка исключений 201
.format(error, filename, lino, column))
if skip_on_first_error:
raise
...
Если возникает исключение, связанное с синтаксиче
ским анализом, оно будет перехвачено блоком except.
Используя базовый класс InvalidEntityError, мы пере
хватим оба типа исключений – InvalidNumericEntityError
и InvalidAlphaEntityError. После этого с помощью функ
ции isinstance() проверяется, какое именно исключение
возникло, и определяется соответствующее сообщение
об ошибке. Встроенная функция isinstance() возвращает
True, если первый ее аргумент имеет тот же тип, что
и тип (или один из его базовых типов), переданный во
втором аргументе.
Можно было бы использовать отдельные блоки except для каждого из
трех наших собственных исключений синтаксического анализа, но
в данном случае, объединив обработку в одном блоке, нам удалось из
бежать необходимости повторять четыре последние строки (от инст
рукции print() до инструкции raise) в каждом из них.
Программа имеет два режима работы. Если переменная skip_on_first_
error имеет значение False, программа продолжит проверку файла да
же после обнаружения синтаксической ошибки, что может привести
к выводу множества сообщений об ошибках для каждого файла. Если
переменная skip_on_first_error имеет значение True, то после выявле
ния синтаксической ошибки (одной и только одной) в файле програм
ма выведет сообщение об ошибке и повторно возбудит исключение
синтаксического анализа, которое будет перехвачено внешним блоком
try (где выполняется обработка каждого файла).
...
elif state == PARSING_ENTITY:
raise EOFError("missing ';' at end of " + filename)
...
По завершении синтаксического анализа нам необходимо проверить,
не оказались ли мы в середине сущности. Если это произошло, возбуж
дается встроенное исключение EOFError, сообщающее о встрече конца
файла, которому мы передаем собственный текст сообщения. Точно
так же для этой цели мы могли бы использовать свое собственное ис
ключение.
except (invalidEntityError, InvalidTagContentError):
pass # Уже было обработано
except EOFError as err:
print("ERROR unexpected EOF:", err)
except EnvironmentError as err:
print(err)
Функция isin
stance() ,
стр. 284

202 Глава 4. Управляющие структуры и функции
finally:
if fh is not None:
fh.close()
Во внешнем блоке try мы использовали отдельные блоки except, пото
му что в каждом конкретном случае обработка выполняется поразно
му. Если было получено исключение синтаксического анализа, мы
знаем, что соответствующее сообщение уже было выведено и нам нуж
но лишь прервать работу с эти файлом и перейти к следующему, поэто
му нам ничего не требуется делать в обработчике исключений. Если
было получено исключение EOFError, это может быть результат дейст
вительно преждевременного достижения конца файла либо повторно
го его возбуждения. В любом случае мы выводим сообщение и текст
исключения. Если возникло исключение EnvironmentError (то есть если
возникло исключение IOError или OSError), мы просто выводим сообще
ние исключения. В заключение, независимо от того, что произошло,
если файл оказался открытым, мы закрываем его.
Собственные функции
Функции представляют собой средство, дающее возможность упако
вывать и параметризовать функциональность. В языке Python можно
создать четыре типа функций: глобальные функции, локальные функ
ции, лямбдафункции и методы.
Все функции, которые мы создавали до сих пор, являются глобальны
ми функциями. Глобальные объекты (включая функции) доступны из
любой точки программного кода в том же модуле (то есть в том же са
мом файле .py), которому принадлежит объект. Глобальные объекты
доступны также и из других модулей, как будет показано в следующей
главе.
Локальные функции (их еще называют вложенными функциями) –
это функции, которые объявляются внутри других функций. Эти
функции видимы только внутри тех функций, где они были объявле
ны – они особенно удобны для создания небольших вспомогательных
функций, которые нигде больше не используются. Мы познакомимся
с ними в главе 7.
Лямбдафункции – это выражения, поэтому они могут создаваться не
посредственно в месте их использования; они имеют множество огра
ничений по сравнению с обычными функциями.
Методы – это те же функции, которые ассоциированы с определенным
типом данных и могут использоваться только в связке с этим типом
данных; методы будут представлены в главе 6, когда будут рассматри
ваться вопросы объектноориентированного программирования.
В языке Python имеется множество встроенных функций, а стандарт
ная библиотека и библиотеки сторонних разработчиков добавляют

Собственные функции 203
еще сотни (тысячи, если посчитать еще и методы) поэтому большинст
во функций, которые нам могут потребоваться, уже написаны. По
этой причине всегда стоит обращаться к электронной документации,
чтобы увидеть, какие функции доступны. Смотрите врезку «Электрон
ная документация».
Электронная документация
В этой книге дается полный охват языка Python 3, встроенных
функций и наиболее часто используемых модулей из стандарт
ной библиотеки, тем не менее в электронной документации мож
но найти значительный объем справочной информации о языке
Python и особенно об обширнейшей стандартной библиотеке.
Электронная документация доступна на сайте docs.python.org,
а также поставляется в составе самого интерпретатора Python.
Для операционной системы Windows документация поставляет
ся в формате справочных файлов Windows. Выберите пункт ме
ню Пуск→Все программы→Python 3.x→Python Manuals (Start→All Pro
grams→
Python 3.x→Python Manuals), чтобы запустить средство про
смотра справочных файлов Windows. Этот инструмент обладает
функциями индексирования и поиска, которые упрощают воз
можность поиска по документу. Пользователи операционной
системы UNIX получают документацию в формате HTML. В до
полнение к различным гиперссылкам в ней содержатся различ
ные страницы с предметными указателями. Кроме того, в левой
части каждой страницы присутствует очень удобная функция
«Quick Search».
Наиболее часто начинающими пользователями используется до
кумент «Library Reference», а опытными пользователями – до
кумент «Global Module Index». Оба документа содержат ссылки,
ведущие на страницы с описанием всей стандартной библиотеки
Python, а, кроме того, документ «Library Reference» содержит
ссылки на страницы с описанием всех встроенных функцио
нальных возможностей языка Python.
Определенно имеет смысл ознакомиться с документацией, осо
бенно с документами «Library Reference» и «Global Module In
dex», чтобы получить представление о том, что может предло
жить стандартная библиотека, и пощелкать мышью на темах, ко
торые вас заинтересуют. Это даст вам первое впечатление о том,
что доступно, и поможет запомнить, где можно отыскать доку
ментацию, которая будет представлять для вас интерес. (Крат
кое описание стандартной библиотеки языка Python приводится
в главе 5.)

204 Глава 4. Управляющие структуры и функции
Синтаксис создания функции (глобальной или локальной) имеет сле
дующий вид:
def functionName(parameters):
suite
Параметры parameters являются необязательными и при наличии бо
лее одного параметра записываются как последовательность иденти
фикаторов через запятую или в виде последовательности пар identifi
er=value, о чем вскоре будет говориться подробнее. Например, ниже
приводится функция, которая вычисляет площадь треугольника по
формуле Герона:
def heron(a, b, c):
s = (a + b + c) / 2
return math.sqrt(s * (s  a) * (s  b) * (s  c))
Внутри функции каждый параметр, a, b и c, инициализируется соот
ветствующими значениями, переданными в виде аргументов. При вы
зове функции мы должны указать все аргументы, например, heron(3,
4, 5). Если передать слишком мало или слишком много аргументов,
будет возбуждено исключение TypeError. Производя такой вызов, мы
говорим, что используем позиционные аргументы, потому что каж
дый переданный аргумент становится значением параметра в соответКроме того, в интерпретаторе также имеется справочная систе
ма. Если вызвать встроенную функцию help() без аргументов, вы
попадете в электронную справочную систему – чтобы получить
в ней нужную информацию, просто следуйте инструкциям, а что
бы вернуться в интерпретатор – введите символ «q» или команду
«quit». Если вы знаете, описание какого модуля или типа данных
хотите получить, можно вызвать функцию help(), передав ей имя
модуля или типа в виде аргумента. Например, выполнив инст
рукцию help(str), вы получите информацию о типе данных str,
включая описания всех его методов; инструкция help(dict.up
date) выведет информацию о методе update() типа данных dict;
а инструкция help(os) отобразит информацию о модуле os (если
перед этим он был импортирован).
Если вы уже знакомы с языком Python, то часто бывает доста
точно просто просмотреть, какие атрибуты (например, методы)
имеет тот или иной тип данных. Эту информацию можно полу
чить с помощью функции dir(), например, вызов dir(str) пере
числит все методы строк, а вызов dir(os) перечислит все кон
станты и функции модуля os (опять же при условии, что модуль
был предварительно импортирован).

Собственные функции 205
ствующей позиции. То есть в данном случае при вызове функции пара
метр a получит значение 3, параметр b – значение 4 и параметр с – зна
чение 5.
Все функции в языке Python возвращают какоелибо значение, хотя
вполне возможно (и часто так и делается) просто игнорировать это зна
чение. Возвращаемое значение может быть единственным значением
или кортежем значений, а сами значения могут быть коллекциями,
поэтому практически не существует никаких ограничений на то, что
могут возвращать функции. Мы можем покинуть функцию в любой
момент, используя инструкцию return. Если инструкция return ис
пользуется без аргументов или если мы вообще не используем инст
рукцию return, функция будет возвращать значение None. (В главе 6 мы
рассмотрим инструкцию yield, которая в функциях определенного ти
па может использоваться вместо инструкции return.)
Некоторые функции имеют параметры, для которых может существо
вать вполне разумное значение по умолчанию. Например, ниже при
водится функция, которая подсчитывает количество алфавитных сим
волов в строке; по умолчанию подразумеваются алфавитные символы
из набора ASCII:
def letter_count(text, letters=string.ascii_letters):
letters = frozenset(letters)
count = 0
for char in text:
if char in letters:
count += 1
return count
Здесь при помощи синтаксиса parameter=default было определено зна
чение по умолчанию для параметра letters. Это позволяет вызывать
функцию letter_count() с единственным аргументом, например, let
ter_count("Maggie and Hopey"). В этом случае внутри функции параметр
letter будет содержать строку, которая была задана как значение по
умолчанию. Но за нами сохраняется возможность изменить значение
по умолчанию, например, указав дополнительный позиционный аргу
мент: letter_count("Maggie and Hopey", "aeiouAEIOU"), или используя
именованный аргумент (об именованных аргументах рассказывается
ниже): letter_count("Maggie and Hopey", letters="aeiouAEIOU").
Синтаксис параметров не позволяет указывать параметры, не имею
щие значений по умолчанию, после параметров со значениями по
умолчанию, поэтому такое определение: def bad(a, b=1, c):, будет вы
зывать синтаксическую ошибку. С другой стороны, мы не обязаны пе
редавать аргументы в том порядке, в каком они указаны в определе
нии функции – мы можем использовать именованные аргументы и пе
редавать их в виде name=value.
Ниже демонстрируется короткая функция, возвращающая заданную
строку, если ее длина меньше или равна заданной длине, и усеченную

206 Глава 4. Управляющие структуры и функции
версию строки с добавлением в конец значения параметра indicator –
в противном случае:
def shorten(text, length=25, indicator="..."):
if len(text) > length:
text = text[:length  len(indicator)] + indicator
return text
Вот несколько примеров вызова этой функции:
shorten("The Road") # вернет: 'The Road'
shorten(length=7, text="The Road") # вернет: 'The ...'
shorten("The Road", indicator="&", length=7) # вернет: 'The Ro&'
shorten("The Road", 7, "&") # вернет: 'The Ro&'
Поскольку оба параметра, length и indicator, имеют значение по умол
чанию, любой из них или даже оба сразу могут быть опущены, тогда
будут использоваться значения по умолчанию – этот случай соответст
вует первому вызову. Во втором вызове оба аргумента являются име
нованными, поэтому их можно указывать в любом порядке. В третьем
вызове используются позиционный аргумент и именованные аргумен
ты. Первым указан позиционный аргумент (позиционные аргументы
всегда должны предшествовать именованным аргументам), а за ним
следуют два именованных аргумента. В четвертом вызове все аргумен
ты позиционные.
Различие между обязательным и необязательным пара
метром заключается в наличии значения по умолчанию,
то есть параметр со значением по умолчанию является
необязательным (интерпретатор может использовать
значение по умолчанию), а параметр без значения по
умолчанию является обязательным (интерпретатор не
может делать никаких предположений). Осторожное ис
пользование значений по умолчанию может упростить
программный код и сделать вызовы функций более по
нятными. Вспомните, что функция open() имеет один
обязательный аргумент (имя файла) и шесть необяза
тельных аргументов. Используя смесь из позиционных
и именованных аргументов, мы можем указывать толь
ко необходимые аргументы, опуская другие. Это дает
нам возможность записать такой вызов: open(filename,
encoding="utf8"), вместо того чтобы указывать все аргу
менты, например: open(filename, "r", None, "utf8", None,
None, True). Еще одно преимущество использования име
нованных аргументов состоит в том, что они способны
сделать вызов функции более удобочитаемым, особенно
в случае использования логических аргументов.
Значения по умолчанию создаются на этапе выполнения инст
рукции def (то есть в момент создания функции), а не в момент
Врезка «Чте
ние и запись
текстовых
файлов»,
стр. 157

Собственные функции 207
ее вызова. Для неизменяемых аргументов, таких как строки или чис
ла, это не имеет никакого значения, но в использовании изменяемых
аргументов кроется труднозаметная ловушка.
def append_if_even(x, lst=[]): # ОШИБКА!
if x % 2 == 0:
lst.append(x)
return lst
В момент создания этой функции параметр lst ссылается на новый
список. Всякий раз, когда эта функция вызывается с одним первым
параметром, параметр lst будет ссылаться на список, созданный как
значение по умолчанию вместе с функцией – то есть при каждом таком
вызове новый список создаваться не будет. Как правило, это не совсем
то, что нам хотелось бы – мы ожидаем, что каждый раз, когда функ
ция вызывается без второго аргумента, будет создаваться новый пус
той список. Ниже приводится новая версия функции, на этот раз ис
пользующая правильный подход к работе с изменяемыми аргумента
ми, имеющими значения по умолчанию:
def append_if_even(x, lst=None):
if lst is None:
lst = []
if x % 2 == 0:
lst.append(x)
return lst
Здесь, всякий раз, когда функция вызывается без второго аргумента,
мы создаем новый список. А если аргумент lst определен, использует
ся он, как и в предыдущей версии функции. Такой прием, основанный
на использовании значения по умолчанию None и создании нового объ
екта, должен применяться к словарям, спискам, множествам и любым
другим изменяемым типам данных, которые предполагается исполь
зовать в виде аргументов со значениями по умолчанию. Ниже приво
дится немного более короткая версия функции, которая обладает тем
же поведением:
def append_if_even(x, lst=None):
lst = [] if lst is None else lst
if x % 2 == 0:
lst.append(x)
return lst
Использование условного выражения позволяет сократить размер
функции на одну строку для каждого параметра, имеющего изменяе
мое значение по умолчанию.
Имена и строки документирования
Использование осмысленных имен для функций и их параметров помо
гает понимать назначение функции другим программистам, а также,

208 Глава 4. Управляющие структуры и функции
спустя некоторое время после создания функции, и самому автору
функции. Ниже приводятся несколько основных правил, которых мы
рекомендуем придерживаться.
•Используйте единую схему именования и придерживайтесь ее не
уклонно. В этой книге имена ИМЕНА
КОНСТАНТ записываются символа
ми в верхнем регистре; имена
Классов (и исключений) записываются
символами верхнего и нижнего регистра, причем каждое слово
в имени начинается с символа верхнего регистра; похожим образом
записываются имена
Функций и методов графического интерфейса, за
исключением первого символа, который всегда записывается в ниж
нем регистре; а все остальные имена записываются только символа
ми нижнего регистра или символами_нижнего_регистра_с_символом_под
черкивания.
•Избегайте использовать аббревиатуры в любых именах, если эти аб
бревиатуры не являются стандартными и не получили широкого
распространения.
•Соблюдайте разумный подход при выборе имен для переменных
и параметров: имя x прекрасно подходит для координаты x, а имя i
отлично подходит на роль переменной цикла, но вообще имена
должны быть достаточно длинными и описательными. Имя должно
описывать скорее назначение элемента данных, чем его тип (напри
мер, имя amount_due предпочтительнее, чем имя money), если только
имя не является универсальным для конкретного типа данных, на
пример, имя параметра text в функции shorten() (стр. 209).
•Имена функций и методов должны говорить о том, что они делают
или что они возвращают (в зависимости от их назначения), и нико
гда – как они это делают, потому что эта характеристика может из
мениться со временем.
Ниже приводятся несколько примеров имен:
def find(l, s, i=0): # НЕУДАЧНЫЙ ВЫБОР
def linear_search(l, s, i=0): # НЕУДАЧНЫЙ ВЫБОР
def first_index_of(sorted_name_list, name, start=0): # ХОРОШИЙ ВЫБОР
Все три функции возвращают индекс первого вхождения имени в спи
ске имен, причем поиск в списке начинается с указанного индекса
и используется алгоритм поиска, который предполагает, что список
уже отсортирован.
Первый случай приходится признать неудачным, потому что имя
функции ничего не говорит о том, что будут искать, а имена ее пара
метров (по всей видимости) указывают на их типы (list, str, int), но
ничего не говорят об их назначении. Второй вариант также следует
признать неудачным, потому что имя функции описывает алгоритм,
использованный первоначально, но с течением времени алгоритм мо
гут изменить. Это может быть неважно для того, кто будет пользовать
ся функцией, но может вводить в заблуждение тех, кто будет сопрово

Собственные функции 209
ждать программный код, если имя функции предполагает реализацию
алгоритма линейного поиска, а в действительности со временем функ
цию могли переписать под использование алгоритма поиска методом
дихотомии. Третий вариант можно назвать удачным, потому что имя
функции говорит о том, что она возвращает, а имена параметров не
двусмысленно показывают, что ожидает получить функция.
Ни одна из функций не имеет возможности указать, что произойдет,
если поиск завершится неудачей – вернут ли они, скажем, значение
–1, или возбудят исключение? В какомто виде такая информация
должна быть включена в описание, предоставляемое пользователям
функции.
Мы можем добавить описание к любой функции, используя строки до
кументирования – это обычные строки, которые следуют сразу за
строкой с инструкцией def и перед программным кодом функции. На
пример, ниже приводится функция shorten(), которую мы уже видели
ранее, но на этот раз приводится полный ее текст:
def shorten(text, length=25, indicator="..."):
"""Возвращает text или усеченную его копию с добавлением
indicator в конце
text – любая строка; length – максимальная длина возвращаемой
строки string (включая indicator); indicator – строка,
добавляемая в конец результата, чтобы показать,
что текст аргумента text был усечен
>>> shorten("The Road")
'The Road'
>>> shorten("No Country for Old Men", 20)
'No Country for Ol...'
>>> shorten("Cities of the Plain", 15, "*")
'Cities of the *'
"""
if len(text) > length:
text = text[:length  len(indicator)] + indicator
return text
Нет ничего необычного в том, что текст описания длиннее самой функ
ции. В соответствии с общепринятыми соглашениями первая строка
в описании должна представлять собой краткое, однострочное описа
ние функции, затем следует пустая строка и далее следует полное опи
сание функции, в конце которого приводится несколько примеров то
го, как может выглядеть использование функции в интерактивной
оболочке. В главе 5 мы узнаем, как примеры, присутствующие в опи
сании функции, могут использоваться для нужд модульного тестиро
вания.

210 Глава 4. Управляющие структуры и функции
Распаковывание аргументов и параметров
В предыдущей главе мы видели, что для передачи пози
ционных аргументов можно использовать оператор рас
паковывания последовательностей (*). Например, если
возникает необходимость вычислить площадь треуголь
ника, а длины всех его сторон хранятся в списке, то мы
могли бы вызвать функцию так: heron(sides[0], sides[1],
sides[2]), или просто распаковать список и сделать вызов
намного проще: heron(*sides). Если элементов в списке
(или в другой последовательности) больше, чем парамет
ров в функции, мы можем воспользоваться операцией из
влечения среза, чтобы извлечь нужное число аргументов.
Мы можем также использовать оператор распаковывания последова
тельности в списке параметров функции. Это удобно, когда необходи
мо создать функцию, которая может принимать переменное число по
зиционных аргументов. Ниже приводится функция product(), которая
вычисляет произведение своих аргументов:
def product(*args):
result = 1
for arg in args:
result *= arg
return result
Эта функция имеет единственный аргумент с именем args. Наличие
символа * перед ним означает, что внутри функции параметр args об
ретает форму кортежа, значениями элементов которого будут значе
ния переданных аргументов. Ниже приводятся несколько примеров
вызова функции:
product(1, 2, 3, 4) # args == (1, 2, 3, 4); вернет: 24
product(5, 3, 8) # args == (5, 3, 8); вернет: 120
product(11) # args == (11,); вернет: 11
Мы можем использовать именованные аргументы вслед за позицион
ными, как в функции, которая приводится ниже, вычисляющей сумму
своих аргументов, каждый из которых возводится в заданную степень:
def sum_of_powers(*args, power=1):
result = 0
for arg in args:
result += arg ** power
return result
Эта функция может вызываться только с позиционными аргументами,
например: sum_of_powers(1, 3, 5), или как с позиционными, так и с име
нованным аргументами, например: sum_of_powers(1, 3, 5, power=2).
Допускается также использовать символ «*» в качестве самостоятель
ного «параметра». В данном случае он указывает, что после символа
Распаковыва
ние последо
вательностей,
стр. 137

Собственные функции 211
«*» не может быть других позиционных параметров, однако указание
именованных аргументов допускается. Ниже приводится модифици
рованная версия функции heron(). На этот раз функция принимает
точно три позиционных аргумента и один необязательный именован
ный аргумент.
def heron2(a, b, c, *, units="meters"):
s = (a + b + c) / 2
area = math.sqrt(s * (s  a) * (s  b) * (s  c))
return "{0} {1}".format(area, units)
Ниже приводятся несколько примеров вызовов функции:
heron2(25, 24, 7) # вернет: '84.0 meters'
heron2(41, 9, 40, units="inches") # вернет: '180.0 inches'
heron2(25, 24, 7, "inches") # ОШИБКА! Возбудит исключение TypeError
В третьем вызове мы попытались передать четыре позиционных аргу
мента, но оператор * не позволяет этого и вызывает исключение Type
Error.
Поместив оператор * первым в списке параметров, мы тем самым пол
ностью запретим использование любых позиционных аргументов и вы
нудим тех, кто будет вызывать ее, использовать именованные аргу
менты. Ниже приводится пример сигнатуры такой (вымышленной)
функции:
def print_setup(*, paper="Letter", copies=1, color=False):
Мы можем вызывать функцию print_setup() без аргументов, допуская
использование значений по умолчанию. Или изменить некоторые или
все значения по умолчанию, например: print_setup(paper="A4", color=
True). Но если мы попытаемся использовать позиционные аргументы,
например: print_setup("A4"), будет возбуждено исключение TypeError.
Так же, как мы распаковываем последовательности для заполнения
позиционных параметров, можно распаковывать и отображения –
с помощью оператора распаковывания отображений (**).
1 Мы можем
использовать оператор **, чтобы передать содержимое словаря в функ
цию print_setup(). Например:
options = dict(paper="A4", color=True)
print_setup(**options)
В данном случае пары «ключзначение» словаря options будут распа
кованы, и каждое значение будет ассоциировано с параметром, чье
имя соответствует ключу этого значения. Если в словаре обнаружится
ключ, не совпадающий ни с одним именем параметра, будет возбужде
но исключение TypeError. Любые аргументы, для которых в словаре не
1 Как мы уже видели в главе 2, когда ** используется в качестве двухместно
го оператора, он является аналогом функции pow().

212 Глава 4. Управляющие структуры и функции
найдется соответствующего элемента, получат значение по умолча
нию, но если такие аргументы не имеют значения по умолчанию, бу
дет возбуждено исключение TypeError.
Кроме того, имеется возможность использовать оператор распаковы
вания вместе с параметрами в объявлении функции. Это позволяет
создавать функции, способные принимать любое число именованных
аргументов. Ниже приводится функция add_person_details(), которая
принимает номер карточки социального страхования и фамилию в ви
де позиционных аргументов, а также произвольное число именован
ных аргументов:
def add_person_details(ssn, surname, **kwargs):
print("SSN =", ssn)
print(" surname =", surname)
for key in sorted(kwargs):
print(" {0} = {1}".format(key, kwargs[key]))
Функция print()
Функция print() может принимать произвольное число позици
онных аргументов и имеет три именованных аргумента: sep, end
и file. Все именованные аргументы имеют значение по умолча
нию. В качестве значения по умолчанию для параметра sep ис
пользуется пробел – если функции передано два или более пози
ционных аргументов, при выводе они отделяются друг от друга
значением sep, но если функция получит единственный позици
онный аргумент, этот параметр в выводе не участвует. В качест
ве значения по умолчанию для параметра end используется сим
вол \n, именно по этой причине функция print() завершает вы
вод своих аргументов переводом строки. В качестве значения по
умолчанию для параметра file используется sys.stdout, поток
стандартного вывода, который обычно представляет консоль.
Имеется возможность переопределять значение любого именован
ного аргумента, если значения по умолчанию чемто не устраива
ют. Например, в аргументе file можно передать объект файла, от
крытый на запись или на дополнение в конец, а в аргументах sep
и end можно передавать любые строки, включая пустые.
Когда необходимо вывести несколько элементов в одной и той
же строке, обычно применяется прием, когда функция print()
вызывается с аргументом end, в качестве значения которого ис
пользуется требуемый разделитель, а в самом конце вызывается
функция print() без аргументов, только для того, чтобы вывести
символ перевода строки. Например, смотрите функцию print_di
gits() (стр. 213).

Собственные функции 213
Эта функция может вызываться как только с двумя позиционными ар
гументами, так и с дополнительной информацией, например: add_per
son_details(83272171, "Luther", forename="Lexis", age=47). Такая возмож
ность обеспечивает огромную гибкость. Конечно, мы можем также од
новременно принимать переменное число позиционных аргументов
и переменное число именованных аргументов:
def print_args(*args, **kwargs):
for i, arg in enumerate(args):
print("positional argument {0} = {1}".format(i, arg))
for key in kwargs:
print("keyword argument {0} = {1}".format(key, kwargs[key]))
Эта функция просто выводит полученные аргументы. Она может вы
зываться вообще без аргументов или с произвольным числом позици
онных и именованных аргументов.
Доступ к переменным в глобальной области видимости
Иногда бывает удобно иметь несколько глобальных переменных, дос
тупных из разных функций программы. В этом нет ничего плохого, ес
ли речь идет о «константах», но в случае переменных – это не самый
лучший выход, хотя для коротких одноразовых программ это в неко
торых случаях можно считать допустимым.
Программа digit_names.py принимает необязательный код языка
(«en» или «fr») и число в виде аргументов командной строки и выво
дит названия всех цифр заданного числа. То есть если в командной
строке программе было передано число «123», она выведет «one two
three». В программе имеется три глобальные переменные:
Language = "en"
ENGLISH = {0: "zero", 1: "one", 2: "two", 3: "three", 4: "four",
5: "five", 6: "six", 7: "seven", 8: "eight", 9: "nine"}
FRENCH = {0: "zйro", 1: "un", 2: "deux", 3: "trois", 4: "quatre",
5: "cinq", 6: "six", 7: "sept", 8: "huit", 9: "neuf"}
Мы следуем соглашению, в соответствии с которым имена перемен
ных, играющих роль констант, записываются только символами верх
него регистра, и установили английский язык по умолчанию. (В языке
Python отсутствует прямой способ создания констант, вместо этого он
полностью полагается на то, что программист будет неуклонно следо
вать общепринятым соглашениям.) В некотором другом месте про
граммы выполняется обращение к переменной Language, и ее значение
используется при выборе соответствующего словаря:
def print_digits(digits):
dictionary = ENGLISH if Language == "en" else FRENCH
for digit in digits:
print(dictionary[int(digit)], end=" ")
print()

214 Глава 4. Управляющие структуры и функции
Когда интерпретатор Python встречает имя переменной Language внут
ри функции, он пытается отыскать его в локальной области видимости
(в области видимости функции) и не находит. Поэтому он продолжает
поиск в глобальной области видимости (в области видимости файла
.py), где и обнаруживает его. Назначение именованного аргумента end,
используемого в первом вызове функции print(), описывается во врез
ке «Функция print()».
Ниже приводится содержимое функции main() программы. Она изме
няет значение переменной Language в случае необходимости и вызывает
функцию print_digits() для вывода результата.
def main():
if len(sys.argv) == 1 or sys.argv[1] in {"h", "help"}:
print("usage: {0} [en|fr] number".format(sys.argv[0]))
sys.exit()
args = sys.argv[1:]
if args[0] in {"en", "fr"}:
global Language
Language = args.pop(0)
print_digits(args.pop(0))
Обратите внимание на использование инструкции global в этой функ
ции. Эта инструкция используется для того, чтобы сообщить интер
претатору, что данная переменная существует в глобальной области
видимости (в области видимости файла .py) и что операция присваива
ния должна применяться к глобальной переменной; без этой инструк
ции операция присваивания создаст локальную переменную с тем же
именем.
Если не использовать инструкцию global, программа сохранит
свою работоспособность, но когда интерпретатор встретит пере
менную Language в условной инструкции if, он попытается оты
скать ее в локальной области видимости (в области видимости
функции) и, не обнаружив ее, создаст новую локальную пере
менную с именем Language, оставив глобальную переменную
Language без изменений. Эта малозаметная ошибка будет прояв
ляться только в случае запуска программы с аргументом «fr»,
потому что в этом случае будет создана новая локальная пере
менная Language, в которую будет записано значение «fr», а гло
бальная переменная Language, которая используется функцией
print_digits(), попрежнему будет иметь значение «en».
В сложных программах лучше вообще не использовать глобальные пе
ременные, за исключением констант, которые не требуют употребле
ния инструкции global.

Собственные функции 215
Лямбдафункции
Лямбдафункции – это функции, для создания которых используется
следующий синтаксис:
lambda parameters: expression
Часть parameters является необязательной, а если она
присутствует, то обычно представляет собой простой
список имен переменных, разделенных запятыми, то
есть позиционных аргументов, хотя при необходимости
допускается использовать полный синтаксис определе
ния аргументов, используемый в инструкции def. Выра
жение expression не может содержать условных инструк
ций или циклов (хотя условные выражения являются
допустимыми), а также не может содержать инструкцию
return (или yield). Результатом лямбдавыражения явля
ется анонимная функция. Когда вызывается лямбда
функция, она возвращает результат вычисления выра
жения expression. Если выражение expression представ
ляет собой кортеж, оно должно быть заключено в круг
лые скобки.
Ниже приводится пример простой лямбдафункции, которая добавля
ет (или не добавляет) суффикс «s» в зависимости от того, имеет ли ар
гумент значение 1:
s = lambda x: "" if x == 1 else "s"
Лямбдавыражение возвращает анонимную функцию, которая при
сваивается переменной s. Любая (вызываемая) переменная может вы
зываться как функция при помощи круглых скобок, поэтому после
выполнения некоторой операции можно при помощи функции s() вы
вести сообщение с числом обработанных файлов, например: print("{0}
file{1} processed".format(count, s(count)).
Лямбдафункции часто используются в виде аргумента key встроенной
функции sorted() или метода list.sort(). Предположим, что имеется
список, элементами которого являются трехэлементные кортежи (но
мер группы, порядковый номер, название), и нам необходимо отсорти
ровать этот список различными способами. Ниже приводится пример
такого списка:
elements = [(2, 12, "Mg"), (1, 11, "Na"), (1, 3, "Li"), (2, 4, "Be")]
Отсортировав список, мы получим следующий результат:
[(1, 3, 'Li'), (1, 11, 'Na'), (2, 4, 'Be'), (2, 12, 'Mg')]
Ранее, когда мы рассматривали функцию sorted(), то ви
дели, что имеется возможность изменить порядок сорти
ровки, если в аргументе key передать требуемую функ
Функции
генераторы,
стр. 324
Функция sorted() ,
стр. 164, 170

216 Глава 4. Управляющие структуры и функции
цию. Например, если необходимо отсортировать список не по естест
венному порядку: номер группы, порядковый номер и название, а по
порядковому номеру и названию, то мы могли бы написать маленькую
функцию def ignore0(e): return e[1], e[2] и передавать ее в аргументе
key. Но создавать в программе массу крошечных функций, подобных
этой, очень неудобно, поэтому часто используется альтернативный
подход, основанный на применении лямбдафункций:
elements.sort(key=lambda e: (e[1], e[2]))
Здесь в качестве значения аргумента key используется выражение
lambda e: (e[1], e[2]), которому в виде аргумента e последовательно
передаются все трехэлементные кортежи из списка. Круглые скобки,
окружающие лямбдавыражение, обязательны, когда выражение яв
ляется кортежем и лямбдафункция создается как аргумент другой
функции. Для достижения того же эффекта можно было бы использо
вать операцию извлечения среза:
elements.sort(key=lambda e: e[1:3])
Немного более сложная версия обеспечивает возможность сортировки
по названию, без учета регистра символов, и порядковому номеру:
elements.sort(key=lambda e: (e[2].lower(), e[1]))
Ниже приводятся два эквивалентных способа создания функции, вы
числяющей площадь треугольника по известной формуле
× основание × высота:
def area(b, h):
area = lambda b, h: 0.5 * b * h return 0.5 * b * h
Мы можем вызвать функцию area(6, 5) независимо от того, была ли
она создана как лямбдафункция или с помощью инструкции def, и ре
зультат будет один и тот же.
Другая замечательная область применения лямбдафунк
ций – создание словарей со значениями по умолчанию.
В предыдущей главе говорилось, что при обращении к та
кому словарю с несуществующим ключом будет создан
соответствующий элемент с указанным ключом и со зна
чением по умолчанию. Ниже приводятся несколько при
меров создания таких словарей:
minus_one_dict = collections.defaultdict(lambda: 1)
point_zero_dict = collections.defaultdict(lambda: (0, 0))
message_dict = collections.defaultdict(lambda: "No message available")
При обращении к словарю minus_one_dict с несуществующим ключом
будет создан новый элемент с указанным ключом и со значением –1.
Точно так же при обращении к словарю point_zero_dict вновь создан
ный элемент получит в качестве значения кортеж (0, 0), а при обра
1
2 ---
Словари со
значениями
по умолча
нию, стр. 161

Собственные функции 217
щении к словарю message_dict значением по умолчанию будет строка
«No message available».
Утверждения
Что произойдет, если функция получит аргументы, имеющие ошибоч
ные значения? Что случится, если в реализации алгоритма будет допу
щена ошибка и вычисления будут выполнены неправильно? Самое не
приятное, что может произойти, – это то, что программа будет выпол
няться без какихлибо видимых проблем, но будет давать неверные ре
зультаты. Один из способов избежать таких коварных проблем
состоит в том, чтобы писать тесты, о которых кратко будет рассказано
в главе 5. Другой способ состоит в том, чтобы определить предвари
тельные условия и ожидаемый конечный результат, и сообщать об
ошибке, если они не соответствуют друг другу. В идеале следует ис
пользовать как тестирование, так и метод на основе сравнения предва
рительных условий и ожидаемых результатов.
Предварительные условия и ожидаемый результат можно задать с по
мощью инструкции assert, которая имеет следующий синтаксис:
assert boolean_expression, optional_expression
Если выражение boolean_expression возвращает значение False, возбу
ждается исключение AssertionError. Если задано необязательное выра
жение optional_expression, оно будет использовано в качестве аргумен
та исключения AssertionError, что удобно для передачи сообщений об
ошибках. Однако следует отметить, что утверждения предназначены
для использования разработчиками, а не конечными пользователями.
Проблемы, возникающие в процессе нормальной эксплуатации про
граммы, такие как отсутствующие файлы или ошибочные аргументы
командной строки, должны обрабатываться другими средствами, на
пример, посредством вывода сообщений об ошибках или записи сооб
щений в файл журнала.
Ниже приводятся две версии функции product(). Обе версии эквива
лентны в том смысле, что обе они требуют, чтобы все передаваемые
им аргументы имели ненулевое значение, а вызов с нулевыми значе
ниями рассматривается как ошибка программиста.
def product(*args): # пессимистичная def product(*args): # оптимистичная
assert all(args), "0 argument" result = 1
result = 1 for arg in args:
for arg in args: result *= arg
result *= arg assert result, "0 argument"
return result return result
«Пессимистичная» версия, слева, проверяет все аргументы (точнее –
до первого нулевого значения) при каждом вызове. «Оптимистичная»
версия, справа, проверяет результат – если хотя бы один аргумент
имеет нулевое значение, то и результат будет равен 0.

218 Глава 4. Управляющие структуры и функции
Если любую из этих версий вызвать со значением 0 в одном из аргу
ментов, будет возбуждено исключение AssertionError и в поток стан
дартного вывода сообщений об ошибках (sys.stderr – обычно консоль)
будет выведено следующее:
Traceback (most recent call last):
File "program.py", line 456, in
x = product(1, 2, 0, 4, 8)
File "program.py", line 452, in product
assert result, "0 argument"
AssertionError: 0 argument
Интерпретатор автоматически выведет диагностическую информацию
с именем файла, именем функции и номером строки, а также текст со
общения, указанного нами.
Но как быть с инструкциями assert, после того как программа будет
готова к выпуску в виде окончательной версии (при этом она, безус
ловно, успешно проходит все тесты и не нарушает ни одного утвержде
ния)? Мы можем сообщить интерпретатору о том, что больше не требу
ется выполнять инструкции assert, то есть их нужно отбрасывать во
время выполнения программы. Для этого программа должна запус
каться с ключом командной строки
–O, например python –O program.py.
Другой способ добиться этого состоит в том, чтобы установить пере
менную окружения PYTHONOPTIMIZE в значение O.
1 Если наши пользова
тели не пользуются строками документирования (обычно им этого и не
требуется), мы можем использовать ключ
–OO, который эффективно
удаляет как инструкции assert, так и строки документирования: обра
тите внимание, что для установки такого поведения нет переменной
окружения. Некоторые разработчики используют упрощенный под
ход: они создают копии программ, где все инструкции assert заком
ментированы, и в случае прохождения всех тестов они выпускают вер
сию программы без инструкций assert.
Пример: make_html_skeleton.py
В этом разделе мы объединим некоторые приемы, описанные в этой
главе, и продемонстрируем их в контексте законченной программы.
Очень маленькие вебсайты часто создаются и обслуживаются вруч
ную. Один из способов облегчить эту работу состоит в том, чтобы напи
сать программу, которая будет генерировать заготовки файлов HTML,
которые позднее будут наполняться содержимым. Программа make_
html_skeleton.py выполняется в интерактивном режиме, она запраши
вает у пользователя различные сведения и затем создает заготовку
файла HTML. Функция main() содержит цикл, позволяющий созда
вать одну заготовку за другой, и сохраняет общую информацию (на
1 Это буква «O», а не цифра 0. – Прим. перев.

Пример: make_html_skeleton.py 219
пример, информацию об авторских правах), что избавляет пользовате
лей от необходимости вводить ее снова и снова. Ниже приводится при
мер типичного сеанса работы с программой:
make_html_skeleton.py
Make HTML Skeleton
Enter your name (for copyright): Harold Pinter
Enter copyright year [2008]: 2009
Enter filename: careersynopsis
Enter title: Career Synopsis
Enter description (optional): synopsis of the career of Harold Pinter
Enter a keyword (optional): playwright
Enter a keyword (optional): actor
Enter a keyword (optional): activist
Enter a keyword (optional):
Enter the stylesheet filename (optional): style
Saved skeleton careersynopsis.html
Create another (y/n)? [y]:
Make HTML Skeleton
Enter your name (for copyright) [Harold Pinter]:
Enter copyright year [2009]:
Enter filename:
Cancelled
Create another (y/n)? [y]: n
Обратите внимание, что при создании второй заготовки имя и год по
лучили значения по умолчанию, введенные ранее, поэтому пользова
телю не пришлось вводить их вторично. Но для имени файла значение
по умолчанию отсутствует, поэтому, когда имя файла не было указа
но, процедура создания заготовки была прервана.
Теперь, когда мы увидели, как пользоваться программой, мы готовы
приступить к изучению программного кода. Программа начинается
двумя инструкциями импорта:
import datetime
import xml.sax.saxutils
Модуль datetime предоставляет ряд простых функций для создания
объектов datetime.date и datetime.time. Модуль xml.sax.saxutils содер
жит удобную функцию xml.sax.saxutils.escape(), которая принимает
строку и возвращает эквивалентную ей строку, в которой специаль
ные символы языка разметки HTML («&», «<» и «>») замещаются их
эквивалентами («&», «<» и «>»).
Далее определяются три глобальные строки, которые используются
в качестве шаблонов.

220 Глава 4. Управляющие структуры и функции
COPYRIGHT_TEMPLATE = "Copyright (c) {0} {1}. All rights reserved."
STYLESHEET_TEMPLATE = (' 'media="all" href="{0}" />\n')
HTML_TEMPLATE = """
"http://www.w3.org/TR/xhtml1/DTD/xhtml1strict.dtd">


{title}




{stylesheet}\




"""
Эти строки будут использоваться как шаблоны для вызо
ва метода str.format(). В шаблоне HTML_TEMPLATE в качест
ве замещаемых полей мы использовали не числовые ин
дексы, а имена, например, {title}. Ниже мы увидим,
что для передачи значений в эти поля нам необходимо
будет использовать именованные аргументы.
class CancelledError(Exception): pass
Затем определяется нестандартное исключение; мы встретимся с ним
в паре функций программы.
Функция main() программы устанавливает некоторые начальные зна
чения и входит в цикл. На каждой итерации пользователю предлага
ется ввести некоторую информацию о странице HTML, которая будет
сгенерирована, и после создания каждой страницы предоставляется
возможность завершить программу.
def main():
information = dict(name=None, year=datetime.date.today().year,
filename=None, title=None, description=None,
keywords=None, stylesheet=None)
while True:
try:
print("\nMake HTML Skeleton\n")
populate_information(information)
make_html_skeleton(**information)
except CancelledError:
print("Cancelled")
if (get_string("\nCreate another (y/n)?", default="y").lower()
Метод str.
format() ,
стр. 100

Пример: make_html_skeleton.py 221
not in {"y", "yes"}):
break
Функция datetime.date.today() возвращает объект datetime.date, кото
рый хранит текущую дату. Нам требуется лишь значение атрибута
year этого объекта. Во все остальные элементы данных записывается
значение None, так как для них не существует разумных значений по
умолчанию.
В цикле while программа выводит заголовок и вызывает функцию
populate_information(), передавая ей словарь information. Внутри функ
ции populate_information() производится заполнение этого словаря. За
тем вызывается функция make_html_skeleton(), она принимает большое
число аргументов, но чтобы явно не указывать значение каждого из
них, мы просто распаковываем словарь information.
Если пользователь прерывает процесс создания заготовки страницы,
например, отказом от ввода обязательного значения, программа выво
дит сообщение «Cancelled» (отменено). В конце каждой итерации (не
зависимо от того, было ли отменено создание заготовки страницы или
нет) пользователю задается вопрос: не желает ли он создать еще одну
заготовку. Если пользователь отвечает отказом, производится выход
из цикла и программа завершает работу.
def populate_information(information):
name = get_string("Enter your name (for copyright)", "name",
information["name"])
if not name:
raise CancelledError()
year = get_integer("Enter copyright year", "year",
information["year"], 2000,
datetime.date.today().year + 1, True)
if year == 0:
raise CancelledError()
filename = get_string("Enter filename", "filename")
if not filename:
raise CancelledError()
if not filename.endswith((".htm", ".html")):
filename += ".html"
...
information.update (name=name, year=year, filename=filename,
title=title, description=description,
keywords=keywords, stylesheet=stylesheet)
Мы опустили программный код, который запрашивает заголовок
и текст описания, ключевые слова HTML и имя файла с таблицами сти
лей. Во всех этих случаях используется функция get_string(), которую
мы увидим очень скоро. Достаточно лишь отметить, что эта функция
принимает текст вопроса, «имя» соответствующей переменной (для
вывода в сообщении об ошибке) и необязательное значение по умолча
нию. Точно так же функция get_integer() принимает текст вопроса,

222 Глава 4. Управляющие структуры и функции
имя переменной, значение по умолчанию, минимальное и максималь
ное значения, а также признак – допустимо ли значение 0.
В конце функция заполняет словарь information новыми значениями,
используя именованные аргументы. В каждой паре key=value имя key
соответствует имени ключа в словаре, значение которого замещается
указанным значением value, и в данном случае каждое значение value
является переменной с тем же именем, что и соответствующий ей
ключ словаря.
Эта функция не имеет явного возвращаемого значения (поэтому она
возвращает значение None). Она может также завершаться в случае по
явления исключения CancelledError, в этом случае исключение будет
передано вверх по стеку вызовов и обработано в функции main().
Функцию make_html_skeleton() мы рассмотрим в два этапа.
def make_html_skeleton(year, name, title, description, keywords,
stylesheet, filename):
copyright = COPYRIGHT_TEMPLATE.format(year,
xml.sax.saxutils.escape(name))
title = xml.sax.saxutils.escape(title)
description = xml.sax.saxutils.escape(description)
keywords = ",".join([xml.sax.saxutils.escape(k)
for k in keywords]) if keywords else ""
stylesheet = (STYLESHEET_TEMPLATE.format(stylesheet)
if stylesheet else "")
html = HTML_TEMPLATE.format(title=title, copyright=copyright,
description=description,
keywords=keywords,
stylesheet=stylesheet)
Чтобы получить текст с указанием авторских прав, мы вызываем ме
тод str.format() для строки COPYRIGHT_TEMPLATE, передавая год и имя (до
полнительно выполняя экранирование служебных символов HTML)
в виде позиционных аргументов для замены полей {0} и {1}. В тексте за
головка и описания мы просто экранируем служебные символы HTML.
В случае ключевых слов у нас может быть два варианта действий, ко
торые реализуются с использованием условного выражения. Если
ключевые слова не были введены, то в качестве значения переменной
keywords будет использоваться пустая строка. В противном случае с по
мощью генератора списков выполняется обход всех ключевых слов
и создается новый список строк, в каждой из которых выполняется эк
ранирование служебных символов HTML. После этого, с помощью ме
тода str.join(), мы объединяем элементы списка в единую строку, раз
деляя их запятыми.
Текст переменной stylesheet создается аналогичным способом, что
и текст с указанием авторских прав, но с применением условного вы
ражения, чтобы в случае отсутствия имен файлов таблиц стилей полу
чалась пустая строка.

Пример: make_html_skeleton.py 223
Текст для переменной html создается из шаблона HTML_
TEMPLATE, где для передачи данных в замещаемые поля
используются не позиционные аргументы, как в других
строках шаблонов, а именованные.
fh = None
try:
fh = open(filename, "w", encoding="utf8")
fh.write(html)
except EnvironmentError as err:
print("ERROR", err)
else:
print("Saved skeleton", filename)
finally:
if fh is not None:
fh.close()
Как только заготовка файла HTML будет готова, мы записываем ее
в файл с заданным именем. После этого пользователь извещается, что
файл заготовки был сохранен, или выводится сообщение об ошибке,
если чтото пошло не так. Как обычно, чтобы гарантировать закрытие
файла, если он был открыт, используется предложение finally.
def get_string(message, name="string", default=None,
minimum_length=0, maximum_length=80):
message += ": " if default is None else " [{0}]: ".format(default)
while True:
try:
line = input(message)
if not line:
if default is not None:
return default
if minimum_length == 0:
return ""
else:
raise ValueError("{0} may not be empty".format(
name))
if not (minimum_length <= len(line) <= maximum_length):
raise ValueError("{0} must have at least {1} and "
"at most {2} characters".format(
name, minimum_length, maximum_length))
return line
except ValueError as err:
print("ERROR", err)
Функция имеет один обязательный аргумент message и четыре необяза
тельных аргумента. Если значение аргумента default определено, оно
включается в строку message, чтобы пользователь мог видеть значение
по умолчанию, которое будет использоваться, если он просто нажмет
клавишу Enter, не вводя никакого текста. Остальная часть функции
заключена в бесконечный цикл. Цикл может быть прерван вводом
Метод str.
format() ,
стр. 100

224 Глава 4. Управляющие структуры и функции
допустимой строки или в результате простого нажатия клавиши Enter,
когда используется значение по умолчанию (если определено).
Кроме того, пользователь может прервать цикл и завершить работу
программы, нажав комбинацию клавиш Ctrl+C, – в этом случае возбуж
дается исключение KeyboardInterrupt, но так как это исключение не об
рабатывается ни одним из обработчиков, имеющихся в программе, это
приведет к завершению программы с выводом диагностической ин
формации. Следовало ли оставлять такую возможность прерывать
цикл? Если бы мы этого не сделали и в программе обнаружилась бы
ошибка, мы не оставили бы пользователю никакой возможности пре
рвать работу программы, кроме как уничтожить процесс. Если нет
достаточно веских причин препятствовать завершению программы по
нажатию комбинации Ctrl+C, не следует обрабатывать это исключение
ни в одном из обработчиков.
Примечательно, что эта функция достаточно универсальна и может
использоваться не только в программе make_html_skeleton.py, но и во
многих других интерактивных программах подобного типа. Такого
многократного использования функции можно было бы добиться про
стым копированием текста, но такой прием может стать источником
головной боли для того, кто будет сопровождать программы. В следую
щей главе мы узнаем, как создавать собственные модули, вмещающие
функциональные возможности, которые могут совместно использо
ваться большим числом программ.
def get_integer(message, name="integer", default=None, minimum=0,
maximum=100, allow_zero=True):
...
Эта функция по своей структуре настолько близка к функции get_
string(), что нет необходимости воспроизводить ее здесь. (Безусловно,
эта функция присутствует в исходных текстах примеров к книге.) Па
раметр allow_zero может быть полезен, когда 0 не является допусти
мым значением, но когда желательно обеспечить возможность ввода
ошибочного значения, чтобы предоставить способ прервать процедуру
создания заготовки. Другой способ, который можно было бы использо
вать, заключается в том, чтобы определить недопустимое значение
в качестве значения по умолчанию; тогда возврат такого значения оз
начал бы, что пользователь отменил операцию.
Последняя инструкция в программе – это простой вызов функции
main(). Общий объем программы составляет чуть больше 150 строк,
и она демонстрирует некоторые особенности языка Python, которые
были представлены в этой и в предыдущих главах.

В заключение 225
В заключение
В этой главе мы рассмотрели полный синтаксис всех управляющих
структур языка Python. Здесь также было показано, как возбуждать
и обрабатывать исключения и как создавать свои типы исключений.
Большая часть главы была посвящена созданию собственных функ
ций. Мы увидели, как создаются функции, познакомились с некото
рыми правилами выбора имен для функций и их параметров. Мы так
же увидели, как можно добавлять описание функций. Подробно был
рассмотрен универсальный синтаксис определения параметров и пере
дачи аргументов в языке Python, включая возможность передачи фик
сированного и переменного числа позиционных и именованных аргу
ментов, а также возможность определения для аргументов значений
по умолчанию – как неизменяемых, так и изменяемых типов. Кроме
того, мы коротко повторили порядок использования оператора распа
ковывания последовательностей * и показали, как выполнять распа
ковывание отображений с помощью оператора **.
Когда возникает необходимость присвоить глобальной переменной но
вое значение внутри функции, она должна быть объявлена с помощью
инструкции global, чтобы предотвратить создание оператором при
сваивания новой локальной переменной. Однако вообще глобальные
переменные лучше использовать только в качестве констант.
Лямбдафункции часто используются в качестве значения аргументов
других функций или в других случаях, где функция должна переда
ваться в виде параметра. В этой главе было показано, что лямбда
функции могут использоваться как для создания анонимных функ
ций, так и для создания коротких именованных функций – путем при
сваивания их переменным.
В главе также было рассмотрено применение инструкции assert. Эта
инструкция очень удобна для проверки истинности предварительных
условий и результатов при каждом обращении к функции и может
оказать действенную помощь в создании надежных программ и в лик
видации ошибок.
В этой главе мы рассмотрели фундаментальные основы создания
функций, а, кроме того, в нашем распоряжении имеется еще масса
других приемов. Сюда входят возможность создания динамических
функций (эти функции создаются во время выполнения программы,
причем их реализация может изменяться в зависимости от обстоя
тельств), рассматриваемых в главе 5; локальных (вложенных) функ
ций, рассматриваемых в главе 7; а также рекурсивных функций, гене
раторов функций и т. д., о чем будет рассказываться в главе 8.
В языке Python имеется значительное число встроенных функций
и обширнейшая стандартная библиотека, тем не менее все равно остает
ся вероятность, что мы напишем такие функции, которые пригодятся

226 Глава 4. Управляющие структуры и функции
во многих наших программах. Копирование функций из файла в файл
может превратить в кошмар сопровождение таких программ, но,
к счастью, Python предоставляет простое решение этой проблемы: мо
дули. В следующей главе мы узнаем, как создавать свои собственные
модули со своими функциями в них. Мы увидим, как выполнять им
портирование функциональных возможностей из стандартной библио
теки и из наших модулей, а также коротко рассмотрим, что может
предложить стандартная библиотека, чтобы нам не пришлось повтор
но изобретать колесо.
Упражнения
Напишите интерактивную программу обслуживания списков строк
вфайлах.
При запуске программа должна создать список всех файлов с расшире
нием .lst в текущем каталоге. Воспользуйтесь функцией os.listdir("."),
чтобы получить список всех файлов, и отфильтруйте из него те файлы,
которые не имеют расширения .lst. В случае отсутствия таких файлов
программа должна попросить пользователя ввести имя файла и доба
вить расширение .lst, если пользователь не сделал этого. Если были
найдены один или более файлов .lst, программа должна вывести их
имена в виде списка пронумерованных строк, начиная с 1. Пользова
телю должно быть предложено ввести номер желаемого файла или 0; в
последнем случае программа должна попросить у пользователя ввести
имя нового файла.
Если был указан существующий файл, программа должна прочитать
его содержимое. Если файл пуст или было указано имя нового файла,
программа должна вывести сообщение «no items are in the list» (спи
сок не содержит элементов).
В случае отсутствия элементов должно быть предложено два варианта
действий: «Add» (добавить) и «Quit» (выйти). Если список содержит
один или более элементов, строки из списка должны выводиться про
нумерованными, начиная с 1, а из доступных действий должны быть
предложены варианты «Add» (добавить), «Delete» (удалить), «Save»
(сохранить) (если файл еще не сохранялся) и «Quit» (выйти). Если
пользователь выбирает действие «Quit» и при этом имеются несохра
ненные изменения, ему должна быть предоставлена возможность со
хранить их. Ниже приводится пример сеанса работы с программой
(большая часть пустых строк, а также заголовок «List Keeper», кото
рый выводится всякий раз при выводе списка, были удалены из лис
тинга):
Choose filename: movies
 no items are in the list 
[A]dd [Q]uit [a]: a
Add item: Love Actually

Упражнения 227
1: Love Actually
[A]dd [D]elete [S]ave [Q]uit [a]: a
Add item: About a Boy
1: About a Boy
2: Love Actually
[A]dd [D]elete [S]ave [Q]uit [a]:
Add item: Alien
1: About a Boy
2: Alien
3: Love Actually
[A]dd [D]elete [S]ave [Q]uit [a]: k
ERROR: invalid choiceenter one of 'AaDdSsQq'
Press Enter to continue...
[A]dd [D]elete [S]ave [Q]uit [a]: d
Delete item number (or 0 to cancel): 2
1: About a Boy
2: Love Actually
[A]dd [D]elete [S]ave [Q]uit [a]: s
Saved 2 items to movies.lst
Press Enter to continue...
1: About a Boy
2: Love Actually
[A]dd [D]elete [Q]uit [a]:
Add item: Four Weddings and a Funeral
1: About a Boy
2: Four Weddings and a Funeral
3: Love Actually
[A]dd [D]elete [S]ave [Q]uit [a]: q
Save unsaved changes (y/n) [y]:
Saved 3 items to movies.lst
Функция main() должна быть не очень большой (не более 30 строк)
и должна содержать только основной цикл программы. Напишите
функцию, которая будет получать имя нового или существующего
файла (и в последнем случае загружать элементы списка), и функцию,
которая будет выводить перечень доступных действий и принимать
выбор пользователя. Напишите также функции, которые будут добав
лять элемент, удалять элемент, выводить список (либо имен файлов,
либо элементов списка строк), загружать список и сохранять список.
Вставьте в свою программу копии функций get_string() и get_integer()
из программы make_html_skeleton.py или напишите свои собственные
версии.
При выводе элементов списка строк или имен файлов ширина поля
для вывода номеров строк должна быть равна 1, если список содержит
менее десяти элементов, 2 – если в списке менее 100 элементов и 3 –
в противном случае.

228 Глава 4. Управляющие структуры и функции
Всегда выводите элементы списка в алфавитном порядке, без учета ре
гистра символов, и следите за состоянием списка (за наличием несо
храненных изменений). Действие «Save» должно предлагаться только
при наличии несохраненных изменений, а перед выходом программа
должна спрашивать у пользователя, не желает ли он сохранить изме
нения, только если таковые имеются. Добавление и удаление элемен
тов считаются действиями, которые изменяют список, а после выпол
нения операции сохранения список снова должен считаться неизме
ненным.
Пример решения находится в файле Listkeeper.py и занимает менее
200 строк программного кода.

5
Модули
Функции позволяют упаковывать фрагменты программного кода, что
бы его можно было многократно использовать по всей программе,
а модули обеспечивают средство объединения функций (и, как мы уви
дим в следующей главе, наших собственных типов данных) в коллек
ции, чтобы их можно было использовать в разных программах. В язы
ке Python имеются также средства создания пакетов – наборов моду
лей, объединенных, как правило, по функциональным признакам или
вследствие зависимости друг от друга.
В первом разделе этой главы описывается синтаксис операции импор
тирования функциональных возможностей из модулей и пакетов, вхо
дящих в состав стандартной библиотеки, или из наших собственных
модулей и пакетов. Затем в этом же разделе будет показано, как созда
вать собственные пакеты и модули. Будут продемонстрированы два
примера собственных модулей. Из них первый пример является ввод
ным, а во втором демонстрируется, как решаются многочисленные
проблемы, возникающие на практике, такие как платформенная неза
висимость и тестирование.
Во втором разделе дается краткий обзор стандартной
библиотеки языка Python. Очень важно знать, что мо
жет предложить стандартная библиотека, потому что ис
пользование предопределенных функциональных воз
можностей существенно ускоряет программирование,
позволяя не создавать все и вся с чистого листа. Кроме
того, многие модули из стандартной библиотеки исполь
зуются очень широко. Они тщательно протестированы
и обладают высокой надежностью. Помимо краткого об
зора будет приведено несколько небольших примеров,
иллюстрирующих типичные случаи использования. До
Врезка
«Электрон
ная докумен
тация»,
стр. 203
•Модули и пакеты
•Обзор стандартной библиотеки
языка Python

230 Глава 5. Модули
полнительно будут приводиться ссылки на описания модулей в других
главах.
Модули и пакеты
Модуль в языке Python – это обычный файл с расширением .py. Мо
дуль может содержать любой программный код на языке Python. Ка
ждая программа, которую мы писали до сих пор, находилась в отдель
ном файле .py, который можно считать не только программой, но и мо
дулем. Основное различие между модулем и программой состоит в том,
что программа предназначена для того, чтобы ее запускали, тогда как
модуль предназначен для того, чтобы его импортировали и использо
вали в программах.
Не все модули располагаются в файлах с расширением .py, например,
модуль sys встроен в Python, а некоторые модули написаны на других
языках программирования (чаще всего на языке C). Однако большая
часть библиотеки языка Python написана именно на языке Python,
так, например, добавляя инструкцию import collections, мы получаем
возможность создавать именованные кортежи вызовом функции col
lections.namedtuple(), а функциональные возможности, к которым мы
получаем доступ, находятся в файле модуля collections.py. Для наших
программ совершенно неважно, на каком языке программирования
написан модуль, потому что все модули импортируются и используют
ся одним и тем же способом.
Импортирование может выполняться несколькими синтаксическими
конструкциями, например:
import importable
import importable1, importable2, ..., importableN
import importable as preferred_name
Здесь под importable подразумевается имя модуля, такое
как collections, но точно так же это может быть пакет
или модуль из пакета, и тогда все части имени отделяют
ся друг от друга точками (.), например, os.path. Первые
две конструкции мы используем на протяжении всей
книги. В них нет ничего сложного, и они являются са
мыми безопасными, потому что позволяют избежать
конфликтов имен, так как вынуждают программиста
всегда использовать полные квалифицированные имена.
Третья конструкция позволяет давать импортируемому модулю или
пакету имя по выбору – теоретически это может привести к конфлик
там имен, но на практике синтаксис as обычно используется, чтобы
как раз избежать их. Подобное переименование, в частности, удобно
использовать при экспериментировании с различными реализациями
одного и того же модуля. Например, допустим, что у нас имеется два
модуля MyModuleA и MyModuleB, которые имеют один и тот же API (Appli Пакеты,
стр. 234

Модули и пакеты 231
cation Programming Interface – прикладной программный интерфейс);
мы могли бы в программе записать инструкцию import MyModuleA as My
Module, а позднее легко переключиться на использование import MyModu
leB as MyModule.
Где должна находиться инструкция import? Обычно все инструкции
import помещаются в начало файла .py, после строки «shebang» и после
описания модуля. И, как уже говорилось в главе 1, мы рекомендуем
сначала импортировать модули стандартной библиотеки, затем моду
ли сторонних разработчиков и в последнюю очередь свои собственные
модули.
Ниже приводятся еще несколько вариантов использования инструк
ции import:
from importable import object as preferred_name
from importable import object1, object2, ..., objectN
from importable import (object1, object2, object3, object4, object5,
object6, ..., objectN)
from importable import *
Эти синтаксические конструкции могут приводить к конфликтам
имен, поскольку они обеспечивают непосредственный доступ к импор
тируемым объектам (переменным, функциям, типам данных или мо
дулям). Если для импортирования большого числа объектов необходи
мо использовать синтаксис from ... import, мы можем расположить
инструкцию импорта в нескольких строках, либо экранируя каждый
символ перевода строки, кроме последнего, либо заключая список
имен объектов в круглые скобки, как показано в третьем примере.
В последней синтаксической конструкции символ «*»
означает «импортировать все имена, которые не являют
ся частными». На практике это означает, что будут им
портированы все объекты из модуля за исключением
тех, чьи имена начинаются с символа подчеркивания,
либо, если в модуле определена глобальная переменная
__all__ со списком имен, будут импортированы все объ
екты, имена которых перечислены в переменной __all__.
Ниже приводятся несколько примеров использования инструкции
import:
import os
print(os.path.basename(filename)) # безопасный доступ по полным
# квалифицированным именам
import os.path as path
print(path.basename(filename)) # есть риск конфликта имен с модулем path
from os import path
print(path.basename(filename)) # есть риск конфликта имен с модулем path
from os.path import basename
print(basename(filename)) # есть риск конфликта имен с модулем basename
Функция __all__() ,
стр. 236

232 Глава 5. Модули
from os.path import *
print(basename(filename)) # есть риск множественных конфликтов имен
Синтаксис from importable import * используется для импортирования
всех объектов из модуля (или из всех модулей пакета) – это могут быть
сотни имен. В случае from
os.path import * будет импортировано почти
40 имен, включая имена dirname, exists и split, которые, вполне веро
ятно, мы могли бы использовать в качестве имен для наших собствен
ных переменных или функций.
Например, если записать инструкцию from
os.path import dirname, мы
получим удобную возможность вызывать функцию dirname(), не ука
зывая полное квалифицированное имя. Но если ниже в нашем про
граммном коде будет встречена инструкция dirname
= ".", то после ее
выполнения ссылка на объект dirname будет указывать уже не на функ
цию dirname(), а на строку ".". Поэтому, если мы попытаемся вызвать
функцию dirname(), мы получим исключение TypeError, потому что те
перь имя dirname ссылается на строку, а не на вызываемый объект.
Ввиду того, что синтаксис import
* потенциально опасен появлением
конфликтов имен, некоторые коллективы разработчиков вырабатыва
ют свои правила, устанавливающие, что в их разработках может ис
пользоваться только синтаксис import importable. Однако некоторые
крупные пакеты, в частности библиотеки GUI (Graphical User Inter
face – графический интерфейс пользователя), нередко импортируются
таким способом, потому что они включают огромное число функций
и классов (собственных типов данных), для которых было бы слишком
утомительно вводить вручную полные имена.
Возникает естественный вопрос – как интерпретатор узнает, где ис
кать импортируемые модули и пакеты? Встроенный модуль sys имеет
список с именем sys.path, в котором хранится перечень каталогов, со
ставляющих путь поиска Python. Первый каталог в этом списке – это
каталог, где находится сама программа, даже если она вызывается из
другого каталога. Далее в списке находятся пути к каталогам из пере
менной окружения PYTHONPATH, если она определена. И в конце списка
находятся пути к каталогам стандартной библиотеки языка Python –
они определяются на этапе установки Python.
Когда модуль импортируется впервые, если он не является
встроенным, интерпретатор пытается отыскать его поочередно
в каждом из каталогов, перечисленных в списке sys.path. Как
следствие этого, если мы создаем модуль или программу, имя
которого совпадает с именем библиотечного модуля, наш мо
дуль будет найден первым, что неизбежно будет приводить
к проблемам. Чтобы избежать этого, никогда не создавайте
программы или модули, имена которых совпадают с именами
модулей или каталогов верхнего уровня в библиотеке, если
только вы не пытаетесь подставить свою собственную реализа
цию и ваше переопределение преднамеренно. (Модулем верх

Модули и пакеты 233
него уровня называется файл .py, который находится в одном из ката
логов, включенных в путь поиска Python, а не в какомнибудь подка
талоге, вложенном в один из этих каталогов.) Например, в системе
Windows в путь поиска Python обычно включается каталог с именем
C:\Python30\Lib, поэтому на этой платформе мы не должны создавать
модуль с именем Lib.py, так же как модуль, имя которого совпадает
с именем любого модуля из каталога C:\Python30\Lib.
Один из способов быстро проверить, используется ли то или иное имя
модуля, состоит в том, чтобы попытаться импортировать модуль. Сде
лать это можно в консоли, вызвав интерпретатор с ключом
–c («execu
te code» – выполнить программный код), за которым следует указать
инструкцию import. Например, если необходимо проверить, существу
ет ли модуль с именем Music.py (или каталог верхнего уровня Music
в пути поиска Python), можно ввести в консоли следующую команду:
python c "import Music"
Если в ответ будет получено исключение ImportError, можно быть уве
ренным, что модуль или каталог верхнего уровня с таким именем не
используется; любой другой вывод (или его отсутствие) означает нали
чие такого имени. К сожалению, такой прием не дает полной гаран
тии, что впоследствии с этим именем не будет возникать никаких про
блем, поскольку позднее мы можем установить пакет или модуль, соз
данный сторонним разработчиком, имеющий такое же имя, хотя на
практике такая проблема возникает достаточно редко.
Например, если мы создадим модуль os.py, он будет конфликтовать
с библиотечным модулем os. Но если мы создадим модуль path.py, то
никаких проблем возникать не будет, поскольку этот модуль при
шлось бы импортировать как модуль path, тогда как библиотечный мо
дуль должен импортироваться как os.path. В этой книге имена файлов
наших собственных модулей всегда будут начинаться с символа верх
него регистра; это позволит избежать конфликтов имен (по крайней
мере в UNIX), потому что имена файлов библиотечных модулей состо
ят исключительно из символов нижнего регистра.
Программа может импортировать некоторые модули, которые в свою
очередь импортируют другие модули, включая те, что уже были им
портированы. Это не является проблемой. Всякий раз, когда выполня
ется попытка импортировать модуль, интерпретатор Python сначала
проверяет – не был ли импортирован требуемый модуль ранее. Если
модуль еще не был импортирован, Python выполняет скомпилирован
ный байткод модуля, создавая тем самым переменные, функции
и другие объекты модуля, после чего добавляет во внутреннюю струк
туру запись о том, что модуль был импортирован. При любых после
дующих попытках импортировать этот модуль интерпретатор будет
обнаруживать, что модуль уже импортирован и не будет выполнять
никаких действий.

234 Глава 5. Модули
Когда интерпретатору требуется скомпилированный байткод модуля,
он генерирует его автоматически – этим Python отличается от таких
языков программирования, как Java, где компилирование в байткод
должно выполняться явно. Сначала интерпретатор попытается оты
скать файл, имя которого совпадает с именем файла, имеющего рас
ширение .py, но имеющий расширение .pyo – это оптимизированный
байткод скомпилированной версии модуля. Если файл с расширени
ем .pyo не будет найден (или он более старый, чем файл с расширением
.py), интерпретатор попытается отыскать одноименный файл с расши
рением .pyc – это неоптимизированный байткод скомпилированной
версии модуля. Если интерпретатор обнаружит актуальную скомпи
лированную версию модуля, он загрузит ее; в противном случае Py
thon загрузит файл с расширением .py и скомпилирует его в байткод.
В любом случае интерпретатор загрузит в память модуль в виде ском
пилированного байткода.
Если интерпретатор выполнил компиляцию файла с расширением .py,
он сохранит скомпилированную версию в одноименном файле с рас
ширением .pyc (или .pyo, если интерпретатор был запущен с ключом
командной строки
–O1, или если в переменной окружения PYTHONOPTI
MIZE установлено значение O), при этом каталог должен быть доступен
для записи. Сохранения байткода можно избежать, если запускать
интерпретатор с ключом командной строки
–B или установив перемен
ную окружения PYTHONDONTWRITEBYTECODE.
Использование файлов со скомпилированным байткодом ускоряет за
пуск программы, поскольку интерпретатору остается только загру
зить и выполнить программный код, минуя этап компиляции (и со
хранения, если это возможно), хотя сама скорость работы программы
от этого не зависит. При установке Python компиляция модулей стан
дартной библиотеки в байткод обычно является частью процесса уста
новки.
Пакеты
Пакет – это простой каталог, содержащий множество модулей и файл
с именем __init__.py. Например, допустим, что у нас имеется некото
рое множество файлов модулей, предназначенных для чтения и записи
графических файлов различных форматов с именами Bmp.py, Jpeg.py,
Png.py, Tiff.py и Xpm.py, в каждом из которых имеются функции
load(), save() и т. д.
2 Мы могли бы сохранить все эти модули в одном
каталоге с программой, но в крупных программных продуктах, ис
1 Это символ «O», а не цифра 0. – Прим. перев.
2 Широкая поддержка операций с графическими файлами обеспечивается
различными модулями сторонних разработчиков, из которых наиболее
примечательной является библиотека Python Imaging Library (www.python
ware.com/products/pil).

Модули и пакеты 235
пользующих массу собственных модулей, модули для работы с графи
кой, скорее всего, лучше хранить отдельно. Поместив их в свой собст
венный подкаталог, например Graphics, их можно хранить все вместе.
А если поместить в каталог Graphics пустой файл __init__.py, этот ка
талог превратится в пакет:
Graphics/
__init__.py
Bmp.py
Jpeg.py
Png.py
Tiff.py
Xpm.py
Пока каталог Graphics является подкаталогом каталога с программой
или находится в пути поиска Python, мы будем иметь возможность
импортировать любой из этих модулей и использовать их. Мы должны
сделать все возможное, чтобы гарантировать несовпадение имени на
шего модуля верхнего уровня (Graphics) с какимлибо из имен верхнего
уровня в стандартной библиотеке – с целью избежать конфликтов
имен. (В системе UNIX это легко обеспечить, достаточно лишь исполь
зовать в качестве первого символа имени символ верхнего регистра,
так как в именах модулей стандартной библиотеки используются
только символы нижнего регистра.) Ниже показано, как импортиро
вать и использовать наши модули:
import Graphics.Bmp
image = Graphics.Bmp.load("bashful.bmp")
В небольших программах некоторые программисты предпочитают ис
пользовать более короткие имена, и язык Python позволяет делать это
двумя, немного отличающимися способами.
import Graphics.Jpeg as Jpeg
image = Jpeg.load("doc.jpeg")
Здесь мы импортировали модуль Jpeg из пакета Graphics и сообщили
интерпретатору, что вместо полного квалифицированного имени
Graphics.Jpeg хотим использовать более короткое имя Jpeg.
from Graphics import Png
image = Png.load("dopey.png")
Этот фрагмент программного кода напрямую импортирует модуль Png
из пакета Graphics. Данная синтаксическая конструкция (import ...
from) обеспечивает непосредственный доступ к модулю Png.
Мы не обязаны использовать в нашем программном коде оригиналь
ные имена модулей. Например:
from Graphics import Tiff as picture
image = picture.load("grumpy.tiff")

236 Глава 5. Модули
Здесь мы используем модуль Tiff, но внутри нашей программы пере
именовали его в модуль picture.
В некоторых ситуациях бывает удобно загружать все модули пакета
одной инструкцией. Для этого необходимо отредактировать файл
__init__.py пакета, записав в него инструкцию, которая указывала бы,
какие модули должны загружаться. Эта инструкция должна присваи
вать список с именами модулей специальной переменной __all__. На
пример, ниже приводится необходимая строка для файла Graphics/
__init__.py:
__all__ = ["Bmp", "Jpeg", "Png", "Tiff", "Xpm"]
Этим ограничивается необходимое содержимое файла __init__.py, по
мимо этого, мы можем поместить в него любой программный код, ка
кой только пожелаем. Теперь мы можем использовать другую разно
видность инструкции import:
from Graphics import *
image = Xpm.load("sleepy.xpm")
Синтаксис from package import * напрямую импортирует все имена мо
дулей, упомянутые в списке __all__. То есть после выполнения этой
инструкции мы получим прямой доступ не только к модулю Xpm, но
и ко всем другим модулям.
Как отмечалось ранее, этот синтаксис может применяться и к моду
лям, то есть from module import *, в этом случае будут импортированы
все функции, переменные и другие объекты, определяемые модулем
(за исключением тех, чьи имена начинаются с символа подчеркива
ния). При необходимости точно указать, что должно быть импортиро
вано при использовании синтаксической конструкции from module im
port *, мы можем определить список __all__ непосредственно в моду
ле; в этом случае инструкция from module import * будет импортировать
только те объекты, имена которых присутствуют в списке __all__.
До сих пор мы демонстрировали только один уровень вложенности, но
Python позволяет создавать столько уровней вложенности пакетов,
сколько нам заблагорассудится. То есть мы можем поместить в ката
лог Graphics подкаталог, скажем, Vector, с файлами модулей внутри
него Eps.py и Svg.py:
Graphics/
__init__.py
Bmp.py
Jpeg.py
Png.py
Tiff.py
Vector/
__init__.py
Eps.py

Модули и пакеты 237
Svg.py
Xpm.py
Чтобы каталог Vector превратился в пакет, в него необходимо помес
тить файл __init__.py, который, как уже говорилось, может быть пус
тым или определять список __all__ для обеспечения удобства тем про
граммистам, которые предпочтут использовать инструкцию импорти
рования from Graphics.Vector import *.
Для доступа к вложенному пакету мы можем использовать обычный
синтаксис, который использовали ранее:
import Graphics.Vector.Eps
image = Graphics.Vector.Eps.load("sneezy.eps")
Полные квалифицированные имена могут оказаться слишком длин
ными, поэтому некоторые программисты пытаются привести иерар
хию модулей к плоскому виду, чтобы избежать необходимости вруч
ную вводить такие имена:
import Graphics.Vector.Svg as Svg
image = Svg.load("snow.svg")
Мы всегда можем использовать свои собственные короткие имена для
модулей, как показано в этом примере, хотя это повышает риск появ
ления конфликтов имен.
Собственные модули
Поскольку модули – это всего лишь файлы с расширением .py, они соз
даются без особых формальностей. В этом разделе мы рассмотрим два
нестандартных модуля. Первый модуль, TextUtil (в файле TextUtil.py),
содержит всего три функции: is_balanced(), возвращающую True, если
в строке, переданной ей, соблюдена парность скобок разных типов; shor
ten() (продемонстрированную ранее, на стр. 209) и simplify(), способ
ную удалять лишние пробелы и другие символы из строки. При рассмот
рении этого модуля мы также покажем, как использовать программный
код в строках документирования в качестве модульных тестов.
Второй модуль, CharGrid (в файле CharGrid.py), содержит сетку симво
лов и позволяет «рисовать» линии, прямоугольники и текст в сетке
и отображать сетку в консоли. Этот модуль демонстрирует некоторые
приемы, с которыми мы не сталкивались ранее, и является более ти
пичным примером более крупных и более сложных модулей.
Модуль TextUtil
Структура этого модуля (и большинства других модулей) немного от
личается от структуры программы. Первая строка модуля – это строка
«shebang», вслед за которой следует несколько строк комментариев
(обычно упоминание об авторских правах и информация о лицензион
ном соглашении). Затем, как правило, следует строка в тройных ка

238 Глава 5. Модули
вычках, в которой дается краткий обзор содержимого модуля, часто
с несколькими примерами использования – это строка документиро
вания модуля. Ниже приводится начало файла TextUtil.py (правда, без
комментария с упоминанием о лицензионном соглашении):
#!/usr/bin/env python3
# Copyright (c) 2008 Qtrac Ltd. All rights reserved.
"""
Этот модуль предоставляет несколько функций манипулирования строками.
>>> is_balanced("(Python (is (not (lisp))))")
True
>>> shorten("The Crossing", 10)
'The Cro...'
>>> simplify(" some text with spurious whitespace ")
'some text with spurious whitespace'
"""
import string
Строку документирования этого модуля можно сделать доступной
программам (или другим модулям), если импортировать модуль как
TextUtil.__doc__. Вслед за строкой документирования следуют инст
рукции импортирования, в данном случае – единственная инструк
ция, и далее находится остальная часть модуля.
Мы уже видели полный текст функции shorten(), поэто
му не будем повторно воспроизводить его здесь. И по
скольку в настоящее время нас интересуют модули, а не
функции, мы продемонстрируем только программный
код функции is_balanced(), хотя функцию simplify() при
ведем полностью, вместе со строкой документирования.
Ниже приводится функция simplify(), разбитая на две части:
def simplify(text, whitespace=string.whitespace, delete=""):
r"""Возвращает текст, из которого удалены лишние пробелы.
Параметр whitespace  это строка символов, каждый из которых
считается символом пробела.Если параметр delete не пустой,
он должен содержать строку, и тогда все символы, входящие
в состав строки delete, будут удалены из строки результата.
>>> simplify(" this and\n that\t too")
'this and that too'
>>> simplify(" Washington D.C.\n")
'Washington D.C.'
>>> simplify(" Washington D.C.\n", delete=",;:.")
'Washington DC'
>>> simplify(" disemvoweled ", delete="aeiou")
'dsmvwld'
"""
Функция shorten() ,
стр. 209

Модули и пакеты 239
Вслед за строкой с инструкцией def следует строка доку
ментирования функции, первая строка которой в соот
ветствии с соглашениями является коротким одностроч
ным описанием; за ней следуют пустая строка, более
подробное описание и затем несколько примеров, запи
санных так, как если бы они выполнялись в интерактив
ной оболочке. Поскольку в строке документирования
присутствуют кавычки, мы должны либо экранировать
их символом обратного слеша, либо, как в данном слу
чае, использовать «сырую» строку в тройных кавычках.
result = []
word = ""
for char in text:
if char in delete:
continue
elif char in whitespace:
if word:
result.append(word)
word = ""
else:
word += char
if word:
result.append(word)
return " ".join(result)
Список result используется для хранения «слов» – строк, не имеющих
пробельных или удаляемых символов. Внутри функции выполняются
итерации по символам в параметре text, с пропуском удаляемых сим
волов. Если встречается пробельный символ и в переменной word со
держится хотя бы один символ, полученное слово добавляется в спи
сок result, после чего в переменную word записывается пустая строка;
в противном случае пробельный символ пропускается. Любые другие
символы добавляются к создаваемому слову. В конце функция возвра
щает единственную строку, содержащую все слова из списка result,
разделенные пробелом.
Функция is_balanced() следует тому же шаблону: за строкой с инст
рукцией def находится строка документирования с коротким одно
строчным описанием, пустой строкой, полным описанием и несколь
кими примерами, вслед за которой идет сам программный код. Ниже
приводится только программный код функции, без строки документи
рования:
def is_balanced(text, brackets="()[]{}<>"):
counts = {}
left_for_right = {}
for left, right in zip(brackets[::2], brackets[1::2]):
assert left != right, "the bracket characters must differ"
counts[left] = 0
«Сырые»
строки,
стр. 85

240 Глава 5. Модули
left_for_right[right] = left
for c in text:
if c in counts:
counts[c] += 1
elif c in left_for_right:
left = left_for_right[c]
if counts[left] == 0:
return False
counts[left] = 1
return not any(counts.values())
Функция создает два словаря. Ключами словаря counts являются сим
волы открывающих скобок («(», «[», «{» и «<»), а значениями – целые
числа. Ключами словаря left_for_right являются символы закрываю
щих скобок («)», «]», «}» и «>»), а значениями – соответствующие им
символы открывающих скобок. Сразу после создания словарей функ
ция начинает выполнять итерации по символам в параметре text. Вся
кий раз, когда встречается символ открывающей скобки, соответст
вующее ему значение в словаре count увеличивается на 1. Точно так
же, когда встречается символ закрывающей скобки, функция опреде
ляет соответствующий ему символ открывающей скобки. Если счет
чик для этого символа равен 0, это означает, что была встречена лиш
няя закрывающая скобка, поэтому можно сразу же возвращать False;
в противном случае счетчик уменьшается на 1. По окончании просмот
ра текста, если все открывающие скобки имеют парные им закрываю
щие скобки, все счетчики должны быть равны 0, поэтому, если хотя
бы один счетчик не равен 0, функция возвращает False; в противном
случае она возвращает True.
До этого момента рассматриваемый модуль ничем не отличался от лю
бого другого файла с расширением .py. Если бы файл TextUtil.py был
программой, вполне возможно, что в нем присутствовали бы и другие
функции, а в конце стоял бы единственный вызов одной из этих функ
ций, запускающий обработку. Но так как это модуль, который предна
значен для того, чтобы его импортировали, одних определений функ
ций вполне достаточно. Теперь любая программа или модуль смогут
импортировать модуль TextUtil и использовать его:
import TextUtil
text = " a puzzling conundrum "
text = TextUtil.simplify(text) # text == 'a puzzling conundrum'
Если нам потребуется сделать модуль TextUtil доступным определенной
программе, нам достаточно будет поместить файл TextUtil.py в один ка
талог с программой. Сделать файл TextUtil.py доступным для всех на
ших программ можно несколькими способами. Первый состоит в том,
чтобы поместить модуль в подкаталог sitepackages, находящийся в де
реве каталогов, куда был установлен Python (в системе Windows это
обычно каталог C:\Python30\Lib\sitepackages, но в Mac OS X и других

Модули и пакеты 241
версиях UNIX путь к этому каталогу будет иным). Данный каталог
находится в пути поиска Python, поэтому интерпретатор всегда будет
отыскивать любые модули, находящиеся здесь. Второй способ заклю
чается в создании каталога, специально предназначенного для наших
собственных модулей, которые мы предполагаем использовать в на
ших программах, и добавлении пути к этому каталогу в переменную
окружения PYTHONPATH. Третий способ состоит в том, чтобы поместить
модуль в локальный подкаталог sitepackages – каталог %APPDATA%/
Python/Python30/sitepackages в Windows, и ~/.local/lib/python3.0/site
packages в UNIX (включая Mac OS X), который находится в пути поис
ка Python. Второй и третий подходы предпочтительнее, так как в этих
двух случаях ваш программный код будет храниться отдельно от офи
циальной версии Python.
Иметь модуль TextUtil само по себе уже неплохо, но если в конечном
счете предполагается использовать его во множестве программ, то на
верняка хотелось бы пребывать в уверенности, что он работает именно
так, как заявлено. Один из самых простых способов состоит в том, что
бы выполнить примеры, которые приводятся в строках документиро
вания, и убедиться, что они дают ожидаемые результаты. Сделать это
можно, добавив всего три строки в конец файла модуля:
if __name__ == "__main__":
import doctest
doctest.testmod()
Всякий раз, когда выполняется импортирование модуля, интерпрета
тор создает для него переменную с именем __name__ и сохраняет имя
модуля в этой переменной. Имя модуля – это просто имя файла .py,
только без расширения. Поэтому в данном случае, когда модуль будет
импортироваться, переменная __name__ получит значение "TextUtil"
и условие в инструкции if не будет соответствовать True, то есть две по
следние строки выполняться не будут. Это означает, что последние три
строки ничего не меняют, когда модуль импортируется.
Всякий раз, когда файл с расширением .py запускается как програм
ма, интерпретатор Python создает в программе переменную с именем
__name__ и записывает в нее строку "__main__". То есть, если мы запус
тим файл TextUtil.py как программу, интерпретатор запишет в пере
менную __name__ строку "__main__", условие в инструкции if вернет True
и две последние строки будут выполнены.
Функция doctest.testmod() с помощью механизма интроспекции Py
thon выявляет все функции в модуле и их строки документирования,
после чего пытается выполнить все фрагменты программного кода, ко
торые приводятся в строках документирования. При запуске модуля
таким способом вывод на экране появится только при наличии ошибок.
Сначала это может привести в замешательство, так как создается впе
чатление, будто вообще ничего не происходит; но если интерпретатору

242 Глава 5. Модули
передать ключ командной строки –v, на экране может появиться при
мерно следующее:
Trying:
is_balanced("(Python (is (not (lisp))))")
Expecting:
True
ok
...
Trying:
simplify(" disemvoweled ", delete="aeiou")
Expecting:
'dsmvwld'
ok
4 items passed all tests:
3 tests in __main__
5 tests in __main__.is_balanced
3 tests in __main__.shorten
4 tests in __main__.simplify
15 tests in 4 items.
15 passed and 0 failed.
Test passed.
Мы использовали многоточия, чтобы показать, что было опущено мно
жество строк. Если в модуле имеются функции (или классы, или мето
ды), не имеющие тестов, при запуске интерпретатора с ключом
–v они
будут перечислены. Обратите внимание, что модуль doctest обнару
жил тесты как в строке документирования модуля, так и в строках до
кументирования функций.
Примеры в строках документирования, которые могут выполняться
как тесты, называют доктестами (doctests). Обратите внимание, что
при написании доктестов мы вызываем функцию simplify(), не ис
пользуя полное квалифицированное имя (поскольку доктесты нахо
дятся непосредственно в самом модуле). За пределами модуля, после
выполнения инструкции import TextUtil, мы должны использовать
квалифицированные имена, например, TextUtil.is_balanced().
В следующем подразделе мы увидим, как реализовать более полноцен
ные тесты – в частности, проверку случаев, когда ожидаются отказы, –
например, когда неверные входные данные должны приводить к воз
буждению исключения. Мы также рассмотрим некоторые другие про
блемы, связанные с созданием модулей, включая инициализацию мо
дуля, учет различий между платформами и обеспечение возможности
импортировать программами или модулями, при использовании син
таксиса from module import *, только тех объектов, которые мы хотим
сделать общедоступными.

Модули и пакеты 243
Модуль CharGrid
Модуль CharGrid хранит в памяти сетку символов. Он предоставляет
функции, позволяющие «рисовать» в сетке линии, прямоугольники
и текст, а также функции отображения сетки в консоли. Ниже приво
дятся доктесты из строки документирования модуля:
>>> resize(14, 50)
>>> add_rectangle(0, 0, *get_size())
>>> add_vertical_line(5, 10, 13)
>>> add_vertical_line(2, 9, 12, "!")
>>> add_horizontal_line(3, 10, 20, "+")
>>> add_rectangle(0, 0, 5, 5, "%")
>>> add_rectangle(5, 7, 12, 40, "#", True)
>>> add_rectangle(7, 9, 10, 38, " ")
>>> add_text(8, 10, "This is the CharGrid module")
>>> add_text(1, 32, "Pleasantville", "@")
>>> add_rectangle(6, 42, 11, 46, fill=True)
>>> render(False)
Функция CharGrid.add_rectangle() принимает четыре обязательных ар
гумента: номер строки и номер столбца верхнего левого угла, а также
номер строки и номер столбца правого нижнего угла. В пятом необяза
тельном аргументе можно определить символ, который будет исполь
зоваться для рисования сторон прямоугольника, а в шестом аргументе
типа Boolean можно указать, следует ли выполнять заливку прямо
угольника (тем же самым символом, который используется для рисо
вания сторон). В первом вызове третий и четвертый аргументы переда
ются путем распаковывания двухэлементного кортежа (ширина и вы
сота), который возвращает функция CharGrid.get_size().
По умолчанию, прежде чем вывести содержимое сетки, функция Char
Grid.render() очищает экран, но чтобы предотвратить это, ей можно
передать значение False, что и было сделано в данном случае. Ниже
приводится изображение сетки, полученной в результате выполнения
доктестов:
%%%%%*********************************************
% % @@@@@@@@@@@@@@@ *
% % @Pleasantville@ *
% % ++++++++++ @@@@@@@@@@@@@@@ *
%%%%% *
* ################################# *
* ################################# **** *
* ## ## **** *
* ## This is the CharGrid module ## **** *
* ! ## ## **** *
* ! | ################################# **** *
* ! | ################################# *
* | *
**************************************************

244 Глава 5. Модули
Модуль CharGrid начинается точно так же, как и модуль TextUtil –
со строки «shebang», с упоминания об авторских правах и лицензион
ном соглашении. В строке документирования модуля приводится его
описание, вслед за которым находятся доктесты, упомянутые выше.
Следующий ниже программный код начинается двумя инструкциями
импорта: одна импортирует модуль sys, а другая – модуль subprocess.
Модуль subprocess подробно будет рассматриваться в главе 9.
В модуле используется две тактики обработки ошибок. Некоторые
функции имеют параметр типа char, то есть фактически строку, содер
жащую единственный символ. Нарушение этого требования рассмат
ривается как фатальная ошибка программирования, поэтому для про
верки длины аргументов используется инструкция assert. Передача
номеров строк и столбцов со значениями, выходящими за пределы сет
ки, хотя и считается ошибкой, но рассматривается как нормальная си
туация, поэтому в подобных случаях возбуждается наше собственное
исключение.
Теперь мы рассмотрим наиболее показательные и наиболее важные
фрагменты программного кода модуля, начав с исключений:
class RangeError(Exception): pass
class RowRangeError(RangeError): pass
class ColumnRangeError(RangeError): pass
Ни одна из функций в модуле, возбуждающих исключения, не возбу
ждает исключение RangeError, они всегда возбуждают конкретное ис
ключение в зависимости от того, номер строки или столбца вышел за
пределы сетки. Но, используя существующую иерархию исключений,
мы даем пользователю модуля возможность выбирать, будет ли он об
рабатывать конкретные исключения или перехватывать их по базово
му классу RangeError. Обратите также внимание на то, что внутри док
тестов используются неквалифицированные имена исключений, но
когда модуль импортируется инструкцией import CharGrid, необходимо
использовать полные квалифицированные имена исключений: Char
Grid.RangeError, CharGrid.RowRangeError и CharGrid.ColumnRangeError.
_CHAR_ASSERT_TEMPLATE = ("char must be a single character: '{0}' "
"is too long")
_max_rows = 25
_max_columns = 80
_grid = []
_background_char = " "
Здесь определяются некоторые частные данные для использования
внутри модуля. Имена частных переменных начинаются с символа
подчеркивания, поэтому, когда модуль будет импортироваться инст
рукцией from CharGrid import *, ни одна из этих переменных не будет
импортирована. (Как вариант, можно было бы использовать список
__all__.) Переменная _CHAR_ASSERT_TEMPLATE – это строка, предназначен
ная для вызова метода str.format(), – позднее мы увидим, что такой

Модули и пакеты 245
прием широко используется для генерации сообщений об ошибках
в инструкциях assert. Назначение остальных переменных будет пояс
няться по мере того, как мы будем сталкиваться с ними.
if sys.platform.startswith("win"):
def clear_screen():
subprocess.call(["cmd.exe", "/C", "cls"])
else:
def clear_screen():
subprocess.call(["clear"])
clear_screen.__doc__ = """Clears the screen using the underlying \
window system's clear screen command"""
Очистка экрана консоли в разных системах выполняется поразному.
В Windows необходимо выполнить программу cmd.exe с соответствую
щими аргументами, а в большинстве систем UNIX запускается про
грамма clear. Функция subprocess.call() из модуля subprocess позволя
ет запускать внешние программы, поэтому мы можем использовать ее
для очистки экрана с учетом особенностей системы. Строка sys.plat
form хранит имя операционной системы, под управлением которой вы
полняется программа, например, «win32» или «linux2». Поэтому один
из способов учесть различия между платформами – определить функ
цию clear_screen(), как показано ниже:
def clear_screen():
command = (["clear"] if not sys.platform.startswith("win") else
["cmd.exe", "/C", "cls"])
subprocess.call(command)
Недостаток такого подхода заключается в том, что, даже зная, что тип
платформы не изменится в процессе работы программы, мы все равно
вынуждены выполнять проверку при каждом вызове функции.
Чтобы избежать необходимости проверки типа операционной системы,
под управлением которой выполняется программа, при каждом вызове
функции clear_screen(), мы создаем платформозависимую функцию
clear_screen() на этапе импортирования модуля и с этого момента по
стоянно используем ее. Это возможно благодаря тому, что в языке Py
thon инструкция def является самой обычной инструкцией – когда ин
терпретатор достигает условной инструкции if, он выполняет либо
первую, либо вторую инструкцию def, динамически создавая ту или
иную версию функции clear_screen(). Так как определение функции
находится за пределами какойлибо другой функции (или класса,
о чем будет рассказываться в следующей главе), она попрежнему ос
тается в глобальной области видимости и обращаться к ней можно так
же, как к любой другой функции в модуле.
После создания функции мы явно определяем строку документирова
ния для нее – такой прием позволяет избежать необходимости дважды
записывать одну и ту же строку документирования в двух местах,
а, кроме того, иллюстрирует, что строка документирования – это всего

246 Глава 5. Модули
лишь атрибут функции. В число прочих атрибутов входят имя модуля
функции и ее собственное имя.
def resize(max_rows, max_columns, char=None):
"""Изменяет размер сетки, очищает содержимое и изменяет символ фона,
если аргумент char не равен None
"""
assert max_rows > 0 and max_columns > 0, "too small"
global _grid, _max_rows, _max_columns, _background_char
if char is not None:
assert len(char) == 1, _CHAR_ASSERT_TEMPLATE.format(char)
_background_char = char
_max_rows = max_rows
_max_columns = max_columns
_grid = [[_background_char for column in range(_max_columns)]
for row in range(_max_rows)]
Эта функция использует инструкцию assert для обеспечения полити
ки выявления ошибок программирования; в первом случае – ошибки
при попытке установить размеры сетки меньше, чем 1×1. Если символ
фона определен, применяется еще одна инструкция assert, чтобы га
рантировать, что эта строка содержит точно один символ; в противном
случае возбуждается исключение с текстом сообщения из шаблона
_CHAR_ASSERT_TEMPLATE, в котором поле {0} замещается полученной стро
кой char.
К сожалению, мы вынуждены использовать инструкцию global, пото
му что внутри этой функции приходится изменять глобальные пере
менные. Это как раз тот случай, когда на помощь может прийти объ
ектноориентированное программирование, с которым мы познако
мимся в главе 6.
Содержимое для переменной _grid создается с помощью
двух вложенных друг в друга генераторов списков. При
ем с применением оператора дублирования списка, та
кой как [[char] * columns] * rows, не даст должного ре
зультата, потому что внутренние списки будут представ
лять собой всего лишь поверхностные копии одного и то
го же списка. Вместо генераторов списков можно было
бы использовать вложенные циклы for ... in:
_grid = []
for row in range(_max_rows):
_grid.append([])
for column in range(_max_columns):
_grid[1].append(_background_char)
Но такой фрагмент сложнее для понимания и гораздо длиннее, чем ге
нераторы списков.
Поскольку основной целью нашего рассмотрения является реализа
ция модуля, мы рассмотрим лишь одну функцию рисования, чтобы
Генераторы
списков,
стр. 142

Модули и пакеты 247
получить представление о том, как это рисование выполняется. Ниже
приводится функция add_horizontal_line(), поделенная на две части:
def add_horizontal_line(row, column0, column1, char=""):
"""Добавляет в сетку горизонтальную линию, используя указанный символ
>>> add_horizontal_line(8, 20, 25, "=")
>>> char_at(8, 20) == char_at(8, 24) == "="
True
>>> add_horizontal_line(31, 11, 12)
Traceback (most recent call last):
...
RowRangeError
"""
Строка документирования содержит два теста, один из которых, как
предполагается, будет проходить успешно, а другой будет возбуждать
исключение. Задавая в доктестах исключения, необходимо вставлять
строку «Traceback»; она всегда одна и та же и сообщает модулю
doctest, что ожидается исключение. Затем взамен строк с диагностиче
скими сообщениями (количество которых может быть разным) следу
ет указать многоточие и завершить тест строкой с именем исключе
ния, которое ожидается получить. Функция char_at() – одна из тех,
что предоставляется этим модулем; она возвращает символ в заданной
позиции строки и столбца сетки.
assert len(char) == 1, _CHAR_ASSERT_TEMPLATE.format(char)
try:
for column in range(column0, column1):
_grid[row][column] = char
except IndexError:
if not 0 <= row <= _max_rows:
raise RowRangeError()
raise ColumnRangeError()
Реализация функции начинается с той же проверки длины аргумента
char, которая производилась и в функции resize(). Вместо того чтобы
явно проверять аргументы с номерами строки и столбцов, функция ра
ботает в предположении, что аргументы имеют допустимые значения.
Если изза обращения к несуществующей строке или столбцу возбуж
дается исключение IndexError, функция перехватывает его и возбуж
дает соответствующее исключение, характерное для модуля. Такой
стиль программирования соответствует выражению «проще попро
сить прощения, чем разрешения» и считается более свойственным
программированию на языке Python, чем стиль «осмотрись, прежде
чем прыгнуть», при котором проверки выполняются заранее. Реализа
ция с опорой на исключения вместо предварительной проверки явля
ется более эффективной, когда исключения возникают достаточно
редко. (Контрольные инструкции assert мы не относим к стилю «ос
мотрись, прежде чем прыгнуть», потому что такие ошибки никогда не

248 Глава 5. Модули
должны возникать и они часто убираются из окончательной версии
программного кода.)
Практически в самом конце модуля, после определения всех функций,
имеется единственный вызов функции resize():
resize(_max_rows, _max_columns)
Этот вызов инициализирует сетку с размерами по умолчанию (25×80),
чем обеспечивает безопасное использование модуля в импортирующем
программном коде. Без этого вызова импортирующая программа или
модуль должны были бы явно вызывать функцию resize() для ини
циализации сетки, что вынуждало бы программистов помнить об этом
факте и приводило бы к множественным попыткам инициализации.
if __name__ == "__main__":
import doctest
doctest.testmod()
Последние три строки в модуле являются обычными для модулей, ис
пользующих модуль doctest для выполнения доктестов.
Модуль CharGrid имеет один существенный недостаток: он поддержи
вает только одну сетку символов. Одно из решений, избавляющих от
этого недостатка, заключается в создании коллекции сеток, но это по
путно означает, что пользователи модуля должны были бы указывать
ключ или индекс требуемой сетки во всех вызовах функций, чтобы
идентифицировать сетку, над которой выполняется операция. В слу
чаях, когда возникает необходимость иметь несколько экземпляров
объекта, лучшее решение состоит в том, чтобы определить класс (соб
ственный тип данных) и создать столько экземпляров (объектов дан
ного типа), сколько потребуется. Дополнительное преимущество реа
лизации на основе класса заключается в том, что мы можем отказать
ся от использования инструкции global, сохраняя данные в виде атри
бутов (статических элементов) класса. Как создавать классы, мы
узнаем в следующей главе.
Обзор стандартной библиотеки языка Python
Стандартная библиотека языка Python обычно описывается, как «ба
тарейки, входящие в комплект поставки», и обеспечивает доступ к ши
рокому кругу функциональных возможностей, насчитывая в своем со
ставе свыше 200 пакетов и модулей. Этот раздел представляет собой
краткий обзор того, что может предложить стандартная библиотека,
разделенный на тематические подразделы; из обзора исключены паке
ты и модули, представляющие слишком узконаправленный интерес,
а также модули, характерные для той или иной платформы. В ходе
описания будут демонстрироваться небольшие примеры, чтобы дать
представление, что представляют собой те или иные пакеты и модули,

Обзор стандартной библиотеки языка Python 249
а перекрестные ссылки будут указывать страницы в книге, где можно
найти дополнительные сведения об этих пакетах и модулях.
Обработка строк
Модуль string содержит ряд полезных констант, таких как string.ascii_
letters и string.hexdigits. Кроме того, он предоставляет класс string.
Formatter, на основе которого можно создать подкласс, обеспечивающий
собственные средства форматирования.
1 Модуль textwrap может ис
пользоваться для расстановки в тексте символов перевода строки, что
бы ограничить ширину строк заданным значением, а также для
уменьшения отступов.
Модуль struct содержит функции упаковывания и рас
паковывания чисел, логических значений и строк в/из
объекты типа bytes, используя их двоичное представле
ние. Это может потребоваться для организации передачи
данных между программой и низкоуровневыми библио
теками, написанными на языке C. Модули struct и text
wrap используются программой convertincidents.py, опи
сываемой в главе 7.
Модуль difflib содержит классы и методы сравнения последователь
ностей, таких как строки, способные воспроизводить результаты срав
нения как в стандартных форматах «diff», так и в формате HTML.
Самый мощный модуль в языке Python, связанный с обработкой
строк, – это модуль re (regular expression – регулярные выражения).
Он подробно будет рассматриваться в главе 12.
Класс io.StringIO может использоваться для создания объектов, подоб
ных строкам, которые ведут себя как текстовые файлы, размещенные
в памяти. Это может быть удобно, когда необходимо использовать про
граммный код, выполняющий запись в файл, для записи в строку.
Пример: класс io.StringIO
Выполнить запись в текстовый файл в языке Python можно разными
способами. Один из способов состоит в использовании метода write()
объекта файла, другой – в использовании функции print() с именован
ным аргументом file, указывающим на объект файла, открытый для
записи. Например:
print("An error message", file=sys.stdout)
sys.stdout.write("Another error message\n")
1 Создание подкласса (subclassing), или специализация класса (spezializing),
означает создание собственного типа данных (класса) на основе (на базе,
based on) другого класса. Эта тема подробно рассматривается в главе 6.
Ти п д а н н ы хbytes ,
стр. 344
Модуль struct ,
стр. 349

250 Глава 5. Модули
В обоих случаях текст сообщения будет выведен в sys.stdout – объект
файла, представляющий «стандартный поток вывода», который обыч
но связан с консолью и отличается от sys.stderr, «стандартного потока
вывода сообщений об ошибках», только тем, что при работе с послед
ним используется небуферизованный вывод. (Интерпретатор автома
тически создает и открывает sys.stdin, sys.stdout и sys.stderr при за
пуске программы.) По умолчанию функция print() добавляет символ
перевода строки, хотя такое ее поведение можно изменить с помощью
именованного аргумента end, передав в нем пустую строку.
В некоторых ситуациях бывает удобно перехватывать и записывать
в строку все то, что предназначено для вывода в файл. Добиться этого
можно с помощью класса io.StringIO, экземпляр которого можно ис
пользовать как обычный объект файла, который записывает данные
в строку. Если при создании объекта io.StringIO была указана началь
ная строка, его также можно использовать для чтения, как если бы это
был файл.
Выполнив инструкцию import io, мы сможем использовать io.StringIO
для перехвата любой информации, предназначенной для вывода в объ
ект файла, такой как sys.stdout:
sys.stdout = io.StringIO()
Если эту строку поместить в начало программы, вслед за инструкция
ми импорта, но перед любыми инструкциями, использующими
sys.stdout, то любой текст, записываемый в sys.stdout, в действитель
ности будет передаваться объекту io.StringIO, созданному этой строкой
кода и заменившему стандартный объект файла sys.stdout. Теперь при
выполнении приведенных выше строк с вызовами print() и sys.std
out.write() выводимый ими текст будет попадать в объект io.StringIO,
а не на консоль. (Оригинальное значение sys.stdout можно восстано
вить в любой момент, для чего достаточно выполнить инструкцию
sys.stdout = sys.__stdout__.)
Чтобы получить все строки, записанные в объект io.StringIO, можно
вызвать метод io.StringIO.getvalue(). В данном случае вызовом метода
sys.stdout.getvalue() можно получить строку, содержащую весь выво
дившийся текст. Эту строку можно напечатать, сохранить в файл жур
нала или отправить через сетевое соединение, как и любую другую
строку. Немного ниже (на стр. 266) мы увидим еще один пример ис
пользования класса io.StringIO.
Работа с аргументами командной строки
Если нам потребуется иметь возможность в программе обрабатывать
текст, который может быть получен в результате перенаправления
в консоли или находиться в файлах, имена которых перечислены в ко
мандной строке, мы можем воспользоваться функцией fileinput.in
put() из модуля fileinput. Эта функция выполняет итерации по всем

Обзор стандартной библиотеки языка Python 251
строкам, полученным в результате операции перенаправления в кон
соли (если они есть), или по всем строкам из файлов, имена которых
перечислены в командной строке, как будто это единая последователь
ность строк. Модуль может сообщать имя текущего файла и номер
строки с помощью функций fileinput.filename() и fileinput.lineno(),
а также предоставляет возможность работать с некоторыми типами
сжатых файлов.
Для работы с параметрами командной строки в стандартной библиоте
ке имеется два модуля – optparse и getopt. Модуль getopt популярен,
так как он прост в использовании и к тому же давно входит в состав
библиотеки. Модуль optparse более новый и обладает более широкими
возможностями.
Пример: модуль optparse
Вспомните описание программы csv2html.py, которое
приводилось в главе 2. В упражнениях к этой главе мы
предложили расширить программу так, чтобы она могла
принимать аргументы командной строки: аргумент «max
width», принимающий целое число, и аргумент «format»,
принимающий строку. В решении (csv2html2_ans.py)
для обработки аргументов имеется функция объемом
26 строк. Ниже приводится начало функции main() для
csv2html2_opt.py – версии программы, в которой вместо
нашей собственной функции для обработки аргументов
командной строки используется модуль optparse:
def main():
parser = optparse.OptionParser()
parser.add_option("w", "maxwidth", dest="maxwidth", type="int",
help=("the maximum number of characters that can be "
"output to string fields [default: %default]"))
parser.add_option("f", "format", dest="format",
help=("the format used for outputting numbers "
"[default: %default]"))
parser.set_defaults(maxwidth=100, format=".0f")
opts, args = parser.parse_args()
Для обработки аргументов потребовалось всего девять строк про
граммного кода плюс строка с инструкцией import optparse. Кроме то
го, нам не пришлось явно обрабатывать параметры
–h и ––help – эти па
раметры обслуживаются самим модулем optparse, который для вывода
соответствующего сообщения использует текст из именованных аргу
ментов help, где текст «%default» замещается значениями по умолча
нию соответствующих параметров.
Обратите также внимание, что теперь параметры можно указывать
в привычном для системы UNIX стиле – как с помощью коротких, так
с помощью длинных имен параметров, начинающихся с символа дефи
Пример csv2html.
py, стр. 119

252 Глава 5. Модули
са. Короткие имена удобны для организации взаимодействий с пользо
вателем в консоли, а длинные имена более понятны при использова
нии в сценариях командной оболочки. Например, чтобы ограничить
максимальную ширину 80 символами, мы можем использовать любой
из следующих вариантов определения параметра:
–w80, –w 80, ––max
width=80 или
––maxwidth 80. После разбора параметров командной стро
ки доступ к их значениям можно получить с помощью имен, указы
ваемых в аргументах dest, например, opts.maxwidth и opts.format. Все
аргументы командной строки, которые не были обработаны (обычно
это имена файлов), помещаются в список args.
Если в процессе разбора командной строки возникает ошибка, синтак
сический анализатор модуля optparse произведет вызов sys.exit(2).
Это приведет к завершению программы и возврату операционной сис
теме числа 2 в качестве возвращаемого значения программы. Тради
ционно значение 2 свидетельствует об ошибке в использовании про
граммы, значение 1 используется для индикации об ошибках любого
другого типа и значение 0 означает благополучное завершение. Когда
функция sys.exit() вызывается без аргументов, операционной системе
возвращается значение 0.
Математические вычисления и числа
В дополнение к встроенным типам чисел int, float и complex библиоте
ка предоставляет числовые типы decimal.Decimal и fractions.Fraction.
Также в библиотеке имеется три математические библиотеки: math, со
держащая стандартные математические функции; cmath – математиче
ские функции для работы с комплексными числами; random, содержа
щая множество функций генерации случайных чисел. Все эти модули
были представлены в главе 2.
В модуле numbers имеются различные числовые абстрактные классы
языка Python (классы, которые могут наследоваться, но которые не
могут использоваться непосредственно). Их удобно использовать для
проверки того, что объект, пусть это будет x, принадлежит к любому
числовому типу с помощью вызова isinstance(x, numbers.Number) или
к какомунибудь определенному типу, например, isinstance(x, num
bers.Integral).
Специалисты, занимающиеся программированием научных или ин
женерных вычислений, найдут полезным пакет NumPy, разрабатывае
мый сторонними разработчиками. Этот пакет предоставляет высоко
эффективную реализацию многомерных массивов, основных функций
линейной алгебры и преобразований Фурье, а также инструменты ин
теграции с программным кодом на языках C, C++ и Fortran. Пакет
SciPy включает NumPy и дополняет его модулями, предназначенными
для выполнения статистических вычислений, обработки сигналов
и изображений, модулями с генетическими алгоритмами и многими
другими. Оба пакета доступны бесплатно на сайте www.scipy.org.

Обзор стандартной библиотеки языка Python 253
Время и дата
Модули calendar и datetime содержат функции и классы, предназначен
ные для работы с датами и временем. Однако они основаны на абст
рактном Григорианском календаре, поэтому они не годятся для рабо
ты с датами в календарях, предшествовавших Григорианскому. Дата
и время – это очень сложная тема. В разное время и в разных местах
использовались разные календари. Продолжительность суток не рав
на точно 24 часам, продолжительность года не равна точно 365 дням,
существует летнее и зимнее время, а также различные часовые пояса.
Класс datetime.datetime (но не в классе datetime.date) предоставляет
поддержку работы с часовыми поясами, хотя она не включается по
умолчанию. Однако имеются модули сторонних производителей, ко
торые с успехом восполняют этот недостаток, например, dateutil
(www.labix.org/pythondateutil) и mxDateTime (www.egenix.com/products/
python/mxBase/mxDateTime).
Модуль time используется для работы с отметками времени, которые
являются простыми числовыми значениями, представляющими чис
ло секунд, прошедших от начала эпохи (19700101T00:00:00 в UNIX).
Этот модуль может использоваться для получения на машине отметок
текущего времени UTC (Coordinated Universal Time – универсальное
глобальное время) или локального времени, учитывающего переход
на летнее время, а также для создания строк, представляющих дату,
время и дату/время, отформатированных разными способами. Кроме
того, он может использоваться для анализа строк, содержащих дату
ивремя.
Пример: модули calendar, datetime и time
Объекты типа datetime.datetime обычно создаются программным спо
собом, тогда как объекты, хранящие дату/время UTC, обычно получа
ют информацию из внешних источников, таких как время создания
файла. Ниже приводится несколько примеров:
import calendar, datetime, time
moon_datetime_a = datetime.datetime(1969, 7, 20, 20, 17, 40)
moon_time = calendar.timegm(moon_datetime_a.utctimetuple())
moon_datetime_b = datetime.datetime.utcfromtimestamp(moon_time)
moon_datetime_a.isoformat() # вернет: '19690720T20:17:40'
moon_datetime_b.isoformat() # вернет: '19690720T20:17:40'
time.strftime("%Y%m%dT%H:%M:%S", time.gmtime(moon_time))
Переменная moon_datetime_a является объектом типа datetime.datetime
и хранит дату и время посадки корабля «Аполлон 11» на поверхность
Луны. Переменная moon_time имеет тип int и хранит число секунд, про
шедших от начала эпохи до момента посадки на Луну. Это число воз
вращает функция calendar.timegm(), которая принимает объект типа
time_struct, возвращаемый функцией datetime.datetime.utctimetuple(),
и возвращает число секунд, которое представляет тип time_struct.

254 Глава 5. Модули
(Поскольку посадка на Луну произошла до начала эпохи UNIX, число
получится отрицательным.) Переменная moon_datetime_b является объ
ектом типа datetime.datetime, и ее значение было получено из целочис
ленной переменной moon_time, чтобы продемонстрировать возможность
преобразования числа секунд, прошедших с начала эпохи в объект ти
па datetime.datetime.
1 Последние три строки возвращают идентичные
строки, содержащие дату/время в формате ISO 8601.
Текущие дату/время UTC можно получить в виде объекта datetime.da
tetime, вызвав функцию datetime.datetime.utcnow(), а в виде числа се
кунд, прошедших с начала эпохи, – вызвав функцию time.time(). Для
получения локальных даты/времени можно использовать datetime.da
tetime.now() или time.mktime(time.localtime ()).
Алгоритмы и типы коллекций
Модуль bisect содержит функции поиска в отсортированных последо
вательностях, таких как отсортированные списки, а также функции
вставки элементов с сохранением порядка сортировки. Функции этого
модуля используют алгоритм поиска методом половинного деления,
поэтому они отличаются очень высокой скоростью работы. Модуль
heapq содержит функции для преобразования последовательности, та
кой как список, в «кучу» – разновидности коллекции, где первым эле
ментом (в позиции с индексом 0) всегда является наименьший эле
мент, и функции для добавления и удаления элементов, при которых
последовательность остается кучей.
Пакет collections содержит определения таких типов
данных, как словарь collections.defaultdict и кортеж
collections.namedtuple, которые уже рассматривались ра
нее. Кроме того, в этом модуле объявляются типы дан
ных collections.UserList и collections.UserDict, хотя на
практике чаще используются встроенные подклассы ти
пов list и dict, чем эти типы данных. Еще один тип дан
ных – collections.deque – похож на список, но если спи
сок обеспечивает очень быстрое добавление и удаление
элементов в конце списка, то очереди collections.deque
обеспечивают очень быстрое добавление и удаление эле
ментов на обоих концах очереди – как в конце, так
ивначале.
В пакете collections также присутствуют определения нечисловых аб
страктных классов Python (классы, которые могут наследоваться, но
которые нельзя использовать непосредственно). Они будут обсуждать
ся в главе 8.
1 К сожалению, в системе Windows функция datetime.datetime.utcfromtimes
tamp() не может обрабатывать отрицательные отметки времени, то есть от
метки времени, предшествующие дате 1 января 1970 года.
Словари со
значениями
по умолча
нию, стр. 161
Именованные
кортежи,
стр. 134

Обзор стандартной библиотеки языка Python 255
Модуль array содержит определение типа последовательности array.ar
ray, способной хранить числа или символы весьма экономным спосо
бом. Этот тип данных напоминает списки, за исключением того, что
объекты этого типа могут хранить только элементы определенного ти
па, который определяется на этапе его создания, поэтому, в отличие от
списков, они не могут одновременно хранить объекты разных типов.
Упоминавшийся ранее пакет NumPy также предоставляет эффективную
реализацию массивов.
Модуль weakref содержит средства создания слабых ссылок, которые
ведут себя подобно обычным ссылкам на объекты, за исключением то
го, что если единственная ссылка на объект – слабая ссылка, то такой
объект может считаться готовым к утилизации. Это предотвращает со
хранение объекта в памяти изза присутствия ссылки на него. Естест
венно, имеется возможность проверить существование объекта, на ко
торый указывает слабая ссылка, и при его наличии мы можем с помо
щью этой ссылки обратиться к объекту.
Пример: модуль heapq
Модуль heapq содержит средства преобразования списка в кучу, а также
для добавления элементов в кучу и удаления их из кучи, сохраняя по
рядок следования элементов в списке, характерный для кучи. Куча –
это двоичное дерево, обладающее свойствами кучи, когда первый эле
мент (находящийся в позиции с индексом 0) является самым малень
ким.
1 Каждое поддерево в куче также является кучей, поэтому любое
поддерево тоже обладает всеми свойствами кучи. Ниже показано, как
можно создать кучу с чистого листа:
import heapq
heap = []
heapq.heappush(heap, (5, "rest"))
heapq.heappush(heap, (2, "work"))
heapq.heappush(heap, (4, "study"))
Если список уже существует, его можно преобразовать в кучу с помо
щью функции heapq.heapify(alist), которая выполнит необходимое пе
реупорядочивание элементов списка. Наименьший элемент может
быть удален из кучи с помощью функции heapq.heappop(heap).
for x in heapq.merge([1, 3, 5, 8], [2, 4, 7], [0, 1, 6, 8, 9]):
print(x, end=" ") # выведет: 0 1 1 2 3 4 5 6 7 8 8 9
Функция heapq.merge() принимает произвольное число отсортирован
ных итерируемых объектов в виде аргументов и возвращает итератор,
позволяющий выполнить итерации по всем элементам всех итерируе
мых объектов в порядке возрастания.
1 Строго говоря, модуль heapq реализует тип кучи min heap. Кучи, где первый
элемент всегда является наибольшим, относятся к типу max heap.

256 Глава 5. Модули
Форматы файлов, кодировки и сохранение данных
Стандартная библиотека имеет обширную поддержку
стандартных форматов файлов и кодировок. Модуль
base64 содержит функции чтения и записи с использова
нием кодировок Base16, Base32 и Base64 в соответствии
с RFC 3548.
1 Модуль quopri содержит функции чтения
и записи в формате «quotedprintable». 2 Этот формат оп
ределяется документом RFC 1521 и используется для
представления данных MIME (Multipurpose Internet Mail
Extensions – многоцелевые расширения электронной поч
ты Интернета). Модуль uu содержит функции чтения и за
писи данных в формате uuencode. Документ RFC 1832 оп
ределяет «External Data Representation Standard» (стан
дарт представления внешних данных), а модуль xdrlib со
держит функции чтения и записи данных в этом формате.
Существуют также модули, предоставляющие возможность чтения
и записи архивных файлов наиболее популярных форматов. Модуль
bz2 обеспечивает возможность работы с файлами .bz2, модуль gzip обес
печивает возможность работы с файлами .gz, модуль tarfile обеспечи
вает возможность работы с файлами .tar, .tar.gz (а также .tgz) и .tar.bz2
и модуль zipfile обеспечивает возможность работы с файлами .zip.
В этом подразделе мы увидим пример использования модуля tarfile,
а немного ниже (на стр. 266) будет представлен небольшой пример,
в котором используется модуль gzip. Еще раз с модулем gzip мы встре
тимся в главе 7.
Кроме того, стандартная библиотека обеспечивает поддержку некото
рых форматов представления аудиоданных – например, модуль aifc
реализует поддержку формата AIFF (Audio Interchange File Format –
формат файлов для обмена аудиоданными) и модуль wave обеспечивает
возможность для работы с файлами .wav (несжатыми). Некоторыми
разновидностями аудиоданных можно манипулировать с помощью
модуля audioop, а модуль sndhdr предоставляет пару функций, позво
ляющих определить тип аудиоданных, хранящихся в файле, и некото
рые характеристики этих данных, такие как частота дискретизации.
Формат представления конфигурационных файлов (подобный форма
ту файлов .ini в системе Windows) определяется документом RFC 822,
1 RFC (Request for Comments – запрос на комментарии и предложения) – это
документы, используемые для определения различных интернеттехноло
гий. Каждый документ имеет уникальный идентификационный номер,
и многие из них со временем становятся официальными стандартами.
2 Способ 7битной кодировки, когда символы, не входящие в набор ASCII,
преобразуются в их шестнадцатеричные коды, записанные латиницей. –
Прим. перев.
Кодировки
символов,
стр. 112

Обзор стандартной библиотеки языка Python 257
а модуль configparser предоставляет функции чтения и записи таких
файлов.
Многие приложения, такие как Excel, могут читать и писать данные
в формате CSV (Comma Separated Value – значения, разделенные запя
тыми) или в его разновидностях, таких как значения, разделенные
символами табуляции. Модуль csv обеспечивает средства чтения и за
писи этих форматов и в состоянии учитывать некоторые особенности,
препятствующие возможности непосредственной обработки файлов
CSV.
В дополнение к поддержке различных форматов файлов стандартная
библиотека содержит пакеты и модули, обеспечивающие средства со
хранения данных. Модуль pickle используется для сохранения на дис
ке и восстановления с диска произвольных объектов Python (включая
целые коллекции) – подробнее об этом модуле рассказывается в гла
ве 7. Помимо этого, стандартная библиотека поддерживает файлы
DBM различных типов – эти файлы напоминают словари за исключе
нием того, что их содержимое хранится на диске, а не в памяти, а их
ключи и значения должны быть либо объектами типа bytes, либо
строками. Модуль shelve, описываемый в главе 11, может использо
ваться для работы с файлами DBM со строковыми ключами и произ
вольными объектами Python в качестве значений – модуль незамет
но для пользователя преобразует объекты Python в объекты типа
bytes и обратно. Модули для работы с файлами DBM, прикладной
программный интерфейс к базам данных и использование встроен
ной базы данных SQLite рассматриваются в главе 11.
Пример: модуль base64
Модуль base64 главным образом используется для обработки двоичных
данных, внедренных в сообщения электронной почты в виде текста
ASCII. Он также может использоваться для сохранения двоичных дан
ных в файлах с расширением .py. Первый шаг состоит в том, чтобы
преобразовать двоичные данные в формат Base64. В следующем фраг
менте предполагается, что модуль base64 уже был импортирован, а путь
к файлу .png хранится в переменной left_align_png:
binary = open(left_align_png, "rb").read()
ascii_text = ""
for i, c in enumerate(base64.b64encode(binary)):
if i and i % 68 == 0:
ascii_text += "\\\n"
ascii_text += chr(c)
Этот фрагмент программного кода читает файл в режиме
двоичного доступа и преобразует его в строку символов
ASCII, в формате Base64. После каждого шестьдесят вось
мого символа к строке добавляется комбинация символа
обратного слеша и перевода строки. Это ограничивает
left_align.png
Ти п д а н н ы хbytes ,
стр. 344

258 Глава 5. Модули
ширину строк 68 символами ASCII и гарантирует, что при обратном
чтении данных символы перевода строки будут проигнорированы (по
тому что символы обратного слеша экранируют их). Текст ASCII, по
лученный таким способом, может сохраняться в виде литерала типа
bytes в файле с расширением .py, например:
LEFT_ALIGN_PNG = b"""\
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAA\
...
bmquu8PAmVT2+CwVV6rCyA9UfFMCkI+bN6p18tCWqcUzrDOwBh2zVCR+JZVeAAAAAElF\
TkSuQmCC"""
Мы опустили большую часть строк, заместив их многоточием.
Данные могут быть преобразованы обратно в первоначальный формат,
как показано ниже:
binary = base64.b64decode(LEFT_ALIGN_PNG)
Двоичные данные могут быть записаны в файл с помощью цепочки
вызовов: open(filename, "wb").write(binary). Двоичные данные в фай
лах .py занимают значительно больше места, чем в оригинальной фор
ме, но такая возможность может быть полезной, когда нам потребует
ся написать программу, хранящую все необходимые двоичные данные
в виде единственного файла .py.
Пример: модуль tarfile
В большинстве версий Windows отсутствует встроенная поддержка ра
боты с форматом .tar, который очень широко используется в системах
UNIX. Этот недостаток легко можно ликв