Самое полное руководство по разработке на Python в примерах от сообщества Stack Overflow [Коллектив авторов] (pdf) читать онлайн

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]

САМОЕ ПОЛНОЕ
РУКОВОДСТВО
ПО РАЗРАБОТКЕ

в примерах от сообщества
Stack Overflow

ПРОГРАММИРОВАНИЕ
ОТ ЭКСПЕРТОВ

САМОЕ ПОЛНОЕ
РУКОВОДСТВО
ПО РАЗРАБОТКЕ

PYTHON
в примерах от сообщества
Stack Overflow

МОСКВА
ИЗДАТЕЛЬСТВО АСТ

УДК 004.42
ББК 32.973.26-018.2
С17

Последнюю версию этой книги на английском языке можно скачать с сайта:
https://GoalKicker.com/PythonBook. Пожалуйста, не стесняйтесь поделиться этим
PDF-файлом с кем угодно бесплатно.
Книга Python® Notes for Professionals составлена на основе документации
Stack Overflow (https://archive.org/details/documentation-dump.7z), содержание написано прекрасными людьми из Stack Overflow. Текстовое содержимое предоставлено на условиях Creative Commons BY-SA. В конце книги указаны авторы,
внесшие вклад в создание различных глав. Авторские права на изображения принадлежат их соответствующим владельцам, если не указано иное.

С17

Самое полное руководство по разработке на Python в примерах
от сообщества Stack Overflow. — Москва : Издательство АСТ, 2024. —
672 с. : ил. — (Программирование от экспертов).
ISBN 978-5-17-160252-9.

Данное руководство по программированию на одном из широко распространенных языков — Python — основано на практических примерах кодов, написанных
специалистами и экспертами сообщества Stack Overflow, в котором лучшие разработчики программного обеспечения со всего мира делятся своими знаниями и опытом,
отвечая на многие технические вопросы. Опытные Python-программисты найдут
в книге множество примеров кода с подробными комментариями, что поможет им
усовершенствовать свои навыки и достичь новых высот в отрасли. Однако данное
издание будет полезно и начинающим специалистам с минимальным опытом
и уровнем знаний, так как содержит исчерпывающее объяснение важнейших
концепций Python с примерами, которые позволят избежать погружения в сухую
теорию и помогут быстро повысить уровень своих компетенций. Читатели найдут
здесь мощный и универсальный инструментарий для профессиональной работы
в самых разных областях применения: с базами данных, веб-фреймворком Flask,
XML и JSON, звуковыми данными, синтаксическим анализатором Lex-Yacc, а также
при сетевом программировании, визуализации данных, многопоточности и многопроцессорности, программировании «интернета вещей». Кроме того, в книге
представлена информация о применении Python в сфере науки, например, в математике, химии и криптографии. Отдельные главы посвящены секретам повышения
скорости работы Python-кода и оптимизирования его производительности.
УДК 004.42
ББК 32.973.26-018.2

ISBN 978-5-17-160252-9

Перевод на русский язык: ООО «Интеджер».
Издание на русском языке: ООО «Издательство АСТ».

Содержание
Глава 1. Начало работы с языком Py1hon ........................................................................................27
1.1. Начало работы .........................................................................................................................27
1.2. Создание переменных и присвоение им значений...........................................................31
1.3. Отступы блоков........................................................................................................................34
1.4. Типы данных.............................................................................................................................36
1.5. Типы коллекций .......................................................................................................................39
1.6. IDLE - графический интерфейс Py1hon ................................................................................ 43
1.7. Ввод данных пользователем ................................................................................................44
1.8. Встроенные модули и функции .............................................................................................45
1.9. Создание модуля .....................................................................................................................47
1.1 О. Установка Py1hon 2.7.х и 3.х.................................................................................................48
1.11. Строковые функции - str() и герг() .....................................................................................50
1.12. Установка внешних модулей с помощью pip ...................................................................51
1.13. Справочная утилита..............................................................................................................52
Глава 2. Типы данных в Python ...........................................................................................................53
2.1. Строковый тип данных ...........................................................................................................53
2.2. Множества (set и frozenset) ...................................................................................................53
2.3. Числовые типы данных .........................................................................................................54
2.4. Тип данных "список" (list) .......................................................................................................54
2.5. Тип данных "словарь" (dic) .....................................................................................................54
2.6. Тип данных "кортеж" (tuple) ...................................................................................................54
Глава 3. Отступы ...................................................................................................................................55
3.1. Простой пример .......................................................................................................................55
3.2. Как происходит разбор отступов..........................................................................................55
3.3. Ошибки отступа........................................................................................................................56
Глава 4. Комментарии и документация ............................................................................................56
4.1. Однострочные, строчные и многострочные комментарии ............................................. 56
4.2. Программный доступ к строкам документации (docstrings) .......................................... 57
4.3. Написание документации с использованием docstrings ................................................. 58
Глава 5. Дата и время ..........................................................................................................................60
5.1. Разбор строки в объект даты и времени (datetime) с учетом часового пояса ........... 60
5.2. Построение временных интервалов с учетом временных зон ...................................... 60
5.3. Вычисление разницы во времени........................................................................................62
5.4. Использование базовых объектов datetime ......................................................................62
5.5. Переключение между часовыми поясами ......................................................................... 63
5.6. Простые арифметические действия с датами...................................................................63
5.7. Преобразование временной метки (timestamp) в объект datetime ............................... 64
5.8. Точное вычитание месяцев из даты ................................................................................... 64
5.9. Разбор (парсинг) произвольной временной метки ISO 8601 с минимальным
использованием библиотек .........................................................................................................64
5.1 О. Получение временной метки ISO 8601 .............................................................................. 65
5.11. Разбор строки с коротким именем часового пояса в объект datetime с учетом
часового пояса ................................................................................................................................ 65
5.12. Нечеткий синтаксический анализ времени
(извлечение даты и времени из текста) ....................................................................................66
5.13. Итерация по датам ................................................................................................................66
Глава 6. Форматирование даты .........................................................................................................67
6.1. Время между двумя датами ..................................................................................................67
6.2. Вывод объекта datetime в строку .........................................................................................67
6.3. Парсинг строки в объект datetime ........................................................................................67

4

Содержание

Глава 7. Модуль Enum ..........................................................................................................................68
7.1. Создание перечисления (PY1hon 2.4-3.3} ...........................................................................68
7.2. Итерация ...................................................................................................................................68
Глава 8. Множества .............................................................................................................................. 68
8.1. Операции над множествами .................................................................................................68
8.2. Получение уникальных элементов списка .........................................................................69
8.3. Множество множеств .............................................................................................................70
8.4. Операции над множествами с использованием методов и встроенных модулей ....70
8.5. Множества и мультимножества ...........................................................................................72
Глава 9. Простые математические операторы ...............................................................................72
9.1. Деление .....................................................................................................................................73
9.2. Сложение ..................................................................................................................................74
9.3. Возведение в степень .............................................................................................................74
9.4. Тригонометрические функции ..............................................................................................75
9.5. Операторы присваивания ("операции на месте") ..............................................................76
9.6. Вычитание.................................................................................................................................76
9.7. Умножение ................................................................................................................................ 77
9.8. Логарифмы ..............................................................................................................................77
9.9. Остаток от деления .................................................................................................................77
Глава 1О. Побитовые операторы........................................................................................................78
10.1. Побитовое НЕ ........................................................................................................................78
10.2. Побитовое XOR (исключающее ИЛИ} ...............................................................................80
10.3. Побитовое И ..........................................................................................................................80
10.4. Побитовое ИЛИ .....................................................................................................................80
10.5. Побитовый сдвиг влево ...................................................................................................... 81
10.6. Побитовый сдвиг вправо .................................................................................................... 81
10.7. Операторы присваивания ("операции на месте") ............................................................81
Глава 11. Логические операции ......................................................................................................... 82
11.1. "and" и "ог" не гарантируют возврата булевого значения ............................................. 82
11.2. Простой пример ..................................................................................................................... 82
11.3. Вычисления по короткой схеме ("короткое замыкание") ..............................................82
11.4. Оператор "and" ("и") ............................................................................................................... 83
11.5. Оператор "ог" ("или") ............................................................................................................. 83
11.6. Оператор "not" ("не") .............................................................................................................. 84
Глава 12. Приоритет операторов ....................................................................................................... 84
12.1. Примеры приоритета операторов в PY1hon ......................................................................84
Глава 13. Область видимости и привязка переменных ................................................................85
13.1. Нелокальные переменные .................................................................................................. 85
13.2. Глобальные переменные ..................................................................................................... 86
13.3. Локальные переменные ...................................................................................................... 87
13.4. Команда "del" ..........................................................................................................................87
13.5. Функции пропускают область видимости класса при поиске имен............................ 88
13.6. Локальные и глобальные области ..................................................................................... 89
13.7. Возникновение привязки ....................................................................................................92
Глава 14. Условные выражения ........................................................................................................ 92
14.1. Условное выражение ("тернарный оператор") ............................................................... 92
14.2. if, elif и else ............................................................................................................................ 92
14.3. Значения истинности .......................................................................................................... 93
14.4. Выражения булевой логики ............................................................................................... 93
14.5. Использование функции "cmp" для получения результата сравнения
двух объектов ................................................................................................................................ 95

Содержание

5

14.6. Оператор "else" ..................................................................................................................... 95
14.7. Проверка принадлежности объекта к None и его присвоение ................................... 95
14.8. Оператор "if" .......................................................................................................................... 96
Глава 15. Сравнение ............................................................................................................................ 96
15.1. Цепное сравнение ................................................................................................................ 96
15.2. Сравнение по принципу "is" и "==" .................................................................................... 97
15.3. Больше или меньше ............................................................................................................. 98
15.4. Не равно ................................................................................................................................. 98
15.5. Равно ...................................................................................................................................... 99
15.6. Сравнение объектов ........................................................................................................... 99
Глава 16. Циклы ................................................................................................................................. 100
16.1. Перерыв и продолжение в циклах .................................................................................. 100
16.2. Циклы For ............................................................................................................................ 102
16.3. Итерирование по спискам ................................................................................................ 102
16.4. Циклы с "else" ...................................................................................................................... 103
16.5. Заявление о прохождении ............................................................................................... 105
16.6. Итерация в словарях ......................................................................................................... 105
16.7. "Полуцикл" do-while ............................................................................................................ 106
16.8. Циклирование и распаковка ............................................................................................ 1Об
16.9. Итерирование части списка с разным размером шага .............................................. 107
16.1 О. Цикл While ......................................................................................................................... 108
Глава 17. Массивы............................................................................................................................. 108
17.1. Доступ к отдельным элементам через индексы.......................................................... 109
17.2. Введение в массивы.......................................................................................................... 109
17.3. Добавление произвольного значения в массив с помощью метода append{}....... 11О
17.4. Вставка значения в массив с помощью метода insert() ............................................ 11 О
17.5. Расширение массива с помощью метода extend(} ...................................................... 11 О
17.6. Добавление элементов из списка в массив с помощью метода fromlist() ............. 11 О
17.7. Удаление любого элемента массива с помощью метода remove() .......................... 11 О
17.8. Удаление последнего элемента массива с помощью метода рор() ........................ 11 О
17.9. Получение любого элемента по его индексу с помощью метода index() ............... 111
17.1О. Обратное преобразование массива с помощью метода reverse() ......................... 111
17.11. Получение информации о буфере массива с помощью метода buffer_info () ....... 111
17.12. Проверка количества вхождений элемента с помощью метода count () .............. 111
17.13. Преобразование массива в строку с помощью метода tostring () .......................... 111
17.14. Преобразование массива в список с одинаковыми элементами
с использованием метода tolist () ............................................................................................ 111
17.15. Добавление строки в массив char, используя метод fromstring () .......................... 112
Глава 18. Многомерные массивы................................................................................................... 112
18.1. Списки в списках ................................................................................................................ 112
18.2. Списки в списках в списках в ... ...................................................................................... 113
Глава 19. Словарь .............................................................................................................................. 113
19.1. Введение в словарь ........................................................................................................... 113
19.2. Избегание исключений KeyError ...................................................................................... 114
19.3. Итерация по словарю ....................................................................................................... 115
19.4. Словарь со значениями по умолчанию ........................................................................ 115
19.5. Слияние словарей .............................................................................................................. 116
19.6. Доступ к ключам и значениям......................................................................................... 116
19.7. Доступ к значениям словаря ........................................................................................... 117
19.8. Создание словаря .............................................................................................................. 117
19.9. Создание упорядоченного словаря ................................................................................ 118
19.1 О. Распаковка словарей с помощью оператора ** ......................................................... 118

6

Содержание
19.11.
19.12.
19.13.
19.14.

Запятая в конце строки ................................................................................................... 119
Конструктор dict() ............................................................................................................ 119
Пример словарей ............................................................................................................. 119
Все комбинации значений словаря .............................................................................. 119

Глава 20. Список ................................................................................................................................ 120
20.1. Методы перечисления и поддерживаемые операторы ............................................. 120
20.2. Доступ к значениям списка ............................................................................................. 125
20.3. Проверка списка на пустоту ............................................................................................. 126
20.4. Итерирование по списку .................................................................................................. 126
20.5. Проверка наличия элемента в списке .......................................................................... 126
20.6. Функции "апу" и "all" ........................................................................................................... 127
20.7. Реверсирование элементов списка ............................................................................... 127
20.8. Конкатенация и слияние списков ................................................................................... 128
20.9. Длина списка ..................................................................................................................... 129
20.1О. Удаление дублирующихся значений в списке ........................................................... 129
20.11. Сравнение списков .......................................................................................................... 129
20.12. Доступ к значениям во вложенном списке ................................................................ 129
20.13. Инициализация списка с фиксированным числом элементов .............................. 130
Глава 21. Генератор списков ........................................................................................................... 131
21.1. Списковые вычисления ................................................................................................... 131
21.2. Условные генераторы списков ........................................................................................ 133
21.3. Избегание повторных и ресурсоемких операций с помощью условного
предложения ................................................................................................................................ 134
21.4. Генератор словаря (словарь включений} ...................................................................... 136
21.5. Генераторы списков с вложенными циклами ............................................................. 137
21.6. Генераторные выражения ................................................................................................ 138
21.7. Генераторы наборов .......................................................................................................... 140
21.8. Рефакторинг функций filter и map в генераторы списков .......................................... 140
21.9. Генераторы с использованием кортежей ..................................................................... 141
21.1О. Подсчет вхождений при использованиии генераторов ............................................ 142
21.11. Изменение типов в списке ............................................................................................. 142
21.12. Вложенные генераторы списков ................................................................................... 142
21.13. Итерация двух и более списков одновременно внутри генератора списка .......... 143
Глава 22. Срезы списков (выделение частей списков) .............................................................. 143
22.1. Использование третьего аргумента "шаг" ..................................................................... 143
22.2. Выбор подсписка из списка ............................................................................................. 144
22.3. Реверсирование списка с помощью среза ................................................................... 144
22.4. Смещение списка с помощью среза .............................................................................. 144
Глава 23. Метод groupby() ................................................................................................................. 145
23.1. Пример ................................................................................................................................. 145
23.2. Пример 2 .............................................................................................................................. 146
23.3. Пример 3 .............................................................................................................................. 146
Глава 24. Связные списки ................................................................................................................ 147
24.1. Пример ОДНОСВЯЗНОГО списка .......................................................................................... 147
Глава 25. Узел связного списка ...................................................................................................... 150
25.1. Написание простого узла связного списка на языке Python ..................................... 150
Глава 26. Фильтр ................................................................................................................................ 151
26.1. Основное использование фильтра ................................................................................. 151
26.2. Фильтр без функции ........................................................................................................... 151
26.3. Фильтр для проверки на "короткое замыкание"
(вычисления по короткой схеме) ............................................................................................. 152

Содержание

7

26.4. Дополняющая функция: filterfalse, ifilterfalse ................................................................ 152
Глава 27. Модуль "Heapq" ................................................................................................................. 153
27.1. Самые большие и самые маленькие предметы в коллекции .................................. 153
27.2. Наименьший элемент в коллекции ................................................................................ 154
Глава 28. Кортеж (tuple) .................................................................................................................... 154
28.1. Кортеж .................................................................................................................................. 154
28.2. Кортежи являются неизменяемыми .............................................................................. 155
28.3. Упаковка и распаковка кортежей ................................................................................... 155
28.4. Встроенные функции кортежей ....................................................................................... 156
28.5. Кортежи являются поэлементно хешируемыми и сравниваемыми ........................ 157
28.6. Индексирование кортежей ............................................................................................... 157
28.7. Реверсирование элементов ............................................................................................. 158
Глава 29. Основы ввода и вывода данных ................................................................................... 158
29.1. Использование функции вывода "print" ......................................................................... 158
29.2. Ввод из файла .................................................................................................................... 158
29.3. Чтение из stdin ................................................................................................................... 160
29.4. Использование функций input() и raw_input() ................................................................ 160
29.5. Функция запроса числа у пользователя ........................................................................ 161
29.6. Печать строки без новой строки в конце ...................................................................... 161
Глава 30. Ввод/вывод файлов и папок .......................................................................................... 162
30.1. Режимы работы с файлами ............................................................................................. 162
30.2. Построчное чтение file....................................................................................................... 164
30.3. Итерация файлов (рекурсивно) ....................................................................................... 164
30.4. Получение полного содержимого файла ...................................................................... 165
30.5. Запись в файл ..................................................................................................................... 165
30.6. Проверка наличия файла или пути ................................................................................. 166
30.7. Произвольный доступ к файлам с помощью mmap ................................................... 167
30.8. Замена текста в файле ...................................................................................................... 167
30.9. Проверка того, что файл пуст ........................................................................................... 167
30.1 О. Чтение файла в диапазоне строк .................................................................................. 168
30.11. Копирование дерева каталогов .................................................................................... 168
30.12. Копирование содержимого файла в другой файл ..................................................... 168
Глава 31. Модуль os.path .................................................................................................................. 168
31.1. Объединение путей ............................................................................................................ 168
31.2. Управление компонентами пути ..................................................................................... 169
31.3. Получение родительского каталога ................................................................................ 169
31.4. Проверка существования пути ........................................................................................ 169
31.5. Проверка того, является ли данный путь каталогом, файлом, символической
ссылкой, точкой монтирования ................................................................................................ 169
31.6. Абсолютный путь из относительного пути ................................................................... 169
Глава 32. Итерируемые типы данных и итераторы ..................................................................... 170
32.1. Итератор (iterator), итерируемый объект (iteraЫe) и генератор (generator) .............. 170
32.2. Извлечение значений по одному ................................................................................... 171
32.3. Итерирование по всему итерируемому .......................................................................... 171
32.4. Проверка только одного элемента в итерируемом ..................................................... 171
32.5. Что может быть итерируемым ......................................................................................... 171
32.6. В итератор нельзя входить повторно! ............................................................................ 172
Глава 33. Функции.............................................................................................................................. 172
33.1. Создание и вызов простых функций ............................................................................. 172
33.2. Определение функции с произвольным числом аргументов .................................... 174

8

Содержание
33.3. Лямбда-функции (встроенные/анонимные) ................................................................ 176
33.4. Создание функции с необязательными аргументами ................................................ 178
33.5. Определение функции с необязательными изменяемыми аргументами ............... 178
33.6. Передача аргументов и изменяемость .......................................................................... 179
33.7. Возврат значений из функций ........................................................................................ 180
33.8. Замыкание (closure) ........................................................................................................... 181
33.9. Принудительное использование именованных параметров ..................................... 182
33.1О. Вложенные функции ........................................................................................................ 182
33.11. Предел рекурсии .............................................................................................................. 183
33.12. Рекурсивная лямбда-функция с использованием присваиваемой
переменной................................................................................................................................... 183
33.13. Рекурсивные функции ..................................................................................................... 184
33.14. Определение функции с аргументами.......................................................................... 185
33.15. Распаковка итерируемых объектов и словарей......................................................... 185
33.16. Определение функции с несколькими аргументами ................................................. 187

Глава 34. Создание функций со списочными аргументами ...................................................... 187
34.1. Функция и вызов ................................................................................................................ 187
Глава 35. Функциональное программирование в Python ........................................................... 188
35.1. Лямбда-функция ................................................................................................................. 188
35.2. Функция map ....................................................................................................................... 188
35.3. Функция reduce ................................................................................................................... 188
35.4. Функция filter ....................................................................................................................... 188
Глава 36. Частичные функции ......................................................................................................... 189
36.1. Возведение в степень ........................................................................................................ 189
Глава 37. Декораторы ....................................................................................................................... 189
37.1. Функция-декоратор ............................................................................................................ 190
37.2. Класс декоратора ............................................................................................................... 191
37.3. Декоратор с аргументами (фабрика декораторов) ...................................................... 192
37.4. Приведение декоратора к виду декорируемой функции ............................................ 193
37.5. Использование декоратора для определения времени выполнения функции ..... 194
37.6. Создание класса-синглтона (одиночки) с помощью декоратора ............................. 194
Глава 38. Классы................................................................................................................................ 195
38.1. Введение в классы ............................................................................................................. 195
38.2. Связанные, несвязанные и статические методы ......................................................... 197
38.3. Базовое наследование...................................................................................................... 199
38.4. Манкипатчинг (Monkey Patching) ..................................................................................... 200
38.5. Классы нового и старого стиля ....................................................................................... 201
38.6. Методы класса: альтернативные инициализаторы ..................................................... 202
38.7. Множественное наследование ........................................................................................ 203
38.8. Свойства .............................................................................................................................. 204
38.9. Значения по умолчанию для переменных экземпляра .............................................. 206
38.1О. Переменные класса и экземпляра ............................................................................... 207
38.11. Композиция классов ....................................................................................................... 208
38.12. Перечисление всех членов класса .............................................................................. 209
38.13. Класс-синглтон ................................................................................................................. 209
38.14. Дескрипторы и точечный поиск ................................................................................... 211
Глава 39. Метаклассы ....................................................................................................................... 211
39.1. Базовые метаклассы ........................................................................................................ 211
39.2. Синглтоны, использующие метаклассы ........................................................................ 212
39.3. Использование метакласса ............................................................................................. 212
39.4. Введение в метаклассы .................................................................................................... 213

Содержание

9

39.5. Пользовательская функциональность в метаклассах................................................ 214
39.6. Метакласс по умолчанию ................................................................................................. 214
Глава 40. Форматирование строк ................................................................................................... 215
40.1. Основы форматирования строк ...................................................................................... 215
40.2. Выравнивание и заполнение ........................................................................................... 216
40.3. Форматные литералы (f-строка) ..................................................................................... 217
40.4. Форматирование чисел с плавающей точкой .............................................................. 217
40.5. Именованные заполнители .............................................................................................. 218
40.6. Форматирование строк с использованием типа datetime .......................................... 219
40.7. Форматирование числовых значений ........................................................................... 219
40.8. Вложенное форматирование ........................................................................................... 219
40.9. Форматирование с помощью функций Getitem и Getattr ........................................... 220
40.1О. Комбинированное заполнение и усечение строк ...................................................... 220
40.11. Пользовательское форматирование класса .............................................................. 221
Глава 41. Строковые методы........................................................................................................... 222
41.1. Изменение регистра букв в строке ................................................................................. 222
41.2. str.translate: замена символов в строке ........................................................................ 223
41.3. str.format и f-strings: форматирование значений в строку ......................................... 223
41.4. Полезные константы модуля string ............................................................................... 224
41.5. Удаление ненужных ведущих и завершающих символов из строки ....................... 225
41.6. Реверсирование строки ................................................................................................... 226
41.7. Разбиение строки по разделителю на список строк ................................................... 226
41.8. Замена всех вхождений одной подстроки на другую подстроку .............................. 227
41.9. Проверка содержимого строк ......................................................................................... 228
41.1 О. Проверка содержания подстроки в строке ................................................................. 230
41.11. Объединение списка строк в одну строку ................................................................... 230
41.12. Подсчет количества вхождений подстроки в строке ............................................... 230
41.13. Сравнение строк без учета регистра ............................................................................ 230
41.14. Выравнивание строк ....................................................................................................... 232
41.15. Проверка начальных и конечных символов строки ................................................ 232
41.16. Преобразование между строковыми или байтовыми данными
и символами Unicode .................................................................................................................. 233
Глава 42. Использование циклов внутри функций...................................................................... 234
42.1. Оператор возврата внутри цикла в функции ................................................................ 234
Глава 43. Импорт модулей ............................................................................................................... 235
43.1. Импорт модуля .................................................................................................................. 235
43.2. Специальная переменная _all_ ..................................................................................... 236
43.3. Импорт модулей из произвольного места файловой системы ................................. 237
43.4. Импорт всех имен из модуля ........................................................................................... 237
43.5. Программный импорт ... . . . . . . . . ... . . . . . . . . . . . . . . . ...... . . . . .... . . . . . . . . ... . . . . . . . . ... . . . . . . . . . . . . . . . ......... . . ... . . . . . . . . . 237
43.6. Правила РЕР8 для импорта .............................................................................................. 238
43.7. Импорт конкретных имен из модуля .............................................................................. 238
43.8. Импорт субмодулей ........................................................................................................... 239
43.9. Повторный импорт модуля............................................................................................... 239
43.1О. Функция _import_() ........................................................................................................ 240
Глава 44. Разница между модулем и пакетом ............................................................................. 240
44.1. Модули ................................................................................................................................. 240
44.2. Пакеты .................................................................................................................................. 240
Глава 45. Математический модуль math ....................................................................................... 241
45.1. Округление: round, floor, ceil, trunc ................................................................................... 241
45.2. Тригонометрия .................................................................................................................... 242

10

Содержание
45.3. Функция pow для быстрого возведения в степень ...................................................... 243
45.4. Бесконечность и NaN ("не число") ................................................................................... 244
45.5. Логарифмы ......................................................................................................................... 246
45.6. Константы ........................................................................................................................... 246
45.7. Мнимые числа .................................................................................................................... 247
45.8. Копирование знаков ......................................................................................................... 247
45.9. Комплексные числа и модуль cmath ............................................................................. 247

Глава 46. Комплексная математика............................................................................................... 250
46.1. Расширенная комплексная арифметика ...................................................................... 250
46.2. Основы комплексной арифметики ................................................................................. 250
Глава 47. Модуль collections ........................................................................................................... 251
47.1. collections.Counter .............................................................................................................. 251
47.2. collections.OrderedDict ........................................................................................................ 252
47.3. collections.defaultdict.......................................................................................................... 253
47.4. collections.namedtuple ....................................................................................................... 254
47.5. collections.deque ................................................................................................................ 254
47.6. collections.ChainMap .......................................................................................................... 256
Глава 48. Модуль орегаtог ................................................................................................................ 257
48.1. Функция ltemgetter ............................................................................................................ 257
48.2. operator как альтернатива инфиксному оператору ..................................................... 257
48.3. Methodcaller ......................................................................................................................... 257
Глава 49. Модуль JSO N ..................................................................................................................... 258
49.1. Хранение данных в файле ............................................................................................... 258
49.2. Получение данных из файла ............................................................................................ 258
49.3. Форматирование вывода JSO N ....................................................................................... 258
49.4. "load" и "loads", "dump" и "dumps" ..................................................................................... 259
49.5. Вызов "json.tool" из командной строки для эстетичного вывода JSON .................. 260
49.6. JSОN-кодирование пользовательских объектов ......................................................... 260
49.7. Создание JSO N из словаря Python .................................................................................. 261
49.8. Создание словаря Python из JSON .................................................................................. 261
Глава 50. Модуль Sqlite3 ................................................................................................................... 261
50.1. Sqlite3 не требует отдельного серверного процесса ................................................... 261
50.2. Получение значений из базы данных и обработка ошибок ....................................... 261
Глава 51. Модуль os........................................................................................................................... 262
51.1. makedirs - рекурсивное создание каталогов ............................................................... 262
51.2. Создание каталога ............................................................................................................. 263
51.3. Получение текущего каталога ......................................................................................... 263
51.4. Определение операционной системы ........................................................................... 263
51.5. Удаление каталога ............................................................................................................ 263
51.6. Следование по симлинку (POSIX) ................................................................................... 263
51.7. Изменение разрешений файла ........................................................................................ 264
Глава 52. Модуль locale..................................................................................................................... 264
52.1. Форматирование валюты в долларах США с использованием модуля locale ....... 264
Глава 53. Модуль ltertools................................................................................................................. 264
53.1. Метод комбинаций в модуле ltertools ............................................................................ 264
53.2. itertools.dropwhile ............................................................................................................... 265
53.3. Использование zip_longest для двух итераторов до тех пор, пока они оба не будут
исчерпаны .................................................................................................................................... 265
53.4. Получение среза генератора ............................................................................................ 265

Содержание

11

53.5. Группировка элементов из итерируемого объекта с помощью функции ................ 266
53.6. itertools.takewhile ............................................................................................................... 267
53.7. itertools.permutations ......................................................................................................... 267
53.8. itertools.repeat...................................................................................................................... 268
53.9. Получение накопленной суммы чисел в итерируемом объекте ............................... 268
53.1 О. Циклический переход по элементам итератора ........................................................ 268
53.11. itertools.product ................................................................................................................. 268
53.12. itertools.count..................................................................................................................... 269
53.13. Объединение нескольких итераторов в цепочку ...................................................... 270
Глава 54. Модуль Asyncio ................................................................................................................. 270
54.1. Синтаксис корутин (сопрограмм) и делегирования ................................................... 270
54.2. Асинхронные исполнители (Executors) .......................................................................... 271
54.3. Использование UVLoop .................................................................................................... 272
54.4. Примитив синхронизации: Event ..................................................................................... 272
54.5. Простой Websocket ............................................................................................................ 273
54.6. Распространенные заблуждения, связанные с модулем asyncio ............................ 274
Глава 55. Модуль Random................................................................................................................. 274
55.1. Создание случайного пароля пользователя ................................................................ 274
55.2. Создание криптографически защищенных случайных чисел .................................. 275
55.3. Случайности и последовательности: перемешивание, выбор и выборка............... 275
55.4. Создание чисел типов int и float: randint, randrange, random и uniform ...................... 276
55.5. Воспроизводимые случайные числа: методы Seed и State ........................................ 277
55.6. Случайное двоичное решение ......................................................................................... 277
Глава 56. Модуль Functools .............................................................................................................. 278
56.1. Функция partial .................................................................................................................... 278
56.2. cmp_to_key ........................................................................................................................... 278
56.3. @lru_cache ........................................................................................................................... 278
56.4. @total_ordering .................................................................................................................... 279
56.5. reduce ................................................................................................................................... 279
Глава 57. Модуль dis.......................................................................................................................... 280
57.1. Что такое байт-код Python? ............................................................................................... 280
57.2. Константы в модуле dis..................................................................................................... 280
57.3. Демонтаж модулей ........................................................................................................... 280
Глава 58. Модуль base64 .................................................................................................................. 281
58.1. Кодирование и декодирование Base64 .......................................................................... 282
58.2. Кодирование и декодирование Base32 .......................................................................... 283
58.3. Кодирование и декодирование Base16 .......................................................................... 284
58.4. Кодирование и декодирование ASCll85 ......................................................................... 284
58.5. Кодирование и декодирование Base85 .......................................................................... 285
Глава 59. Модуль Queue .................................................................................................................... 285
59.1. Простой пример .................................................................................................................. 285
Глава 60. Модуль Deque .................................................................................................................... 286
60.1. Базовое использование deque ........................................................................................ 286
60.2. Доступные методы в deque .............................................................................................. 286
60.3. Ограничение размера deque ............................................................................................ 287
60.4. Поиск по ширине (Breadth First Search} .......................................................................... 287
Глава 61. Модуль Webbrowser.......................................................................................................... 288
61.1. Открытие URL-aдpeca браузером по умолчанию ........................................................ 288
61.2. Открытие URL-aдpeca с помощью различных браузеров .......................................... 289

12

Содержание

Глава 62. tkinter................................................................................................................................... 289
62.1. Менеджеры геометрии ..................................................................................................... 289
62.2. Минимальное приложение tkinter .................................................................................. 291
Глава 63. Модуль pyautogui .............................................................................................................. 291
63.1. Функции мыши .................................................................................................................... 291
63.2. Функции клавиатуры ......................................................................................................... 292
63.3. Распознавание скриншотов и изображений ................................................................. 292
Глава 64. Индексация и нарезка ..................................................................................................... 292
64.1. Базовая нарезка ................................................................................................................. 292
64.2. Реверсирование объекта ................................................................................................. 293
64.3. Назначение среза............................................................................................................... 293
64.4. Создание неглубокой копии массива ............................................................................. 294
64.5. Индексация пользовательских классов: _getitem_, _setitem_ и _delitem_...... 294
64.6. Базовая индексация .......................................................................................................... 295
Глава 65. Построение графиков с помощью Matplotlib .............................................................. 296
65.1. Графики с общей осью х, но разными осями у: использование twinx() ................... 296
65.2. Графики с общей осью у, но разными осями х: использование функции twiny() ... 297
65.3. Простой график в Matplotlib ............................................................................................. 299
65.4. Добавление дополнительных функций к простому графику: названия осей,
заголовок, метки осей, сетка и легенда .................................................................................. 300
65.5. Создание нескольких кривых на одном графике путем наложения,
аналогично MATLAB .................................................................................................................... 301
65.6. Создание нескольких кривых на одном графике с помощью наложения
с использованием отдельных команд plot ............................................................................. 302
Глава 66. graph-tool ............................................................................................................................ 303
66.1. PyDotPlus .............................................................................................................................. 303
66.2. PyGraphviz ............................................................................................................................ 303
Глава 67. Генераторы ........................................................................................................................ 304
67.1. Введение .............................................................................................................................. 304
67.2. Бесконечные последовательности................................................................................. 306
67.3. Отправка объектов генератору ........................................................................................ 307
67.4. Предоставление всех значений из другого итерируемого объекта.......................... 308
67.5. Итерация .............................................................................................................................. 308
67.6. Функция next() ..................................................................................................................... 308
67.7. Корутины (сопрограммы) ................................................................................................. 309
67.8. Рефакторинг кода построения списков ......................................................................... 309
67.9. Использование оператора yield с рекурсией: рекурсивное перечисление
всех файлов в каталоге .............................................................................................................. 31 О
67.1О. Генераторные выражения .............................................................................................. 31О
67.11. Использование генератора для получения чисел Фибоначчи ................................ 311
67.12. Поиск .................................................................................................................................. 311
67.13. Параллельный итерационный перебор генераторов ................................................ 311
Глава 68. Функция reduce ................................................................................................................. 312
68.1. Обзор .................................................................................................................................... 312
68.2. Использование reduce ....................................................................................................... 312
68.3. Накопительный продукт ................................................................................................... 313
68.4. Вариант без "короткого замыкания" функций any/all .................................................. 313
Глава 69. Функция map ..................................................................................................................... 313
69.1. Базовое использование map, itertools.imap и future_builtins.map .............................. 313
69.2. Сопоставление каждого значения в итерируемом объекте ...................................... 314

Содержание

13

69.3. Отображение (mapping) значений различных итерируемых объектов .................... 315
69.4. Транспонирование с помощью map: использование "None"
в качестве аргумента функции (только для Python 2.х) ....................................................... 316
69.5. Последовательное и параллельное отображение ....................................................... 317
Глава 70. Возведение в степень ...................................................................................................... 318
70.1. Возведение в степень с использованием встроенных модулей: ** и pow() ............ 318
70.2. Квадратный корень: math.sqrt() и cmath.sqrt ................................................................ 319
70.3. Возведение в степень по модулю: функция pow() с тремя аргументами ................ 319
70.4. Вычисление больших целых корней............................................................................... 320
70.5. Возведениие в степень с использованием модуля math: math.pow() ...................... 321
70.6. Экспоненциальная функция: math.exp() и cmath.exp() ................................................ 321
70.7. Экспоненциальная функция -1: math.expm1 () ............................................................... 321
70.8. Магические методы и возведение в степень: builtin, math и cmath .......................... 322
70.9. Корень n-й степени с дробным показателем степени ................................................. 323
Глава 71. Поиск .................................................................................................................................. 324
71.1. Поиск элемента .................................................................................................................. 324
71.2. Поиск в пользовательских классах: contains_ и _iter_ ........................................ 324
71.3. Получение индекса для строк: str.index(), str.rindex() и str.find(), str.rfind() .............. 325
71.4. Получение индексов в списках и кортежах: list.index(), tuple.index() ....................... 325
71.5. Поиск ключа или ключей для значения в словаре...................................................... 326
71.6. Получение индекса в отсортированных последовательностях:
bisect.Ьisect_left() ........................................................................................................................ 326
71.7. Поиск во вложенных последовательностях ................................................................. 327
Глава 72. Сортировка, минимум и максимум ............................................................................... 328
72.1. Упорядоченность в пользовательских классах ........................................................... 328
72.2. Особый случай: словари ................................................................................................... 330
72.3. Использование ключевого аргумента ........................................................................... 331
72.4. Аргумент по умолчанию для max, min ............................................................................ 331
72.5. Получение отсортированной последовательности ..................................................... 331
72.6. Извлечение N наибольших или N наименьших элементов
из итерируемого объекта ........................................................................................................... 332
72.7. Получение минимального или максимального из нескольких значений ............... 332
72.8. Минимум и максимум последовательности ................................................................. 333
Глава 73. Подсчет .............................................................................................................................. 333
73.1. Подсчет всех вхождений всех элементов в итерируемом объекте:
collections.Counter ........................................................................................................................ 333
73.2. Получение наиболее часто встречающегося значения:
collections.Counter.mosLcommon() ........................................................................................... 334
73.3. Подсчет количества вхождений элемента в последовательность:
list.count() и tuple.count() ............................................................................................................. 334
73.4. Подсчет вхождений подстроки в строку: str.count() .................................................... 334
73.5. Подсчет вхождений в numру-массиве............................................................................ 335
Глава 74. Функция print ..................................................................................................................... 335
74.1. Основы вывода на экран .................................................................................................. 335
74.2. Параметры функции print.................................................................................................. 336
Глава 75. Регулярные выражения (Regex) .................................................................................... 337
75.1. Сопоставление начала строки ......................................................................................... 337
75.2. Поиск .................................................................................................................................... 338
75.3. Предварительно скомпилированные шаблоны (precompiled patterns) ................... 339
75.4. Флаги .................................................................................................................................... 339
75.5. Замена .................................................................................................................................. 340

14

Содержание
75.6. Поиск всех непересекающихся совпадений ................................................................. 340
75.7. Проверка на разрешенные символы.............................................................................. 341
75.8. Разбиение строки с помощью регулярных выражений .............................................. 341
75.9. Группировка ........................................................................................................................ 341
75.1О. Исключение специальных символов ........................................................................... 342
75.11. Сопоставление выражения только в определенных местах ................................... 343
75.12. Итерация по совпадениям с помощью re.finditer ....................................................... 343

Глава 76. Копирование данных ....................................................................................................... 344
76.1. Копирование словаря........................................................................................................ 344
76.2. Неглубокое копирование .................................................................................................. 344
76.3. Глубокое копирование ....................................................................................................... 344
76.4. Неглубокое копирование списка ..................................................................................... 345
76.5. Копирование набора .......................................................................................................... 345
Глава 77. Менеджеры контекста (инструкция with) .................................................................... 345
77.1. Введение в менеджеры контекста и инструкцию with ................................................ 345
77.2. Написание собственного менеджера контекста .......................................................... 346
77.3. Написание собственного менеджера контекста с использованием
синтаксиса генератора ............................................................................................................... 347
77.4. Использование нескольких менеджеров контекста ................................................... 348
77.5. Назначение цели ................................................................................................................ 348
77.6. Управление ресурсами ...................................................................................................... 348
Глава 78. Специальная переменная _name_ ............................................................................. 349
78.1. Проверка _name_ == '_main_' ...................................................................................... 349
78.2. Использование в протоколировании ............................................................................. 349
78.3. function_class_or_module._name_ .................................................................................. 349
Глава 79. Проверка существования пути и прав доступа .......................................................... 351
79.1. Выполнение проверки с помощью os.access .............................................................. 351
Глава 80. Создание пакетов Python ................................................................................................ 351
80.1. Введение ............................................................................................................................. 351
80.2. Загрузка в каталог пакетов PyPI ..................................................................................... 352
80.3. Создание исполняемого пакета ...................................................................................... 354
Глава 81. Использование модуля "pip": Менеджер пакетов PyPI .............................................. 354
81.1. Пример использования команд ...................................................................................... 354
81.2. Обработка исключения lmportError ................................................................................. 355
81.3. Принудительная установка .............................................................................................. 355
Глава 82. pip: менеджер пакетов PyPI ...................................................................................... 356
82.1. Установка пакетов ............................................................................................................. 356
82.2. Получение списка пакетов, установленных с помощью 'pip· .................................... 356
82.3. Обновление пакетов .......................................................................................................... 356
82.4. Удаление пакетов ............................................................................................................... 357
82.5. Обновление всех устаревших пакетов в Linux ............................................................. 357
82.6. Обновление всех устаревших пакетов под Windows .................................................. 357
82.7. Создание файла requirements.txt file всех пакетов в системе ................................... 357
82.8. Использование определенной версии Python с помощью pip .................................. 357
82.9. Создание файла requirements.txt file пакетов только текущей virtualenv ................ 358
82.1О. Установка пакетов, еще не включенных в pip в качестве wheels ........................... 358
Глава 83. Разбор аргументов командной строки ........................................................................ 361
83.1. Hello world в argparse ......................................................................................................... 361
83.2. Использование аргументов командной строки с помощью argv ............................. 362

Содержание

15

83.3. Задание взаимоисключающих аргументов с помощью argparse ............................. 363
83.4. Базовый пример с использованием docopt .................................................................. 363
83.5. Пользовательское сообщение об ошибке синтаксического анализатора
с помощью argparse .................................................................................................................... 364
83.6. Концептуальная группировка аргументов с помощью
argparse.add_argumenLgroup() .................................................................................................. 364
83.7. Расширенный пример с docopt и docopt_dispatch ....................................................... 365
Глава 84. Библиотека подпроцессов .............................................................................................. 366
84.1. Больше возможностей с помощью Рореп .................................................................... 366
84.2. Вызов внешних команд .................................................................................................... 367
84.3. Как создать аргумент списка команд ............................................................................ 368
Глава 85. Файл setup.py..................................................................................................................... 368
85.1. Назначение файла setup.py .............................................................................................. 368
85.2. Использование метаданных системы управления версиями в файле setup.py .... 369
85.3. Добавление скриптов командной строки в пакет Python .......................................... 369
85.4. Добавление параметров установки ............................................................................... 370
Глава 86. Рекурсия............................................................................................................................. 370
86.1. Что, как и когда делать с рекурсией ............................................................................... 370
86.2. Задача "исследования дерева" с помощью рекурсии ................................................. 373
86.3. Сумма чисел от 1 до п ....................................................................................................... 374
86.4. Увеличение максимальной глубины рекурсии ............................................................. 375
86.5. Хвостовая рекурсия - плохая практика ........................................................................ 375
86.6. Оптимизация хвостовой рекурсии с помощью интроспекции стека ....................... 376
Глава 87. Подсказки типов .............................................................................................................. 377
87.1. Добавление типов в функцию ......................................................................................... 377
87.2. Функция NamedTuple ........................................................................................................ 378
87.3. Общие типы ......................................................................................................................... 378
87.4. Переменные и атрибуты ................................................................................................... 378
87.5. Члены и методы классов .................................................................................................. 379
87.6. Подсказки типов для именованных аргументов ......................................................... 379
Глава 88. Исключения....................................................................................................................... 379
88.1. Перехват исключений ....................................................................................................... 379
88.2. Не перехватывайте все подряд! ...................................................................................... 380
88.3. Повторный вызов исключений ....................................................................................... 380
88.4. Перехват множественных исключений ......................................................................... 381
88.5. Иерархия исключений ....................................................................................................... 381
88.6. Else ........................................................................................................................................ 383
88.7. Вызов исключений............................................................................................................. 384
88.8. Создание пользовательских типов исключений ......................................................... 384
88.9. Практические примеры обработки исключений.......................................................... 384
88.1 О. Исключения - это тоже объекты .................................................................................. 385
88.11. Выполнение кода очистки с помощью f1nally ............................................................. 386
88.12. Создание цепочек исключений с использованием raise from................................. 386
Глава 89. Вызов пользовательских ошибок и исключений....................................................... 387
89.1. Пользовательское исключение....................................................................................... 387
89.2. Перехват пользовательского исключения .................................................................... 387
Глава 90. Исключения сообщества ................................................................................................ 387
90.1. Прочие ошибки ................................................................................................................... 387
90.2. Ошибка "NameError. name '??' is not def1ned" .................................................................. 388
90.3. Ошибки типов данных ....................................................................................................... 389

16

Содержание
90.4. Синтаксическая ошибка в хорошем коде ...................................................................... 391
90.5. Ошибки отступа (или ошибки синтаксиса отступов) ................................................... 391

Глава 91. urllib ..................................................................................................................................... 392
91.1. НТТ Р GET.............................................................................................................................. 392
91.2. НТТ Р POST ........................................................................................................................... 393
91.3. Декодирование полученных байтов в соответствии с кодировкой типа
содержимого ................................................................................................................................ 394
Глава 92. Веб-скрапинг с помощью Python ................................................................................... 394
92.1. Скрапинг с использованием фреймворка Scrapy ........................................................ 394
92.2. Скрапинг с использованием Selenium WebDriver ......................................................... 395
92.3. Базовый пример использования запросов и lxml для скрапинга
некоторых данных ....................................................................................................................... 395
92.4. Поддержание сессии веб-скрапинга с помощью запросов ....................................... 396
92.5. Скрапинг с использованием BeautifuISoup4 ................................................................. 396
92.6. Простое скачивание веб-контента с помощью urllib.request ..................................... 396
92.7. Модификация пользовательского агента Scrapy ......................................................... 397
92.8. Скрапинг с использованием инструмента командной строки cURL ........................ 397
Глава 93. Парсинг HTML ................................................................................................................... 397
93.1. Использование СSS-селекторов в библиотеке BeautifulSoup .................................... 397
93.2. Библиотека PyQuery ........................................................................................................... 398
93.3. Нахождение текста после элемента в библиотеке BeautifulSoup.............................. 399
Глава 94. Работа с XML ..................................................................................................................... 399
94.1. Открытие и чтение с помощью библиотеки ElementТree ............................................ 399
94.2. Создание и построение ХМL-документов ...................................................................... 399
94.3. Изменение ХМL-файла ...................................................................................................... 400
94.4. Поиск в XML с помощью XPath ........................................................................................ 400
94.5. Открытие и чтение больших ХМL-файлов с помощью инкрементного парсинга ..... 401
Глава 95. Python Requests Post ........................................................................................................ 402
95.1. Простая операция Post...................................................................................................... 402
95.2. Данные, закодированные в форме ................................................................................. 403
95.3. Загрузка файлов ................................................................................................................ 403
95.4. Ответы .................................................................................................................................. 403
95.5. Аутентификация.................................................................................................................. 404
95.6. Прокси-серверы.................................................................................................................. 405
Глава 96. Распространение кода .................................................................................................... 405
96.1. ру2арр ................................................................................................................................... 405
96.2. cx_Freeze .............................................................................................................................. 406
Глава 97. Объекты свойств .............................................................................................................. 407
97.1. Использование декоратора @property для свойств чтения и записи ...................... 407
97.2. Еще об использовании декоратора @property .............................................................. 407
97.3. Переопределение только функций getter, setter или deleter объекта свойства ...... 408
97.4. Использование свойств без декораторов ..................................................................... 408
Глава 98. Перегрузка ......................................................................................................................... 41 О
98.1. Перегрузка операторов ..................................................................................................... 41 О
98.2. "Магические" методы, или Duпdег-методы .................................................................... 412
98.3. Типы контейнеров и последовательностей................................................................... 413
98.4. Вызываемые типы ............................................................................................................. 413
98.5. Обработка нереализованного поведения...................................................................... 414

Содержание

17

Глава 99. Полиморфизм ................................................................................................................... 415
99.1. "Утиная типизация" ............................................................................................................ 415
99.2. Базовый полиморфизм ..................................................................................................... 415
Глава 100. Переопределение методов........................................................................................... 418
100.1. Переопределение базовых методов ............................................................................ 418
Глава 101. Пользовательские методы........................................................................................... 418
101.1. Создание пользовательских объектов метода .......................................................... 418
101.2. Пример с использованием Turtle Graphics .................................................................. 420
Глава 102. Строковые представления экземпляров классов: _str_ и _герг_ методы ..... 420
102.1. Мотивация ......................................................................................................................... 420
102.2. Реализация обоих методов, _герг_() в стиле eval-round-trip .................................. 424
Глава 103. Отладка ............................................................................................................................ 424
103.1. При помощи IPython и ipdb ............................................................................................. 424
103.2. Отладчик Python. Пошаговая отладка с помощью _pdb_ ......................................... 425
103.3. Удаленный отладчик ....................................................................................................... 426
Глава 104. Чтение и запись в формате CSV .................................................................................. 426
104.1. Использование pandas.................................................................................................... 426
104.2. Запись файла TSV ............................................................................................................ 427
Глава 105. Запись в формат CSV из строки или списка ............................................................. 427
105.1. Пример базовой записи .................................................................................................. 427
105.2. Добавление строки в качестве новой строчки в СSV-файл ..................................... 428
Глава 106. Динамическое выполнение кода с помощью функций ехес и eval ...................... 428
106.1. Выполнение кода, предоставленного недоверенным пользователем,
с использованием ехес, eval или ast.literal_eval ..................................................................... 428
106.2. Оценка строки, содержащей литерал Python, с помощью ast.literal_eval ............... 429
106.3. Оценка утверждений с помощью функции ехес......................................................... 429
106.4. Оценка выражения с помощью функции eval ............................................................. 429
106.5. Предварительная компиляция выражения для его многократной оценки ......... 429
106.6. Оценка выражения с помощью eval с использованием пользовательских
глобальных переменных ............................................................................................................ 430
Глава 107. Pylnstaller - распространение кода Python................................................................ 430
107.1. Установка и настройка.................................................................................................... 430
107.2. Использование Pyinstaller .............................................................................................. 431
107.3. Объединение в одну папку ............................................................................................ 431
107.4. Объединение в один файл .............................................................................................. 431
Глава 108. Визуализация данных с помощью Python ................................................................. 432
108.1. Seaborn ............................................................................................................................... 432
108.2. Matplotlib ............................................................................................................................ 434
108.3. Plotly.................................................................................................................................... 435
108.4. MayaVI ................................................................................................................................ 436
Глава 109. Интерпретатор (консоль командной строки) ............................................................ 437
109.1. Получение общей помощи.............................................................................................. 437
109.2. Ссылка на последнее выражение................................................................................. 437
109.3. Открытие консоли Python ............................................................................................... 438
109.4. Переменная PYTHO NSTART U P....................................................................................... 438
109.5. Аргументы командной строки ....................................................................................... 438
109.6. Получение справки об объекте...................................................................................... 439

18

Содержание

Глава 11 О. *args и **kwargs ............................................................................................................... 440
110.1. Использование **kwargs при написании функций..................................................... 440
110.2. Использование *args при написании функций ........................................................... 441
110.3. Заполнение значений kwarg с помощью словаря ..................................................... 441
110.4. Аргументы только для ключевых слов (keyword-only arguments) и аргументы,
требующие ключевых слов (keyword-required arguments) .................................................... 441
110.5. Использование **kwargs при вызове функций........................................................... 441
110.6. **kwargs и значения по умолчанию .............................................................................. 442
110.7. Использование *args при вызове функций ................................................................. 442
Глава 111. Сборка мусора ................................................................................................................ 442
111.1. Повторное использование примитивов ...................................................................... 442
111.2. Последствия команды del .............................................................................................. 443
111.3. Подсчет ссылок ................................................................................................................ 443
111.4. Сборщик мусора для ссылочных циклов .................................................................... 444
111.5. Принудительное удаление объектов ............................................................................ 445
111.6. Просмотр счетчика ссылок (refcount) объекта........................................................... 445
111.7. Не ждите, пока сборщик мусора наведет порядок .................................................... 446
111.8. Управление сборкой мусора .......................................................................................... 446
Глава 112. Сериализация данных при помощи модуля Pickle................................................... 447
112.1. Использование модуля Pickle для сериализации и десериализации объекта ..... 447
112.2. Данные для Pickle ............................................................................................................ 448
Глава 113. Двоичные данные .......................................................................................................... 449
113.1. Форматирование списка значений в байтовый объект ............................................ 449
113.2. Распаковка байтового объекта в соответствии со строкой формата.................... 449
113.3. Упаковка структуры......................................................................................................... 449
Глава 114. Идиомы ............................................................................................................................ 450
114.1. Инициализация ключей словаря .................................................................................. 450
114.2. Переключение переменных ........................................................................................... 450
114.3. Использование проверки значения истинности........................................................ 451
114.4. Тест на функцию _main_, чтобы избежать неожиданного выполнения кода .... 451
Глава 115. Сериализация данных ................................................................................................... 452
115.1. Сериализация с использованием формата JSO N ..................................................... 452
115.2. Сериализация с использованием модуля Pickle ........................................................ 452
Глава 116. Многопроцессорная обработка ................................................................................... 453
116.1. Запуск двух простых процессов.................................................................................... 453
116.2. Использование класса Pool and функции pool.map ................................................... 454
Глава 117. Многопоточность ........................................................................................................... 454
117.1. Основы многопоточности .............................................................................................. 454
117.2. Общение между потоками.............................................................................................. 455
117.3. Создание пула процессов (workers) .............................................................................. 456
117.4. Продвинутое использование многопоточности ......................................................... 457
117.5. Останавливаемый поток с циклом while ..................................................................... 458
Глава 118. Процессы и потоки ........................................................................................................ 459
118.1. Глобальная блокировка интерпретатора..................................................................... 459
118.2. Работа с несколькими потоками................................................................................... 460
118.3. Работа с несколькими процессами .............................................................................. 461
118.4. Совместное использование состояния потоками ..................................................... 461
118.5. Совместное использование состояния процессами................................................. 462
Глава 119. Параллелизм в Python ................................................................................................... 462
119.1. Многопроцессорный модуль ......................................................................................... 462

Содержание

19

119.2. Модуль потокообразования (threading) ....................................................................... 463
119.3. Передача данных между многопроцессорными процессами................................. 464
Глава 120. Параллельные вычисления ......................................................................................... 465
120.1. Использование модуля многопроцессорной обработки
для распараллеливания задач.................................................................................................. 465
120.2. Использование С-расширения для распараллеливания задач............................... 465
120.3. Использование родительских и дочерних скриптов для параллельного
выполнения кода ......................................................................................................................... 465
120.4. Использование модуля РуРаг для распараллеливания ........................................... 466
Глава 121. Сокеты .............................................................................................................................. 466
121.1. Сырые сокеты (Raw Sockets) в Linux ........................................................................... 467
121.2. Передача данных по протоколу UDP ............................................................................ 467
121.3. Получение данных по протоколу UDP .......................................................................... 467
121.4. Отправка данных по протоколу ТСР ............................................................................. 468
121.5. Многопоточный сервер ТСР Socket Server................................................................... 468
Глава 122. Веб-сокеты ....................................................................................................................... 470
122.1. Простое эхо с помощью aiohttp ..................................................................................... 470
122.2. Класс-обертка с aiohttp ................................................................................................... 470
122.3. Использование пакета Autobahn в качестве фабрики вебсокетов......................... 471
Глава 123. Сокеты и шифрование/дешифрование сообщений между клиентом
и сервером .......................................................................................................................................... 472
123.1. Реализация на стороне сервера ................................................................................... 473
123.2. Реализация на стороне клиента .................................................................................... 474
Глава 124. Сетевое взаимодействие на языке Python ................................................................ 476
124.1. Создание простого Http-cepвepa ................................................................................... 476
124.2. Создание ТСР-сервера ................................................................................................... 477
124.3. Создание UDP-cepвepa .................................................................................................... 477
124.4. Запуск простого HttpServer в потоке и открытие браузера ...................................... 478
124.5. Простейший пример клиент-сервер сокетов на Python ............................................ 478
Глава 125. Python НТТ Р Server ......................................................................................................... 479
125.1. Запуск простого НТТ Р-сервера...................................................................................... 479
125.2. Обслуживание файлов .................................................................................................... 479
125.3. Базовая обработка GET, РОSТ, PUT с помощью BaseHTTPRequestHandler ............ 480
125.4. Программное API SimpleHTTPServer ............................................................................. 481
Глава 126. Микровебфреймворк Flask........................................................................................... 482
126.1. Файлы и шаблоны ............................................................................................................ 482
126.2. Основы ............................................................................................................................... 483
126.3. Маршрутизация URL-aдpecoв ....................................................................................... 483
126.4. НТТ Р-методы..................................................................................................................... 484
126.5. Jinja Templating ................................................................................................................. 485
126.6. Объект запроса................................................................................................................. 485
Глава 127. Использование библиотеки AMQ PStorm в брокере сообщений RabbltMQ ......... 486
127.1. Как получать сообщения из RabbltMQ.......................................................................... 486
127.2. Как публиковать сообщения в RabbltMQ ..................................................................... 487
127.3. Как создать очередь с задержкой в RabbltMQ ........................................................... 488
Глава 128. Дескриптор...................................................................................................................... 489
128.1. Простой дескриптор ........................................................................................................ 489
128.2. Двусторонние преобразования ..................................................................................... 490

20

Содержание

Глава 129. Временный файл NamedTemporaryFile ....................................................................... 491
129.1. Создание известного постоянного временного файла и запись в него ................ 491
Глава 130. Ввод, подмножество и вывод внешних файлов данных
с помощью библиотеки Pandas....................................................................................................... 492
130.1. Базовый код для импорта, подмножества и записи внешних файлов данных
с помощью библиотеки Pandas................................................................................................. 492
Глава 131. Распаковка файлов ....................................................................................................... 493
131.1. Использование Py1hon ZipFile.extractall() для распаковки ZIР-файла ................... 494
131.2. Использование Py1hon TarFile.extractall() для распаковки tагЬаll-архива .............. 494
Глава 132. Работа с ZIР-архивами .................................................................................................. 494
132.1. Изучение содержимого ZIР-файла ................................................................................ 494
132.2. Открытие ZIР-файлов ...................................................................................................... 494
132.3. Извлечение содержимого ZIР-файла в каталог ......................................................... 495
132.4. Создание новых архивов ................................................................................................ 495
Глава 133. Работа с GZip ................................................................................................................... 495
133.1. Чтение и запись файлов G NU zip ................................................................................... 496
Глава 134. Стек ................................................................................................................................... 496
134.1. Создание класса Stack с объектом List........................................................................ 496
134.2. Разбор (парсинг) круглых скобок.................................................................................. 497
Глава 135. Работа в обход глобальной блокировки интерпретатора {GIL) ............................. 498
135.1. Multiprocessing.Pool ......................................................................................................... 498
135.2. Использование Cython nogil ........................................................................................... 499
Глава 136. Развертывание ............................................................................................................... 499
136.1. Загрузка пакета Conda .................................................................................................... 499
Глава 137. Модуль logging ................................................................................................................ 500
137.1. Введение в использование модуля logging ................................................................. 500
137.2. Протоколирование исключений.................................................................................... 501
Глава 138. Стандарт Web Server Gateway lnterface (WSG I) .......................................................... 503
138.1. Объект (метод) сервера .................................................................................................. 503
Глава 139. События, посылаемые сервером (SSE) ...................................................................... 504
139.1. Flask SSE............................................................................................................................. 504
139.2. Asyncio SSE ........................................................................................................................ 505
Глава 140. Альтернативы оператору switch из других языков .................................................. 505
140.1. Используйте то, что предлагает Python: конструкция if/else ................................... 505
140.2. Использование словаря функций ................................................................................ 505
140.3. Использование интроспекции классов ....................................................................... 506
140.4. Использование менеджера контекста ......................................................................... 507
Глава 141. Деструктуризация списка, упаковка и распаковка ................................................. 508
141.1. Присваивание деструктуризации ................................................................................. 508
141.2. Аргументы функции упаковки ....................................................................................... 509
141.3. Распаковка аргументов функции .................................................................................. 511
Глава 142. Доступ к исходному коду Py1hon и байт-коду ............................................................ 511
142.1. Отображение байт-кода функции .................................................................................. 511
142.2. Отображение исходного кода объекта ......................................................................... 512
142.3. Исследование кодового объекта функции.................................................................. 512

Содержание

21

Глава 143. Миксины .......................................................................................................................... 513
143.1. Миксин ............................................................................................................................... 513
143.2. Переопределение методов в миксинах ....................................................................... 514
Глава 144. Доступ к атрибутам........................................................................................................ 515
144.1. Базовый доступ к атрибутам с использованием точечной нотации ...................... 515
144.2. Методы setter, getter и свойства .................................................................................... 515
Глава 145. Пакет АгсРу ...................................................................................................................... 516
145.1. Использование createDissolvedGDB для создания gdЬ-файла
в рабочей области ...................................................................................................................... 516
145.2. Печать значения одного поля для всех строк класса признаков в файле
geodatabase с помощью функции SearchCursor ..................................................................... 516
Глава 146. Абстрактные базовые классы (аЬс) ........................................................................... 517
146.1. Установка метакласса A BCMeta ................................................................................... 517
146.2. Зачем и как использовать A BCMeta и @abstractmethod .......................................... 517
Глава 147. Плагины и расширения ................................................................................................. 519
147.1. Миксины ............................................................................................................................ 519
147.2. Плагины с пользовательскими классами ................................................................... 520
Глава 148. Неизменяемые типы данных (int, ftoat, str, tuple и frozenset) .................................. 521
148.1. Отдельные символы строк не подлежат присвоению .............................................. 521
148.2. Индивидуальные члены кортежа не могут быть присвоены ................................... 521
148.3. Frozenset является неизменяемым и не подлежит присваиванию ........................ 521
Глава 149. Несовместимости при переходе с Python 2 на Python 3 ......................................... 521
149.1. Целочисленное деление ................................................................................................. 521
149.2. Распаковка итерируемых объектов.............................................................................. 523
149.3. Строки: байты и Unicode ................................................................................................. 524
149.4. Оператор Print и функция Print ....................................................................................... 526
149.5. Отличия между функциями range и xrange .................................................................. 526
149.6. Возникновение и обработка исключений ................................................................... 528
149.7. Утечка переменных в генераторе списка .................................................................... 529
149.8. True, False и None .............................................................................................................. 530
149.9. Ввод данных пользователем ......................................................................................... 530
149.1О. Сравнение разных типов .............................................................................................. 531
149.11. Переименование метода .next() для итераторов ..................................................... 531
149.12. Функции f1lter(), map() и zip() возвращают итераторы вместо
последовательностей ................................................................................................................. 532
149.13. Переименованные модули ........................................................................................... 532
149.14. Удалены операторы < > и --, синонимичные операторам != и герг() ....................... 533
149.15. Типы данных long и int................................................................................................... 533
149.16. Все классы в Python 3 являются "классами нового стиля" ................................... 534
149.17. Функция reduce больше не является встроенной .................................................... 535
149.18. Абсолютный и относительный импорт ...................................................................... 535
149.19. Функция map() ................................................................................................................ 536
149.20. Функция round() - "тай-брейкинг" и возвращаемый тип ........................................ 537
149.21. Файловый ввод/вывод ................................................................................................. 538
149.22. Функция cmp отсутствует в Python 3 .......................................................................... 539
149.23. Восьмеричные константы ............................................................................................ 539
149.24. Возвращаемое значение при записи в файловый объект .................................... 539
149.25. В Python 3 ехес является функцией ............................................................................ 539
149.26. Кодирование в шестнадцатеричный формат и декодирование из него
больше не доступно .................................................................................................................... 540
149.27. Изменения в методах словарей .................................................................................. 541

22

Содержание
149.28. Булево значение класса ............................................................................................... 541
149.29. Ошибка функции hasattr в Python 2 ............................................................................. 542

Глава 150. Инструмент 2to3 ............................................................................................................. 542
150.1. Базовое использование .................................................................................................. 543
Глава 151. Неофициальные реализации Python .......................................................................... 544
151.1. lronPython ........................................................................................................................... 544
151.2. Jython.................................................................................................................................. 545
151.3. Transcrypt ........................................................................................................................... 545
Глава 152. Абстрактное синтаксическое дерево ......................................................................... 548
152.1. Анализ функций в скрипте Python................................................................................. 548
Глава 153. Unicode и bytes ................................................................................................................ 549
153.1. Обработка ошибок кодирования и декодирования ................................................... 549
153.2. Файловый ввод/вывод .................................................................................................. 550
153.3. Основы ............................................................................................................................... 550
Глава 154. Последовательное соединение в Python (pyserial) .................................................. 551
154.1. Инициализация последовательного устройства ...................................................... 551
154.2. Чтение из последовательного порта ............................................................................ 551
154.3. Проверка доступности последовательных портов.................................................... 552
Глава 155. Работа с Neo4j и Cypher с использованием библиотеки Py2Neo ........................... 552
155.1. Добавление узлов в граф в графовой СУБД Neo4j .................................................... 552
155.2. Импорт и аутентификация .............................................................................................. 552
155.3. Добавление отношений в граф Neo4j ........................................................................... 552
155.4. Запрос 1: автозаnолнение заголовков новостей ....................................................... 553
155.5. Запрос 2: получение новостных статей по местоположению
на определенную дату ................................................................................................................ 553
155.6. Образцы запросов языка Cypher................................................................................... 553
Глава 156. Основы работы с библиотекой Curses в Python ....................................................... 554
156.1. Вспомогательная функция wrapper() ............................................................................ 554
156.2. Пример базового обращения ......................................................................................... 554
Глава 157. Шаблоны в Python.......................................................................................................... 555
157.1. Простая программа вывода данных с использованием шаблона ......................... 555
157.2. Изменение разделителя ................................................................................................. 555
Глава 158. Библиотека Pillow ........................................................................................................... 555
158.1. Чтение файла изображения ........................................................................................... 555
158.2. Преобразование файла в формат J PEG ....................................................................... 555
Глава 159. Оператор pass ................................................................................................................. 556
159.1. Игнорирование исключения .......................................................................................... 556
159.2. Создание нового исключения, которое может быть перехвачено ......................... 556
Глава 160. Подкоманды CLI (Command Line lnterface) для аккуратного вывода
справочной информации ................................................................................................................. 556
160.1. Способ без использования библиотек ......................................................................... 556
160.2. Использование argparse (средство форматирования справки по умолчанию) ... 557
160.3. Использование argparse (создание пользовательского средства
форматирования справки) ......................................................................................................... 557
Глава 161. Доступ к базам данных ................................................................................................. 559
161.1. SQLite .................................................................................................................................. 559

Содержание

23

161.2. Доступ к базе данных MySQL с помощью MySQLdb .................................................. 563
161.3. Соединение ....................................................................................................................... 564
161.4. Доступ к базе данных PostgreSQL с помощью psycopg2 .......................................... 564
161.5. Работа с базами данных Oracle .................................................................................... 565
161.6. Использование бибилиотеки sqlalchemy ..................................................................... 567
Глава 162. Подключение Python к SQL Server ............................................................................... 567
162.1. Подключение к серверу, создание таблицы, запрос данных................................... 567
Глава 163. Реляционная база данных PostgreSQL....................................................................... 568
163.1. Начало работы .................................................................................................................. 568
Глава 164. Python и Excel .................................................................................................................. 569
164.1. Чтение данных Excel с помощью модуля xlrd ............................................................. 569
164.2. Форматирование файлов Excel с помощью модуля xlsxwriter ................................. 570
164.3. Помещение списковых данных в файл Excel .............................................................. 570
164.4. Модуль OpenPyXL ............................................................................................................. 571
164.5. Создание диаграмм Excel с помощью xlsxwriter ........................................................ 571
Глава 165. Turtle Graphics ("Черепашья графика") ....................................................................... 573
165.1. Создание Ninja Twist при помощи Turtle Graphics....................................................... 573
Глава 166. Продолжающиеся действия (Persistence) в Python ................................................. 574
166.1. Продолжающиеся действия в Python........................................................................... 574
166.2. Функциональная утилита для сохранения и загрузки............................................... 575
Глава 167. Шаблоны проектирования ........................................................................................... 575
167.1. Введение в паттерны проектирования и паттерн синглтон ..................................... 576
167.2. Стратегический шаблон .................................................................................................. 577
167.3. Прокси-объект .................................................................................................................. 578
Глава 168. Модуль hashlib ................................................................................................................ 580
168.1. МD5-хеш строки ................................................................................................................ 580
168.2. Алгоритм, предоставляемый OpenSSL......................................................................... 581
Глава 169. Создание службы Windows с помощью Python ........................................................ 581
169.1. Сценарий на языке Python, который может быть запущен как служба ................. 582
169.2. Запуск веб-приложения Flask в качестве сервиса..................................................... 582
Глава 170. Изменяемые, неизменяемые и хешируемые в Python ............................................ 583
170.1. Изменяемые и неизменяемые типы ............................................................................ 583
170.2. Изменяемые и неизменяемые типы как аргументы ................................................. 585
Глава 171. Модуль configparser ....................................................................................................... 586
171.1. Создание конфигурационного файла программным способом ............................. 586
171.2. Базовое использование .................................................................................................. 587
Глава 172. Оптическое распознавание символов (OCR) ............................................................ 587
172.1. PyTesseract ........................................................................................................................ 587
172.2. PyOCR ................................................................................................................................. 588
Глава 173. Виртуальные среды ....................................................................................................... 589
173.1. Создание и использование виртуальной среды ........................................................ 589
173.2. Указание версии Python для использования в скрипте на Unix/Linux ................... 590
173.3. Создание виртуальной среды для различных версий Python ................................. 591
173.4. Создание виртуальных сред с помощью Anaconda ................................................... 591
173.5. Управление несколькими виртуальными средами с помощью утилиты
virtualenvwrapper .......................................................................................................................... 592

24

Содержание
173.6. Установка пакетов в виртуальной среде ..................................................................... 593
173.7. Определение используемой виртуальной среды....................................................... 594
173.8. Проверка на работу в виртуальной среде ................................................................... 594
173.9. Использование виртуальной среды с оболочкой Fish shell ..................................... 594

Глава 174. Виртуальная среда Python - virtualenv ....................................................................... 595
174.1. Установка .......................................................................................................................... 595
174.2. Использование ................................................................................................................. 595
174.3. Установка пакета в виртуальной среде ....................................................................... 595
174.4. Другие полезные команды virtualenv ........................................................................... 595
Глава 175. Создание виртуальной среды с помощью надстройки virtualenvwrapper ........... 596
Глава 176. Создание виртуальной среды с помощью virtualenvwrapper в Windows.............. 597
Глава 177. Модуль sys ....................................................................................................................... 597
177.1. Аргументы командной строки ............................................................................................. 598
177.2. Имя скрипта ...................................................................................................................... 598
177.3. Ошибки и стандартный вывод ....................................................................................... 598
177.4. Преждевременное завершение процесса и возврат кода выхода ......................... 598
Глава 178. Пакет ChemPy ................................................................................................................. 598
178.1. Разбор формул.................................................................................................................. 599
178.2. Стехиометрия химических реакций.............................................................................. 599
178.3. Уравнение химической реакции .................................................................................... 599
178.4. Химическое равновесие ................................................................................................. 599
178.5. Ионная сила ...................................................................................................................... 600
178.6. Химическая кинетика (система обыкновенных дифференциальных уравнений)..... 600
Глава 179. Библиотека Pygame ....................................................................................................... 601
179.1. Модуль mixer в Pygame ................................................................................................... 601
179.2. Установка Pygame ............................................................................................................ 602
Глава 180. Модуль Pyglet .................................................................................................................. 602
180.1. Установка Pyglet .............................................................................................................. 602
180.2. "Hello World" в Pyglet ........................................................................................................ 603
180.3. Воспроизведение звука в Pyglet.................................................................................... 603
180.4. Использование Pyglet для OpenGL................................................................................ 603
180.5. Рисование точек с помощью Pyglet и OpenGL ............................................................ 603
Глава 181. Работа со звуком ............................................................................................................ 603
181.1. Работа с файлами в формате WAV................................................................................ 603
181.2. Преобразование любого звукового файла с помощью Python и ffmpeg ............... 604
181.3. Воспроизведение звуковых сигналов Windows ......................................................... 604
181.4. Воспроизведение звука с помощью Pyglet ................................................................. 604
Глава 182. Pyaudio.............................................................................................................................. 605
182.1. Режим обратного вызова звукового ввода/вывода ................................................ 605
182.2. Режим блокировки звукового ввода/вывода ............................................................ 606
Глава 183. Модуль Shelve ................................................................................................................. 607
183.1. Использование Shelve ..................................................................................................... 607
183.2. Пример кода для Shelve .................................................................................................. 608
183.3. Обобщение информации о интерфейсе ....................................................................... 608
183.4. Обратная запись (writeback) ........................................................................................... 608

Содержание

25

Глава 184. Программирование "интернета вещей" {loT) с помощью Py1hon
и Raspberry Pi ...................................................................................................................................... 61 О
184.1. Пример - датчик температуры ...................................................................................... 61 О
Глава 185. Кivy - кроссплатформенный Руthоп-фреймворк для разработки
естественных пользовательских интерфейсов (NUI) .................................................................. 611
185.1. Первое приложение ......................................................................................................... 612
Глава 186. Использование Pandas transform: предварительное выполнение
операций с группами и конкатенация результатов..................................................................... 613
186.1. Простое преобразование ................................................................................................ 613
186.2. Несколько результатов для одной группы .................................................................. 614
Глава 187. Сходство в синтаксисе, различия в значении: Python и JavaScript ....................... 615
187.1. 'iп' со списками ................................................................................................................ 615
Глава 188. Вызов Py1hon из С# ........................................................................................................ 615
188.1. Ру1hоп-скрипт, который вызывается С#-приложением ............................................ 615
188.2. Код на С#, вызывающий Ру1hоп-скрипт ....................................................................... 615
Глава 189. Библиотека ctypes .......................................................................................................... 617
189.1. Массивы ctypes ................................................................................................................ 617
189.2. Обертывающие функции для ctypes ............................................................................. 617
189.3. Базовое использование .................................................................................................. 618
189.4. Основные "подводные камни" ....................................................................................... 618
189.5. Базовый объект ctypes.................................................................................................... 619
189.6. Комплексное использование ........................................................................................ 619
Глава 190. Написание расширений ................................................................................................ 620
190.1. "Hello World" расширение на С ....................................................................................... 620
190.2. Расширение на языке С с помощью С++ и библиотеки Boost ................................. 621
190.3. Передача открытого файла в С-расширения .............................................................. 622
Глава 191. Py1hon Lex-Yacc ............................................................................................................... 622
191.1. Начало работы с Py1hon Lex-Yacc .................................................................................. 622
191.2. "Hello, World!" от PLY - простой калькулятор ............................................................... 623
191.3. Часть 1. Токенизация входных данных с помощью Lex ........................................... 624
191.4. Часть 2. Парсинг токенизированного ввода с помощью Уасс ................................ 627
Глава 192. Модульное тестирование.............................................................................................. 630
192.1. Использование методов setUp и tearDown при работе с unittest.TestCase ............ 630
192.2. Утверждения при исключениях..................................................................................... 631
192.3. Тестирование исключений ............................................................................................. 632
192.4. Выбор утверждений в рамках модуля Unittests ......................................................... 632
192.5. Модульные тесты с помощью py1est ............................................................................ 633
192.6. Подражание функциям при помощи unittest.mock.create_autospec ....................... 635
Глава 193. Библиотека py.test .......................................................................................................... 636
193.1. Настройка py.test.............................................................................................................. 636
193.2. Введение в тестовые фикстуры (Fixtures) ................................................................... 637
193.3. Неудачные испытания..................................................................................................... 640
Глава 194. Профилирование............................................................................................................ 640
194.1. %%timeit и %timeit в 1 Py1hon ............................................................................................ 640
194.2. Использование cProf1le (предпочтительный инструмент) ....................................... 641
194.3. Функция timeit() ................................................................................................................ 641
194.4. Командная строка timeit ................................................................................................. 641
194.5. Использование модуля line_profiler и скрипта kernprof в командной строке........ 642

26

Содержание

Глава 195. Скорость работы программы на языке Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642
195.1. Dеquе-операции . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642
195.2. Алгоритмические нотации . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643
195.3. Нотация Big-O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 644
195.4. Операции со списком . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 644
195.5. Операции с множеством . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 644
Глава 196. Оптимизация производительности . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645
196.1. Профилирование кода . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645
Глава 197. Безопасность и криптография . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 646
197.1. Безопасное хеширование паролей . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 646
197.2. Вычисление дайджеста сообщения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 647
197.3. Доступные алгоритмы хеширования . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 647
197.4. Хеширование файлов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 647
197.5. Генерация RSА-подписей с помощью библиотеки Pycrypto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 648
197.6. Асимметричное RSА-шифрование с помощью Pycrypto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 649
197.7. Симметричное шифрование с использованием Pycrypto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 649
Глава 198. SSН-протокол в Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 650
198.1. SSН-соединение . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 650
Глава 199. Антипаттерны программирования в Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651
199.1. Чрезмерное использование except . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651
199.2. Процессороемкие функции . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651
Глава 200. Общие ошибки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652
200.1. Умножение списков и общие ссылки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652
200.2. Изменяемый аргумент по умолчанию . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 656
200.3. Изменение последовательности, над которой выполняется итерация . . . . . . . . . . . . . . . . . 657
200.4. Целочисленные и строковые тождества . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659
200.5. Словари неупорядочены. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 660
200.6. Утечка переменных в генераторах списков и циклах for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661
200.7. Цепочка операторов "ог" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661
200.8. sys.argv[0] - имя исполняемого файла . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662
200.9. Доступ к атрибутам целочисленных литералов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662
200.1 О. Глобальная блокировка интерпретатора (G IL) и блокировка потоков . . . . . . . . . . . . . . . . 663
200.11. Многократное использование оператора return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
200.12. JSОN-ключи в Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 664
Глава 201. Скрытые возможности . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 664
201.1. Перегрузка операторов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 664
Благодарности . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666

Глава 1 . Начало работы с я зы ком Python
Python З.x

Дата выпуска версии
3.11

2022-10-24

3.10

2021-10-04

3.9

2020-10-05

3.8

2020-04-29

3.7

2018-06-27

3.6

2016-12-23

3.5

2015-09-13

3.4

2014-03-17

3.3

2012-09-29

3.2

2011-02-20

3.1

2009-06-26

3.0

2008-12-03

Python 2.x

Дата выпуска версии
2.7

2010-07-03

2.6

2008-10-02

2.5

2006-09-19

2.4

2004-11-30

2.3

2003-07-29

2.2

2001-12-21

2.1

2001-04-15

2.0

2000-10-16

1 . 1 . Начало работы

Python - широко распространенный язык программирования высокого уровня для про­
граммирования общего назначения, созданный Гвидо ван Россумом и впервые выпущен­
ный в 1991 году. Python имеет динамическую систему типов и автоматическое управление
памятью, поддерживает множество парадигм программирования, включая объектно-ори­
ентированное, императивное, функциональное программирование и процедурные стили.
Он имеет большую и обширную стандартную библиотеку.
В настоящее время активно используются две основные версии Python:
• Python 3.х является текущей версией и находится в стадии активной разработки.
• Python 2.х является устаревшей версией и до 2020 года будет получать только
обновления безопасности. Никаких новых возможностей реализовано не будет.

28

Глава 1 . Начало работы с языком Python

Отметим, что многие проекты по-прежнему используют Python 2, хотя переход
на Python 3 становится все проще.
Подробнее об отличиях версий можно узнать в главе 149 "Несовместимости при перехо­
де с Python 2 на Python З". Кроме того, некоторые сторонние разработчики выпускают пере­
пакованные версии Python, в которые добавлены часто используемые библиотеки и другие
возможности, облегчающие настройку для обычных областей использования, таких как ма­
тематика, анализ данных или научные исследования.
Проверьте, установлен JП1 Python

Чтобы убедиться в том, что Python бьш установлен правильно, можно выполнить следу­
ющую команду в терминале (если вы используете ОС Windows, то перед использованием в ко­
мандной строке необходимо добавить путь к python в переменную окружения):
$ python --version

Версия Python 3.х ::с: 3.0:

Если у вас установлен Python 3 и это версия по умолчанию (подробнее см.
"IDLE - графический интерфейс Python"), то вы должны увидеть нечто подобное:

главу 1.6.

$ python --version Python 3.6.0

Версия Python 2.х :,; 2. 7:

Если у вас установлен Python 2 и это версия по умолчанию (подробнее см.
"IDLE - графический интерфейс Python"), то вы должны увидеть нечто подобное:

главу 1.6.

$ python --version Python 2. 7 . 1 3

Если у вас установлен Python 3, но команда $ python --version выводит версию Python 2, зна­
чит, у вас также установлен Python 2. Это часто встречается в MacOS и многих дистрибутивах
Linux. Для явного использования интерпретатора Python 3 используйте команду $ python3.
Hello , World на языке Python с использованием IDLE

IDLE - это простой редактор для Python, поставляемый в комплекте с Python.
• Откройте IDLE на выбранной вами системе.
о

В старых версиях Windows
Windows.

его можно найти в разделе "Все программы" меню

В Windows 8+ выполните поиск I DLE или найдите его в приложениях,
присутствующих в системе.
о В системах на базе Unix (включая Мае) его можно открыть из оболочки,
набрав $ idle python_fi le.py.
• Откроется оболочка с опциями в верхней части.
В оболочке имеется подсказка из трех прямых угловых скобок:
о

>>>

Теперь напишите после подсказки следующий код:



print("Hel lo, World")

Нажмите Enter.

»> print("Hel lo, World")
Hel lo, World

Файл "Hello , World" на Python

Создайте новый файл hel lo. py, содержащий следующую строку:

Версия Python 3.х ::с: 3.0:
print('Hel lo, World')

1 . 1 . Начало работы

29

Версия Python 2.х � 2.6:

Вы можете использовать функцию print Python 3 в Python 2 с помощью следующего опе­
ратора import:
from _future_ import print_function
Python 2 имеет ряд функциональных возможностей, которые могут бьrгь опционально
импортированы из Python 3 с помощью модуля _future_.

Версия Python 2.х :,; 2. 7:

Если используется Python 2, то можно также набрать строку, представленную ниже.
Обратите внимание, что в Python 3 это не работает и, следовательно, не рекомендуется, так
как снижает кросс-версионную совместимость кода.

print 'Hello, World'
В терминале перейдите в каталог, содержащий файл hello.py. Введите python hello.py, а затем
нажмите клавишу "Enter".
$ python hello.py
Hello, World
В консоли должно появиться сообщение Hello, World .
В ы также можете заменить hello.py н а путь к вашему файлу. Например, если файл
находится в домашнем каталоге, а ваш пользователь в Linux это "user", то вы можете набрать
python/home/user/hel lo.py.

Запуск интерактивной оболочки Python

При запуске команды python в терминале вы получаете интерактивную оболочку Python.
Она также известна как интерпретатор Python или REPL (от английского Read Evaluate Print
Loop).
$ python
Python 2.7. 1 2 (по умолчанию, Jun 28 201 6, 08:46:01 )
[GCC 6.1 . 1 201 60602] на linux
Для получения допол нител ьной и нформации введите "help", "copyright", "credits" или "license".
»> print 'Hello, World'
Hello, World
>>>
Если вы хотите запустить Python 3 из терминала, выполните команду python3.
$ python3
Python 3.6.0 (по умолчанию, 1 3 я н варя 201 7 г., 00:00:00)
[GCC 6.1 . 1 201 60602] на linux
Для получения допол нител ьной и нформации введите "help", "copyright", "credits" или "license".
»> print{'Hello, World') Hello, World
>>>
В качестве альтернативы запустите интерактивное приглашение и загрузите файл с помощью команды python -i . В командной строке выполните команду:
$ python -i hello.py
"Hello World"
>>>
Существует несколько способов закрыть оболочку Python:
»> exitQ
или
»> quit()

30

Глава 1 . Начало работы с языком Python

В качестве альтернативы комбинация клавиш "CTRL + D" закроет оболочку и вернет вас в
командную строку вашего терминала.
Если вы хотите отменить команду, которую вы вводите, и вернуться в чистую командную
строку, оставаясь внутри оболочки интерпретатора, нажмите "CTRL + С".
Другие онлайн-оболочки

Различные сайты предоставляют онлайн-доступ к оболочкам Python. Онлайн-оболочки
могут быть полезны для следующих целей.
• Запуск небольшого фрагмента кода с устройств, на которых не установлен Python
(смартфоны, планшеты и т. д.).
• Изучение или преподавание основ языка Python.
• Для работы с онлайн-судьями.
Примеры:
Отказ от ответственности: автор(ы) документации не связан(ы) с какими-либо ресурса­
ми, перечисленными ниже.
• https://www.python.org/shell/ - Онлайн-оболочка Python, размещенная
на официальном сайте Python.
• https://ideone.com/ - Ресурс, широко используемый в сети для иллюстрации
поведения фрагментов кода.
• https://repl.it/languages/pythonЗ - Мощный и простой онлайн-компилятор, IDE
и интерпретатор. Кодируйте, компилируйте и выполняйте код на языке Python.
• https://www.tutorialspoint.com/execute_python_online.php - Полнофункциональная
UNIХ-оболочка и удобный проводник проектов.
• http://rextester.com/l/python3_online_compiler - Простая и удобная
в использовании IDE, показывающая время выполнения.
Выполнение команд в виде строки

В оболочке Python можно передавать произвольный код в виде строки:

$ python -с 'print("Hello, World")'
Hello, World

Это может быть полезно при конкатенации результатов работы скриптов в оболочке.
Оболочки и не только

Управление пакетами. Рекомендуемым инструментом РуРА для установки пакетов
Python является P I P. Для установки выполните в командной строке команду pip install
. Например, pip instal l numpy. ( Примечание: в Windows необходимо доба­
вить pip в переменные окружения РАТН. Чтобы избежать этого, используйте команду
python -m pip install ).

Оболочки. До сих пор мы обсуждали различные способы выполнения кода с помощью
встроенной интерактивной оболочки Python. Оболочки используют интерпретирующие
возможности Python для экспериментов с кодом в реальном времени. Альтернативными
оболочками являются IDLE - готовый графический интерфейс, IPython, известный своей
возможностью расширения интерактивных возможностей, и др.
Программы. Для долговременного хранения можно сохранять содержимое в файлы
с расширением .ру и редактировать или исполнять их как скрипты или программы с по­
мощью внешних инструментов, например, оболочек, IDE (например, PyCharm), Jupyter
notebooks и т. д. Продвинутые пользователи могут использовать эти инструменты, однако
рассмотренные здесь методы являются достаточными для начала работы.
Python tutor позволяет пошагово просмотреть код Python, чтобы наглядно представить,
как будет работать программа, и помогает понять, где в программе допущены ошибки.
РЕРВ содержит рекомендации по форматированию кода Python. Правильное формати­
рование кода важно для того, чтобы можно было быстро прочитать, что он делает.

1 .2. Создание переменных и присвоение им значений

31

1 .2. Создание переменных и присвоение им значений
Чтобы создать переменную в Python, достаточно указать имя переменной, а затем присвоить
ей значение.
=

В Python для присвоения значений переменным используется символ =. Нет необходимо­
сти объявлять переменную заранее (или присваивать ей тип данных), присвоение значения
переменной само по себе объявляет и иmщиализирует переменную этим значением. Невоз­
можно объявить переменную, не присвоив ей начального значения.
# Целое число
а=2
print(a)
# Выходное значение: 2
# Целое число
Ь = 9223372036854775807
print(b)
# Выходное значение: 9223372036854775807
# Числа с плавающей точкой
pi = 3,1 4
print(pi)
# Выходное значение: 3 .1 4
# Строка
с = 'JJ:
print(c)
# Выходное значение: А
# Строка
name = 'John Doe'
print(name)
# Выходное значение: John Doe
# Логическое значение
q = True
print(q)
# В ыходное значение: True
# Пустое значение или тип дан н ых nu l l
х = None
print(x)
# В ыходное значение: None
Присвоение переменных работает слева направо. Поэтому в следующем примере будет
допущена синтаксическая ошибка:

О=х

=> Output: SyntaxError: can't assign to literal
Вы не можете использовать кшочевые слова Python в качестве допустимого имени перемен­
ной. Список ключевых слов можно посмотреть при помощи:
import keyword
print(keyword.kwlist)
Правила именования переменных:
1. Имена переменных должны начинаться с буквы или символа подчеркивания.
х = True
_у = True

# допустимо
# допустимо

32

Глава 1 . Начало работы с языком Python

# начинается с цифры
9х = False
=> SyntaxError: invalid syntax
# начинается с сим вола
$у = False
=> SyntaxError: invalid syntax

2. Остальная часть имени переменной может состоять из букв, цифр и знаков подчерки­
вания.
has_0_in_it = "Still Valid"

3. Имена чувствительны к регистру.
х=9

у = Х* 5

=>NameError: name 'Х' is not def1ned.

Несмотря на то, что при объявлении переменной в Python нет необходимости указывать
тип данных, при выделении необходимой области в памяти под переменную интерпретатор
Python автоматически выбирает для нее наиболее подходящий встроенный тип:
а=2
print(type(a))
# В ыходное значение:
Ь = 9223372036854775807
print(type(b))
# В ыходное значение:
pi = 3,1 4
print(type(pi))
# В ыходное значение:
с = 'А'
print(type(c))
# В ыходное значение:
name = 'John Doe'
print(type(name))
# В ыходное значение:
q = True
print(type(q))
# В ыходное значение:
х = None
print(type(x))
# В ыходное значение:

Теперь, когда вы знаете основы присваивания, давайте разберемся с тонкостями присва­
ивания в Python.
Когда вы используете символ = для выполнения операции присваивания, то, что находится
слева от = , является именем расположенного справа объекта. В конечном итоге символ =
присваивает имени, расположенному слева, ссылку на объект, находящийся справа.
То есть:
a_name = an_object

# "a_name" теперь я вляется именем для ссылки на объект "an_object"

Так, если из множества приведенных выше примеров мы выберем pi = 3, 1 4, то pi - это имя
(не название, поскольку у объекта может быть несколько имен) для объекта 3, 1 4. Если вы
что-то не поняли, вернитесь к этому пункту и прочитайте еще раз!

1 .2. Создание переменных и присвоение им значений

33

В одной строке можно присвоить несколько значений нескольким переменным. Обратите
внимание, что справа и слева от оператора = должно быть одинаковое количество аргументов:
а , Ь, с = 1 , 2, 3
print(a, Ь, с)
# В ыходное значение: 1 2 3
а, Ь, с = 1 , 2
= > Traceback (most recent call last):
=>
Файл "паmе.ру", строка N, in
=>
а, Ь, с = 1 , 2
= > ValueError: need more than 2 values to unpack
а, Ь = 1, 2, 3
= > Traceback (most recent call last):
=>
File "паmе.ру", line N, in
=>
а, Ь = 1 , 2, 3
= > ValueError: too many values to unpack

Ошибку в последнем примере можно устранить, присвоив оставшиеся значения равному
количеству произвольных переменных. Эта фиктивная переменная может иметь любое имя,
но для присвоения ненужных значений принято использовать знак подчеркивания U:
а, Ь, _ = 1, 2, 3
print(a, Ь)
# В ыходное значение: 1 , 2

Обратите внимание, что количество знаков подчеркивания U и количество оставшихся
значений должны быть равны. В противном случае будет выдана ошибка 'too many values to
unpack':
а, Ь, _ = 1 ,2,3,4
(most recent call last):
=>File "паmе.ру", line N, in
= >а, Ь, _ = 1 ,2,3,4
= >ValueError: too many values to unpack (expected 3)
= >Traceback

Также можно присвоить одно значение нескольким переменным одновременно.
а=Ь=с=1
print(a, Ь, с)
# В ыходное значение: 1 1 1

При использовании такого каскадного присваивания важно отметить, что все три
переменные а, Ь и с ссылаются на один и тот же объект в памяти - объект int со значением 1.
Другими словами, а, Ь и с - это три разных имени, присвоенных одному и тому же объекту
типа int. Присвоение впоследствии одному из них другого объекта не меняет остальных, как
и ожидалось:
а=Ь

=

с=1

# все три имени - а, Ь и с - ссылаются на один и тот же объект int
# со значением 1

print(a, Ь, с)
# В ыходное значение: 1 1 1
# Ь теперь ссылается на другой объект int, имеющий значение 2
Ь=2
print(a, Ь, с)
# В ыходное значение: 1 2 1
# теперь в ыходное значение соответствует ожиданиям.

Сказанное выше справедливо для изменяемых типов (таких как l ist, dict и т. д.) так же,
как и для неизменяемых (таких как int, string, tuple и т. д.):
х = у = [7, 8, 9]
х = [1 3, 8, 9]

# х и у ссылаются на один и тот же только что созданный объект списка [7, 8, 9]
# х теперь ссылается на другой только что созданный объект списка [1 3, 8, 9].

Глава 1 . Начало работы с языком Python

34

# у по-прежнему ссылается на список, которому он был присвоен в первый раз
print(y)
# Выходное значение: [7, 8, 9]

Пока все хорошо. Несколько иначе обстоиг дело с изменением объекта (в отличие от при­
своения имени другому объекту, что мы делали выше), когда каскадное присваивание исполь­
зуется для изменяемых типов. Посмотрите пример ниже:

х = у = [7, 8, 9]
# х и у - это два разных имени для одного и того же тол ько что созданного
объекта списка [7, 8, 9]
# м ы обновляем значение списка [7, 8, 9] через одно из его и мен, в данном
х[О] = 1 3
случае х
# печать значения списка с использованием его другого имени
print(y)
# Выходные данные: [1 3, 8, 9]
# следовательно, изменение отражается

Вложенные списки также допустимы в Python. Это означает, что список может содержать
в качестве элемента другой список.
х = [1 , 2, [3, 4, 5], 6, 7]
# это вложенный список
print х[2]
# Выходное значение: [3, 4, 5]
print х[2][1 ]
# Выходное значение: 4
Напоследок отметим, что переменные в Python не обязательно должны оставаться того
же типа, в котором они были впервые определены - можно просто использовать = , чтобы
присвоить переменной новое значение, даже если это значение имеет другой тип.
а=2
print(a)
# Выходное значение: 2

а = "New Value"
print(a)
# Выходное значение: New Value
Если вас это смущает, подумайте о том, что то, что находится слева от =, это просто имя объ­
екта. Сначала вы называете объект int со значением 2 именем а, затем передумываете и решае­
те присвоить имя а объекту string, имеющему значение "New Value". Просто, не так ли?

1 .3. Отступы блоков
Python использует отступы для выделения управляющих и циклических конструкций.
Это способствует повышению удобочитаемости Python, однако требует от программиста
пристального внимания к использованию пробельных символов. Таким образом, неправильная
настройка редактора может привести к тому, что код будет вести себя неожиданным образом.
В Python для указания места начала и конца блоков кода используется символ
двоеточия (:) и отступ (если вы пришли из другого языка, не путайте это с тем, что это как­
то связано с тернарным оператором). То есть блоки в Python, такие как функции, циклы,
предложения if и другие конструкции не имеют концевых идентификаторов. Все блоки
начинаются сдвоеточия, а затем содержат расположенные под ним строки с отступами.
Например:
def my_function():
а=2
return а
print(my_function())
или

if а > Ь:
print(a)

# Это определение фун кци и . Обратите внимание на двоеточие (:)
# Эта строка принадлежит функции, так как имеет отступ
# Эта строка также принадлежит той же фун кции
# Эта строка находится ВНЕ блока фун кций
# блок if начинается здесь
# Это часть блока if

35

1 .3. Отступы блоков
else:
print(b)

# else должен находиться на том же уровне, что и if
# Эта строка я вляется частью блока else

Блоки, содержащие ровно одно однострочное утверждение, могут быть помещены на одну
строку, хотя такая форма обычно не считается хорошим стилем в программировании:
if а > Ь: print(a)
else: print(b)

Попытка сделать это с помощью более чем одного оператора не даст результата:
if х > у: у = х
print(y) # lndentationError: неожиданны й отступ

if х > у: while у != z: у -= 1

# SyntaxError: неверный синтаксис

Пустой блок вызывает ошибку IndentationError (ошибку отступа). Используйте команду
которая ничего не делает), если у вас есть блок без содержимого:

pass (команда,

def wil l_be_implemented_laterO:
pass

Пробелы и табуляции

Вкратце: всегда используйте четыре пробела для отступа.
Использование только табуляции возможно, но в РЕР 8, руководстве по стишо кода Python,
говорится, что пробелы предпочтительнее.
Версия Python З.х :::: 3.0:

В Python 3 запрещено смешивать использование табуляции и пробелов для отступов.
В этом случае возникает ошибка компиляции, связанная с непоследовательным использо­
ванием табуляций и пробелов в отступах, в результате чего программа не запускается.

Версия Python 2.х :,; 2. 7:

В Python 2 допускается смешивание символов табуляции и пробела в отступах, но делать
это категорически не рекомендуется. Символ табуляции дополняет предыдущий отступ до
значения, кратного 8 пробелам. Поскольку обычно редакторы настроены на отображение
символов табуляции как кратных 4 пробелам, это может привести к возникновению труд­
ноуловимых ошибок.
Процитируем руководство РЕР 8:
При вызове интерпретатора командной строки Python 2 с опцией -t он выдает преду­
преждения о коде, в котором неправомерно смешиваются табуляции и пробелы. При
использовании опции -tt эти предупреждения превращаются в ошибки. Эти возмож­
ности настоятельно рекомендуются!

Многие редакторы имеют настройку перевода табуляций в пробелы. При настройке ре­
дактора следует различать символ табуляции ('\t') и клавишу "ТаЬ".




Символ табуляции должен бьггь преобразован в 8 пробелов, чтобы соответствовать
семантике языка - по крайней мере, в тех случаях, когда возможен (случайный)
смешанный отступ. Редакторы могут также автоматически преобразовывать
символ табуляции в пробелы.
Однако, возможно, вам может оказаться полезным настроить редактор таким
образом, чтобы нажатие на клавишу ''ТаЬ" вместо вставки символа табуляции
добавляло бы четыре пробела.

Исходный код Python, написанный с использованием смеси табуляций и пробелов или
с нестандартным количеством отступов, может быть приведен в соответствие с рер8 с помо­
щью инструмента autopep8 (менее мощная альтернатива поставляется с большинством ин­
сталляций Python: это reindent.py).

36

Глава 1 . Начало работы с языком Python

1 .4. Типы данных

ВстроеШiые типы
Логические значения (булева логика)
bool: это значение булевой логики, содержащее либо True, либо False. Логические операции типа and, ог, not могут быть выполнены при помощи булевой логики.
х ог у # если х - False, то у, иначе х
х and у # если х - False, то х, иначе у
not х # если х - True, то False, иначе True

В Python 2.х и Python 3.х логические значения также являются int. Тип bool является
подклассом типа int, а True и False - единственные его экземпляры:
issubclass(bool, int) # True
isinstance(True, bool) # True
isinstance(False, bool) # True

Если в арифметических операциях используются логические значения, то их целочис­
ленные значения (1 и О для True и False соответственно) будут использоваться для возврата
целочисленного результата:
# 1 + О == 1
True + False == 1
# 1 * 1 == 1
True * True == 1
Числа


int:

Целое число

а=2

Ь = 100
с = 123456 789
d = 38563846326424324

Целочисленные числа в Python имеют произвольный размер.
Примечание: в старых версиях Python был доступен тип long, который отличался от int.
Эти два типа были объединены.
• float: число с плавающей точкой; точность зависит от реализации и архитектуры
системы, для CPython тип данных float соответствует типу douЫe в С.
а = 2.0

Ь = 100.еО
с = 123456789.е1


complex: комплексные числа

а = 2 + 1j
Ь = 100 + 10j

Операторы < , < =, > и > = вызывают исключение ТуреЕггог, если любой из операндов является
комплексным числом.
Строки
Версия Python З.х ;:,: 3.0:
• str: строка в кодировке Unicode ('hello')

bytes: массив байтов (b'hello')
Версия Python 2.х :,; 2. 7:


str: байтовая строка ('hel lo')

37

1 .4. Типы данных


bytes: то же, что и str



unicode: строка в формате Unicode (Тип u'hel lo')

Последовательности и коллекции
В Python различают упорядоченные последовательности и неупорядоченные коллекции
(такие как set и dict).
• строки (str, bytes, unicode) - это последовательности.
• reversed: обратный порядок str с функцией reversed.
а = reversed('hello')


tuple:

упорядоченная коллекция из n значений любого типа (n >= О) ("кортеж'').

а = (1 , 2, 3)
Ь = ('а', 1 , 'python', (1 , 2))
Ь[2] = 'something else'

# возвращает ошибку типа ТуреЕггог

Поддерживает индексацию; неизменяемый; хешируемый, если все его члены хешируемы.

l ist: Упорядоченная коллекция из n значений (п >= О).
а = [1 , 2, З]
Ь = ['а', 1, 'python', (1 , 2), [1 , 2]] .
Ь[2] = 'something else'
# такое присвоение разрешено

Не хешируемый; изменяемый.
• set: неупорядоченная коллекция уникальных значений. Элементы должны быть
хешируемыми.
а = {1 , 2, 'а'}



dict: неупорядоченная коллекция уникальных пар "ключ-значение"; ключи
должны быть хешируемыми.

а = {1 : 'one',
2: 'two'}
Ь = {'а': [1 , 2, З],
'Ь': 'а string'}
Объект является хешируемым, если он имеет хеш-значение, которое никогда не меняется в те­
чение его жизни (для этого нужен метод _hash_ ()), и может сравниваться с друтими объекта­
ми (для этого нужен метод _eq_()). Хешируемые объекты, которые сравниваются на равенство,
должны иметь одинаковое хеш-значение.

Встроенные константы
Вместе со встроенными типами данных во встроенном пространстве имен имеется небольшое количество встроенных констант.
• True: истинное значение встроенного типа bool .

False: ложное значение встроенного типа bool.

None: синглтон-объект, который сигнализирует об отсутствии значения.

Ellipsis или . . . : используется в ЯдРе PythonЗ+ везде и ограниченно используется
в Python2.7+ как часть нотации массивов; numpy и родственные пакеты используют
это в качестве ссылки "включить все" в массивы.

Notl mplemented: синглтон, используемый для указания Python на то, что
специальный метод не поддерживает указанные аргументы, и Python будет
пробовать альтернативные варианты, если они доступны.
а = None

# Значение не присваивается. Позже может быть присвоен любой допустимый
# тип данных

38

Глава 1 . Начало работы с языком Python

Версия Python З.х ;:,: 3.0:
None не имеет естественного упорядочения. ИспользоваЮ1е операторов сравнения упоря­
дочивания ( ) больше не поддерживается и приведет к ошибке ТуреЕггог.
Версия Python 2.х :;; 2. 7:
None всегда меньше любого числа (None < -32 оценивается как True).
Тестирова1П1 е типа перемеmп.IХ

В Python мы можем проверить тип данных объекта с помощью встроенной функции type.
а = '1 23'

print(type(a))
# Результат:

Ь = 1 23

print(type(b))
# Результат:

В условных операторах можно проверять тип данных с помощью функции isinstance.
Однако полагаться на тип переменной обычно не рекомендуется.
i=7
if isinstance(i, int):
i += 1
elif isinstance(i, str):
i = int(i)
i += 1

Чтобы проверить, является ли что-либо пmом NопеТуре:
х = None

if х is Nопе:
ргiпt('Ничего удивительного, я просто определил х как None.')

Пр еобраз ование между типами данных

Можно выполнить явное преобразование типов данных.
Например, '123' имеет тип str и может быть преобразован в целое число с помощью
функции int.
а = '1 23'
Ь

=

int(a)

ПреобразоваЮ1е из float строки, например '123.456', можно выполнить с помощью функции

float.

а = '1 23.456'

Ь = float(a)
с = int(a)
d = int(b)

# ValueError: недопустим ы й литерал для int() с основанием 1 О: '1 23.456'
# 1 23

Можно также преобразовывать типы последовательностей или коллекций
а = 'hel lo'
list(a) # ['h', 'е', '1', '1', 'о']
set(a) # {'о', 'е', '1', 'h'}
tuple(a) # ('h', 'е', '1', '1', 'о')

Явный строковый тип при определе1П1и литералов

С помощью однобуквенных меток,расположенных непосредственно перед кавычками,
можно определить тип строки, которую требуется определить.

b'foo Ьаг': результатом будет bytes в Python 3, str в Python 2

u'foo Ьаг': результатом будет str в Python 3, unicode в Python 2

39

1 . 5. Типы коллекций



'foo Ьаг': результатом будет str

г'fоо Ьаг': результатом будет так называемая сырая строка, где все символы будут
интерпретироваться как есть (т. е. без обработки специальных символов, таких
как обратные слеши)

normal = 'foo\nbar'
escaped = 'foo\\пЬаг'
raw
= r'foo\nbar'

# foo
# Ьаг
# foo\nbar
# foo\nbar

Изменяемые и неизменяемые типы данных
Объект называется изменяемым, если он может изменять свое состояние или содержимое.
Например, при передаче списка в некоторую функцию список может бьггь изменен:
def f(m):
m.append(З)

# добавляет число в список. Это изменение.

х = [1 , 2]
f(x)
х == [1 , 2]

# теперь это ложно, так как в список был добавлен элемент

Объект называется неизменяемым, если он не может быть изменен никаким образом.
Например, целые числа являются неизменяемыми, поскольку их невозможно изменить:
def Ьаг():
х = (1 , 2)
g(x)
х == (1 , 2)

# всегда будет правдивым, так как ни одна функция не может изменить объект (1 , 2)

Заметим, что сами переменные являются изменяемыми, поэтому мы можем переназна­
чить переменную х, но это не изменит объект, на который ранее указывала х. Это только за­
ставит х указывать на новый объект.
Типы данных, экземпляры которых являются изменяемыми, называются изменяемыми
типами дюrnых; аналогично для неизменяемых объектов и типов данных.
Примеры неизменяемых типов данных:
• int, long, float, complex
• str
• bytes
• tuple
• frozenset
Примеры изменяемых типов данных:
• bytearray
• list
• set
• dict

1 .5. Типы коллекц ий

В Python существует несколько типов коллекций. В то время как такие типы как int и str
хранят одно значение, типы коллекций хранят несколько значений.

Списки (lists)

тип list является, пожалуй, наиболее часто используемым типом коллекции в Python. Не­
смотря на свое название, список больше похож на массив в других языках, особенно в JavaScript.

40

Глава 1 . Начало работы с языком Python

В Python list (список) - это просто упорядочеlШая коллекция допустимых значений Python. Спи­
сок можно создать, заюпочив значения, разделеllliые запятыми, в квадратные скобки:
int_list = [1, 2, З]
string_list = ['аЬс', 'defghi']

Список может быть пустым:
empty_list

=

D

mixed_list

=

[1, 'аЬс', True, 2.34, None]

Элементь1 списка не ограничены одним типом данных, что вполне логично, учитьшая, что
Python - динамический язык:
Список может содержать в качестве своего элемента другой список:

nested_l ist = [['а', 'Ь', 'с'], [1, 2, З]] .

Доступ к элементам списка может осуществляться через индекс, или числовое представление
их положения. Списки в Python имеют нулевой индекс, это означает, что первый элемент в
списке имеет индекс О, второй элемент имеет индекс 1 и так далее:
names = ['Alice', 'ВоЬ', 'Craig', 'Diana', 'Eric'].
print(names[O]) # Alice
print(names[2]) # Craig

Индексы могут быть и отрицательными, что означает отсчет от конца списка (-1 - индекс
последнего элемента). Таким образом, используя список из приведенного выше примера:
print(names[-1]) # Eric
print(names[-4]) # ВоЬ

Списки являются изменяемыми, значения в них можно менять:
names[O] = 'Ann'
print(names)
# В ыходное значение ['Ann', 'ВоЬ', 'Craig', 'Diana', 'Егiс'].

Кроме того, можно добавлять и/или удалять элементы из списка.
Добавить объект в конец списка можно с помощью команды L.append(object), возвращающей Nоnе.
names = ['Alice', 'ВоЬ', 'Craig', 'Diana', 'Eric']
names.append("Sia")
print(names)
# В ыходное значение ['Alice', 'ВоЬ', 'Craig', 'Diana', 'Eric', 'Sia'].

Добавить новый элемент в список по заданному индексу можно при помощи команды
L.insert(index, object).
names.insert(1, "Nikki")
print(names)
# Выходное значение ['Alice', 'Nikki', 'ВоЬ', 'Craig', 'Diana', 'Егiс', 'Sia'].

Удалить первое вхождение значения можно с помощью команды L.remove(value), которая
возвращает None.
names.remove("Bob")
print(names)
# В ыходное значение ['Alice', 'Nikki', 'Craig', 'Diana', 'Егiс', 'Sia'].

Получить индекс в списке первого элемента, значение которого равно х (при отсутствии
такого элемента выдаст ошибку):
name.index("Alice")

о

41

1 . 5. Типы коллекций

Подсчитать длину списка:
len(names)
б

Подсчет количества вхождений любого элемента в списке:
а = [1 , 1 , 1 , 2, 3, 4]
a.count(1 )

3

Обратить последовательность в списке:
a. reverseO
[4, 3, 2, 1 , 1 , 1 ]
# или
а[::-1 ]
[4, 3, 2, 1 , 1 , 1 ]

Удаление и возврат элемента по индексу (по умолчанию последний элемент) с помощью
возвращает элемент:

L.pop([index])

names. pop()

# Выведет 'Sia'

Вы можете перебирать элементы списка, как показано ниже:

for element in my_list:
print (element)

Кортежи (tuples)

похож на список, за исключением того, что он имеет фиксированную длину и
является неизменяемым. Таким образом, значения в кортеже не могут быть изменены, а так­
же не могут быть добавлены в кортеж или удалены из него. Кортежи обычно используются
для небольших коллекций значений, которые не должны меняться, например IР-адрес и
порт. В кортежах используются круглые скобки вместо квадратных:
tuple

ip_address = ('1 0.20.30.40', 8080)

Правила индексирования списков применяются и к кортежам. Кортежи также могут быть
вложенными, а их значения могут быть любыми допустимыми в Python значениями.
Кортеж, имеющий только один элемент, должен быть определен (обратите внимание на
запятую) таким образом:
one_member_tuple = ('Only member',)

или

one_member_tuple = 'Only member',

# Без скобок

или просто использовать синтаксис кортежей:

one_member_tuple = tuple(['Only member'])

Словари (dictionaries)

Словарь в Python -это набор пар "ключ-значение". Словарь окружен фигурными скобками.
Каждая пара разделяется запятой, а ключ и значение - двоеточием. Приведем пример:
state_capitals = {
'Arkansas': 'Little Rock',
'Colorado': 'Denver',
'California': 'Sacramento',
'Georgia': 'Atlanta'

Чтобы получить значение, обратитесь к нему по ключу:
= state_capitals['California']

ca_capital

42

Глава 1 . Начало работы с языком Python

Можно также получить все ключи в словаре и затем перебирать их:
for k in state_capitals.keys():
print('{} является столицей {}'.format(state_capitals[k], k))

Словари напоминают синтаксис JSON. Для преобразования между JSON и словарями
можно использовать собственный модуль json стандартной библиотеки Python.
Set (набор)

Set - это совокупность элементов без повторов и порядка вставки, но с отсортированным

порядком. Они используются в ситуациях, когда важно только то, что некоторые элементы
сгруппированы вместе, а не то, в каком порядке они были включены. Для больших групп
данных гораздо быстрее проверить, входит ли элемент в set (набор), чем сделать то же самое
для list (списка).
Определение набора очень похоже на определение словаря:
firsLnames = {'Adam', 'Beth', 'Charlie'}

Или можно создать set, используя существующий list:

my_list = [1 ,2,3]
my_set = set(my_list)

Проверить принадлежность к набору можно с помощью функции in:
if name in f1rst_names:
print(name)

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

defaultdict - это словарь со значением по умолчанию для ключей, так что к ключам,
для которых не бьшо явно определено значение, можно обращаться без ошибок. defaultdict
особенно полезен, когда значения в словаре представляют собой коллекции (списки, словари
и т. д.) в том смысле, что его не нужно инициализировать каждый раз, когда используется
новый ключ.
В defaultdict никогда не возникает ошибка KeyError. Для любого несуществующего ключа
возвращается значение по умолчанию. Например, рассмотрим следующий словарь:
>» state_capitals = {
'Arkansas': 'Little Rock',
'Colorado': 'Denver',
'California': 'Sacramento',
'Georgia': 'Atlanta'

Если мы попытаемся получить доступ к несуществующему ключу, Python выдаст нам
ошибку, как показано ниже:
»> state_capitals['Alabama']
Traceback (most recent call last):
File "", строка 1 , in
state_capitals['Alabama'].
KeyError: 'Alabama'

Попробуем использовать defaultdict. Его можно найти в модуле collections.
»> from collections import defaultdict
>» state_capitals = defaultdict{lambda: 'Boston')

1 .6. IDLE - графический и нтерфейс Python

43

Здесь мы зада;rn значеIШе по умолчанию (Boston) на случай, ec;rn ключ не существует.
Теперь заполните dict, как и раньше:
»> state_capitals['Arkansas'] = 'Little Rock'
»> state_capitals['California'] = 'Sacramento'
»> state_capitals['Colorado'] = 'Denver'
»> state_capitals['Georgia'] = 'Atlanta'
Если мы попытаемся обратиться к dict с несуществующим ключом, Python вернет нам
значение по умолчанию, т.е. Boston:
>» state_capitals['A labama']
'Boston'
и возвращает созданнь1е значения для существующего ключа так же, как и обычньIЙ словарь
»> state_capitals['Arkansas']
'Little Rock'

1 .6. IDLE - графический интерфейс Python

IDLE - это интегрированная среда разработки и обучения Python, являющаяся альтернативой
командной строке. Как следует из названия, IDLE очень полезна для разработки нового кода и;m
изучения языка Python. В Windows она поставляется вместе с интерпретатором Python, но в дРУ­
гих операционнь�х системах может потребоваться ее установка через менеджер пакетов.
Среди возможностей IDLE следует выдеJШТЬ:
• многооконный текстовый редактор с подсветкой синтаксиса, автозаполнеIШем
и интеллектуальным отступом;
• оболочку Python с подсветкой синтаксиса;
• встроенный отладчик с пошаговым выполнением, постоянными точками
останова и видимостью стека вызовов;
• автоматические отступы (полезно для начинающих изучать отступы в Python);
• сохранив программу на языке Python в формате .ру, в дальнейшем с помощью
IDLE ее можно запускать и редактировать в любом месте.
В IDLE для запуска интерпретатора нажмите FS или запустите оболочку Python. Исполь­
зование IDLE может быть более удобным для начинающих пользователей, поскольку код ин­
терпретируется по мере написания.
Заметим также, что существует множество альтернатив IDLE.
Поиск и устранение неисправностей (Тrouhleshooting)

• Windows
Если вы работаете под Windows, то по умолчанию используется команда python. Ec;rn вы
получаете ошибку '"python' is not recognized", то, скорее всего, причина в том, что местоположе­
ние Python не указано в переменной окружения РАТН вашей системы. Доступ к ней можно по­
лучить, щелкнув правой кнопкой мыши на "Мой компьютер" и выбрав "Свойства" и;m переЙДЯ
в "Система" через "Панель управления". Выберите пункт "ДопоЛIШТельные параметры систе­
мы", а затем "Переменнь1е среды ... ". Отредактируйте переменную РАТН, указав в ней каталог
установки Python, а также папку Script (обычно это C:\Python27; C:\Python27\Scripts). Это требу­
ет прав администратора; также может понадобиться перезагрузка.
При использовании нескольких версий Python на одном устройстве возможным решени­
ем является переименование одного из файлов python.exe. Например, если назвать одну из
версий python27.exe, то python27 станет командой запуска Python для этой версии.
Можно также использовать Python Launcher for Windows, который доступен через про­
грамму установки и поставляется по умолчанию. Он позволяет выбрать версию Python для
запуска с помощью команды ру -[х.у] вместо python[x.y]. Вы можете использовать последнюю
версию Python 2, запуская скрипты с ру -2, и последнюю версию Python 3, запуская скрипты
с ру -3.

44

Глава 1 . Начало работы с языком Python

Deblan/UЬuntu/MacOS

В данном разделе предполагается, что местоположение исполняемого файла python бьmо
добавлено в переменную окружения РАТН.
Если вы работаете на Deblan/UЬuntu/MacOS, откройте терминал и введите python для
Python 2.х или pythonЗ для Python 3.х.
Введите which python, чтобы узнать, какой интерпретатор Python будет использоваться.
Arch Linux

По умолчанию в Arch Linux (и его потомках) используется Python 3, поэтому используйте
2.х.

python или pythonЗ для Python 3.х и python2 для Python

Другие системы

Python 3 иногда связывают с python вместо pythonЗ. Для использования Python 2 на таких
системах, где он установлен, можно использовать команду python2.

1 .7. Ввод данных пользователем
Интерактивный ввод

Чтобы получить ввод от пользователя, используйте функцию input (примечание: в Python 2.х
эта функция называется raw_input, хотя в Python 2.х есть своя, совершенно отличная версия input):
Версия Python 2.х ::с: 2.3:
name = raw_input("What is your name? ")
# В ы вод: What is your name? _

Замечание по безопасности. Не используйте функцию input() в Python 2 - введенный текст будет
оценен как выражение в Python (эквивалентно eval(input()) в Python 3), что может легко стать
уязвимостью.
Версия Python З.х ::с: 3.0:
name = input("What is your name? ")
# Вы вод: What is your name? _

В остальной части этого примера будет использоваться синтаксис Python 3.
Функция принимает строковый аргумент, который отображает это в виде подсказки
и возвращает строку. Приведенный код выдает подсказку, ожидая ввода пользователя.
name = input("What is your name? ")
# В ы вод: What is your name?

Если пользователь наберет "ВоЬ" и нажмет клавишу Enter, то переменной name будет присвоена строка "ВоЬ":
name = input("What is your name? ")
# В ы вод: What is your name? ВоЬ
print(name)
# Вы вод: ВоЬ

Обратите внимание, что input всегда имеет тип str, что важно, если вы хотите, чтобы поль­
зователь вводил числа. Поэтому необходимо преобразовать str, прежде чем пытаться исполь­
зовать его в качестве числа:
х = input("Write а number:")
# В ы вод: Write а number: 1 О

х/2

# В ы вод: ТуреЕггог: unsupported operand type(s) for /: 'str' and 'int'
float(x) / 2
# В ы вод: 5.0

45

1 .8. Встроенные модули и функции

NB: При работе с пользовательским вводом рекомендуется использовать блоки try/except
для перехвата искшочений. Например, если ваш код хочет привести raw_input к int, а то, что
IШшет пользователь, оказывается неприводимым (некастируемым), то возникает ошиб­
ка ValueError.

1 .8. Встроенные модули и функции
Модуль - это файл, содержащий определения и операторы языка Python. Функция - это
фрагмент кода, выполняющий некоторую логику.
»> pow(2,3) #8
Для проверки встроенной функции в python мы можем использовать dir(). Если она вы­
зывается без аргумента, то возвращает имена в текущей области видимости. В противном
случае возвращается алфавитный список имен, содержащих (некоторые) атрибуты данного
объекта, а также атрибуты, доступные из него.
»> dir(_builtins_)
[
'ArithmeticError',
'AssertionError',
'AttributeError',
'BaseException',
'BufferError',
'BytesWarning',
'DeprecationWarning',
'EOFError',
'Ellipsis',
'EnvironmentError',
'Exception',
'False',
'Floating PointError',
'FutureWarning',
'GeneratorExit',
'IOError',
'lmportError',
'lmportWarning',
'1 ndentationError',
'1 ndexError',
'КеуЕггог',
'Keyboard I nterrupt',
'LookupError',
'MemoryError',
'NameError',
'None',
'Notl mplemented',
'Notl mplemented Error',
'ОSЕггог',
'OverflowError',
'PendingDeprecationWarning',
'ReferenceError',
'RuntimeError',
'RuntimeWarning',
'StandardError',
'Stoplteration',
'SyntaxError',
'SyntaxWarning',

'SystemError',
'System Exit',
ТаЬЕггог',
True',
ТуреЕггог',
'Unbound local Error',
'UnicodeDecodeError',
'UnicodeEncodeError',
'UnicodeError',
'UnicodeTranslateError',
'UnicodeWarning',
'UserWarning',
'ValueError',
'Warning',
'ZeroDivisionError',
'_debug_',
'_doc_',
'_import_',
'_name_',

'_package_',
'abs',
'al l',
'апу',
'apply',
'basestring',
'Ьiп',

'bool',
'buffer',
'bytearray',
'bytes',
'callaЫe',
'chr',
'classmethod',
'cmp',
'соегсе',
'compile',
'complex',
'copyright',
'credits',

46

Глава 1 . Начало работы с языком Python
'delattr',
'dict',
'dir',
'divmod',
'enumerate',
'eval',
'execf1 le',
'exit',
'file',
'filter',
'float',
'format',
'frozenset',
'getattr',
'globals',
'hasattr',
'hash',
'help',
'hex',
'id',
'input',
'int',
'intern',
'isinstance',
'issubclass',
'iter',
'len',
'license',
'list',
'locals',
'long',
'map',

'max',

'memoryview',
'min',

'next',
'object',
'oct',
'ореп',
'ord',
'pow',
'print',
'property',
'qu it',
'range',
'raw_input',
'reduce',
'reload',
'repr',
'reversed',
'round',
'set',
'setattr',
's lice',
'sorted',
'staticmethod',
'str',
'sum',

'super',
'tuple',
'type',
'unichr',
'unicode',
'vars',
'xrange',
'zip'

Для того чтобы узнать функциональность какой-либо функции, можно воспользоваться
встроенной функцией help.
»> help(max)
Help оп built-in function max in module _builtin_:
max(...)
max(iteraЫe[, key=func]) -> value
max(a, Ь, с, . . . [, key=func]) -> value
With а single iteraЫe argument, return its largest item.
With two or more arguments, return the largest argument.
("П ри наличии одного итерируемого аргумента возвращает наибольшее значение.
При наличии двух или более аргументов возвращает наибольший аргумент".)
Встроенные модули содержат дополнительные функциональные возможности. Например,
для получения квадратного корня из числа необходимо вкшочить модуль math.
>» import math
»> math.sq rt(1 6) # 4.0
Чтобы узнать все функции в модуле, можно присвоить переменной список функций,
а затем вывести его на печать.
>» import math
»> dir(math)
['_doc_', '_name_', '_package_', 'acos', 'acosh',
'asin', 'asinh', 'atan', 'atan2', 'atanh', 'cei l', 'copysign',

1 .9. Создание модуля

47

'cos', 'cosh', 'degrees', 'е', 'erf', 'erfc', 'ехр', 'expm1 ',
'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma',
'hypot', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log 1 О',
'log1 р', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc'].

при помощи doc можно получить некоторую документацию по функциям:
»> math. doc_
'Тhis module is always availaЫe. lt provides access to the\nmathematical functions defined Ьу the С
standard.'
{"Этот модуль всегда доступен. Он обеспечивает доступ к \пматематическим функция м,
определенным стандартом С".)

Помимо функций документация может быть представлена и в модулях. Так, если у вас
есть модуль с именем helloWorld.py, как в примере:
"""This is the module docstring."'"'
def sayHello():
"""This is the function docstring."""
return 'Hello World'

Получить доступ к его документам можно следующим образом:
import helloWorld
helloWorld._doc_
'Тhis is the module docstring.'
»> helloWorld.sayHello._doc_
'Тhis is the function docstring.'
»>
»>


»>

Для любого заданного пользователем типа с помощью d ir() можно получить его
атрибуты, атрибуты его класса и рекурсивно атрибуты базовых классов этого класса.

class MyClassObject{object):
pass

dir{MyClassObject)
['_class_', '_delattr_', '_dict_', '_doc_', '_format_', '_getattribute_', '_hash_', '_iniL',
'_module_', '_пеw_', '_reduce_', '_reduce_ex_', '_герг_', '_setattr_', '_sizeof_', '_str_',
'_subclasshook_', '_weakref_1
»>

Любой тип данных может быть просто преобразован в строку с помощью встроенной
функции str. Эта функция вызывается по умолчанию, когда тип данных передается в print
»> str{1 23)

# "1 23"

1 .9. Создание модуля

Модуль - это импортируемый файл, содержащий определения и выражения. Модуль мо­
жет быть сделан путем создания файла .ру.
# hello.py
def say_hello():
print{"Hello!")

Функции модуля могут быть использованы путем импорта модуля.
Созданные модули должны находиться в том же каталоге, что и файл, в который вы их им­
портируете. (Их можно поместить и в каталог библиотек Python liЬ вместе с предустановлен­
ными модулями, но по возможности этого следует избегать.)
$ python
import hello

»>

48

Глава 1 . Начало работы с языком

Python

>» hello.say_hello()
=> "Hello!"

Модули могут быть импортированы другими модулями.
# greet.py
import hello
hello.say_hello()
Импортировать можно конкретные функции модуля.
# greet.py
from hello import say_hello
say_hello()
Модули могут быть псевдонимами.
# greet.py
import hello as ai
ai.say_hello()
Модуль может представлять собой отдельный запускаемый скрипт.
# run_hello.py
if _name_ == ' main ':
from hello import say_hello
say_hello()
Запускайте!
$ python run_hello.py
=> "Hello!"
Если модуль находится в каталоге и должен быть обнаружен Python'oм, то в каталоге
должен находиться file с именем _init_.py.

1 . 1 О. Установка Python 2.7.х и З.х
Примечание: следующие инструкции написаны для Python 2.7 ( если это не оговорено особо).

Инструкции для Python З.х аналогичны.

Windows
Сначала загрузите последmою версию Python 2.7 с официального сайта (https://www.python.
org/downloads/). Версия предоставляется в виде пакета MSI. Чтобы установить ее вручную,
просто дважды щелкните на файле.
По умолчанию Python устанавливается в каталог:
C:\Python27\
Внимание: установка не приводит к автоматическому изменению переменной окруже­
ния РАТИ. Предполагая, что ваша установка Python находится в каталоге C:\Python27, добавь­
те в РАТИ:
C:\Python27\; C:\Python27\Scripts\
Теперь для проверки правильности установки Python напишите в cmd:
python --version
Python 2.х и 3.х вместе
Для установки и использования Python 2.х и 3.х на компьютере под управлением Windows:
1. Установите Python 2.х с помощью программы установки MSI.

1 . 1 О. Установка Python 2.7.х и 3.х

49

Убедитесь, что Python установлен для всех пользователей.
По желанию: добавьте Python в РАТ Н, чтобы Python 2.х можно бьmо вызьmать из
командной строки с помощью команды python.
2. Установите Python 3.х с помощью соответствующего инсталлятора.
о
Снова убедитесь, что Python установлен для всех пользователей.
о По желанию: добавьте Python в РАТ Н, чтобы сделать Python 3.х доступным для
вызова из командной строки с помощью команды python. Это может отменить
настройки РАТН для Python 2.х, поэтому дважды проверьте РАТН и убедитесь,
что он настроен в соответствии с вашими предпочтениями.
о Убедитесь, что ру launcher установлен для всех пользователей.
о
о

В Python 3 будет установлена программа Python launcher, с помощью которой из командной
строки можно запускать и Python 2.х, и Python 3.х:
Р:\>ру -3
Python 3.6.1 (v3.6.1 :69c0db5, Маг 21 201 7, 1 7:54:52) [MSC v. 1 900 32 Ьit (l ntel)] оп win32
Туре "help", "copyright", "credits" or "license" for more information.
("Для получения допол нительной информации введите "help", "copyright", "credits" или "license"").
>>>
С:\>ру -2
Python 2.7. 1 3 (v2.7.1 3:а06454Ь1 afa1 , Dec 1 7 201 6, 20:42:59) [MSC v. 1 500 32 lntel)] оп win32
Туре "hel p", "copyright", "credits" or "license" for more information.
("Для получения дополнительной информации введите "help", "copyright", "credits" или "license"").
>>>

Чтобы использовать соответствующую версию pip для конкретной версии Python, используйте:
С:\>ру -3 -m pip -V
pip 9.0.1 from C:\Python36\l ib\site-packages (python 3.6)
С:\>ру -2 -m pip -V
pip 9.0.1 from C:\Python27\l ib\site-packages (python 2.7)

Linux

Последние версии CentOS, Fedora, Red Hat Enterprise (RHEL) и UЬuntu поставляются с уста­
новленным Python 2. 7. Чтобы установить Python 2. 7 на linux вручную, просто выполните сле­
дующие действия в терминале:
wget --no-check-certif1cate https://www.python.org/ftp/python/2.7.X/Python-2.7.X.tgz
tar -xzf Python-2.7.X.tgz
cd Python-2.7.X
./conf1gure
make
sudo make install

Также добавьте путь к новому Python в переменную окружения РАТИ. Если новый
Python установлен в каталог /root/python-2.7.X, то выполните команду export РАТН = $ РАТН:/
root/python-2. 7 .Х
Теперь для проверки правильности установки Python напишите в терминале:
python -version

UЬuntu

Если вам нужен Python 3.6, вы можете установить его приведенным ниже способом (для
UЬuntu 16.10 и 17.04 версия 3.6 находится в универсальном репозитории). Для UЬuntu 16.04 и бо­

лее низких версий необходимо выполнить следующие шаги:

50

Глава 1 . Начало работы с языком Python

sudo apt install build-essential checkinstall
sudo apt instal l libreadl ine-gplv2-dev libncursesw5-dev libssl-dev libsq liteЗ-dev tk-dev l ibgdbm- dev
libc6-dev libbz2-dev
wget https://www.python.org/ftp/python/3.6.1 /Python-3.6.1 .tar.xz
tar xvf Python-3.6.1 .tar.xz
cd Python-3.6. 1 /
./configure --enaЫe-optimizations
sudo make altinstall

macOS
В настоящее время на macOS устанавmшается Python 2.7.10, но эта версия устарела и немного
отличается от обычного Python.
Версия Python, поставляемая с OS Х, отJШЧНо подходит для обучения, но не очень годится для разра­
ботки. Версия, поставляемая с OS Х, может быть устаревшей по сравнению с текущим выпуском
Python, который считается стабильной производственной версией.

Установите Homebrew при помощи:
/usr/Ьin/ruby -е "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Установите Python 2.7 при помощи:

brew install python

Для Python З.х вместо этого используйте команду brew install pythonЗ.

1 . 1 1 . Строковые функции - str() и repr()

Для получения читаемого представления объекта можно использовать две функции.
repr(x) вызывает x._repr_(): представление х. eval обычно преобразует результат этой
функции обратно в исходный объект.
str(x) вызывает x._str_(): читабельная для человека строка, описывающая объект.
При этом могут быть не учтены некоторые технические детали.
repr()
Для многих типов эта функция пытается вернуть строку, которая при передаче в eval() бу­
дет возвращать объект с тем же значением. В противном случае представление представля­
ет собой строку, заключенную в угловые скобки, которая содержит имя типа объекта вместе
с дополнительной информацией. Часто это включает имя и адрес объекта.
str()
Для строк эта функция возвращает саму строку. Разница между ней и repr(object) заключа­
ется в том, что str(object) не всегда пытается вернуть строку, приемлемую для eval (). Скорее, ее
целью является возвращение печатаемой или "читабельной для человека" строки. Если аргу­
мент не указан, то функция возвращает пустую строку ' '.
Пример 1:
s = """ w'o' w ''''"

repr(s)
str(s)
eval(str(s)) == s
eval(repr(s)) == s

# Вы вод: ' \ 'w \ \ \ \ \'o'w\"
# Вы вод: 'w\'o'w'
# Выдает SyntaxError (ошибку синтаксиса)
# Вы вод: True

Пример 2:
import datetime
today = datetime.datetime.now()
# В ы вод: '201 6-09-1 5 06:58:46.9 1 5000'
str(today)
repr(today)
# В ы вод: 'datetime.datetime(201 6, 9, 1 5, 6, 58, 46, 9 1 5000)'

При написании класса вы можете переопределить эти методы, чтобы сделать то, что вам
нужно:

1 . 1 2. Установка внешних модулей с помощью pip

51

класс Represent(object):
def _iniL(self, х, у):
self.x, self.y = х, у
def _repr_(self}:
return "Represent(x = {},y = \"{}\"}".format(self.x, self.y)
def _str_(self}:
return "Representing х as {} and у as {}".format(self.x, self.y}

Используя приведенный выше класс, мы можем увидеть результаты:
г = Represent(1 , "Норрег")
print(r)
# печатает str
print(r._repr_) # печатает _repr_: '< связанный метод Represent. repr of Represent(x= 1 ,y="Hopper")>'
гер = г. repr ()
# устанавливает выполнение repr в новую переменную
# печатает 'Represent(x = 1 ,y = "Hopper")'
print(rep)
г2 = eval(rep)
# оценивает гер
print(r2}
# печатает _str_ из нового объекта
print(r2 == г)
# выводит 'False', так как это разные объекты

1 . 1 2. Установка внешних модулей с помощью pip
Вам пригодится pip, когда понадобится установить любой пакет из множества доступ­
ных в индексе пакетов Python (PyPI). pip уже установлен, еСJШ вы используете Python 2 версии
вьппе 2.7.9 или Python 3 версии вьппе 3.4, загруженные с сайта python.org. На компьютерах, ра­
ботающих под управлением Linux или другой *nix операционной системы с собственным
менеджером пакетов, pip часто приходится устанавливать вручную.
На устройствах, на которых установлен и Python 2, и Python 3, программа pip часто ссы­
лается на Python 2, а рiрЗ - на Python 3. pip будет устанавливать только пакеты для Python 2,
а рiрЗ - только пакеты для Python 3.
Поиск / установка пакета
Поиск пакета осуществляется просто - достаточно набрать
$ pip search
# Поиск пакетов, имя или краткое содержание которых содержит .

Установить пакет очень просто: наберите (в терминале / командной строке, а не в интер­
претаторе Python)
$ pip instal l [имя_пакета]
$ pip instal l [имя_пакета] = = х.х.х
$ pip instal l '[имя_пакета]> =х.х.х'

# последняя версия пакета
# конкретная версия пакета
# минимальная версия пакета

где х.х.х - номер версии пакета, который вы хотите установить.
Если ваш сервер находится за прокси-сервером, вы можете установить пакет с помощью
следующей команды:
$ pip -ргоху http://: instal l

О бновлеIШе установленных пакетов
При появлении новых версий установленных пакетов они не устанавливаются в систему ав­
томатически. Чтобы узнать, какие из установленных пакетов устарели, выполните команду:
$ pip list -outdated

Для обновления конкретного пакета используйте

$ pip instal l [имя_пакета] --upgrade

Обновление всех устаревших пакетов не является стандартной функциональностью pip.

52

Глава 1 . Начало работы с языком Python

Повышение версии pip

Обновить существующую установку pip можно с помощью следующих команд.
• В Linux или macOS Х:

$ pip instal l -U pip
В некоторых системах Linux может потребоваться использование sudo вместе с pip.
• В операционной системе Windows:
ру -m pip instal l -U pip
или
python -m pip install -U pip

1 . 1 3. Справочная утилита
В Python есть несколько функций, встроенных в интерпретатор. Если вы хотите получить
информацию о ключевых словах, встроенных функциях, модулях или темах, откройте кон­
соль Python и введите:
»> help()
Вы будете получать информацию, вводя ключевые слова напрямую:
»> help(hel p)
или внутри утилиты:
help> help
в результате чего появится пояснение:
Help on _Hel per in module _sitebuiltins object:
class _Hel per(bui ltins.object)
Def1ne the builtin 'help'.
This is а wrapper around pydoc.help that provides а helpful message
when 'help' is typed at the Python interactive prompt.
Calling help() at the Python prompt starts an interactive hel p session.
Calling help(thing) prints hel p for the python object 'thing'.
Methods defined here:
_call_(self, *args, **kwds)
_repr_(self)
Data descriptors def1ned here:
_dict_
dictionary for instance variaЫes (if defined)
_weakref_
list of weak references to the object (if def1ned)
Можно также запрашивать подклассы модулей:
help(pymysql .connections)

53

2.1 . Строковый тип данных

Вы можете использовать help для доступа к документам различных импортированных
модулей, например, попробуйте сделать следующее:
»>

help(math)

и вы получите ошибку
»>
»>

import math
help(math)

Теперь вы получите список доступных методов в модуле, но только ПОСЛЕ того, как вы его
импортировали. Закрыть справочную утилиту можно с помощью команды quit.

Глава 2. Типы данных в Python
типы данных - это не что иное как переменные, которые используются для резервиро­
вания некоторого пространства в памяти. Для резервирования места в памяти переменные
Python не требуют явного объявления. Объявление происходит автоматически при присвое­
нии переменной значения.

2.1 . Строковый тип данных

Строка идентифIЩируется как непрерывный набор символов, заключенных в кавычки.
Python допускает использование пар одинарных или двойных кавычек. Строки являются не­
изменяемым последовательным типом данных, т. е. при каждом изменении строки создается
совершенно новый строковый объект.
a_str = 'Hello World'
print(a_str)
print(a_str[O])
print(a_str[0:5])

#вы вод будет представлять собой целую строку. Hello World
#вы водом будет перв ы й символ. Н
#вы вод будет состоять из первых пяти символов. Hello

2.2. Множества (set и frozenset)

Множества - это неупорядоченные коллекции уникальных объектов. Существует два их
типа:
1. Sets - изменяемы, новые элементы могут быть добавлены после того, как множества
определены.
basket = {'apple', 'огапgе', 'apple', 'реаг', 'огапgе', 'Ьапапа'}
print(basket)
# дубликаты будут удалены
> {'огапgе', 'Ьапапа', 'реаг', 'apple'}
а = set('abracadabra')
# вы ведет уникал ьные буквы в а
print(a)
> {'а', 'г', 'Ь', 'с', 'd'}
a.add('z')
print(a)
> {'а', 'с', 'г', 'Ь', 'z', 'd'}

2. Frozensets - неизменяемы, новые элементы не могут быть добавлены после определения.
Ь = frozenset('asdfagsa')
print(b)
> frozenset({'f', 'g', 'd', 'а', 's'})
города = frozenset(["Frankfurt", "Basel", "Freiburg"])
print(cities)
> frozenset({'Frankfurt', 'Basel', 'Freiburg'})

54

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

2.3. Числовые типы данных
Числа в Python имеют четыре типа: int, float, complex и long.
int_num = 1 0
float_num = 1 0.2
complex__num = 3. 1 4j
long_num = 1 234567L

#значение int (целочисленное)
# значение float (число с плавающей точкой}
# значение complex (комплексное число)
# значение long ("длинное" целое число неограниченного размера)

2.4. Тип данных "список" (list)
Список содержит элементы, разделе1rnые запятыми и заключенные в квадратные скобки [].
Списки практически аналогичны массивам в языке С. Отличие состоит в том, что все элемен­
ты, входящие в список, могут иметь различный тип данных.
list = [1 23;abcd', 1 o.2;d']
list1 = ['hello';world']
print(l ist)
print(l ist[0:2]}
print(l ist1 * 2)
print(l ist + list1 )

#может быть массивом данных любого типа или одного типа данных
#вы ведет весь список. [1 23, 'abcd' , 1 0.2, 'd']
#вы ведет первые два элемента списка [1 23, 'abcd']
#выдаст list1 два paзa. ['hello', 'world', 'hello', 'world']
#вы ведет конкатенацию (соединение) обоих списков. [1 23, 'abcd',
#1 0.2, 'd', 'hello';world']

2.5. Тип данных "словарь" (dic)
Словарь состоит из пар "ключ-значение". Он заключен в фигурные скобки {}, а для присвоения значений и доступа к ним используются квадратные скобки [] .
dic={'name':'red', 'age':1 О}
print(dic)
print(dic['name'])
print(dic.values())
print(dic.keys())

#вы ведет все пары "ключ-значение" {'name':'red', 'age':1 О}
#вы ведет только значение с ключом 'пате'. 'геd'
#вы ведет список значений в d ic ['геd',1 О]
#вы ведет список ключей ['name';age']

2.6. Тип данных "кортеж" (tuple)
Списки заключаются в скобки [ ], и их элементы и размер могут быть изменены, в то время
как кортежи заключаются в круглые скобки ( ) и не могут быть обновлены. Кортежи являют­
ся неизменяемыми.
tuple = (1 23;hello')
tuple1 = ('world'}
print(tuple}
print(tuple[0]}
print(tuple + tuple1 )
tuple[1 ]='update'

#вы ведем весь кортеж (1 23;hello')
#вы ведем первое значение (1 23}
#вы ведем (1 23;hello';world'}
#это приведет к ошибке.

3.1 . Простой пример

55

Глава 3. О тступы
3.1 . Простой пример

При создании языка Python Гвидо ван Россум использовал группировку выражений
по отступам. Двоеточие используется для объявления блока кода с отступом, например:
class ExampleClass:
#Каждая функция, принадлежащая классу, должна иметь одинаковы й отступ
def _iniL(self):
name = "example"
def someFunction(self, а):
#Обратите внимание, что все, принадлежащее функции, должно быть выделено отступом
if а > 5:
return True
else:
return False
#Если функция не имеет отступа одного уровня, она не будет рассматриваться как часть
родительского класса
def separateFunction(b):
for i in Ь:
#Циклы также имеют отступ, а вложенные условия начинаются с нового отступа
if i == 1 :
return True
return False
separateFunction([2,3,5,6, 1 ])

Пробелы или табуляции?
Рекомендуемый отступ - 4 пробела, но можно использовать и табуляции, и пробелы со­
гласованно. Не смеппmайте табуляции и пробелы, так как это приведет к ошибке в Python З
и может вызвать ошибки в Python 2.

3.2. Как происходит разбор отступов

Пробельные символы перед разбором обрабатываются лексическим анализатором.
Для хранения уровней отступов лексический анализатор использует стек. В начале стек
содержит только значение О, которое является крайней левой позицией. Каждый раз, когда
начинается вложенный блок, новый уровень отступа заносится в стек, а в поток лексем, пе­
редаваемый синтаксическому анализатору, вставляется токен "INDENT" ("отступ"). В стро­
ке не может быть более одного токена "INDENT" (иначе последует ошибка отступа lndentation Еггог).

Когда встречается строка с меньшим уровнем отступа, значения выгружаются из стека
до тех пор, пока на вершине не окажется значение, равное новому уровню отступа (если та­
ковое не наЙДено, то возникает синтаксическая ошибка). Для каждого извлеченного значе­
ния генерируется токен "DEDENT" ("впадина"). Очевидно, что токенов "DEDENT" может быть
несколько в строке.
Лексический анализатор пропускает пустые строки (содержащие только пробельные
символы и, возможно, комментарии) и никогда не генерирует для них токены "INDENТ" или
"DEDENТ". В конце исходного кода для каждого оставшегося в стеке уровня отступа генери­
руются токены "DEDENТ", пока не останется только О. Например:
if foo:

if Ьаг:
х = 42
else:
print foo

56

Глава 4. Комментарии и документация

анализируется как:

< I NDENT>
< I NDENT> < = >

< I NDENT>


[О]
[0, 4]
[0, 4, 8]
[О]
[О, 2]

Синтаксический анализатор затем использует токены "INDENT" и "DEDENТ" в качестве
разделителей блоков.

3.3. Ошибки отступа

Интервалы должны быть равномерными и одинаковыми. Неправильный отступ может
вызвать ошибку lndentationError или привести к неожиданным действиям программы. Следу­
ющий пример приводит к ошибке lndentationError:
а=7
if а > 5:
print "foo"
else:
print "Ьаг"
print "done"

Или если строка, следующая за двоеточием, не имеет отступа, то также последует ошиб­
ка lndentationError:
if True:
print "true"

Если добавить отступы там, где они не должны быть, результатом также станет ошибка

lndentationError:
if True:
а=б
Ь=5

Если забыть снять отступ, то функциональность может быть потеряна. В данном приме­
ре вместо ожидаемого значения False возвращается None:
def isEven(a):
if а%2 = =О:
return True
#следующая строка должна быть вровень с if
return False
print isEven(7)

Глава 4 . Ком ментарии и документация
4. 1 . Однострочные, строчные и многострочные комментарии

Комментарии используются для пояснения кода, если основной код может быть непоня­
тен сам по себе.
Python игнорирует комментарии, поэтому не будет выполнять содержащийся в них код или
выдавать синтаксические ошибки для обычных предложений. Однострочные комментарии
начинаются с символа xema (#) и завершаются концом строки.
• Однострочный комментарий:
# Это однострочн ы й комментарий в Python

4.2. Программны й доступ к строкам документации (docstrings)


Встроенный комментарий:

print("Hello World")


57

# Эта строка печатает "Hello World"

Комментарии, охватывающие несколько строк, имеют с обеих строи символы " " "

или ' ' '.

Этот тип ком ментария занимает нескол ько строк.
В основном они используются для документации в функциях, классах и модулях.

4.2. Программный доступ к строкам документации (docstrings)
Docstrings - в отличие от обычных комментариев - хранятся как атрибут функции, которую
они документируют, что означает возможность программного доступа к ним.

Пример функции
def func():
""" Это функция, которая вообще ничего не делает"""
return
Доступ к строке документации можно получить с помощью атрибута _doc_ attribute:
print(func._doc_)
Это функция, которая вообще ничего не делает
help(func)
Help on function func в модуле _main_:
func()
Это функция, которая вообще ничего не делает

Еще один пример функции

function._doc_ - это просто фактическая строка документации в виде строки, а функция help
предоставляет общую информацию о функции, включая строку docstring. Вот более полезный
пример:
def greet(name, greeting="Hello"):
"""Вы ведите приветствие пользователю 'name'.
Дополнител ьный параметр 'greeting' может изменить то, с чем они будут поздравлены."""
print("{} {}".format(greeting, name))
help(greet)
Help оп function greet in module _main_:
greet(name, greeting=,Hello,)
Выведите приветствие пол ьзователю ' name'.
Дополнительный параметр 'greeting' может изменить то, с чем они будут
поздравлены.

Преимущества docstrings перед обычными комментариями

Отсутствие в функции docstring или обычного комментария делает ее гораздо менее по­
лезной.
def greet(name, greeting="Hello"):
# В ы вести приветствие пользовател ю 'name'.
# Необязательный параметр 'greeting' позволяет изменять то, как они будут приветствоваться

58

Глава 4. Комментарии и документация

print("{} {}".format(greeting, name))
print(greet._doc_)
None
hel p(greet)
Help оп function greet in module main:
greet(name, greeting='Hello')

4.3. Написание документации
с использованием docstrings

docstring - это многострочный комментарий, используемый для документирования моду­
лей, классов, функций и методов. Он доюкен бьrгь первым высказыванием компонента, кото­
рый он описывает.
def hel lo(}:
""""Поздоровайтесь с кем-нибудь."""
Вы вести приветствие ("Hel lo") для человека с заданным именем.
print("Hello "+name)
class Greeter:
"""Объект, используем ы й для приветствия людей.
Он содержит несколько функций приветствия для нескольких языков и времени суток.

Значение docstring может быть доступно внутри программы и используется, например,
командой help.
Синтаксические соглашения в РЕР 257
257 определяет стандарт синтаксиса для комментариев docstrings. В основном он допу­
скает два типа:
• Однострочные docstrings:
Согласно РЕР 257, их следует использовать с короткими и простыми функциями. Все по­
мещается в одну строку, например
def hel lo(}:
""" Поприветствуй друзей."""
рrint("П ривет, друзья!"}

docstring должна заканчиваться точкой, глагол должен быть в императивной форме.
• Многострочные docstrings:
Многострочные docstring следует использовать для более длинных и сложных функций,
модулей или классов.
def hel lo(name, language="en"):
"""Поздороваться с человеком.
Аргументы:
name: имя человека
language: язык, на котором следует приветствовать собеседника
print(greeting[language]+" "+name)

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

4.3. Написание документации с использованием docstrings

59

Примечание: РЕР 257 определяет, какая информация должна быть представлена
в docstring, но не определяет, в каком формате она должна быть представлена. Это послужи­
ло причиной того, что другие стороны и средства разбора документации определили свои
собственные стандарты для документации, некоторые из которых перечислены ниже.
Синтаксические со глашения в Sphinx

Sphinx - это инструмент для генерации НТМL-документации для проектов на языке
Python на основе docstrings. Используемый язык разметки - это reStructuredText. В Sphinx су­
ществуют собственные стандарты документации, на сайте pythonhosted.org есть очень хоро­
шее их описание. Формат Sphinx используется, например, в pyCharm IDE.
В таком виде функция будет документироваться в формате Sphinx/reStructuredText:
def hel lo(name, language = "en"):
"""Поздороваться с человеком.
param name: имя человека
:type name: str
:param language: язы к, на котором следует приветствовать собеседника
:type language: str
:return: число
:rtype: int
print(greeting[language]+" "+name)
return 4

Руководство по стилю Google Python

Компания Google опубликовала руководство по стилю Google Python Style Guide, которое
определяет правила кодирования на языке Python, включая комментарии к документации.
По сравнению с Sphinx/reST многие отмечают, что документация, составленная в соответ­
ствии с рекомендациями Google, лучше читается человеком.
На упомянутой выше странице pythonhosted.org также приведены некоторые примеры
хорошей документации в соответствии с руководством по стилю Google.
Используя плагин Napoleon, Sphinx может также анализировать документацию в форма­
те, соответствующем Google Style Guide. В формате Google Style Guide функция будет докумен­
тирована следующим образом:
def hel lo(name, language = "en"):
"""Поздороваться с человеком.
Args:
name: имя человека в виде строки
language: строка кода языка
Возвращает:
Число.
print(greeting[language]+" "+паmе)
return 4

60

Глава 5. Дата и время

Глава 5. Дата и время
5 . 1 . Разбор строки в объект даты и времени (datetime)
с учетом часового пояса
В Python 3.2+ появилась поддержка формата 0/oz при разборе строки в объект datetime.
Смещение UТС (UТС offset) задается в виде +ННММ или -ННММ (пустая строка используется, если
объект не содержит информации о временной зоне).

Версия Python 3.х ::с: 3.2:
import datetime
dt = datetime.datetime.strptime("201 6-04-1 5T08:27: 1 8-0500", "%Y-%m-%dT%H:%M:%S%z")
Для других версий Python можно использовать внешнюю библиотеку, например dateutil,
которая позволяет быстро разобрать строку с часовым поясом в объект datetime.
import dateutil.parser
dt = dateutil. parser.parse("201 6-04-1 5T08:27:1 8-0500")
Переменная dt теперь является объектом datetime со следующим значением:
datetime.datetime(201 б, 4, 1 5, 8, 27, 1 8, tzinfo=tzoffset(None, -1 8000))

5.2. Построение временных интервалов с учетом временных зон
По умолчанию все объекты datetime не относятся ни к одному часовому поясу. Чтобы сде­
лать привязку на часовые пояса, необходимо подключить объект tzinfo, который привязыва­
ет функцию даты и времени к стандарту UTC.

Часовые пояса с фиксироваШiым смещением

В Python 3.2+ для часовых поясов, расположенных со смещением от UТС фиксированно,
модуль datetime предоставляет класс timezone, конкретную реализацию tzinfo, который при­
нимает timedelta и необязательный параметр name:

Версия Python 3.х ::с: 3.2:
from datetime import datetime, timedelta, timezone
JST = timezone(timedelta(hours=+9))
dt = datetime(201 5, 1 , 1 , 1 2, О, О, tzinfo=JST)
print(dt)
# 201 5-01 -01 1 2:00:00+09:ОО
print(dt.tzname())
# UTC+09:00
dt = datetime(201 5, 1, 1, 1 2, О, О, tzinfo=timezone(timedelta(hours=9), 'JSТ'))
print(dt.tzname)
# 'JSТ'
Для версий Python до 3.2 необходимо использовать сторонние библиотеки, такие как
dateutil. Эта библиотекапредоставляет эквивалентный класс tzoffset, который (начиная с вер­
сии 2.5.3) принимает аргументы вида dateutil.tz.tzoffset(tzname, offset), где смещение (offset) за­
дается в секундах:

Версия Python 3.х < 3.2:
Версия Python 2.х < 2. 7:
from datetime import datetime, timedelta
from dateutil import tz

5.2. Построение временных интервалов с учетом временных зон

61

JST = tz.tzoffset('JST', 9 * 3600) # 3600 секунд в часе
dt = datetime(201 5, 1 , 1 , 1 2, О, tzinfo=JST)
print(dt)
# 201 5-01 -01 1 2:00:00+09:ОО
print(dt.tzname)
# 'JSТ'

Часовые пояса с переходом на летнее время

Для часовых поясов с летним временем стандартные библиотеки python не предоставля­
ют стандартного класса, поэтому необходимо использовать сторонние библиотеки. Популяр­
ные библиотеки, предоставляющие классы для работы с часовыми поясами - pytz и dateutil.
В дополнение к статическим часовым поясам в dateutil есть классы часовых поясов, ис­
пользующих летнее время (см. документацию к модулю tz). С помощью метода tz.gettz() мож­
но получить объект часового пояса, который затем можно передать непосредственно в кон­
структор datetime:
from datetime import datetime
from dateutil import tz
local = tz.gettz() # Местное время
РТ = tz.gettz('US/Pacific') # Тихоокеанское время
dt_l = datetime(201 5, 1 , 1 , 1 2, tzinfo= local) # Я нахожусь в EST (Североамериканском
# ВОСТОЧНОМ времени)
dt_pst = datetime(201 5, 1 , 1 , 1 2, tzinfo=PT)
dt_pdt = datetime(201 5, 7, 1 , 1 2, tzinfo= PT) # DST (летнее время) обрабатывается автоматически
print(dt_l)
# 201 5-01 -01 1 2:00:00-05:00
print(dt_pst)
# 201 5-01 -01 1 2:00:00-08:00
print(dt_pdt)
# 201 5-07-01 1 2:00:00-07:00
ВНИМАНИЕ: Начиная с версии 2.5.3 dateutil некорректно обрабатывает неоднозначные
даты, и по умолчанию всегда выбирается более поздняя дата. В dateutil нет возможности со­
здать объект с текущим часовым поясом, например 201 5-1 1 -01 1 :30 EDT-4, так как это проис­
ходит во время перехода на летнее время.
Все крайние случаи при использовании pytz обрабатываются правильно, однако часовые
пояса pytz не следует напрямую присоединять через конструктор. Вместо этого часовой пояс
pytz должен быть присоединен с помощью метода localize:
from datetime import datetime, timedelta
import pytz
РТ = pytz.timezone('US/Pacific')
dt_pst = PT. localize(datetime(201 5, 1 , 1 , 1 2))
dt_pdt = PT.localize(datetime(201 5, 1 1 , 1 , О, 30))
print(dLpst)
# 201 5-01 -01 1 2:00:00-08:00
print(dLpdt)
# 201 5-1 1 -01 00:30:00-07:00
Следует иметь в виду, что если вы выполняете вычисление времени в часовом поясе, под­
держивающем pytz, то вычисления должны выполняться либо в UTC (если вы хотите полу­
чить абсолютное истекшее время), либо необходимо вызвать функцию normalize() :
dt_new = dt_pdt + timedelta(hours=3) # Это должно быть 2:30 АМ PST
print(dLnew)
# 201 5-1 1 -01 03:30:00-07:00
dt_corrected = PT.normalize(dt_new)
pri nt( dLcorrected)
# 201 5-1 1 -01 02:30:00-08:00

62

Глава 5. Дата и время

5.3. Вычисление разницы во времени
Для вычисления разницы между временами удобно использовать модуль timedelta:
from datetime import datetime, timedelta
now = datetime.now()
then = datetime(201 6, 5, 23)
# datetime.datetime(201 6, 05, 23, О, О, О)
При создании нового объекта datetime указание времени необязательно.
delta = now-then
delta имеет тип timedelta.
print(delta.days)
# 60
print(delta.seconds)
# 40826
Для получения даты n дней после и n дней до мы можем использовать:

n дней после даты:

def get_n_days_after_date(date_format="%d %8 %У", add_days=1 20):
date_n_days_after = datetime.datetime.now() + timedelta(days=add_days)
геtuгп date_n_days_afteг.strfti me(date_format)

n дней до даты:
def get_n_days_before_date(self, date_format="%d %В %У", days_before=1 20):
date_n_days_ago = datetime.datetime.now() - timedelta(days=days_before)
return date_n_days_ago.strftime(date_format)

5.4. Использование базовых объектов datetime
Модуль datetime содержит три основных типа объектов - дата, время и дата вместе со вре­
менем.
import datetime
# Date object (объект даты)
today = datetime.date.today()
пеw_уеаг = datetime.date(201 7, 01 , 01 ) #datetime.date(201 7, 1 , 1 )
# Time object (объект времени)
пооп = datetime.time(1 2, О, О) #datetime.time(1 2, О)
# Current datetime (текущие дата и время)
now = datetime.datetime.now()
# Datetime object (объект даты и времени)
millenium_turn = datetime.datetime(2000, 1 , 1 , О, О, О) #datetime.datetime(2000, 1 , 1 , О, О)
Арифметические операции для этих объектов поддерживаются только в пределах одного
типа данных. Выполнение простых арифметических операций с экземплярами разных ти­
пов приведет к ошибке TypeError.
# вычитание полудня из сегодняшнего дня
noon-today

5.5. Переключение между часовыми поясами

63

Traceback (most recent call last):
File "", l ine 1, in
TypeError: unsupported operand type(s) for -: 'datetime.time' and 'datetime.date'
# Вместо этого сделайте следующее:
print('Bpeмя с момента наступления тысячелетия в полночь: ',
datetime.datetime(today.year, today.month, today.day) - mil lenium_turn)
# Или так
print('Bpeмя с момента наступления тысячелетия в полдень: ',
datetime.datetime.comblne(today, noon) - mil lenium_turn)

5.5. Переключение между часовыми поясами
Для переключения между часовыми поясами необходимы объекты datetime, которые
учитывают часовые пояса.
from datetime import datetime
from dateutil import tz
utc = tz. tzutc()
local = tz.tzlocal()
utc_now = datetime.utcnow()
utc_now
# Без учета часового пояса.
utc_now = utc_now. replace(tzinfo = utc)
utc_now
# С учетом часового пояса.
local_now = utc_now.astimezone(local)
# Преобразовано в местное время.
local_now

5.6. Простые арифметические действия с датами
Даты н е существуют сами п о себе. Часто возникает необходимость определить промежу­
ток времени между датами или определить, какая дата будет завтра. Это можно сделать с по­
мощью объектов timedelta:
import datetime
today = datetime.date.today()
рrint('Сегодня:·, today)
yesterday = today - datetime.timedelta(days = 1 )
print('Bчepa:', yesterday)
tomorrow = today + datetime.timedelta(days = 1 )
print('Зaвтpa:·, tomorrow)
print('Bpeмя между завтра и вчера:', tomorrow - yesterday)
В результате будут получены результаты:
Сегодня: 201 6-04-1 5
Вчера: 201 6-04-1 4
Завтра: 201 6-04-1 6
Время между завтра и вчера: 2 дня, 0:00:00

64

Глава 5. Д ата и время

5.7. Преобразование временной метки (timestamp) в объект
datetime
Модуль datetime может преобразовывать временную метку POSIX (timestamp) в объект IТС
datetime. Эпохой (epoch, т. н. "эпоха Unix") является полночь 1 января 1970 года.
import time
from datetime import datetime
seconds_since_epoch =time.timeO

#1 4691 82681 .709

utc_date = datetime.utcfromtimestamp(seconds_since_epoch} #datetime.datetime(201 6, 7, 22, 1 О, 1 8,
1 , 709000)

5.8. Точное вычитание месяцев из даты
Используем модуль calendar:
import calendar
from datetime import date
def monthdelta(date, delta):
m, у = (date.month+delta) % 1 2, date.year + ((date.month}+delta-1 } // 1 2
if not m: m = 1 2
d = min(date.day, calendar.monthrange(y, m}[1 ]}
return date.replace(day = d,month = m, уеаг =у)
nexLmonth = monthdelta(date.today(), 1 } #datetime.date(201 6, 1 0, 23}
Использование модуля dateutils:
import datetime
import dateutil.relativedelta
d = datetime.datetime.strptime("201 3-03-3 1 ", "%Y-%m-%d"}
d2 = d - dateuti l.relativedelta.relativedelta(months = 1 ) #datetime.datetime(201 3, 2, 28, О, О}

5. 9. Разбор (парсинг) произвольной временной метки ISO 8601
с минимальным использованием библиотек
Python имеет лишь ограниченную поддержку разбора временных меток ISO 8601. Для ра­
боты с strptime необходимо точно знать, какой формат используется. В качестве усложнения
можно привести строковое преобразование времени даты в метку времени ISO 8601 с пробе­
лом в качестве разделителя и 6-значной дробью:
str(datetime.datetime(201 6, 7, 22, 9, 25, 59, 555555))
# '201 6-07-22 09:25:59.555555'
но если дробная часть равна О, то она не выводится
str(datetime.datetime(201 6, 7, 22, 9, 25, 59, О}}
# '201 6-07-22 09:25:59'
Но эти две формы требуют разного формата для strptime. Более того, strptime вообще не
поддерживает разбор часовых поясов, где часы и минуты разделяются двоеточием, поэто­
му 2016-07-22 09:25:59+0300 может быть проанализирован (разобран), а стандартный формат
2016-07-22 09:25:59+03:ОО' - нет. Существует библиотека из одного файла iso8601 , которая пра­
вильно анализирует временные метки ISO 8601 и только их. Она поддерживает дроби, часо­
вые пояса и разделитель Т, и все это с помощью одной функции:
import iso8601
iso860 1 .parse_date('201 6-07-22 09:25:59'}
# datetime.datetime(201 6, 7, 22, 9, 25, 59, tzinfo = )

5.1 О. Получение временной метки ISO 8601

65

iso8601 . parse_date('201 6-07-22 09:25:59+03:ОО')
# datetime.datetime(201 6, 7, 22, 9, 25, 59, tzinfo = )
iso8601 . parse_date('201 6-07-22 09:25:59Z')
# datetime.datetime(201 6, 7, 22, 9, 25, 59, tzinfo = < iso8601 . Utc>)
iso8601 .parse_date('201 6-07-22T09:25:59.0001 1 1 +03:00')
# datetime.datetime(201 6, 7, 22, 9, 25, 59, 1 1 1 , tzinfo = < FixedOffset '+03:00' . . . >)
Если часовой пояс не задан, то iso8601 . parse_date по умолчанию принимает значение
UTC. Зона по умолчанию может быть изменена с помощью ключевого аргумента defaulL
zone. Примечательно, что если вместо значения по умолчанию указано None, то те значения
даты и времени, у которых нет определенного значения часового пояса, устанавливаются
как простые значения даты и времени:
iso8601 . parse_date('201 6-07-22T09:25:59', defaulLtimezone = None)
# datetime.datetime(201 6, 7, 22, 9, 25, 59)
iso8601 . parse_date('201 6-07-22T09:25:59Z', default_timezone = None)
# datetime.datetime(201 6, 7, 22, 9, 25, 59, tzinfo = < iso8601 . Utc>)

5 . 1 О. Получение временной метки 150 860 1
Без часового пояса, с микросекундами
from datetime import datetime
datetime.now(). isoformat()
# В ы вод: '201 6-07-3 1 Т23:08:20.886783'

С часовым поясом, с микросекундами
from datetime import datetime
from dateutil.tz import tzlocal
datetime.now(tzlocal()).isoformat()
# В ы вод: '201 6-07-3 1 Т23:09:43.535074-07:ОО'

С часовым поясом, без микросекунд
from datetime import datetime
from dateutil.tz import tzlocal
datetime.now(tzlocal()).replace(microsecond = 0). isoformat()
# В ы вод: '201 6-07-3 1 Т23:1 0:30-07:00'

5 . 1 1 . Разбор строки с коротким именем часового пояса
в объект datetime с учетом часового пояса
Используя библиотеку dateutil можно разбирать временные метки с особым коротким
буквенным названием часового пояса.
Для дат с короткими названиями зон или аббревиатурами, которые, как правило, неод­
нозначны (например, CST - может быть Центральным стандартным временем, Стандарт­
ным временем в Китае, Стандартным временем Кубы и т. д.) или недоступны в стандартной
базе данных, необходимо задать соответствие между аббревиатурой часового пояса и объ­
ектом tzinfo.
from dateutil import tz
from dateutil. parser import parse

66

Глава 5. Дата и время

ЕТ = tz.gettz('US/Eastern')
СТ = tz.gettz('US/Central')
МТ = tz.gettz('US/Mountain')
РТ = tz.gettz('US/Pacif1c')
us_tzinfos = {'CST': СТ, 'СDТ': СТ,
'ЕSТ': ЕТ, 'ЕDТ': ЕТ,
'МSТ': МТ, 'М DТ': МТ,
'РSТ': РТ, 'PDT': РТ}
dLest = parse('201 4-01 -02 04:00:00 ЕSТ', tzinfos = us_tzinfos)
dLpst = parse('201 6-03-1 1 1 6:00:00 PST', tzinfos=us_tzinfos)
После выполнения этого:
dLest
# datetime.datetime(201 4, 1 , 2, 4, О, tzinfo =tzfile('/usr/share/zoneinfo/US/Eastern'))
dLpst
# datetime.datetime(201 6, 3, 1 1 , 1 6, О, tzinfo =tzf1 le('/usr/share/zoneinfo/US/Pacific'))
Следует отметить, что при использовании в этом методе часового пояса pytz локализация
будет неправильной:
from dateutil.parser import parse
import pytz
EST = pytz.timezone('America/New_York')
dt = parse('201 4-02-03 09:1 7:00 EST', tzinfos={'ESТ': EST})
Таким способом можно присоединить временную зону pytz к datetime:
dt.tzinfo # Будет в местном среднем времени
# < DstТzlnfo 'America/New_York' LMT-1 день, 1 9:04:00 STD>
При использовании этого метода, вероятно, следует повторно использовать local ize для
простых значений даты и времени::
dLfixed = dt.tzinfo.localize(dt.replace(tzinfo = None))
dLf1xed.tzinfo # Теперь это в ЕSТ.
# < DstТzlnfo 'America/New_York' EST-1 день, 1 9:00:00 STD>)

5 . 1 2. Нечеткий синтаксический анализ времени (извлечение
даты и времени из текста)
Извлечь дату из текста можно с помощью dateutil.parser в "нечетком" (fuzzy) режиме, в
котором компоненты строки, не распознанные как часть даты, игнорируются.
from dateutil.parser import parse
dt = рагsе("Сегодня 1 января 2047 года в 8:21 :ООАМ", fuzzy = True)
print(dt)
Теперь dt - это объект datetime, и на экране появится datetime.datetime(2047, 1 , 1 , 8, 21 ).

5 . 1 З. Итерация по датам
Иногда требуется выполнить итерацию по диапазону дат от начальной даты до конечной. Для этого можно использовать библиотеку datetime и объект timedelta:
import datetime
# Размер каждого шага в днях
day_delta = datetime.timedelta(days = 1 )

6.1 . Время между двумя датами
start_date = datetime.date.today()
end_date = start_date + 7*day_delta
for i in range((end_date - start_date).days):
print(start_date + i*day_delta)
В результате чего образуются:
201 6-07-21
201 6-07-22
201 6-07-23
201 6-07-24
201 6-07-25
201 6-07-26
201 6-07-27

Глава 6. Ф орматирование даты
6. 1 . Время между двумя датами
from datetime import datetime
а = datetime(201 6, 1 0,06,0,0,0)
Ь = datetime(201 6, 1 0,01 ,23,59,59)
а-Ь
# datetime.timedelta(4, 1 )
(а-Ь).дни
#4

(а-Ь) .total_seconds()
# 51 8399.0

6.2. Вы вод объекта datetime в строку
Используются стандартные коды формата С:
from datetime import datetime
datetime_for_string = datetime(201 6, 1 О, 1 ,0,0)
datetime_string_format = '%Ь %d %У, %H:%M:%S'
datetime.strftime(datetime_for_string,datetime_string_format)
# Oct 01 201 6, 00:00:00

6.3. Парсинг строки в объект datetime
Используются стандартные коды формата С:
from datetime import datetime
datetime_string = 'Oct 1 201 6, 00:00:00'
datetime_string_format = '%Ь %d %У, %H:%M:%S'
datetime.strptime(dateti me_string, datetime_string_format)
# datetime.datetime(201 6, 1 О, 1 , О, О)

67

68

Глава 7. Модуль Enum

Глава 7. Модуль Enum

7 . 1 . Создание перечисления {Python 2.4-3.3)

Перечисления были перенесены с Python 3.4 на Python 2.4 и Python 3.3. Вы можете полу­
чить бэкпорт enum34 из PyPI.
pip instal l enum34

Создание перечисления идентично тому, как это происходит в Python 3.4+

from enum import Enum
class Color(Enum):
геd = 1
green = 2
Ыuе = 3
print(Color. red) # Color.red
print(Color(1 )) # Color. red
print(Color['red']) # Color. red

7.2. Итерация

Перечисления являются итерируемыми:

class Color(Enum):
red = 1
green = 2
Ыuе = 3
[с for с in Color] # [, , ]

Глава 8. Множества
8.1 . Операции над множествами
с другими множествами

# Пересечение
{1 , 2, 3, 4, 5}.intersection({3, 4, 5, 6}) # {3, 4, 5}
# {3, 4, 5}
{1 , 2, 3, 4, 5} & {3, 4, 5, 6}
# Объединение
{1 , 2, 3, 4, 5}.union({3, 4, 5, 6})
{1 , 2, 3, 4, 5} 1 {3, 4, 5, 6}

# {1 , 2, 3, 4, 5, 6}
# {1 , 2, 3, 4, 5, 6}

# Разница
{1 , 2, 3, 4}.difference({2, 3, 5})
{1 , 2, 3, 4} - {2, 3, 5}

# {1 , 4}
# {1 , 4}

# Сим метричная разность
{1 , 2, 3, 4}.symmetric_difference({2, 3, 5})
{1 , 2, 3, 4} А {2, 3, 5}

# {1 , 4, 5}
# {1 , 4, 5}

69

8.2. Получение уникальных элементов списка
# П роверка суперпозиции (надмножества)
# False
{1 , 2}.issuperset{{1 , 2, 3})
# False
{1 , 2} >= {1 , 2, 3}
# П роверка подмножества
{1 , 2}.issubset({1 , 2, 3})
{1 , 2} а = {1 , 2, 2, 3, 4}
»> Ь = {3, 3, 4, 4, 5}

Примечание: {1 } создает множество из одного элемента, а {} создает пустой dict. Правильным
способом создания пустого множества является set().

Пересечение

a. intersection(b)

так и в Ь

возвращает новое множество, элементы которого присутствуют как в а,

>» a.intersection(b)
{3, 4}

Объединение

a. union(b) возвращает новый набор

с элементами, присутствующими в любом а и Ь

»> a.union(b)
{1 , 2, 3, 4, 5}
РаЗНIЩа

a.difference(b) возвращает новый набор

»> a.difference(b)
{1 , 2}
»> b.difference(a)
{5}

с элементами , присутствующими в а, но не в Ь

8.4. Операции над множествами с использованием методов и встроенных модулей

Симметричная разница

a.symmetric_difference(b) возвращает новый

бом а или Ь, но не в обоих множествах

71

набор с элементами, присутствующими в лю-

>» a.symmetric_difference(b)
{1 , 2, 5}
»> b.symmetric_difference(a)
{1 , 2, 5}

Примечание: a.symmetric_difference(b) == b.symmetric_difference(a)
Подмножество и надмножество

c.issubset(a) проверяет, соответствует ли каждый элемент с в а
a.issuperset(c) проверяет, является ли каждый элемент с находится

d а.

»> с = {1 , 2}
»> c.issubset(a)
True
»> a.issuperset(c)
True

Последние операции имеют эквивалентные операторы, как показано ниже:

Метод

О п ератор

a.intersection(b)

а&Ь

a.union(b)

alb

a.difference(b)

а-Ь

a.symmetric_d ifference(b)

аль

a.issubset(b)

а = Ь

Множества а и d не пересекаются, если ни один элемент а не находится также в d, и наоборот.
»> d = {5, б}
»> a.isdisjoint(b)
False
»> a.isdisjoint(d)
True

# {2, 3, 4} в обоих множествах

# эквивалентная п роверка, но менее эффективная
»> len(a & d) == О
True
# еще менее эффективная
»> а & d == set()
True

Тестирование вхождения

Встроенное ключевое слово in ищет вхождения:

»> 1 in а
True
>>> 6 in а
False

72

Глава 9. Простые математи ч еские опер аторы

длина

Встроенная функция len() возвращает количество элементов в множестве

»> len(a)
»> len(b }

3

8.5. Множества и мультимножества
Множества (наборы) - это неупорядоченные коллекции отдельных элементов. Но иногда
мы хотим работать с неупорядоченными коллекциями элементов, которые не обязательно
различны и отслеживают множественность элементов.
Рассмотрим пример:
setA = {'а','Ь','Ь','с'}
setA
set(['a', 'с', 'Ь']}

При сохранении строк 'а' , 'Ь' , 'Ь' , 'с' в структуру данных набора (set) мы потеряли инфор­
мацию о том , что 'Ь' встречается дважды. Конечно, сохранение элементов в списке (list) со­
хранит эту информацию.
listA = ['а·;ь·;ь·;с']
listA
['а', 'Ь', 'Ь', 'с']

Но структура данных списка вводит дополнительный ненужный порядок, который за­
медляет наши вычисления.
Для реализации мультимножеств Python предоставляет класс Counter из модуля collec­
tions (начиная с версии 2.7):
Версия Python 2.х ::с: 2. 7:
»> from collections import Counter
>» counterA = Соuntег(['а';ь·;ь·;с'])
>» counterA

Counter({'b': 2, 'а': 1 , 'с': 1 })

Counter представляет собой словарь, где элементы хранятся в виде ключей словаря и их
счетчики хранятся в виде значений словаря. И, как и все словари, он является неупорядо­
ченной коллекцией.

Гла ва 9 . П рост ы е м ате м ати ческие
оператор ы
Числовые тm1ы и их метаклассы
Модуль numbers содержит абстрактные метаклассы для числовых типов:
numbers. Number numbers.lntegral
subclasses
+
bool
+
int
+
+
fractions. Fraction
+
float
+
complex
+
+
decimal.Decimal

numbers. Rational numbers.Real

numbers.Complex

+

+

+

+

+

+

+

+

+

+

+
+

73

9.1 . Деление

В Python реализованы общие математические операции, включая целочисленное деле­
ние, деление с плавающей точкой, умножение, возведение в степень, сложение и вычита­
ние. Математический модуль (включен во все стандартные версии Python) содержит расши­
ренную функциональность - тригонометрические функции, извлечение корня, логарифмы
и многое другое.

9.1 . Деление

Python выполняет целочисленное деление, когда оба операнда являются целыми числа­
ми. Поведение операторов деления Python отличается в различных версиях
а, Ь, с, d, е = 3, 2, 2.0, -3, 1 О

В Python 2 результат использования оператора "/" зависит от типа числителя и знаменателя.
а/Ь
а/с
d/Ь
Ыа
d/e

#= 1
# = 1 ,5
# = -2
#= О
# = -1

Обратите внимание, что поскольку а и Ь являются int (целочисленными), то и результат
является int. Результат всегда округляется до нуля. Поскольку с является float (числом с пла­
вающей точкой), результатом а / с является float. Вы также можете использовать модуль опе­
ратора.
import operator
operator.div (а, Ь)
operator._ div _ (а, Ь)

# Модуль оператора предоставляет арифметические функции с двумя
# аргументами
#= 1
#= 1

Что делать, если вы хотите осуществить деление чисел с плавающей точкой:
Рекомендуемый способ:
from _future_ import division
# = 1 .5
а/Ь
#= 1
а // Ь

# применяет к модул ю деление в стиле Python 3

Если вы не хотите применять ко всему модулю:
а / (Ь * 1 .0)

1 .0 * а / Ь
а / Ь * 1 .0

# = 1 .5
# = 1 .5
# = 1 .0

(будьте осторожны с последовател ьностью операций)

from operator import truediv
truediv(a, Ь)
# = 1 .5

Нерекомендуемый способ (может вызывать TypeError, например, если аргумент - ком­
плексное число):
float(a) / Ь
а / float(b)

# = 1 .5
# = 1 .5

Версия Python 2.х � 2.2:
Оператор "!!" в Python 2 вызывает деление с округлением к меньшему.
а // Ь
а // с

#= 1
# = 1 .0

В Python 3 оператор "/" выполняет "истинное" деление независимо от типа аргументов.
Оператор "//" выполняет деление с округлением к меньшему и сохраняет тип.

74

Глава 9. Простые математические операторы

а/ Ь
е/Ь
а // Ь
а // с

#=
#=
#=
#=

import орегаtог
operator.truediv(a, Ь}
operator.floordiv(a, Ь}
operator.floordiv(a, с)

1 .5

5.0
1
1 .0

# модуль орегаtог предоставляет 2-аргументные арифметические функции
# = 1 .5
#=1
# = 1 .0

Возможные комбинации (для встроенных типов):
• int и int (дает int в Python 2 и float в Python 3)
• int и float (дает float)
• int и complex (дает complex)

float и float (дает float)

float и complex (дает complex)
• complex и complex (дает complex)
См. РЕР 238 для получения дополнительной информации.

9.2. Сложение
а, Ь = 1, 2

# Используется оператор "+" :
а+Ь #=3
# Используется оператор "+ = " для сложения и присваивания:
а + = Ь # а = 3 (эквивалентно а = а + Ь}
import орегаtог

# поддерживает арифметические функции с двумя аргументами

operator.add(a, Ь}

# = 5 так как а присвоено значение 3 перед этой строкой

# Оператор "+ = " эквивалентен:
а = operator.iadd(a, Ь}
# а = 5 так как а присвоено значение 3 перед этой строкой

Возможные комбинации (встроенные типы):
• int и int (дает int)
• int и float (дает float)
• int и complex (дает complex)
• float и float (дает float)

float и complex (дает complex)
• complex и complex (дает complex)
Примечание: оператор "+" также используется для конкатенации строк, списков и кор­
тежей:
"f1rst string " + "second string"

# 'first string second string'

[ 1 , 2, 3] + [4, 5, б]

# = [ 1 , 2, 3, 4, 5, б]

9.3. Возведение в степень
а, Ь = 2, 3
(а ** Ь}
pow(a, Ь}
import math
math.pow(a, Ь}

#=8
#=8
# = 8.0 (всегда число с плавающей точкой; комплексные числа не разрешены)

9.4. Тригонометрические функции
import operator
operator. pow(a, Ь)

75

#= 8

Еще одно различие между встроенной функцией pow и math.pow - то, что встроенная pow
может принимать три аргумента:
а, Ь, с = 2, 3, 2
pow(2, 3, 2)

# О, вычисляет (2 ** 3) % 2, но, по утверждению документации Python,
# делает это более эффективно

Специальные функции

Функция math.sqrt(x) вычисляет квадратный корень из х.

import math
import cmath
с=4
math.sqrt(c)
cmath.sqrt(c)

# = 2.0 (всегда число с плавающей точкой; комплексные числа
# не разрешены)
# = (2+0j) (всегда комплексное число)

Для вычисления других корней, например кубического корня, необходимо возвести чис­
ло в степень, обратную степени корня. Это можно сделать с помощью любой экспоненциаль­
ной функции или оператора.
import math
х=8
math.pow(x, 1 /3)
х**(1 /3)

# оценивается как 2.0
# оценивается как 2.0

Функция math.exp(x) вычисляет е ** х.
math.exp(0) # 1 .0
math.exp(1 ) # 2.71 8281 828459045 (е)
Функция math.expm1 (х) вычисляет е ** х - 1 . При малых значениях х это дает существенно
лучшую точность, чем math.exp(x) - 1 .
math.expm 1 (О)

# О.О

math.exp(1 е-6) - 1
math.expm 1 (1 е-6)
# точны й результат

# 1 .ОООООО4999621 837е-06
# 1 .0000005000001 665е-06
# 1 .0000005000001 66666708333341 666 . . .

9.4. Тригонометрические функции
а, Ь = 1 , 2

import math
math.sin(a) # возвращает синус 'а' в радианах
# Вывод: О.841 4709848078965
math.cosh(b) # возвращает обратны й гиперболический косинус от 'Ь' в радианах
# Вывод: 3.7621 95691 08363 1 4
math.atan(math.pi) # возвращает арктангенс от числа п в радианах
# Вывод: 1 .26262725567891 1 5
math.hypot(a, Ь) # возвращает евклидову норму, аналогично math.sqrt(a*a + Ь*Ь)
# Вывод: 2.23606797749979

76

Глава 9. Простые математические операторы
Заметим, что math.hypot(x, у) - это также длина вектора (или евклидово расстояние) от начала
координат (О, О} в точку (х, у).
Для вычисления евклидова расстояния между двумя точками (х1 , у1 ) и (х2, у2} можно восполь­
зоваться функцией math.hypot следующим образом:
math.hypot(x2-x1 , у2-у1 )

Для преобразования радиан в градусы и градусов в радианы соответственно используются math.degrees и math.radians.
math.degrees(a)
# В ы вод: 57.29577951 308232
math.radians(57.29577951 308232}
# В ы вод: 1 .0

9.5. Операторы присваивания ("операции на месте")
В приложениях ча сто возникает нео бходим ость в п одо бн о м коде:

а=а+1

или

а а*2
=

Суще ствует эффективн о е со кращение для этих о пераций:

а += 1


а *= 2
Перед символом = может быть использован любой математический оператор для выполнения операции присваивания:
• -= декремент переменной на месте
• + = инкремент (приращение) переменной на месте
• *= умножение переменной на месте



/= деление переменной на месте



//= деление с округлением к меньшему на месте (в Python 3)
%= возвращает остаток от деления переменной на месте



**= возведение переменной на месте

Для побитовых операторов существуют другие операторы на месте (Л, 1 и т. д.).

9.6. Вычитание
а, Ь = 1 , 2

# Использование оператора "-":
Ь-а
#=1
import operator
operator.sub(b, а)

# содержит арифметические функции с 2 аргументами
#=1

Возможные комбинации (для встроенных типов):

int и int (дает int)

int и float (дает float)

int и complex (дает complex)
• float и float (дает float)
• float и complex (дает complex)
• complex и complex (дает complex)

77

9.7. Умножение

9.7. Умножение
а, Ь = 2, 3

а*Ь
#= 6
import operator
operator.mul(a, Ь) # = 6
Возможные комбинации (для встроенных типов):
• int и int (дает int)
• int и float (дает float)
• int и complex (дает complex)
• float и float (дает float)
• float и complex (дает complex)
• complex и complex (дает complex)
Примеч ание: оператор * также используется для повторной конкатенации строк, списков и
кортежей:
3 * 'аЬ'
3 * ('а', 'Ь')

# = 'аЬаЬаЬ'
# = ('а', ' Ь', 'а', ' Ь', 'а', ' Ь')

9.8. Логарифмы
По умолчанию функция math.log вычисляет логарифм числа с основанием е. При жела­
нии можно указать основание в качестве второго аргумента.
import math
import cmath
# = 1 .60943791 24341 003
math.log(5)
# Необязательный аргумент основания. По умолчанию is math.e
# = 1 .60943791 24341 003
math.log(5, math.e)
# = (1 . 60943791 24341 003+0j)
cmath.log(5)
math.log(1 ООО, 1 О)
# 3.0 (всегда возвращает float)
cmath.log(1 ООО, 1 О)
# (3+0j)
Для различных оснований существуют специальные варианты функции math.log.
# Логарифм по основанию е - 1 (более высокая точность для малых значений)
math.log1 р(5)
# = 1 .791 759469228055
# Логарифм по основанию 2
math.log2(8)
# = 3.0
# Логарифм по основанию 1 О
math.log1 0(1 00) # = 2.0
cmath.log1 0(1 00) # = (2+0j)

9.9. Остаток от деления
Как и во многих других языках, в Python для вычисления остатка от деления используется оператор %.
3 % 4 #3
10 % 2 # 0
6%4 #2

78

Глава 1 О. Побитовые операторы

Также можно использовать оператор modu le:
import орегаtог

operator. mod(З , 4)
operator. mod(1 О , 2)
operator. mod(6 , 4)

#3

#2

Можно проводить вычисления и с отрицательными числами.
-9 % 7 # 5
9 % -7 # -5
-9 % -7 # -2

Если необходимо вывести результат целочисленного деления и остаток, для сокращения
можно использовать функцию divmod :
quotient, remainder = divmod(9, 4)
# частное = 2, остаток = 1 , так ка к 4 * 2 + 1 == 9

Глава 1 О. П обитовые операторы
Побитовые операции изменяют двоичные строки на уровне битов. Эти операции под­
держиваются непосредственно процессором. Эти немногочисленные операции необходимы
при работе с драйверами устройств, низкоуровневой графикой, криптографией и сетевыми
коммуникациями. В данном разделе представлены полезные знания и примеры побитовых
операторов Python.

1 0. 1 . Побитовое НЕ

Оператор ~ "переворачивает" все биты в числе. Поскольку компьютеры используют зна­
ковое представление чисел - в частности, дополнительный код ("дополнение двойки", англ.
two's complement) для кодирования отрицательных двоичных чисел, где отрицательные
числа записываются с ведущей единицей (1) вместо ведущего нуля (О).
Это означает, что при использовании 8 бит для представления чисел с дополнитель­
ным кодом (двухкомпонентных чисел) используются шаблоны от 0000 0000 до 01 1 1 1 1 1 1 для
представления чисел от О до 127 и резерв 1 ххх хххх для представления отрицательных чисел.
Восьмибитные двухкомпонентные числа
Дополнение

Двухкомпонентное значен ие

0000 001 0

2

2

01 1 1 1 1 1 О

1 26

1 26

01 1 1 1 1 1 1

1 27

1 27

1 000 0000

1 28

-1 28

1 000 0001

1 29

-1 27

1 000 001 0

1 30

-1 26

1111 1110

254

-2

1111 1111

255

-1

Биты
0000 0000

о

о

0000 0001

1 0. 1 . П обитовое Н Е

79

По сути это означает, что если 1 01 О 01 1 О имеет дополнение 1 66 (полученное сложени­
ем (1 28 * 1 ) + (64 * О) + (32 * 1 ) + (1 6 * О) + (8 * О) + (4 * 1 ) + (2 * 1 ) + (1 * О)), то его двухкомпонент­
ное значение -90 (получено путем сложения (1 28 * 1 ) - (64 * О) - (32 * 1 ) - (1 6 * О) - (8 * О) - (4 * 1 ) (2 * 1 ) - (1 * О) и допол нения полученного значен ия). Таким образом, диапазон отрицательных
чисел простирается до -1 28 (1 ООО 0000). Ноль (О) представляется как 0000 0000, а минус один
(-1) - как 1 1 1 1 1 1 1 1 . В общем случае это означает, что ~n = -n - 1.
# О = оьоооо 0000

# Вы вод: -1
# -1 = ОЬ1 1 1 1 1 1 1 1
# 1 = оьоооо 0001
~1
# Вы вод: -2
# -2 = 1 1 1 1 1 1 1 0
# 2 = оьоооо 001 О
~2
# Вы вод: -3
# -3 = ОЬ1 1 1 1 1 1 01
# 1 23 = ОЬ01 1 1 1 01 1
~1 23
# Вы вод: -1 24
# -1 24 = ОЬ1 000 01 00

Заметим, что общий результат этой операции при применении к положительным чис­
лам можно объединить:
~п -> - l n + 1 I

И тогда в применении к отрицательным числам получается:

~-n -> l n-1 1

Это последнее правило иллюстрируется следующими примерами:

# -о = оьоооо 0000
~-О
# Вы вод: -1
# -1 = ОЬ1 1 1 1 1 1 1 1
# О я вляется очевидн ы м исключен ием из этого правила, так как -О == О всегда
# -1 = ОЬ1 000 0001
~-1
# Вы вод: О
# О = оьоооо 0000
# -2 = 0Ь1 1 1 1 1 1 1 0
~-2
# Вы вод: 1
# 1 = оьоооо 0001
# -1 23 = ОЬ1 1 1 1 1 01 1
~-1 23
# Вы вод: 1 22
# 1 22 = ОЬ01 1 1 1 01 О

80

Глава 1 О. Побитовые опер аторы

1 0.2. Побитовое XOR (исключающее ИЛИ)

Оператор л выполняет двоичное XOR, в котором двоичная 1 копируется тогда и только
тогда, когда она является значением одного операнда. По-другому это можно выразить так:
результат равен 1 только в том случае, если операнды различны. Примеры:
#ОЛО
#ОЛ1
#РО
#1 Л1

=
=
=
=

О
1
1
О

# 60 = ОЬ1 1 1 1 00
# 30 = ОЬ01 1 1 1 О
60 л 30
# В ы вод: 34
# 34 = ОЬ1 0001 О
Ып(60 л 30)
# В ы вод: ОЬ1 0001 О

1 0.3. Побитовое И

Оператор & выполняет двоичное И, при котором бит копируется, если он есть в обоих
операндах. Это означает:
#0&0
#0&1
#1 &0
#1 &1

=
=
=
=

0
0
0
1

# 60 = ОЬ1 1 1 1 00
# 30 = ОЬ01 1 1 1 О
60 & 30
# Вы вод: 28
# 28 = ОЬ1 1 1 00
Ьin(60 & 30)
# Вы вод: ОЬ1 1 1 00

1 0.4. Побитовое ИЛИ

Оператор I выполняет двоичное ИЛИ, при котором бит копируется, если он есть в любом
из операндов. Это означает:
#0
#0
#1
#1

1
1
1
1

0
1
0
1

=
=
=
=

0
1
1
1

# 60 = ОЬ1 1 1 1 00
# 30 = ОЬ01 1 1 1 О
60 1 30
# В ы вод: 62
# 62 = ОЬ1 1 1 1 1 О
Ьin(60 1 30)
# В ы вод: ОЬ 1 1 1 1 1 О

1 0.5. П обитовы й с д виг влево

81

1 0.5. Побитовый сдвиг влево
Оператор « выполняет побитовый сдвиг влево, при котором значение левого операнда
сдвигается влево на количество разрядов, заданное правым операндом.
#2=

оы о

2«2
# Вы вод: 8
# 8 = оы ооо
Ьin{2 « 2)

# Вы вод: ОЫ ООО

Выполнение сдвига левого бита на 1 эквивалентно умножению на 2:
7«1
# Вы вод: 1 4

Выполнение сдвига влево на n бит эквивалентно умножению на 2**n:
3«4
# Вы вод: 48

1 0.6. Побитовый сдвиг вправо
Оператор » выполняет побитовый "сдвиг вправо", при котором значение левого операнда сдвигается вправо на количество битов, заданное правым операндом.
#8=

оы ооо

#2=

оы о

8»2
# Вы вод: 2

Ьin{8 » 2)
# Вы вод: ОЫ О

Выполнение сдвига правого бита на 1 эквивалентно целочисленному делению на 2:
36 » 1
# Вы вод: 1 8
15»1
# Вы вод: 7

Выполнение правого битового сдвига на n эквивалентно целочисленному делению на
2**n:
48 » 4
# Вы вод: 3
59 » 3
# Вы вод: 7

1 О. 7. Операторы присваивания ("операции на месте")
Все битовые операторы (кроме ~) имеют свои собственные варианты присваивания:
а = ОЬ001
а &= ОЬ01 0
# а = ОЬООО
а = ОЬ001
а 1 = ОЬ01 0
# а = ОЬ01 1
а = ОЬ001
а > = 2
# а = ОЬ001
а = ОЬ1 01
а л = ОЬ01 1
# а = ОЬ1 1 0

Глава 1 1 . Логические операции
1 1 . 1 . "and" и "or" не гарантируют возврата булевого значения

При использовании or либо возвращается первое значение выражения, если оно истин­
но, либо вслепую возвращается второе значение. Т. е. or эквивалентно:
def or_(a, Ь):
if а:
return а
else:
return Ь

Для and возвращается его первое значение, если оно ложно, в противном случае возвра­
щается последнее значение:
def and_(a, Ь):
if not а:
return а
else:
return Ь

1 1 .2. Простой пример

В Python для сравнения одного элемента можно использовать два бинарных оператора по одному с каждой стороны:
if 3.1 4 < х < 3 . 1 42:
print("x примерно равно pi")

Во многих (большинстве?) языках программирования эта оценка будет противоре­
чить обычной математике: (3, 1 4 < х) < 3,1 42, но в Python она рассматривается как 3, 1 4 < х and
х < 3,1 42, как и ожидало бы большинство непрограммистов.

1 1 .3. Вычисления по короткой схеме ("короткое замыкание")
Python упрощает вычисление логических операций.

>» def true_func():
print("true_func()")
return True
>» def false_func():
print("false_func()")
return False
>» true_func() or false_func()
true_func()
True
»> false_func() ог true_func()

1 1 .4. Оператор "and" ("и")

83

false_func()
true_func()
True
»> true_func() and false_func()
true_func()
false_func()
False
»> false_func() and false_func()
false_func()
False

1 1 .4. Оператор "and" ("и")

Оценивает второй аргумент тогда и только тогда, когда оба аргумента истинны. В про­
тивном случае результатом вычисления является первый ложный аргумент.
х = True
у = True
z = х and у

# z = True

х = True
у = False
z = х and у

# z = False

х = False
у = True
z = х and у

# z = False

х = False
у = False
z = х and у

# z = False

х=1
у=1
z = х and у

# z = у, поэтому z = 1, 'and' и 'ог' не являются гарантированно операторами
# булевой логики

х=О
у=1
z = х and у

# z = х, поэтому z = О (см. вы ше)

х=1
у=О
z = х and у

# z = у, поэтому z = О (см. выше)

х=О
у=О
z = х and у

# z = х, поэтому z = О (см. вы ше)

В приведенном примере единица может быть заменена на любое истинное значение,
а О - на любое ложное.

1 1 .5. Оператор "or" ("или")

Оценивает первый истинный аргумент, если любой из аргументов истинный. Если оба
аргумента ложные, то вычисляется второй аргумент.
х = True
у = True
z = х ог у

# z = True

84

Глава 1 2. Приоритет операторов

х = True
у = False
z = х ог у

# z = True

х = False
у = True
z = х ог у

# z = True

х = False
у = False
z = х ог у

# z = False

х=1
у=1
z = х ог у

# z = х, поэтому z = 1 , 'апd' и 'ог' не я вля ются гарантированно операторами
# Булевой логики

х=1
у=О
z = х ог у

# z = х, поэтому z = 1 (см. выше)

х=О
у=1
z = х ог у

# z = у, поэтому z = 1 (см . выше))

х=О
у=О
z = х ог у

# z = у, поэтому z = О (см . выше)

В приведенном примере единица может быть заменена на любое истинное значение,
а О - на любое ложное.

1 1 . 6. Оператор "not" ("не")
Возвращает противоположное утверждение:
х = True
у = not х

# у = False

х = False
у = not х

# у = True

Глава 1 2. Приоритет операторов
Операторы Python имеют установленный п оря до к стар ш инст ва, который определяет, ка­
кие операторы будут вычислены первыми в потенциально неоднозначном выражении. На­
пример, в выражении 3 * 2 + 7 сначала 3 умножается на 2, а затем полученный результат при­
бавляется к 7, в результате чего получается 1 3.
Ниже приведены список операторов по старшинству и краткое описание того, что они
(обычно) делают.

1 2. 1 . П римеры приоритета операторов в Python
Python следует правилу PEMDAS, что расшифровывается как Parentheses, Exponents,
Multiplication and Division, Addition and Subtraction (скобки, возведение в степень, умноже­
ние и деление, сложение и вычитание).

1 3. 1 . Нелокальные переменные

85

Пример:
>>> а, Ь, с, d = 2, 3, 5, 7
»> а * (Ь + с) # скобки
256
>>> а * Ь ** с
# возведение в степень: то же, что ·а * (Ь ** с)·
7776
»> а + Ь * с / d # умножение / деление: то же, что ·а + (Ь * с / d)'
4.1 428571 428 571 42

Дополнительно: математические правила действуют, но не всегда:
>» 300 / 300 * 200
200.0
»> 300 * 200 / 300
200.0
»> 1 е300 / 1 е300 * 1 е200
1 е+200
»> 1 е300 * 1 е200 / 1 е300
inf

Глава 1 3. Область видимости и привязка
переменных
1 3. 1 . Нелокальные переменные

Версия Python З.х � 3.0:
В Python 3 добавлено новое ключевое слово nonlocal. Ключевое слово nonlocal добавляет
переопределение области видимости к внутренней области видимости. Об этом можно про­
читать в РЕР 3104. Лучше всего это проиллюстрировать на нескольких примерах кода. Один
из наиболее распространенных примеров - функция, которая может создавать инкремент:
def counter():
num = О
def incrementer():
num += 1
return num
return incrementer

Если попытаться выполнить этот код, то будет выдана ошибка UnboundLocalError, по­
скольку ссьшка на переменную num дана до того, как она была присвоена во внутренней
функции. Добавим сюда нелокальность:
def counter():
num = О
def incrementer():
nonlocal num
num += 1
return num
return incrementer
с = counter()
с()
#=1
с()
#=2
с()
#=3

86

Глава 1 3. Область видимости и привязка переменных

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

1 3.2. Глобальные переменные

В Python переменные внутри функций считаются локальными тогда и только тогда, ког­
да они встречаются в левой части выражения присваивания или в каком-либо другом слу­
чае привязки; в противном случае такая привязка ищется в окружающих функциях, вплоть
до глобальной области видимости. Это справедливо даже в том случае, если оператор при­
сваивания никогда не выполняется.
х = 'Hi'
def read_x():
print(x)

# переменная х считается глобал ьной, на нее тол ько ссылаются

read_x()

# В Ы ВОДИТ Hi

def read_y():
print(y)
read_y()

# переменная у считается глобал ьной, на нее тол ько ссылаются
# NameError: глобальное имя 'у' не определено

def read_y():
у = 'Неу'
print(y)

# переменная у фигурирует в присваивании, поэтому она локал ьная
# найдет локал ьную у

read_y()

# В Ы ВОДИТ Неу

def read_x_local_fail():
if False:
х = 'Неу'
# переменная х фигурирует в присваивании, поэтому она локал ьная
# будет искать локал ьную z, которая не присвоена и не будет найдена
print(x)
read_x_local_fail()

# UnboundlocalError: на локальную переменную 'х' ссылаются до
# присваивания

Обычно присваивание внутри области видимости "затеняет" все внешние переменные
с тем же именем:
х = 'Hi'
def change_local_x():
х = 'Вуе'
print(x)
change_local_x() # в ы водит Вуе
# В Ы ВОДИТ Hi
print(x)

Объявление имени глобальным (global) означает, что для остальной части области види­
мости все присваивания имени будут происходить на верхнем уровне модуля:
х = 'Hi'
def change_global_x():
global х

1 3.3. Локал ьные переменные

87

х = 'Вуе'
print(x)
change_global_xQ
print(x)

# В Ы ВОДИТ Вуе
# выводит Вуе

Ключевое слово global означает, что присваивания будут происходить на верхнем уровне
модуля, а не на верхнем уровне программы. Другим модулям по-прежнему будет необходим
обычный точечный доступ к переменным внутри модуля.
Подведем итог: чтобы узнать, является ли переменная х локальной для функции, необхо­
димо прочитать всю функцию.
1. Если вы напти global х, то х является гл обаль но й переменной.
2. Если вы напти nonlocal х, то х принадлежит вмещающей функции и не является ни ло­
кальным, ни глобальным.
3. Если вы напти х = 5, или for х in гапgе(З), или какую-то другую привязку, то х является
л о каль но й переменной.
4. В противном случае х принадлежит некоторой охватывающей области (области функ­
ций, глобальной области или встроенных элементов).

1 З.З. Локальные переменные

Если имя связано внутри функции, то по умолчанию оно доступно только внутри этой
функции:
def fooO:

а=5

print(a)

print(a)

# ok
# NameError: имя 'а' не определено

Управляющие конструкции потока не влияют на область видимости (за исключением
обращение к переменной, которая еще не бьша присвоена, является ошибкой:

except), но
def fooO:
if True:

а=5

print(a)

# ok

Ь=З
def Ьаг():
if False:

Ь=5

print(b)

# Unbound local Error: локальная перемен ная 'Ь' упоминается до присвоения

Общими операциями связывания являются присваивания, циклы for и дополненные
присваивания, такие как а += 5.

1 3.4. Команда "del"

Эта команда имеет несколько родственных, но различных форм.

del v

Если v - переменная, то команда del v удаляет переменную из области видимости. Например:
х=5
print(x) # результат: 5
del х
print(x) # NameError: имя 'f' не определено

88

Глава 1 3. Область видимости и привязка переменных
Заметим, что del является обязательным вхождением, а это означает, что если явно не указано
иное (с помощью nonlocal или global), то del v сделает v локальным для текущей области види­
мости. Если вы собираетесь удалить v во внешней области видимости, используйте nonlocal v
или global v в той же области видимости, что и оператор del v.

Во всех приведенных ниже случаях намерение команды является поведением по умол­
чанию, но не навязывается языком. Класс может быть написан таким образом, что это наме­
рение не выполняется.
del v.name

Эта команда вызывает v._delattr_(name).
Это делается для того, чтобы сделать атрибут недоступным. Например:

class А:
pass
а = А()
а.х = 7
print(a.x)
del а.х
print(a.x)
del v[item]

# результат: 7
# еггог: AttributeError: объект 'Р: не имеет атрибута 'х'

Эта команда вызывает v._delitem_(item).
Имеется в виду, что item не будет принадлежать соответствию, реализуемому объектом v.
Например:
х = {'а': 1 , 'Ь': 2}
del х['а']
print(x)
print(x['a'])
del v[a:b]

# результат: {'Ь': 2}
# еггог: КеуЕггог: 'а'

Фактически это вызывает v._delslice_(a, Ь).
Замысел аналогичен описанному выше, но с использованием срезов (slices) - диапазонов
элементов вместо одного элемента. Например:
х = [О, 1 , 2, 3, 4]
del х[1 :3]
print(x)

# результат: [О, 3, 4]

1 3 .5. Функции пропускают область видимости класса
при поиске имен

Классы имеют локальную область видимости при определении, но функции внутри
класса не используют эту область при поиске имен. Поскольку лямбда-выражения являются
функциями, а генераторы реализуются с использованием области видимости функции, это
может привести к неожиданному поведению.
а = 'global'
class Fred:
а = 'class'
Ь = (а for i in range(1 О))
с = [а for i in range(1 О)]
d=a
е = lambda: а
f = lambda а = а: а

# область видимости класса
# область видимости функции
# область В И Д И М ОСТИ функции
# область видимости класса
# область видимости функции
# аргумент по умолчанию использует область видимости класса

1 3.6. Локальные и глобальные области
@staticmethod
def g():
return а

# или @classmethod, или обычны й метод экземпляра
# область видимости функции

print(Fred.a)
print(next(Fred. b))
print(Fred.c[O])
print(Fred.d)
print(Fred.e())
print(Fred.f())
print(Fred.g())

# класс
# глобальная
# класс в Python 2, глобальная в Python 3
# класс
# глобальная
# класс
# глобальная

89

Пользователи, не знакомые с работой этой области, могут ожидать, что Ь, с и е будут выводить class. Из РЕР 227:
Имена в области видимости класса недоступны. Имена разрешаются в самой внутренней объ­
емлющей области видимости функции. Если определение класса встречается в цепочке вложен­
ных областей видимости, процесс разрешения пропускает определения класса.

Из документации Python по именованию и связыванию:
Область видимости имен, заданных в блоке класса, ограничена блоком класса и не распростра­
няется на блоки кода методов - это касается и генераторов, и генераторных выражений, по­
скольку они реализуются с использованием области видимости функции. Это означает, что сле­
дующие действия буТJУТ неудачными:
class А:
а = 42
Ь = list(a + i for i in range(1 О))

1 3.6. Л окальные и глобальные области
Что такое локальные и глобальные области?

Все переменные Python, доступные в той или иной точке кода, находятся либо в локаль­
ной, либо в глобальной области видимости.
Объясняется это тем, что локальная область видимости включает все переменные, задан­
ные в текущей функции, а глобальная область видимости включает переменные, заданные
за пределами текущей функции.
foo = 1
# глобальная
def func():
# локальная
Ьаг = 2
print(foo) # печатает переменную foo из глобальной области видимости
print(bar) # печатает переменную bar из локальной области видимости

Можно проверить, какие переменные находятся в той или иной области видимости.
Встроенные функции locals() и globals() возвращают все области видимости в виде словарей.
foo = 1
def func():
Ьаг = 2
print(g lobals() . keys())
print(locals(). keys())

# выводит все имена переменных в глобальной области видимости
# выводит все имена переменных в локальной области

Что происходит при конфликте имен?
foo = 1
def func():
foo = 2

# создает новую переменную foo в локальной области видимости,
# глобальная foo не затрагивается

90

Глава 1 3. Область видимости и привязка переменных
print(foo) # вы водит 2
# глобальная переменная foo все ещесуществует, без изменений:
# выводит 1
print(globals(}['foo'])
print(locals(} ['foo'])
# в ы водит 2
Для изменения глобальной переменной используйте ключевое

слово global :

foo = 1
def func():
global foo
foo = 2

# это изменяет глобальную переменную foo, а не создает локальную переменную

Область видимости определяется для всего тела функции!

Это означает, что переменная никогда не будет глобальной для половины функции и ло­
кальной для другой половины.
foo = 1
def func():
# Эта функция имеет локал ьную переменную foo, поскол ьку она определена ниже.
# Таким образом, с этого момента foo является локальной. Глобал ьная foo скрыта.
print(foo) # вызывает ошибку Unboundlocal Error, ведь локальная foo еще не инициализирована
foo = 7
print(foo)

Аналогично, наоборот:
foo = 1
def func():
# В этой функции foo с самого начала является глобал ьной переменной.
foo = 7 # глобал ьная foo изменена
print(foo)
print(g lobals(} ['foo'])

#7
#7

global foo
print(foo)

# это может быть в любом месте в функции
#7

Функции внутри функций

Может существовать много уровней функций, вложенных в функции, но в рамках одной
функции существует только одна локальная область видимости для этой функции и гло­
бальная область видимости. Промежуточных областей видимости не существует.
foo = 1
def f1 ():
bar = 1
def f2(}:
baz = 2
# здесь foo - глобальная переменная, baz - локальная переменная
# Ьаг не ВХОДИТ ни в одну ИЗ областей ВИДИМОСТИ
print(locals(}.keys()) # ['baz']

1 3.6. Локал ьные и глобальные области

91

print('bar' in locals()) # False
print('bar' in globals()) # False
def fЗ():
baz = 3
print(bar

# на Ьаг из f1 есть ссылка, поэтому он nоnадает в локальную область
ВИДИМОСТИ fЗ (закрытие)
print(locals().keys()) # ['Ьаг', 'baz']
print('bar' in locals()) # True
print('bar' in globals()) # False

def f4():
Ьаг = 4 # новая локальная Ьаг, которая скры вает Ьаг из локальной области f1
baz = 4
print(bar)
print(locals().keys()) # ['Ьаг', 'baz']
print('bar' in locals()) # True
print('bar' in globals()) # False

global и nonlocal (только Python 3)

Оба этих ключевых слова используются для получения доступа на запись к переменным,
не являющимся локальными для текущей функции. Ключевое слово global объявляет, что
имя должно рассматриваться как глобальная переменная.
foo = О # глобальная foo
def f1 ():
foo = 1 # новая foo, локальная в f1
def f2():
foo = 2 # новая foo, локальная в f2
def fЗ():
foo = 3
# новая foo, локальная в fЗ
print(foo) # 3
foo = 30 # изменяет локальную foo только в fЗ
def f4():
глобальная foo
print(foo) # О
foo = 1 00 # изменяет глобальную foo
С другой стороны, функция nonlocal (см. раздел "Нелокальные переменные"), появивша­
яся в Python 3, переносит локальную переменную из объемлющей области видимости в ло­
кальную область видимости текущей функции. Из документации по Python о nonlocal :
Оператор nonlocal заставляет перечисленные идентификаторы ссьтаться н а ранее связанные
переменные в ближайшей объемлющей области, исключая глобальные.
Python 3.х � 3.0
def f1 ():
def f2():
foo = 2
def fЗ():
nonlocal foo
print(foo)
foo = 20

# новая переменная foo, локальная в f2
# foo из f2, которая я вляется ближай шей объемлющей областью
#2
# изменяет foo из f2!

92

Глава 1 4. Условные выражения

1 3. 7. В озникновение привязки
х=5
х += 7
for х in iteraЫe: pass

Каждое из приведенных выше утверждений - это возникновение привязки; х становится
связанным с объектом, обозначенным цифрой 5. Если это утверждение встречается внутри
функции, то х по умолчанию будет функционально-локальным. Список операторов связыва­
ния приведен в разделе "Синтаксис".

Глава 1 4. Условные выражения
Условные выражения, включающие такие ключевые слова как if, elif и else, предоставля­
ют программам на языке Python возможность выполнять различные действия в зависимо­
сти от логического условия True или False. В этом разделе рассматривается использование
условных выражений Python, логики булевых операций и тернарных операторов.

1 4. 1 . Условное выражение ("тернарный оператор")

Тернарный оператор используется для встраивания условных выражений. Его лучше
всего использовать в простых, лаконичных операциях, которые легко читаются.
• Порядок аргументов отличается от многих других языков (таких как С, Ruby,
Java и др.), что может привести к ошибкам при использовании Python людьми,
незнакомыми с его "удивительным" поведением (они могут изменить порядок
аргументов).
• Некоторые считают его "неуклюжим", поскольку он противоречит обычному
способу мышления (сначала думают об условии, а затем о его последствиях).
n=5
"Greater than 2" if n > 2 else "Smal ler than ог equal to 2"
# Вы вод: 'Greater than 2'

Результат этого выражения будет таким, как он читается на английском языке - если ус­
ловное выражение является True, тогда он будет соответствовать выражению в левой части,
в противном случае - правой части.
Тернарные операции также могут быть вложенными, как здесь:
n=5
"Hello" if n > 1 О else "Goodbye" if n > 5 else "Good day"

Они также предоставляют способ включения условий в лямбда-функции.

1 4.2. if, elif и else

В Python можно определить серию условий, используя if для первого условия, elif для
остальных, вплоть до последнего (необязательного), else для всего, что не было "поймано"
другими условиями.
number = 5
if number > 2:
print("Чиcлo больше 2.")
elif number < 2: # Необязательное условие (может быть несколько elif)
print("Чиcлo меньше 2.")

1 4.3. Значения истинности

93

# Необязательное условие (может быть только одно else)
else:
ргiпt("Число равно 2.")
Выведет "Число больше 2."
Использование else if вместо elif приведет к синтаксической ошибке и не допускается.

1 4.З. Значения истинности

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


None



False



О, или любое числовое значение, экви валентное нулю, например OL, О.О, Oj



Пустые последовательности:: ", "", (), []



Пустые отображения: {}



Пользовательские тип ы , где _bool_ или _len_ методы возвращают О или

False.

Все остальные значения в Python оцениваются как True.
Примечание: распространенной ошибкой является простая проверка на ложность опе­
рации, которая возвращает различные ложные значения, и различие важно. Например, ис­
пользование if foo() вместо более явного if foo() is None.

1 4.4. Выражения булевой логики

Булевы логические выражения, помимо оценки True или False, возвращают значение, ко­
торое бьшо интерпретировано как True или False. Это характерный для Python способ пред­
ставления логики, которая в противном случае могла бы потребовать проверки типа if-else.
Оператор "and" ("и")
Оператор "and" оценивает все выражения и возвращает последнее выражение, если все
выражения оцениваются как True. В противном случае возвращается первое значение, кото­
рое оценивается как False:
»>

1 and 2

»>

1 and О

2

о

1 and "Hello World"
"Hello World"

»>

>>> ""

and "Pancakes"

Оператор "or" ("или")
Этот оператор оценивает выражения слева направо и возвращает первое значение, рав­
ное True, или последнее значение (если ни одно из них не равно True).
>>> 1 ОГ 2

1

»>

1



None ог 1

»> 0 ОГ



94

Глава 1 4. Условные выражения

"Ленивые" вычисления

При использовании этого подхода следует помнить, что вычисления являются "ленивы­
ми". Выражения, которые не нужно оценивать для определения результата, не оценивают­
ся. Например:
>» def print_me():
print('Я здесь!')
»> О и print_me()

о

В приведенном выше примере print_me никогда не выполняется, поскольку Python может
определить, что все выражение равно False, когда он встречает О (False). Помните об этом,
если prinLme необходимо выполнить для логики вашей программы.
Проверка на несколько условий

Распространенной ошибкой при проверке на наличие нескольких условий является не­
правильное применение логики.
В примере проверяется, не больше ли каждая из двух переменных 2. Утверждение оцени­
вается как if (а) and (Ь > 2). Это дает неожиданный результат, поскольку bool(a) оценивается
как True, если а не равно нулю.
>>> а = 1
>>> Ь = 6
>» if а and Ь > 2:
pri nt('yes')
... else:
print('no')
yes

КаждУЮ переменную необходимо сравнивать отдельно.
»> if а > 2 and Ь > 2:
print('yes')
... else:
print('no')
ПО

Другая подобная ошибка допускается при проверке принадлежности переменной к не­
скольким значениям. Утверждение в этом примере оценивается как if (а = = 3) ог (4) ог (6). Это
приводит к неожиданному результату, поскольку и Ьоо1(4), и Ьоо1(6) являются True.
>>> а = 1
>» if а == 3 ог 4 ог 6:
print('yes')
... else:
print('no')
yes

Каждое сравнение должно проводиться отдельно:
>>> if а
... else:
по

3 ог а == 4 ог а
pri nt('yes')

==

==

6:

print('no')

Общепринятым способом я вляется использование оператора in:
»> if а in (3, 4, 6):
print('yes')
... else:
print('no')
по

1 4.5. Использование функции "cmp" для получения результата сравнения двух объектов

95

1 4.5. Использование функции "cmp" для получения результата
сравнения двух объектов

В Python 2 есть функция cmp, которая позволяет определить, меньше, равен или больше
один объект другого. Эта функция может быть использована для выбора варианта из списка
на основе одного из этих трех вариантов.
Предположим, что необходимо вывести "больше, чем" ("greater than"), если х > у, "мень­
ше, чем" ("less than"), если х < у, и "равно" ("equal"), если х == у.
['equal', 'greater than', 'less than', ][cmp(x,y)]
# х,у = 1 ,1 результат: 'equal'
# х,у = 1 ,2 результат: 'less than'
# х,у = 2, 1 результат: 'greater than'
cmp(x,y) возвращает следующие значения:
Результат с ра в н ения
ху
1

В Python 3 эта функция отсутствует. Для преобразования старых функций сравнения
в ключевые можно использовать вспомогательную функцию cmp_to_key(func) в functools
в Python 3.

1 4.6. Оператор "else"
if condition:
body
else:
body

else выполнит свое тело только в том случае, если все предшествующие условные опера­
торы будут иметь значение False.
if True:
print "lt is true!"
else:
print "This won't get printed ..."
# Вы вод: lt is true!
if False:
print "This won't get printed ..."
else:
print "lt is false!"
# Вы вод:

lt is false!

1 4. 7. Проверка принадлежности объекта к None и его присвоение

Часто возникает необходимость присвоить объекту какое-либо значение, если оно равно
None, что означает, что объект не бьm присвоен. Мы будем использовать aDate. Самый про­
стой способ сделать это - использовать проверку is None.
if aDate is None:
aDate = datetime.date.today()
(Заметим, что для Python правильнее использовать is None, а не == None).
Это можно немного оптимизировать, используя представление о том, что в выражении
булевой логики not None будет оцениваться как True. Следующий код эквивалентен:

96

Глава 1 5. Сравнение

if not aDate:
aDate=datetime.date.today()

Но есть и более "питоновский" способ. Следующий код также равнозначен:
aDate = aDate ог datetime.date.today()
При этом выполняется вычисление по короткой схеме. Если aDate инициализируется
и она not None, то она присваивается самой себе без какого-либо эффекта. Если же она is None,
то aDate присваивается значение datetime.date.today().

1 4.8. Оператор "if"
if condition:
body

Оператор if проверяет условие. Если его значение равно True, то выполняется тело опера­
тора if. Если значение равно False, то тело пропускается.
if True:
print "lt is true!"
» lt is true!
if False:
print 'This won't get printed .."

Условие может быть любым допустимым выражением:
if 2 + 2 == 4:
print "1 know math!"
» 1 know math!

Глава 1 5. Сравнение
Параметр
х
у

Подробности
Первый сравниваемый элемент

Второй сравниваемый элемент

1 5. 1 . Цепное сравнение

С помощью цепного сравнения можно сравнивать несколько элементов с помощью не­
скольких операторов сравнения. Например:
x>y>z

это просто краткая форма для нижеприведенной записи:

х > у and у > z

Это значение будет равно True только в том случае, если оба сравнения равны True. В об­
щем виде это выглядит так:
а ОР Ь ОР с ОР d . . .

где ОР представляет одну из нескольких операций сравнения, которые можно использо­
вать, а буквы - произвольные допустимые выражения.
Заметим, что О != 1 != О имеет значение True, хотя О != О - это False, в отличие от общеприня­
той математической нотации, в которой х ! = у != z означает, что х, у и z имеют разные значения.

1 5.2. Сравнение по принципу "is" и "== "

97

Цепочка операций == в большинств е случаев имеет естеств енный смысл, поскольку рав енств о
в общем случае является транзитивным .
Стиль
Теоретически не существует ограничений на количество элементов и операций сравне­
ния при условии правильного использования синтаксиса:
1 > -1 < 2 > 0.5 < 1 00 ! = 24
Вышеприведенное возвращает True, если каждое сравнение возвращает True. Однако ис­
пользование запутанной цепочки не является хорошим стилем. Хорошая цепочка должна
быть "направленной", не сложнее, чем
1 > х > -4 > у ! = 8
Побочные эффекты

Как только одно из сравнений возвращает значение False, выражение сразу же оценива­
ется в False, пропуская все оставшиеся сравнения. Заметим, что выражение ехр в а > ехр > Ь
будет вычислено только один раз, тогда как в случае
а > ехр и ехр > Ь
ехр будет вычисляться дважды, если а > ехр истинно.

1 5.2. Сравнение по принципу "is" и "=="

Распространенной ошибкой является путаница операторов сравнения равенства is и = = .
а == Ь сравнивает значения а и Ь.
а is Ь сравнивает тождественность а и Ь.
Пример:
а = 'Python is fun!'
Ь = 'Python is fun!'
а = = Ь # возвращает True
а is Ь # возвращает False
а

=

[1 , 2, 3, 4, 5]

Ь = а # Ь ссылается на а
а = = Ь # True
а есть Ь # True
Ь = а[:] # Ь теперь ссылается на копию а
а = = Ь # True
а есть Ь # False [!!!]
В принципе, это можно рассматривать как сокращение для id(a) == id(b).
Кроме того, существуют причуды среды выполнения, которые еще больше усложняют
ситуацию. Короткие строки и небольшие целые числа будут возвращать True при сравнении
с использованием is, что связано с попыткой Python использовать меньше памяти для оди­
наковых объектов.
а = 'short'
Ь = 'short'
с=5
d=S
а is Ь # True
с is d # True
Однако более длинные строки и большие целые числа будут храниться отдельно.
=
а 'not so short'
Ь = 'not so short'
с = 1 000
d = 1 ООО

98

Глава 1 5. Сравнение

а is Ь

с is d

# False
# False

Для проверки на None следует использовать is:
Один из вариантов использования is - проверка на наличие "сентинела" (т.е. уникаль­
ного объекта).
sentinel = object()
def myfunc(var =sentinel):
if var is sentinel:
# значение не было предоставлено
pass
else:
# значение было предоставлено
pass

1 5.3. Больше или меньше
х>у
х4

# True
12 < 4

# False
1 "beta"
# True
"gamma" < "OM EGA"
# False
В этих сравнениях строчные
"OMEGA" является ложным. Если

буквы считаются "больше" прописных, поэтому "gamma" <
бы все они бьши прописными, то бьш бы получен ожидае­
мый результат упорядочивания по алфавиту:
"GAM MA" < "OM EGA"
# True

Каждый тип по-разному определяет свои вычисления с помощью операторов < и >, поэ­
тому перед их использованием необходимо выяснить, что означают эти операторы для дан­
ного типа.

1 5.4. Не равно
х != у

При этом возвращается True, если х и у не равны, а в противном случае возвращается

False.

1 2 != 1
# True
1 2 != ' 1 2'
# True
'1 2' != '1 2'
# False

1 5.5. Равно

99

1 5.5. Равно

Это выражение проверяет, одно ли и то же значение у х и у, и возвращает результат в
виде логического значения. Как правило, тип и значение должны совпадать, поэтому int 1 2
не является тем же самым, что и строка '1 2'.
1 2 == 1 2
# True
1 2 == 1
# False
'1 2' = = '1 2'
# True
'spam' = = 'spam'
# True
'spam' = = 'spam '
# False
'1 2' = = 1 2
# False
Обратите внимание, что для каждого типа необходимо определить функцию, которая бу­
дет использоваться для оценки того, являются ли два значения одинаковыми. Для встроен­
ных типов эти функции ведут себя так, как вы ожидаете, и просто оценивают на основе того,
одно ли это и то же значение. Однако пользовательские типы могут определять проверку ра­
венства как угодно, в том числе всегда возвращать True или всегда возвращать False.

1 5.6. Сравнение объектов

Для сравнения равенства пользовательских классов можно переопределить = = и ! = , соз­
дав методы _eq_ и _пе_. Также можно переопределить lt (). Обратите
внимание, что переопределять нужно только два метода сравнения, с остальным справится
Python (== - это то же самое, что не < и не >, и т. д.).
class Foo(object):
def _iniL(self, item):
self.my_item = item
def _eq_(self, other):
return self.my_item = = other.my_item
а = Foo(5)
Ь = Foo(5)
а = = Ь # True
а ! = Ь # False
а is Ь # False
Обратите внимание: это простое сравнение предполагает, что other (объект, с которым
производится сравнение) - это объект того же типа. Сравнение с другим типом приведет к
ошибке:
class Ваг(object):
def _iniL(self, item):
self.other_item = item
def _eq_(self, other):
return self.other_item = = other.other_item
def _ne_(self, other):
return self.other_item ! = other.other_item
с = Ваг(5)
а = = с # AttributeError: у объекта 'Foo' нет атрибута 'other_item'
Проверка isinstanceO или аналогичная поможет при желании избежать этого.

1 00

Глава 1 6. Циклы

Глава 1 6. Цикл ы

П ара м етр

Подробности

логическое выражение

выражение, которое может быть оценено в логическом
контексте, например х < 1 О

переменная

имя переменной для текущего элемента итерируемого
объекта

итерируемый объект (iteraЬle)

все, что реализует итерации

Являясь одной из самых базовых функций в программировании, циклы представляют
собой важную часть практически любого языка программирования. Циклы позволяют раз­
работчикам задавать определенным участкам кода повторения на несколько циклов, кото­
рые называются итерациями. Далее рассматриваются использование нескольких типов ци­
клов и их применение в Python.

1 6. 1 . Перерыв и продолжение в циклах

Оператор break
Когда оператор break выполняется внутри цикла, поток управления "вырывается"
из цикла немедленно:
i=о

while i < 7:
print(i)
if i == 4:
print("Breaking from loop")
break
i += 1

Условие цикла не будет оцениваться после выполнения инструкции break. Обратите вни­
мание, что инструкции break допускаются только внутри циклов, синтаксически. Оператор
break внутри функции не может использоваться для завершения циклов, которые вызывали
эту функцию.
Выполнение кода выводит числа до 4, когда встречается оператор break и цикл останавливается:

о
1

2
3

4
Breaking from loop
Операторы break также могут использоваться внутри циклов for, другой конструкции ци­
клов в Python:
for i in (О, 1, 2, 3, 4):
print(i)
if i == 2:
break

о

При выполнении этого цикла будет выведено:

1

2

Обратите внимание: 3 и 4 не печатаются, так как цикл закончился.
Если цикл имеет предложение else, то оно не выполняется, когда цикл завершается че­
рез оператор break.

1 6. 1 . Перерыв и продолжение в циклах

1 01

Оператор continue

Оператор continue пропустит следующую итерацию цикла, минуя остальную часть теку­
щего блока, но продолжая цикл. Как и при break, continue может использоваться только вну­
три циклов:
for i in (О, 1, 2, 3, 4, 5):
if i == 2 ог i == 4:
continue
print(i)

о

1
3
5

Обратите внимание: 2 и 4 не печатаются, потому что continue переходит к следующей ите­
рации вместо продолжения print(i) при i == 2 или i == 4.
Вложенные петJШ

Операторы break и continue работают только на одном уровне цикла. В следующем приме­
ре будет выполнен выход только из внутреннего цикла for, а не внешнего while:
while True:
for i in range(1,5):
if i == 2:
# Выйдет только из внутрен него цикла!
break

В Python не предусмотрена возможность одновременного выхода из циклов нескольких
уровней - если это необходимо, могут понадобиться рефакторинг одной или нескольких пе­
тель в функцию и замена break на return.
Использование в фушщии оператора return в качестве оператора break

Оператор return завершает работу функции, не выполняя следующий за ним код.
Если у вас есть цикл внутри функции, использование return внутри этого цикла эквива­
лентно использованию оператора break, поскольку остальная часть кода цикла не выполня­
ется (обратите внимание, что любой код после цикла также не выполняется):
def break__loop():
for i in range(1, 5):
if (i == 2):
return(i)
print(i)
return(5)

Если у вас есть вложенные циклы, оператор return будет прерывать все циклы:
def break__all():
for j in range(1, 5):
for i in range(1,4):
if i*j == б:
return(i)
print(i*j)

выведет:
1 # 1*1
2 # 1*2
3 # 1*3
4 # 1*4
2 # 2*1
4 # 2*2
# возврат, так как 2*3 = б, и оставшиеся итерации обоих циклов не выполняются

1 02

Глава 1 6. Цикл ы

1 6.2. Циклы For

Циклы for выполняют итерации по коллекциям элементов, например list или dict, и запу­
скают блок кода с каждым элементом из коллекции.
for i in [О, 1 , 2, 3, 4]:
print(i)

В приведенном выше примере в цикле for выполняется итерация по списку чисел.
На каждой итерации значение i устанавливается на следующий элемент списка. Так, сначала это будет о, затем 1 , затем 2 и т. д. На выходе получится следующее:

о
1

2
3

4
range - это функция, которая возвращает последовательность чисел в итерируемой фор­
ме, поэтому ее можно использовать для циклов for:
for i in range(S}:
print(i)

Это дает такой же результат, что и первый цикл for. Обратите внимание, что 5 не выво­
дится, так как диапазон здесь - это первые пять чисел, считая с о.
Итерируемые объекты и итераторы

Цикл for может выполнять итерацию по любому итерируемому объекту, который явля­
ется объектом, определяющим функцию _getitem_ или _iter_. Функция _iter_ возвращает
итератор, который является объектом с функцией next, используемой для доступа к следую­
щему элементу итерируемоrо объекта.

1 6.3. Итерирование по спискам

Чтобы перебрать список, вы можете использовать for:

for х in ['one', 'two', 'three', 'four']:
print(x)

В результате будут выведены элементы списка:
опе
two
three
four

Функция range rенерирует числа, которые также часто используются в цикле for.
for х in range(1 , 6}:
print(x)

Результатом будет специальный тип последовательности диапазона в Python версии > = 3
и список (l ist) в Python d = {}
{}
»> d.setdefault('Another_key', D).append("This worked !")
>>> d
{'Another_key': ['Тhis worked !']}

Следует помнить, что при большом количестве добавляемых значений dict.setdefau ltO
при каждом вызове будет создавать новый экземпляр начального значения (в данном при­
мере []), что может создать ненужную нагрузку.

1 9.5. Слияние словарей

Рассмотрим следующие словари:

»> fish = {'name': "Nemo", 'hands': "fins", 'special': "gil ls"}
>>> dog = {'name': "Clifford", 'hands': "paws", 'соlог': "геd"}

Версия Python 3.5+:

»> fishdog = {1Н fishdog
{'hands': 'paws', 'соlог': 'геd', 'name': 'Clifford', 'special': 'gil ls'}

В этом примере дубликаты ключей соответствуют последнему значению (например,
"Клиффорд" переопределяет "Немо").
Версия Python 3.3+:
»> from collections import ChainMap
»> dict(ChainMap(fish, dog))
{'hands': 'fins', 'color': 'red', 'special': 'gil ls', 'name': 'Nemo'}

Здесь первостепенное значение отдается конкретному ключу, а не последнему ("Клиф­
форд" выбрасывается в пользу "Немо").
Версия Python 2.х, 3.х:
»> from itertools import chain
»> dict(chain(f1sh.items(), dog.items()))
{'hands': 'paws', 'соlог': 'геd', 'name': 'Clifford', 'special': 'gil ls'}

При этом используется последнее значение, как и в случае с техникой объединения на
основе ** ("Клиффорд" переопределяет "Немо").
»> fish.update(dog)
»> fish
{'соlог': 'геd', 'hands': 'paws', 'name': 'Clifford', 'special': 'gil ls'}
dict.update использует последний

словарь для перезаписи предыдущего.

1 9.б. Доступ к ключам и значениям

При работе со словарями часто возникает необходимость получить доступ ко всем клю­
чам и значениям словаря либо в цикле for, либо в генераторе списка (list comprehension),
либо просто как обычный список. Дается словарь вида:
* Поваренная книга Python, 3-е издание, авторы Дэвид Бизли и Брайан К. Джоне (O'Reilly). Copyright 2013
David Beazley and Brian Jones, 978-1-449-34037-7.

1 9.7. Д оступ к значениям словаря

117

mydict = {
'а': '1 ',
'Ь': '2'

Вы можете получить список ключей, используя метод keys():
print(mydict.keys())
# Python2: ['а', 'Ь']
# Python3: dict_keys(['b', 'а'])
Если вместо этого вы хотите получить список значений, используйте метод values():
print(mydict.values())
# Python2: ['1 ', '2']
# Python3: dict_values(['2', '1 '])
Если вы хотите работать как с ключом, так и с соответствующим значением, вы можете
использовать метод items():
print(mydict.items())
# Python2: [('а', '1 '), ('Ь', '2')]
# Python3: dict_items([('b', '2'), ('а', '1 ')])
Примечание: поскольку dict является несортированным, keys(), values() и items() не име­
ют порядка сортировки. Используйте sort(), sorted() или OrderedDict, если вам важен порядок,
который возвращают эти методы.
Отличие Python 2 и 3: в Python 3 эти методы возвращают специальные итерируе­
мые объекты, а не списки, и являются эквивалентом методов Python 2 iterkeys(), itervalues()
и iteritems(). Эти объекты в большинстве случаев можно использовать как списки, хотя есть
и некоторые отличия. Более подробную информацию см. в РЕР 3106.

1 9.7. Доступ к значениям словаря
dictionary = {"Hello": 1 234, "World": 5678}
print(dictionary["Hello"])
Приведенный выше код выведет 1 234.
Строка "Hello" в этом примере называется ключом. Она используется для поиска значения
в dict путем заключения ключа в квадратные скобки. После соответствующего двоеточия в
определении словаря встречается число 1 234. Это и называется значением, которому сопо­
ставляется "Hello" в данном словаре.
Поиск такого значения по несуществующему ключу вызовет исключение КеуЕггог, ко­
торое, если не будет поймано, остановит выполнение. Если мы хотим получить доступ к
значению без риска возникновения ошибки КеуЕггог, мы можем воспользоваться методом
dictionary.get. По умолчанию, если ключ не существует, метод возвращает None. Мы можем
передать ему второе значение, которое будет возвращено вместо Nопе в случае неудачного
поиска.
w = dictionary.get("whatever")
х = dictionary.get("whatever", "nuh-uh")
В этом примере w получит значение None, а х - значение "nuh-uh".

1 9.8. Создание словаря

Правила создания словаря:
• Каждый ключ должен быть уникальным (иначе он будет переопределен).
• Каждый ключ должен быть хешируемым (для хеширования можно использовать
функцию hash, в противном случае будет выдана ошибка ТуреЕггог).
• Определенного порядка следования ключей нет.

118

Глава 1 9. Словарь

# Создание словаря и заполнение его значения ми
stock = {'eggs': 5, 'milk': 2}
# или создание пустого словаря
dictionary = {}
# и заполнение его после
dictionary['eggs'] = 5
dictionary['mi l k'] = 2
# Значения также могут быть списками
mydict = {'а': [1 , 2, 3], 'Ь': ['1 ', '2', '3'1}
# Используйте метод list.append() для добавления новых элементов в список значений
mydict['a'].append(4) # = > {'а': [1 , 2, 3, 4], 'Ь': ['1 ', '2', '3']}
mydict['b'].append('four') # = > {'а': [1 , 2, 3, 4], 'Ь': ['1 ', '2', '3', '4']}
# Мы также можем создать словарь, используя список кортежей из двух элементов
itегаЫе = [('eggs', 5), ('milk', 2)] dictionary = dict(iteraЫes)
# или с использованием аргумента keyword:
dictionary = dict(eggs= 5, milk= 2)
# Другим способом будет использование dict.fromkeys:
dictionary = dict.from keys((milk, eggs)) # = > {'mi lk': None, 'eggs': None}
dictionary = dict.fromkeys((milk, eggs), (2, 5)) # = > {'молоко': 2, 'я йца': 5}

1 9.9. Создание упорядоченного словаря
Вы можете создать упорядоченный словарь, который будет следовать определенному по­
рядку при итерации по ключам в словаре. Используйте Ordered Dict из модуля col lections. При
итерации он всегда будет возвращать элементы словаря в исходном порядке вставки.
from col lections import Ordered Dict
d = Ordered Dict()
d ['f1 rst'] = 1
d['second'] = 2
d['third'] = 3
d['last'] = 4
# Выведет "fi rst 1 ", "second 2", "third 3", "last 4"
fог key in d:
print(key, d[key])

1 9. 1 О. Распаковка словарей с помощью оператора

**

Для доставки пар "ключ-значение" из словаря в аргументы функции можно использо­
вать оператор распаковки аргументов по ключевому слову **. Упрощенный пример из офи­
циальной документации:
>>>
>» def parrot(voltage, state, action):
print("This раггоt wou ldn't", action, end = ' ')
print("if you put", voltage, "volts through it.", end = ' ')
print("E's", state, "!")
»> d = {"voltage": "four million", "state": "Ыeedin' demised", "action": "VOOM"}
>» раггоt(**d)
This раггоt wouldn't VOOM if you put fou r mill ion volts through it. E's Ыeedin' demised !

1 9. 1 1 . Запятая в конце строки

119

Начиная с Python 3.5 этот синтаксис можно использовать и для объединения произвольного числа объектов dict.
>>> f1sh =

{'паmе': "Nemo", 'hands': "f1ns", 'special': "gi l ls"}
dog = {'name': "Clifford", 'hands': "paws", 'color': "red"}
»> f1shdog = {**f1sh, **dog}
»> f1shdog
»>

{'hands': 'paws', 'color': 'red', 'паmе': 'Clifford', 'special': 'gi l ls'}
Как видно из этого примера, дублирующиеся ключи отображаются на свое последнее
значение (например, "Clifford" перекрывает "Nemo").

1 9. 1 1 . Запятая в конце строки
Как и в случае со списками и кортежами, в словаре можно ставить закрывающую запятую.
role = {"Ву day": "А typical programmer",
"Ву night": "Still а typical programmer", }
РЕР 8 предписывает оставлять пробел между запятой и закрывающей фигурной скобкой.

1 9. 1 2. Конструктор dict()
Конструктор dict() может быть использован для создания словарей из аргументов-клю­
чей, или из одной итерируемой пары "ключ-значение", или из одного словаря и аргумен­
тов-ключей.
dict(a=1 , Ь=2, с=З) # {'а': 1 , 'Ь': 2, 'с': З}
dict([('d', 4), ('е', 5), ('f', 6)]) # {'d': 4, 'е': 5, 'f': б}
dict([('a', 1 )], Ь=2, с=З) # {'а': 1 , 'Ь': 2, 'с': З}
dict({'a' : 1 , 'Ь' : 2}, с=З) # {'а': 1 , 'Ь': 2, 'с': З}

1 9. 1 З. Пример словарей
Словари сопоставляют ключи со значениями
саг = {)
car["wheels"] = 4
car["color"] = "Red"
car["model"] = "Corvette"
Доступ к значениям словаря осуществляется по их ключам
print "Little " + car["color"] + " " + car["model"] + "!"
# Этот код напечатает "Little Red Corvette!"
Словари также могут быть созданы в стиле JSON:
саг = {"wheels": 4, "color": "Red", "model": "Corvette"}
Значения словаря можно перебирать:
for key in саг:
print key + ": " + car[key]
# wheels: 4
# color: Red
# model: Corvette

1 9. 1 4. Все комбинации значений словаря
options = {

"х": ["а", "Ь"],

"у": [1 О, 20, 30]
}

1 20

Глава 20. Список

Словарь в этом примере выше содержит список, представляющий набор значений для
поиска соответствующего ключа. Предположим, вы хотите найти "х" = "а" с "у" = 1 О, затем
"х" = "Ь" с "у" = 1 О и т. д., пока вы не переберете все возможные комбинации.
Вы можете создать список, который возвращает все такие комбинации значений, ис­
пользуя следующий код:
import itertools
options = {

"х": ["а", "Ь"],

"у": [1 О, 20, 30]

keys = options.keys()
values = (options[key] for key in keys)
comblnations = [dict(zip(keys, comblnation)) for comblnation in itertools.product(-kyalues)]
print(comblnations)
Это дает нам следующий список, хранящийся в переменной comblnations:
#[{'х': 'а', 'у': 1 О},
# {'х': 'Ь', 'у': 1 О},
# {'х': 'а', 'у': 20},
# {'х': 'Ь', 'у': 20},
# {'х': 'а', 'у': 30},
# {'х': 'Ь', 'у': 30}]

Глава 20. Список
В Python список (List) - это общая структура данных, широко используемая в программах.
В других языках их часто называют динамическими массивами. Они являются одновремен­
но изменяемым типом данных и вместе с тем имеют тип данных последовательности, что
позволяет их индексировать и создавать срезы. Список может содержать объекты различ­
ных типов, в том числе и другие объекты списка.

20. 1 . Методы перечисления и поддерживаемые операторы
Начиная с заданного списка а:
а = [1 , 2, 3, 4, 5]
1. append(value) - добавляет новый элемент в конец списка.
# Добавить в список значения 6, 7 и 7

a.append(6)
a.append(7)
a.append(7)

# а: [1 , 2, 3, 4, 5, 6, 7, 7]
# Добавить еще один список
Ь = [8, 9]

a.append(b)

# а: [1 , 2, 3, 4, 5, 6, 7, 7, [8, 91]
# Добавить элемент другого типа, так как элементы списка не обязательно должны иметь
одинаковый тип
my_string = "hello world"

a.append(my_string)
# а: [1 , 2, 3, 4, 5, 6, 7, 7, [8, 9], "hello world"]

20. 1 . Методы перечисления и поддерживаемые операторы

1 21

Обратите внимание, что метод append() добавляет только один новый элемент в конец
списка. Если вы добавляете список в другой список, то добавляемый список становится
единственным элементом в конце первого списка.
# Добавление списка к другому списку
а = [1 , 2, 3, 4, 5, б, 7, 7]
Ь = [8, 9]
a.append(b)
# а: [1 , 2, 3, 4, 5, б, 7, 7, [8, 9]]
а[8]
# Возвращает: [8,9]

2. extend(enumeraЫe) - этот метод расширяет список, добавляя элементы из другого пере­
числяемого.
а = [1 , 2, 3, 4, 5, б, 7, 7]
Ь = [8, 9, 1 0]
# Расширить список, добавив в него все элементы из Ь
a.extend(b)
# а: [1 , 2, 3, 4, 5, б, 7, 7, 8, 9, 1 О]
# Расширение списка элементами несписочного перечисления:
a.extend(range(3))
# а: [1 , 2, 3, 4, 5, б, 7, 7, 8, 9, 1 О, О, 1 , 2]

Списки также можно объединять (конкатенировать) при помощи оператора +. Обратите
внимание, что это не изменяет ни один из исходных списков:
а = [1 , 2, 3, 4, 5, б] + [7, 7] + Ь
# а: [1 , 2, 3, 4, 5, б, 7, 7, 8, 9, 1 О]

3. Метод index(value, [startlndex]) - получает индекс первого вхождения входного значения.
Если входное значение отсутствует в списке, возникает исключение ValueError. Если указан
второй аргумент, поиск начинается с указанного индекса.
a. index(7)
# Возвращает: 6
a. index(49) # ValueError, так как 49 нет в а.
a. index(7, 7)
# Возвращает: 7
a. index(7, 8) # ValueError, так как нет 7, начинающейся с индекса 8
4. Метод insert(index, value) - вставляет value непосредственно перед указанным index. Таким образом, после вставки новый элемент занимает позицию index.
# вставить О в позицию О
a. insert(0, О)
a. insert(2, 5)
# вставить 5 в позицию 2
# а: [О, 1 , 5, 2, 3, 4, 5, б, 7, 7, 8, 9, 1 О]
5. Метод pop([index]) - удаляет и возвращает элемент по index. Без аргумента он удаляет и
возвращает последний элемент списка.
а.рор(2)
# Возвращает: 5
# а: [О, 1 , 2, 3, 4, 5, 6, 7, 7, 8, 9, 1 0]
а. рор(8)
# Возвращает: 7
# а: [О, 1 , 2, 3, 4, 5, б, 7, 8, 9, 1 О]

1 22

Глава 20. Список

# Без аргумента:
а.рор()
# Возвращает: 1О
# а: [О, 1, 2, 3, 4, 5, 6, 7, 8, 9]
6. Метод remove(value) - удаляет первое вхождение указанного значения. Если это значение не может быть найдено, выдается ValueError.
a.remove(0)
a.remove(9)
# а: [1, 2, 3, 4, 5, 6, 7, 8]
a.remove(1О)
# ValueError, так как 1О не находится в а
7.

Метод reverse() - инвертирует список на месте и возвращает None.

a.reverse()
# а: [8, 7, 6, 5, 4, 3, 2, 1]

Существуют и другие способы реверсирования списков.

8. Метод count(value) - подсчитывает количество вхождений некоторого значения в списке.
Метод a.count(7)
# Возвращает: 2
9. Метод sort() - сортирует список в числовом и лексикографическом порядке и возвращает None.
a.sort()
# а = [1, 2, 3, 4, 5, 6, 7, 8]
# Сортирует список в числовом порядке

Списки также могут быть реверсированы при сортировке с помощью параметра

reverse = True в методе sort().
a.sort(reverse = True)
# а = [8, 7, 6, 5, 4, 3, 2, 1]

Если требуется сортировка по атрибутам элементов, можно использовать аргумент key:
import datetime
class Person(object):
def _init_(self, name, Ьirthday, height):
self.name = name
self.Ьirthday = Ьirthday
self. height = height
def _repr_(self):
return self.name
1

=

[Person("John Cena", datetime.date(1992, 9, 12), 175),
Person("Chuck Norris", datetime.date(1990, 8, 28), 180),
Person("Jon Skeet", datetime.date(1991, 7, 6), 185)]

l.sort(key=lambda item: item.name)
# 1: [Chuck Norris, John Сепа, Jon Skeet]
l.sort(key = lambda item: item.Ьirthday)
# 1: [Chuck Norris, Jon Skeet, John Cena]
l.sort(key = lambda item: item.height)
# 1: [John Сепа, Chuck Norris, Jon Skeet]

20. 1 . Методы перечисления и поддерживаемые операторы

1 23

В случае списка словарей используется та же концепция:
import datetime
1 = [{'name':'John Сепа', 'Ьirthday': datetime.date(1 992, 9, 1 2),'height': 1 75},
{'name': 'Chuck Norris', 'Ьirthday': datetime.date(1 990, 8, 28),'height': 1 80},
{'name': 'Jon Skeet', 'Ьirthday': datetime.date(1 991 , 7, 6), 'height': 1 85}1
l.sort(key=lambda item: item['name'])
# 1: [Chuck Norris, John Сепа, Jon Skeet]
l.sort(key=lambda item: item['Ьirthday'])
# 1: [Chuck Norris, Jon Skeet, John Сепа]
l.sort(key=lambda item: item['height'])
# 1: [John Сепа, Chuck Norris, Jon Skeet]

Сортировка по подсписку:

import datetime

1 = [{'name':'John Сепа', 'Ьirthday': datetime.date(1 992, 9, 1 2),'size': {'height': 1 75, 'weight': 1 00}},
{'name': 'Chuck Norris', 'Ьirthday': datetime.date(1 990, 8, 28),'size' : {'height': 1 80, 'weight': 90}},
{'name': 'Jon Skeet', 'Ьirthday': datetime.date(1 991 , 7, 6), 'size': {'height': 1 85, 'weight': 1 1 О}}]
l.sort(key=lambda item: item['size'] ['height'])
# 1: [John Сепа, Chuck Norris, Jon Skeet]

Улучшенный способ сортировки с использованием attrgetter и itemgetter

Списки также можно сортировать с помощью функций attrgetter и itemgetter из модуля
Это может помочь улучшить читаемость и удобство повторного использования. Вот
несколько примеров:
operator.

from operator import itemgetter,attrgetter
people = [{'name':'chandan','age':20,'salary':2000},
{'name':'chetan','age':1 8,'salary':5000},
{'name':'guru','age':30,'salary':3000}]
by_age = itemgetter('age')
by_salary = itemgetter('salary')
people.sort(key=by_age) #сортировка на месте по возрасту
people.sort(key=by_salary) #сортировка на месте по зарплате

Функции itemgetter также может быть присвоен индекс. Это полезно, если вы хотите сортировать по индексам кортежа.
l isLof_tuples = [(1 ,2), {3,4), (5,0)]
lisLof_tuples.sort(key= itemgetter(1 ))
print(list_of_tuples) #[(5, О), (1 , 2), (3, 4)]

Используйте функцию attrgetter, если вы хотите сортировать по атрибутам объекта:
persons = [Person("John Сепа", datetime.date(1 992, 9, 1 2), 1 75),
Person("Chuck Norris", datetime.date(1 990, 8, 28), 1 80),
Person("Jon Skeet", datetime.date(1 991 , 7, 6), 1 85)] # повторное использование класса Person
# из примера выше
person.sort(key=attrgetter('name')) #sort Ьу name
by_Ьirthday = attrgetter('Ьirthday')
#sort Ьу Ьirthday
person.sort(key=by_Ьirthday)

1 24

Глава 20. Список

10. Метод clear() - удаляет все элементы из списка:
a.clear()
#a=D

11. Ре п л и ка ция - умножение существующего списка на целое число приведет к созданию
большего списка, состоящего из множества копий оригинала. Это может быть полезно, на­
пример, для инициализации списка:
Ь = ["Ыаh"] * 3
# Ь = ["Ыаh", "Ыаh", "Ыаh"]
Ь = [1 , 3, 5] * 5
# [1 , 3, 5, 1 , 3, 5, 1 , 3, 5, 1 , 3, 5, 1 , 3, 5]

Будьте осторожны, если ваш список содержит ссьшки на объекты (например, список спи­
сков), см. главу "Общие ошибки" - "Умножение списков и общие ссьшки".
12. Удален ие элемента - можно удалить несколько элементов в списке, используя ключевое слово del и нарезку:
а = list(range{1 О))
del а[::2]
# а = [1 , 3, 5, 7, 9]
del а[-1 ]
# а = [1 , 3, 5, 7]
del а[:]
#a=D

13. Копирование

Назначение по умолчанию, т. е. "=", присваивает новому имени ссьшку на исходный спи­
сок. То есть исходное имя и новое имя указывают на один и тот же объект списка. Измене­
ния, сделанные в одном из них, будут отражены в другом. Зачастую это совсем не то, что вы
задумали.
Ь=а
a.append {б)
# Ь: [1 , 2, 3, 4, 5, 6]

Если вы хотите создать копию списка, то у вас есть несколько вариантов.
Вы можете нарезать его:
new_list = old_l ist[:]

Вы можете использовать встроенную функцию list () :

new_list = list{old_list)

Вы можете использовать сору.сору () :

import сору
new_list = copy.copy{old_list)

# вставляет ссылки на объекты, н айден ные в оригинале.

Это немного медленнее, чем list (), потому что сначала нужно выяснить тип данных у

old_list.

Если список содержит объекты и вы хотите их скопировать, используйте copy.deepcopy ():

import сору
new_list = copy.deepcopy{old_list) #inserts copies of the objects found in the original.

Этот метод самый медленный и требующий много памяти, но иногда его использование
неизбежно.
Версия Python З.х � 3.0:
сору() - возвращает неглубокую (поверхностную) копию списка:
аа = а.сору()
# аа = [1 , 2, 3, 4, 5]

20.2. Доступ к значения м списка

1 25

20.2. Доступ к значениям списка

Списки Python имеют нулевую индексацmо и ведут себя подобно массивам в других языках.
1st = [1 , 2, 3, 4]
lst[0] # 1
lst[1 ] # 2
Попытка доступа к индексу за пределами списка приведет к ошибке lndexError.
lst[4] # lndexError: list index out of range
Отрицательные индексы интерпретируются как считанные с конца списка.
lst[-1 ] # 4
lst[-2] # 3
lst[-5] # lndexError: list index out of range

Это функционально эквивалентно
lst[len(lst)-1 ] # 4
Списки позволяют использовать нотацию срезов (slice notation) в виде lst[start:end:step].
Вывод нотации среза - это новый список, содержащий элементы от индекса start до end-1 .
Если параметры опущены, то start по умолчанию присваивается началу списка, end - концу
списка, а шаr (step) - 1:
# [2, 3, 4]
lst[1 :]
# [1 , 2, 3]
lst[:3]
lst[::2]
# [1 , 3]
# [4, 3, 2, 1 ]
lst[::-1 ]
lst[-1 :0:-1 ]
# [4, 3, 2]
lst[5:8]
# так как начальный и ндекс больше длины 1st, возвращает пустой список
lst[1 :1 О]
# [2, 3, 4] то же, что и отсутствие конечного индекса



Учитывая это, можно вывести обратную версию списка, вызвав команду
lst[::-1 ] # [4, 3, 2, 1 ]
При использовании с отрицательной длиной начальный индекс должен быть больше ко­
нечного, иначе результатом будет пустой список.
lst[3:1 :-1 ]
# [4, 3]
Использование отрицательных шаговых индексов эквивалентно следующему коду:
reversed(lst)[0:2] # О = 1 -1
# 2 = 3 -1

Используемые индексы на 1 меньше, чем те, которые используются при отрицательной
индексации, и являются обратными.

Расширенная нарезка
При нарезке списков вызывается метод _getitem_O объекта списка с объектом slice.
Python имеет встроенный метод среза для создания объектов среза. Можно использовать это
для хранения среза и последующего его использования следующим образом:
data = 'chandan purohit 22 2000' #при условии, что поля данных имеют фиксированную дли ну
name_slice = slice(0, 1 9)
age_slice = slice(1 9,21 )
salary_slice = slice(22,None)
#теперь мы можем получить более читаемые срезы
print(data[name_slice]) #chandan purohit
print(data[age_slice]) #'22'
print(data[salary_slice]) #'2000'
Это может быть очень полезно для обеспечения функциональности нарезки наших объ­
ектов, переопределяя _getitem_ в нашем классе.

1 26

Глава 20. Список

20.3. Проверка списка на пустоту

Пустота списка связана со значением булевой логики False, поэтому можно не проверять
а просто 1st или not 1st:

len(lst) == О,



1st =
if not 1st:
ргiпt("список пуст")
# В ы вод: список пуст

20.4. Итерирование по списку

Python поддерживает использование цикла for непосредственно в списке:

my_list = ['foo', 'Ьаг', 'baz']
for item in my_list:
print(item)
# В ы вод: foo
# В ы вод: Ьаг
# В ы вод: baz

Вы также можете получить информацию о положении каждого элемента:
for (index, item) in enumerate(my_list):
print('Тhe item in position {} is: {}'.format(index, item))
# В ы вод: The item in position О is: foo
# В ы вод: The item in position 1 is: Ьаг
# В ы вод: The item in position 2 is: baz

Другой способ итерации списка на основе значения индекса:
for i in range(O,len(my_list)):
print(my_list[i])
# В ы вод:
>>>
foo
Ьаг
baz

Обратите внимание, что изменение элементов списка во время итерации по нему может
привести к неожиданным результатам:
for item in my_l ist:
if item == 'foo':
del my_list[O]
print(item)
# В ы вод: foo
# В ы вод: baz

В последнем примере мы удалили первый элемент на первой итерации, но это привело
к тому, что элемент Ьаг был пропущен.

20.5. Проверка наличия элемента в списке

Python очень просто проверить, находится ли элемент в списке. Для этого достаточно воспользоваться оператором in.
1st = ['test', 'twest', 'tweast', 'treast']
'test' in 1st
# Результат: True

20.6. Функции "апу" и "all"

1 27

'toast' in 1st
# Резул ьтат: False

Примечание: оператор in на множествах работатет асимптотически быстрее, чем в списках.
Если вам нужно использовать его много раз в потенциально больших списках, возможно, будет
целесообразно преобразовать list в set и проверить наличие элементов в set .
slst = set(lst)
'test' in slst
# Резул ьтат: True

20.6. Функции "апу" и "all"

С помощью функции all() можно определить, все ли значения в итерируемом объекте
имеют значение True.
nums = [1 , 1 , О, 1 ]
all(nums)
# False
chars = ['а', 'Ь', 'с', 'd']
all(chars)
# True

Аналогично функция апу() определяет, имеет ли одно или несколько значений в итерируемой последовательности значение True.
nums = [1 , 1 , О, 1 ]
any(nums)
# True
vals = [None, None, None, False]
any(vals)
# False

Хотя в этом примере используется список, важно отметить, что эти встроенные работы
работают с любым итерируемым типом данных, включая генераторы.
vals = [1 , 2, 3, 4]
any(val > 1 2 for val in vals)
# False
any((val * 2) > б for val in vals)
# True

20. 7. Реверсирование элементов списка

Функция reversed возвращает итератор к перевернутому списку:

ln [3]: rev = reversed(numbers)
ln [4]: rev
Out[4]: [9, 8, 7, б, 5, 4, 3, 2, 1 ]

Обратите внимание, что список "numbers"остается неизменным для этой операции
и остается в том же порядке, что и был изначально. Для реверсирования на месте можно
также использовать метод reverse.
Вы также можете обратить список (фактически получить копию, исходный список при
этом не изменяется), используя синтаксис нарезки, задав в качестве третьего аргумента
(шаг) значение -1:
ln [1 ]: numbers = [1 , 2, 3, 4, 5, 6, 7, 8, 9]
ln [2]: numbers[::-1 ]
Out[2]: [9, 8, 7, б, 5, 4, 3, 2, 1 ]

1 28

Глава 20. Список

20.8. Конкатенация и слияние списков
1. Просте йш и й с п особ кон катена ц ии list1 и list2:
merged = list1 + list2
2. Функция zip во зв ра щает сп исо к кортеже й, где i-й кортеж содержит i-й элемент из каждой из последовательностей аргументов или итерируемых объектов:
al ist = ['а1 ', 'а2', 'а3']
Ыist = ['Ь1 ', 'Ь2', 'Ь3']
for а, Ь in zip(al ist, Ыist):
print(a, Ь)
# Результат:
# а1 Ь1
# а2 Ь2
# а3 Ь3
Если списки имеют разную длину, тогда результат будет содержать лишь столько элементов, сколько кратчайший:
al ist = ['а1 ', 'а2', 'а3']
Ыist = ['Ь1 ·, ·ь2·, 'Ь3', 'Ь4']
for а, Ь in zip(al ist, Ыist):
print(a, Ь)
# Результат:
# а1 Ь1
# а2 Ь2
# а3 Ь3



al ist =
len(list(zip(alist, Ыist)))
# Результат:

Для расширения списков неравной длины до самого длинного с помощью элементов
None используйте itertools.zip_longest (itertools.izip_longest в Python 2):
al ist = ['а1 ', 'а2', 'а3']
Ыist = ['Ь1 ']
clist = ['с1 ', 'с2', 'с3', 'с4']
for а,Ь,с in itertools.zip_longest(alist, Ыist, clist):
print(a, Ь, с)
# Результат:
# а1 Ь1 с1
# а2 None с2
# а3 None с3
# None None с4
3. Вста вка в указ анн ый инде кс з на ч ени й:
al ist = [1 23, 'xyz', 'zага', 'аЬс']
al ist.insert(3, [2009])
print("Final List :", alist)
Результат:
Final List : [1 23, 'xyz', 'zara', 2009, 'аЬс']

1 29

20.9. Длина списка

20.9. Длина списка
Для получения одномерной длины списка используйте функцию len().
lеп(['опе', 'two'])

# возвращает 2

lеп(['опе', [2, 3], 'four'])

# возвращает 3, не 4

Функция len() также работает с строками, словарями и другими структурами данных, по­
хожими на списки. Обратите внимание: len() - это встроенная функция, а не метод объекта
списка.
Также отметим, что len() обходится в 0(1 ), то есть потребуется одинаковое количество
времени, чтобы получить результат, независимо от длины списка.

20. 1 О. Удаление дублирующихся значений в списке
Удаление дублирующихся значений в списке может быть выполнено путем преобразо­
вания списка в set (множество), то есть неупорядоченную коллекцию отдельных объектов.
Если необходима списочная структура данных, то set можно преобразовать обратно в спи­
сок с помощью функции list():
names = ["aixk", "duke", "edik", "tofp", "duke"]
list(set(names))
# Результат: ['duke', 'tofp', 'aixk', 'edik']
Обратите внимание, что путем преобразования списка в set исходный порядок теряется.
Для сохранения порядка списка можно использовать OrderedDict:
import collections
»> collections.OrderedDict.fromkeys(names).keys()
# Результат: ['aixk', 'duke', 'edik', 'tofp']

20. 1 1 . Сравнение списков
С помощью операторов сравнения можно лексикографически сравнивать списки и другие последовательности. Оба операнда должны быть одного типа.
[1 , 1 О, 1 00] < [2, 1 О, 1 00]
# True, потому ЧТО 1 < 2
[1 , 1 о, 1 00] < [1 , 1 о, 1 00]
# False, так как списки равны
[1 , 1 о, 1 00] my_list=[{1 }] * 1 О
»> print(my_list)
[{1 }, {1 }, {1 }, {1 }, {1 }, {1 }, {1 }, {1 }, {1 }, {1 }]
»> my_list[0].add(2)
»> print(my_list)
[{1 , 2}, {1 , 2}, {1 , 2}, {1 , 2}, {1 , 2}, {1 , 2}, {1 , 2}, {1 , 2}, {1 , 2}, {1 , 2}]

Вместо этого, чтобы инициализировать список с фиксированным количеством раз н ых

и з меняем ых объектов, используйте:
my_list=[{1 } for _ in range(1 О)]

Глава 21 . Генератор списков
Генераторы списков (List comprehensions) в языке Python - это лаконичные синтаксиче­
ские конструкции. С их помощью можно формировать списки из других списков, применяя
функции к каждому элементу списка. В следующей главе объясняется и демонстрируется ис­
пользование этих выражений.

21 . 1 . Списковые вычисления

Генератор списка (List comprehension) создает новый список путем применения выраже­
ния к каждому элементу итерируемого списка. В самом простом виде это выглядит так:
[ for in < iteraЬle> ]

Также есть необязательное условие if:

[ for in < iteraЬle> if ]

Каждый в подключается к , если (необязательно)
имеет значение true. Все результаты сразу возвращаются в новый список. Выражения гене­
ратора оцениваются "лениво", а генераторы списков оценивают весь итератор сразу, зани­
мая память, пропорциональную длине итератора.
Чтобы создать список (list) квадратов целых чисел:
squares = [х * х for х in (1 , 2, 3, 4)]
#квадраты: [1 , 4, 9, 1 б]

Выражение for устанавливает х в каждое значение по очереди из (1 , 2, 3, 4). Результат вы­
ражения х * х добавляется во внутренний list. Этот внутренний список присваивается пере­
менной squares после завершения.
Помимо ускорения генераторы списков примерно эквивалентны следующему циклу for:



squares =
for х in (1 , 2, 3, 4):
sq uares.append(x * х)
# квадраты: [1 , 4, 9, 1 б]

Выражение, применяемое к каждому элементу, может быть настолько сложным, насколько это необходимо:
# Получить список заглавных сим волов из строки
[s.upper() for s in "Hello World"]
# ['Н', 'Е', 'L, 'L, 'О', '', 'W', 'О', 'R', 'L, 'D']

Глава 21 . Генератор списков

1 32
# Убрать все запятые с конца строки в списке

[w.strip(',') for w in ['these,', 'words,,', 'mostly', 'have,commas,']]

# ['these', 'words', 'mostly', 'have,commas']

# Орган изовать буквы в словах в алфавитном порядке
sentence = "Beautiful is better than ugly"
["''.join(sorted(word, key = lambda х: x.lower())) for word in sentence.split()]
# ['aBefi ltuu', 'is', 'beertt', 'ahnt', 'gluy']

Условие Else

Условие else можно использовать в списковых включениях, но следует быть осторож­
ным с синтаксисом. Условие if или else следует использовать перед циклом for, а не после:
# создать сп исок символов в слове "apple", заменяя согласн ые на
# Ех - 'apple' --> ['а', '*', '*', '*' ,'е']

''1 1 О]
# [1 6, 25, 36, . . . ]

21 .3. Избегание повторных и ресурсоемких операций с помощью условного предложения

1 35

Это приводит к двум вызовам f(x) для 1000 значений х: один вызов для генерации значе­
ния, а другой для проверки условия if. Если f(x) является особенно ресурсоемкой операцией,
это может существенно повлиять на производительность. Но хуже всего, если вызов f() несет
побочные эффекты, это может привести к неожиданным результатам.
Вместо этого можно оценивать ресурсоемкую операцию только один раз для каждого
значения х, генерируя промежуточные итерируемые данные (генераторное выражение)
следующим образом:
[v for v in (f(x) fог х in гапgе(1 ООО)) if v > 1 О]
# [1 6, 25, 36, . . . ]

или используя встроенную эквивалентную функцию map:
[v for v in map(f, range(1 ООО)) if v > 1 О]
# [1 6, 25, 36, . . . ]

Другой способ, который может сделать код более читабельным, - поместить частичный
результат (v в предыдущем примере) в итерируемый тип (например, список или кортеж), а
затем выполнить над ним итерацию. Поскольку v будет единственным элементом в этом
итерируемом, в результате мы имеем ссылку на вывод нашей "медленной" функции, вычис­
ляемой только один раз:
[v for х in range(1 ООО) for v in [f(x)] if v > 1 О]
# [1 6, 25, 36, ... ]

Однако на практике логика кода может быть более сложной, и важно сохранить его чи­
табельность. Как правило, лучше использовать отдельную функцию-генератор вместо слож­
ной однострочной:
def process_prime_numbers(iteraЫe):
for х in iteraЫe:
if is_prime(x):
yield f(x)
[х fог х in process_prime_numbers(range(1 ООО)) if х > 1 О]
# [1 1 , 1 3, 1 7, 1 9, . . . ]

Другой способ предотвратить многократное вычисление f(x) - это использовать для f(x)
декоратор @functools.lru_cache() (Python 3.2+). Поскольку вывод f для ввода х уже был вы­
числен один раз, второй вызов функции исходного генератора списков будет таким же бы­
стрым, как поиск по словарю. Этот подход использует запоминание (memoization) для повы­
шения эффективности, что сравнимо с использованием генераторных выражений.
Допустим, вы должны "сгладить" (т. е. перевести в одномерный) список:
1 = [[1 , 2, 3], [4, 5, 6], [7], [8, 91]

Одним из методов может быть:
reduce(lambda х, у: х+у, 1)

sum(I, □)
l ist(itertools.chain(*I))

Но лучшую производительность принесет генератор списка:
[item for suЫist in I for item in suЫ ist]

Сокращения, основанные на + (включая подразумеваемое использование в сумме), при
наличии L-подсписков с необходимостью имеют вид O(L A 2) - поскольку список промежу­
точных результатов становится все длиннее, на каждом шаге выделяется новый объект спи­
ска промежуточных результатов, и все элементы предыдущего промежуточного результата
должны быть скопированы (а также несколько новых, добавленных в конце). Поэтому для
простоты допустим, что у вас есть L-подсписков с !-элементов в каждом: первые !-элементы

1 36

Глава 21 . Генератор списков

копируются туда и обратно L-1 раз, вторые !-элементы L-2 раза и т. д.; общее количество ко­
пий равно 1, умноженному на сумму х для х от 1 до L исключенного, т. е. I*(L **2)/2.
Генератор списка создает только один список и копирует каждый элемент (из исходного
места в результирующий список) также ровно один раз.

21 .4. Генератор словаря (словарь включений)

Генератор словаря (dictionary comprehension) аналогичен генератору списка, только вме­
сто списка создает объект словаря.
Основной пример (Версия Python 2.х � 2. 7):
{х: х * х for х in (1 , 2, 3, 4)}
# {1 : 1 , 2: 4, 3: 9, 4: 1 б}

еще один способ написания:
dict((x, х * х) for х in (1 , 2, 3, 4))
# {1 : 1 , 2: 4, 3: 9, 4: 1 б}

Как и в случае с генератором списка, можно использовать условный оператор внутри ге­
нератора словаря, чтобы получить только те элементы словаря, которые удовлетворяют за­
данному критерию.
Версия Python 2.х � 2. 7:
{name: len(name) for name in('Stack', 'Overflow', 'Exchange') if len(name) > 6}
# {'Exchange': 8, 'Overflow': 8}

Это можно переписать с помощью генераторного выражения:
dict((name, len(name)) for name in('Stack', 'Overflow', 'Exchange') if len(name)
# {'Exchange': 8, 'Overflow': 8}

> 6)

Начало работы со словарем и использование генератора словаря как фильтра пары
"юпоч-значение"
Версия Python 2.х � 2. 7:
initial_dict = {'х': 1 , 'у': 2}
{key: value for key, value in initial_dict.itemsO if key == 'х'}
# Рез ул ьтат: {'х': 1 }

Переюпочение юпоча и значения словаря (инвертирование словаря)

Если у вас есть словарь, содержащий простые хешируемые значения (дублирование зна­
чений может привести к неожиданным результатам):
my_dict = {1 : 'а', 2: 'Ь', 3: 'с'}

и вы хотите поменять местами ключи и значения, вы можете использовать несколько
подходов в зависимости от вашего стиля программирования:






swapped = {v: k for k, v in my_dict.items()}
swapped = dict((v, k) for k, v in my_dict.items())
swapped = dict(zip(my_dict.values(), my_dict))
swapped = dict(zip(my_dict.values(), my_dict.keys()))
swapped = dict(map(reversed, my_dict.itemsO))

print(swapped)
# {а: 1 , Ь: 2, с: 3}

Версия Python 2.х � 2.3:

Если ваш словарь велик, импортируйте itertools и используйте izip или imap.

21 .5. Генераторы списков с вложенными циклами

1 37

О бъединение словарей

Объедините словари и при необходимости переопределите старые значения с помощью
вложенного генератора словаря.
dict1 = {'w': 1 , 'х': 1 }
dict2 = {'х': 2, 'у': 2, 'z': 2}
{k: v for d in [dict1 , dict2] for k, v in d.items(}}
# {'w': 1 , 'х': 2, 'у': 2, 'z': 2}

ПримечаJШе : генераторы словарей бьти добавлены в Python 3.0 и перенесены в версию 2. 7+,
в отличие от генераторов списков, которые бьти добавлены в 2.0. Версии до 2. 7 мoryr использо­
вать генераторные выражения и встроенную функцию dictQ для имитации генератора словаря.

21 .5. Генераторы списков с вложенными циклами

Списки могут использовать вложенные циклы for. Вы можете запрограммировать лю­
бое количество вложенных циклов for внутри генератора списка, и каждый цикл for мо­
жет иметь дополнительную связанную проверку на if. При этом порядок следования fог­
конструкций такой же, как при написании очередности вложенных fог-выражений. Общая
структура списочных представлений выглядит следующим образом:
[expression for target1 in iteraЫe1 [if condition 1 ]
for target2 i n iteraЫe2 [if condition2] ...
for targetN in iteraЬleN [if conditionN] ]
Например, следующий код "уплощает" список списков с использованием нескольких выражений for:
data = [[1 , 2], [3, 4], [5, 61]
output =
for each_list in data:
for element in each_list:
output.append(element)
print(output)



# Результат: [1 , 2, 3, 4, 5, 6]
Можно эквивалентно записать в виде генератора списка с множественными конструкциями fоr:
data = [[1 , 2], [3, 4], [5, 61]
output = [element for each_l ist in data for element in each_list]
print(output)
# Результат: [1 , 2, 3, 4, 5, 6]
Как в расширенной форме, так и в генераторе списка внешний цикл идет первым.
Использование вложенных циклов в генераторе списка и компактнее, и значительно бы­
стрее.
ln [1 ]: data = [[1 ,2],[3,4],[5,61 ]
ln [2]: def f():
... : output=
... : for each_list in data:
... : for element in each_list:
... : output.append(element)
... : return output
ln [3]: timeit f()
1 000000 loops, best of 3: 1 .37 µs per loop
ln [4]: timeit [inner for outer in data for inner in outer]
1 000000 loops, best of 3: 632 ns per loop



1 38

Глава 21 . Генератор списков

Дополнительное время на вызов приведенной выше функции примерно 140 ns.
Строчные if вкладываются таким же образом и могут находиться в любом положении по­
сле первого for:
data = [[1 ], [2, 3], [4, 51]
output = [element for each_l ist in data
if len(each_l ist) == 2
for element in each_list
if element != 5]
print(output)
# [2, 3, 4]

Как бы то ни было, ради удобства чтения следует рассмотреть возможность использо­
вания традиционных циклов for. Это особенно актуально, когда глубина вложенности пре­
вышает 2 уровня и/или логика понимания слишком сложна. Генераторы списка с много­
кратным использованием вложенных циклов могут выдавать ошибки или неожиданный
результат.

21 .6. Генераторные выражения

Генераторные выражения очень похожи на генераторы списков. Основное отличие со­
стоит в том, что в них не создается сразу полный набор результатов, а создается объект-гене­
ратор, над которым можно выполнять итерации.
Например, в следующем коде можно увидеть различия:
# генератор списка (l ist comprehension)
[х**2 for х in range(1 О)]
# Результат: [О, 1 , 4, 9, 1 6, 25, 36, 49, 64, 8 1 ]

Версия Python 2.х ;:: 2.4:
# генераторное выражение (generator comprehension)
(х**2 for х in xrange(1 О))
# Результат:

Это два совершенно разных объекта.
• В результате работы генератора списка возвращается объект типа list,
а в результате работы генераторной функции - объект типа generator.
• Объекты типа generator не могут быть проиндексированы, поэтому для
расстановки элементов по порядку используется функция next.
Примечание: мы используем функцию xrange, поскольку она тоже создает объект-генера­
тор. Если бы мы использовали range, то был бы создан список. Кроме того, xrange существует
только в поздней версии Python 2. В Python 3 range просто возвращает генератор. Более под­
робную информацию можно найти в примере "Различия между функциями range и xrange".
Версия Python 2.х ;:: 2.4:
g = (х**2 for х in xrange(1 О))
print(g[O])
Traceback (most recent call last):
File "", line 1 , in
ТуреЕггог: 'generator' object has по attribute '_getitem_'
g.next() # О
g.next() # 1
g.next() # 4
g.next() # 81
g.next() # Throws Stoplteration Exception

21 .6. Генераторные выражения

1 39

Traceback (most recent call last):
File "", line 1 , in
Stoplteration

Версия Python З.х :::: 3.0:
Примечание: функцию g.next() следует заменить на next(g), а xrange - на range, поскольку
в Python 3 не существуют lterator.next() и xrange().

Хотя оба эти варианта можно итерировать аналогичным образом:
for i in [х*2 for х in range(1 О)]:
print(i)
Out:

о
1

4
81

Версия Python 2.х ;:: 2.4:
for i in (х*2 for х in xrange(1 О)):
print(i)
Out:

о

1
4

81

Примеры использования

Выражения генератора вычисляются "лениво", то есть генерируют и возвращают каждое
значение только при итерировании генератора. Это часто бывает полезно при итерациях по
большим наборам данных, позволяя избежать необходимости создания дубликата набора
данных в памяти:
for square in (х*2 for х in range(1 000000)):
# сделать что-то

Другим распространенным вариантом использования является отказ от итерации по
всему итерируемому объекту, если в этом нет необходимости. В данном примере при каж­
дой итерации функции geLobjects() из удаленного API извлекается один объект. Объектов
может быть несколько тысяч, их нужно извлекать по одному, а нам всего лишь нужно знать,
существует ли объект, соответствующий шаблону. Используем генераторное выражение:
def get_objects():
"'"'Gets objects from an API one Ьу one"""
while True:
yield get_next_item()
def objecLmatches_pattern(obj):
# выполнить потенциально сложны й расчет
return matches_pattern

1 40

Глава 21 . Генератор списков

def right_item_exists():
items = (object_matched_pattern(each) for each in get_objects())
for item in items:
if item.is_the_righLone:
return True
return False

21 .7. Генераторы наборов
Генераторы наборов подобны генераторам списков и словарей, но они создают набор,
т. е. неупорядоченную коллекцию уникальных элементов.

Версия Python 2.х � 2. 7:
# Набор, содержащий каждое значение в диапазоне (5):
{х for х in range(5)}
# {О, 1 , 2, 3, 4}
# Набор четных чисел от 1 до 1 О:
{х for х in range(1 , 1 1 ) if х % 2 == О}
# {2, 4, 6, 8, 1 О}
# Уникальные буквен н ы е сим волы в текстовой строке:
text = "When in the Course of human events it becomes necessary for one people . . ."
{ch. lower() for ch in text if ch.isalpha(}}
# Результат: set(['a', 'с', 'Ь', 'е', 'f', 'i', 'h', 'm', '1', 'о',
#
'п', 'р', 's', 'г', 'u', 't', 'w', 'v', 'у'])
Имейте в виду, что наборы неупорядочены. Это значит, что порядок результатов в наборе
может отличаться от того, который представлен в приведенных выше примерах.
Примечание: генераторы наборов доступны с версии Python 2.7+, в отличие от генера­
торов списков, которые бьши добавлены в Python 2.0. В Python версий от 2.2 до 2.6 функция
set() может использоваться с генераторным выражением для получения того же результата:
set(x for х in range(5))
# Результат: {О, 1 , 2, 3, 4}

21 .8. Рефакторинг функций filter и map в генераторы списков
Функции f1 lter или map часто должны быть заменены генераторами списков. Гвидо ван
Россум хорошо описал это в открытом письме в 2005 году:
f1 lter(P, S) почти всегда записывается понятнее в виде [х for х in S if Р(х)], и это имеет то огромное
преимущество, что наиболее часто используются предикаты, которые являются сравнениями,
например, х==42, и использование лямбда-выражения для этого требует гораздо больше усилий
от читателя (к тому же лямбда-выражение работает медленнее, чем генератор списка). Тем бо­
лее это касается функции map(F, S), которая превращается в [F(x) для х в S]. Конечно, во многих
случаях вместо этого можно использовать генераторные выражения.
Следующие строки кода считаются "не питоновскими" и будут вызывать ошибки.
# четные числа < 1 О
f1 lter(lambda х: х % 2 == О, range(1 О))
# умножение каждого числа на 2
map(lambda х: 2*х, range(1 О))
# сум ма всех элементов в списке
functools.reduce(lambda х,у: х+у, range(1 О))
Используя то, что мы узнали из предыдущей цитаты, мы можем преобразовать эти выра­
жения в эквивалентные им генераторы списков; также удалим лямбда-функции из каждого,
чтобы сделать код более читаемым.

21 .9. Генераторы с использованием кортежей

1 41

# Filter:
# Р(х) = х % 2 == О
# S = range(1 О)
[х for х in range(1 О) if х % 2 == О]
# [О, 2, 4, 6, 8]
# Мар
# F(x) = 2*х
# S = range(1 О)
[2*х for х in range(1 О)]
# [О, 2, 4, 6, 8, 1 О, 1 2, 1 4, 1 6, 1 8]

Читаемость становится еще более очевидной при работе с цепочками функций. Если для
удобства чтения результаты одной функции map или f1lter должны быть переданы в качестве
результата следующей, то в простых случаях они могут быть заменены одним списком. Кроме
того, из генератора списка мы можем легко понять, каков результат нашего процесса, в отли­
чие от большой когнитивной нагрузки при понимании процесса с использованием map и f1lter.
# Мар & Filter
filtered = filter(lambda х: х % 2 == О, range(1 О))
results = map(lambda х: 2*х, fi ltered)
# List comprehension
results = [2*х for х in range(1 О) if х % 2 == О]

Рефакториш - краткий справочник



Мар

map(F, S) == [F(x) for х in S]


Filter

filter(P, S) == [х for х in S if Р(х)]

где F и Р являются функциями, которые соответственно преобразуют входные значения
и возвращают булевы значения.

21 .9. Генераторы с использованием кортежей

В предложении for генератора списков может быть указано более одной переменной:
[х + у for х, у in [(1 , 2), (3, 4), (5, 6)]]
# [3, 7, 1 1 ]

[х + у for х, у in zip([1 , 3, 5], [2, 4, 6])]
# [3, 7, 1 1 ]

Это подобно обычному использованию циклов for:
for х, у in [(1 ,2), {3,4), (5,6)]:
print(x+y)
#3
#7
#11

Однако если генератор начинается с кортежа, то он должен быть заключен в круглые
скобки:
[х, у for х, у in [(1 , 2), (3, 4), (5, 6)]]
# SyntaxError: inval id syntax
[(х, у) for х, у in [(1 , 2), (3, 4), (5, 6)1]
# [(1 , 2), (3, 4), (5, 6)]

1 42

Глава 21 . Генератор списков

21 . 1 О. Подсчет вхождений при использованиии генераторов
Когда мы хотим подсчитать количество элементов в итерируемом объекте, удовлетворя­
ющих некоторому условию, мы можем использовать генератор для получения идиоматиче­
ского синтаксиса:
# Подсчет чисел в списке от 1 до 1 ООО ('range(1 ООО}'), которые четны и содержат цифру 9:
print(sum(
1 for х in range(1 ООО}
if х % 2 == О and
'9' in str(x)

))

# Результат: 95

Основная концепция может быть кратко сформулирована следующим образом.
1. Проведем итерации над элементами из range(1 ООО}.
2. Объединим все необходимые условия if.
3. Используем 1 (единицу) в качестве выражения для возврата 1 для каждого элемента,
который соответствует условиям.
4. Суммируем все 1, чтобы определить количество элементов, которые удовлетворяют ус­
ловиям.
Примечание: здесь мы не собираем все единицы в списке (обратите внимание на отсут­
ствие квадратных скобок), но передаем их непосредственно в функцию sum. Это выражение
генератора, похожее на генератор.

21 . 1 1 . Изменение типов в списке
Количественные данные часто считываются в виде строк, которые перед обработкой не­
обходимо преобразовать к числовым типам. Типы всех элементов списка могут быть прео­
бразованы либо с помощью генератора списка, либо с помощью функции map().
# Преобразование списка строк в целые числа.
items = ["1 ", ''2", "3", "4"]
[int(item) for item in items]
# Результат: [1 , 2, 3, 4]
# Преобразование списка строк во float.
items = ["1 ", "2", "З", "4"]
map(float, items)
# Результат: [1 .0, 2.0, 3.0, 4.0]

21 . 1 2. Вложенные генераторы списков
Вложенные генераторы списков, в отличие от генераторов списков с вложенными ци­
клами, представляют собой генераторы списков внутри генераторов списков. Начальным
выражением может быть любое произвольное выражение, в том числе и другой генератор
списка.
# Генератор списка с вложенным циклом
[х + у для х в [1 , 2, 3] для у в [3, 4, 5]]
#Результат: [4, 5, 6, 5, 6, 7, 6, 7, 8]
# Вложенный генератор списка
[[х + у для х в [1 , 2, 3]] для у в [3, 4, 5]]
#Результат: [[4, 5, 6], [5, 6, 7], [6, 7, 8]]

Второй пример эквивалентен коду



1=
for у in [3, 4, 5]:

21 .1 3. Итерация двух и более списков одновременно внутри генератора списка



1 43

temp =
for х in [1 , 2, 3]:
temp.append(x + у)
l.append(temp)
Одним из примеров использования вложенного генератора является транспонирование
матрицы.
matrix = [[1 ,2,3],
[4,5,6],
[7,8,91]
[[row[i] for row in matrix] for i in range(len(matrix}}]
# [[1 , 4, 7], [2, 5, 8], [3, 6, 91]
Как и в случае с вложенными циклами for, нет ограничений на глубину вложения гене­
раторов.
[[[i + j + k for k in 'cd'] for j in 'аЬ'] fог i in '1 2']
# Резул ьтат: [[['1 ас', '1 ad'], ['1 Ьс', '1 bd']], [['2ас', '2ad'], ['2Ьс', '2bd']]]

21 . 1 З. Итерация двух и более списков одновременно
внутри генератора списка
Для одновременной итерации более двух списков внутри генератора списков можно использовать функцию zip():
»>
>>>
>>>

list_ 1 = [1 , 2, 3 , 4]
list_2 = ['а', 'Ь', 'с', 'd'].
list_3 = ['6', '7', '8', '9']

# Два списка
»> [(i, j) for i, j in zip(list_ 1 , list_2)]
[(1 , 'а'), (2, 'Ь'}, (3, 'с'), (4, 'd'}]
# Три списка
»> [(i, j, k} for i, j, k in zip(list_ 1 , list_2, list_3)]
[(1 , 'а', '6'}, (2, 'Ь', '7'), (3, 'с', '8'}, (4, 'd', '9'}]
# и т. д. ...

Гл а в а 22 . Срез ы списко в
(в ыдел ение ч асте й списко в)
22. 1 . Использование третьего аргумента "шаг"
1st = ['а', 'Ь', 'с', 'd', 'е', 'f', 'g', 'h']

lst[::2]
# Резул ьтат: ['а', 'с', 'е', 'g'].
lst[::3]
# Резул ьтат: ['а', 'd', 'g'].

1 44

Глава 22. Срезы списков (выделение частей списков)

22.2. Выбор подсписка из списка
1st = ['а', 'Ь\ 'с\ 'd\ 'е']

lst[2:4]
# Результат: ['с', 'd'].
lst[2:]
# Результат: ['с', 'd', 'е'].
lst[:4]
# Результат: ['а', 'Ь', 'с', 'd'].

22.З. Реверсирование списка с помощью среза
а = [1 , 2, 3, 4, 5]

# п рохождение по списку в обратном порядке (шаг= -1 )
Ь = а[::-1 ]
# встроен н ый метод реверсирования списка 'а'
a.reverse()
if а = Ь:
print(True)
print(b)
# Результат:
# True
# [5, 4, 3, 2, 1 ]

22.4. Смещение списка с помощью среза
def shift_list(array, s):
'""'Сдвигает элементы списка влево или вправо.
Аrгументы:
array (массив) - список для сдвига
s - величина сдвига списка ('+': сдвиг вправо, '-': сдвиг влево)
Возвращает:
shifted_array - сдвинутый список
# вычислить фактическую величину сдвига (например, 1 1 -> 1 , если длина массива равна 5)
s % = len(array)
# изменить направление сдвига для большей интуитивности
s *= -1
# сдвиг массива со срезом списка
shifted_array = array[s:] + array[:s]
return shifted_array
my_array = [1 , 2, 3, 4, 5]
# отрицательные числа
shift_l ist(my_array, -7)
»> [3, 4, 5, 1 , 2]

1 45

23. 1 . Пример
# отсутствие сдвига на числах, равных размеру массива
shift_list(my_array, 5)
»> [ 1 , 2, 3, 4, 5]

# работает для положительных чисел
shift_list(my_array, 3)
»> [3, 4, 5, 1 , 2]

Глава 23. Метод groupby()
Параметр

Итерируемое
(iteraЫe)

П одробн ости

Любой итерируемый тип данных в Python

Ключ
Функция (критерий), по которой rруппируется итерируемое
В Python метод itertools.groupby() позволяет разработчикам rруппировать значения ите­
рируемоrо класса на основе заданного свойства в другой итерируемый набор значений.

2 3. 1 . Пример

В этом примере мы видим, что происходит при использовании разных типов итерабель­
ных данных.
things = [("animal", "Ьеаг"), ("animal", "duck"), ("plant", "cactus"), ("vehicle", "harley"), \
("vehicle", "speed boat"), ("vehicle", "school bus")]
dic = {}
f = lambda х: х[О]
for key, group in groupby(sorted (things, key=f), f):
d ic[key] = list(group)
dic

Результаты:
{'animal': [('animal', 'bear'),('animal', 'duck')],
'plant': [('plant', 'cactus')],
'vehicle': [('vehicle', 'harley'),
('vehicle', 'speed boat'),
('vehicle', 'school bus')]}

Приведенный ниже пример по сути такой же, как и предыдущий. Разница лишь в том,
что все кортежи заменены на списки.
things = [["animal", "Ьеаг"], ["animal", "duck"], ["vehicle", "harley"], ["plant", "cactus"], \
["vehicle", "speed boat"], ["vehicle", "school bus"]]
dic = {}
f = lambda х: х[О]
for key, group in groupby(sorted (things, key=f), f):
d ic[key] = list(group)
dic

Результаты:
{'animal': [['animal', 'Ьеаг'], ['animal', 'duck']],
'plant': [['plant', 'cactus']],
'vehicle': [['vehicle', 'harley'],
['vehicle', 'speed boat'],
['vehicle', 'school bus']]}

1 46

Глава 23. Метод groupby()

23.2. Пример 2

Данный пример иллюстрирует, как выбирается ключ по умолчанию, если ничего не указано.
с = groupby(['goat', 'dog', 'cow', 1 , 1 , 2, 3, 1 1 , 1 O,('persons', 'man', 'woman')])
dic = {}
for k, v in с:
dic[k] = list(v)
dic

Результаты:
{1 : [1 , 1 ],
2: [2],
3: [3],
('persons', 'man', 'woman'): [('persons', 'man', 'woman')],
'cow': ['cow'],
'dog': ['dog'],
1 О: [1 О],

1 1 : [1 1 ],
'goat': ['goat']}

Обратите внимание, что кортеж в целом считается одним ключом в этом списке.

23.3. Пример 3

Обратите внимание, что в этом примере объекты 'mulato' и 'camel' не отображаются в ре­
зультате. Отображается только последний элемент с указанным ключом. Последний резуль­
тат для с фактически стирает два предыдущих результата. Но затем посмотрите на новую
версию, в которой данные отсортированы первыми по тому же ключу.
list_things = ['goat', 'dog', 'donkey', 'mu lato', 'cow', 'cat',('persons', 'man', 'woman'), \
'wombat', 'mongoose', 'mal loo', 'camel']
с = groupby(list_things, key=lambda х: х[О])
dic = {}
for k, v in с:
dic[k] = list(v)
dic

Результаты:
{'с': ['camel'],
'd': ['dog', 'donkey'],
'g': ['goat'],
'm': ['mongoose', 'mal loo'],
'persons': [('persons', 'man', 'woman')],
'w': ['wombat']}

Отсортированная версия:
list_things = ['goat', 'dog', 'donkey', 'mu lato', 'cow', 'cat',('persons', 'man', 'woman'), \
'wombat', 'mongoose', 'mal loo', 'camel']
sorted_list = sorted(lisLthings, key = lambda х: х[О])
print(sorted_l ist)
print()
с = groupby(sorted_list, key=lambda х: х[О])
dic = {}
for k, v in с:
dic[k] = list(v)
dic

24. 1 . Пример односвязного списка

1 47

Результаты:
['cow', 'cat', 'camel', 'dog', 'donkey', 'goat', 'mulato', 'mongoose', 'malloo',('persons', 'man', 'woman'),
'wombat']
{'с': ['cow', 'cat', 'camel'],
'd': ['dog', 'donkey'],
'g': ['goat'],
'm': ['mulato', 'mongoose', 'malloo'],
'persons': [('persons', 'man', 'woman')],
'w': ['wombat']}

Глава 24. Связные списки
Связный список представляет собой набор узлов, каждый из которых состоит из ссьmки
и значения. Узлы объединяются в последовательность с помощью своих ссылок. Связные
списки могут использоваться для реализации более сложных структур данных, таких как
списки, стеки, очереди и ассоциативные массивы.

24. 1 . Пример ОДНОСВЯЗНОГО списка

В этом примере реализован связный список со многими из тех методов, что имеются во
встроенном объекте списка (list).
class Node:
def _iniL(self, val):
self.data = val
self.next = None
def getData(self):
return self.data
def getNext(self):
return self.next
def setData(self, val):
self.data = val
def setNext(self, val):
self.next = val
class Linked list:
def _iniL(self):
self. head = None
def isEmpty(self):
"""Check if the list is empty "'"' # П роверка, пуст ли список
return self.head is None
def add(self, item):
"""Add the item to the list""" # Добавить элемент в список
new_node = Node(item)
new_node.setNext(self. head)
self. head = new_node
def size(self):

1 48

Глава 24. Связные списки
"'"'

"'

Return the length/size of the l ist "' # Возврат длины/размера списка
count = О
current = self.head
while current is not Nопе:
count += 1
current = current.getNext()
return count
def search(self, item):
"""Search for item in list. lf found, return True. lf not found, return False"""
# Поиск элемента в списке. Если он найден, возвращается True. Если не найден,
возвращается False
current = self.head
found = False
while current is not Nопе and not found:
if current.getData() is item:
found = True
else:
current = current.getNext()
return found
def remove(self, item):
"""Remove item from l ist. lf item is not found in list, raise ValueError"'"'
# Удалить элемент из списка. Если элемент не найден в списке, возникает ошибка ValueError
current = self.head
previous = None
found = False
while current is not None and not found:
if current.getData() is item:
found = True
else:
previous = current
current = current.getNext()
if found:
if previous is None:
self. head = current.getNext()
else:
previous.setNext(current.getNext())
else:
raise ValueError
print 'Value not found.'
def insert(self, position, item):
lnsert item at position specif1ed. lf position specified is
out of bounds, raise lndexError # Вставить элемент в указанную позицию. Если указанная
позиция в ыходит за границы, то возникает ошибка lndexError
if position > self.size() - 1 :
raise lndexError
print "lndex out of bounds."
current = self.head
previous = None
pos = О
if position is О:
self.add(item)
else:
new_node = Node(item)
while pos < position:
pos += 1

24.1 . Пример односвязного списка

1 49

previous = current
current = current.getNext()
previous.setNext(new_node)
new_node.setNext(current)
def index(self, item):
Return the index where item is found.
lf item is not found, return None. # Возвращает индекс, по которому найден элемент. Если
элемент не найден, возвращается None
current = self.head
pos = О
found = False
while current is not None and not found:
if current.getData() is item:
found = True
else:
current = current.getNext()
pos += 1
if found:
pass
else:
pos = None
return pos
def pop(self, position = None):
lf по argument is provided, return and remove the item at the head.
lf position is provided, return and remove the item at that position.
lf index is out of bounds, raise lndexError
# Если аргумент не указан, то возвращается и удаляется элемент, находящийся в головной
части. Если указан аргумент position, то возвращается и удаляется элемент в этой позиции.
Если индекс выходит за границы, то возникает ошибка lndexError
if position > self.size():
print 'lndex out of bounds'
raise lndexError
current = self.head
if position is None:
геt = current.getData()
self.head = current.getNext()
else:
pos = О
previous = None
while pos < position:
previous = current
current = current.getNext()
pos += 1
геt = current.getData()
previous.setNext(current.getNext())
print ret
return ret
def append(self, item):
"""Append item to the end of the l ist""" # Добавить элемент в конец списка
current = self.head
previous = None
pos = О
length = self.size()

1 50

Глава 25. Узел связного списка
while pos < length:
previous = current
current = current.getNext()
pos += 1
new_node = Node(item)
if previous is None:
new_node.setNext(current)
self.head = new_node
else:
previous.setNext(new_node)

def printlist(self):
"'
"'Print the list"'"' # Вывод списка
current = self.head
while current is not None:
print current.getData()
current = current.getNext()
Функции использования аналогичны функциям встроенного списка.
11 = Linkedlist()
II.add('I')
II.add('H')
l l . insert(1 ,'e')
II.append('I')
II.append('o')
II.printlist()

н

е

1
1

о

Глава 25. Узел связного списка
25. 1 . Написание простого узла связного списка на языке Python
Связный список - это либо:
• пустой список, представленный значением None, или
• узел, содержащий объект-носитель (cargo object) и ссылку на связный список.
#! /usr/Ьin/env python
class Node:
def _init_(self, cargo=None, next=None):
self.car = cargo
self.cdr = next
def _str_(self):
return str(self.car)
def d isplay(lst):
if 1st:
w("%s " % 1st)
d isplay(lst.cdr)
else:
w("nil\n")

1 51

26. 1 . Основное использование фил ьтра

Глава 26. Фильтр
П ара м етр

Подроб ности

Функция

вызываемая функция, определяющая или условие, или значение None, исполь­
зующая затем функцию тождества для фильтрации (только позиционно)

Итерируемое

итерируемый объект, который будет отфильтрован (только позиционно)

(iteraЫe)

26. 1 . Основное использование фильтра
Фильтр отбрасывает элементы последовательности на основе некоторых критериев:
names = ['Fred', 'Wilma', 'Barney']
def long_name(name):
return len(name) > 5
Python 2.х 2.0:
filter(long_name, names)
# Результат: ['Barney']
[name for name in names if len(name) > 5] # эквивалентн ы й генератор списка
# Резул ьтат: ['Barney']
from itertools import if1 lter
# как генератор (аналогично встроенному фил ьтру Python З.х)
if1 lter(long_name, names)
# Результат:
# эквивалентно фильтру со списками
list(ifi lter(long_name, names))
# Результат: ['Barney']
(name for name in names if len(name) > 5) # эквивалентное генераторное вы ражение
# Результат:
Python 2.х 2. 6
# Помимо опций для старых версий Python 2.х существует функция future_builtin:
from future_bui ltins import f1 lter
# идентична itertools.if1 lter
f1 lter(long_name, names)
# Результат:
Python З.х 3.0
filter(long_name, names)
# возвращает генератор
# Результат:
# п риведение к списку
list(f1 lter(long_name, names))
# Результат: ['Barney']
(name for name in names if len(name) > 5) # equivalent generator expression
# Результат:

26.2. Фильтр без функции
Если параметр функции - None, тогда будет использоваться функция тождества:

list(f1 lt er(None, [1 , О, 2, D, ", 'а']))
# Результат: [1 , 2, 'а']

# отбрасы вает О,

D and "

1 52

Глава 26. Фильтр

Версия Python 2.х ;:,: 2.0.1:



[i for i in [1 , О, 2, . ", 'а'] if i] # эквивалентный генератор списка
Версия Python 3.х ;:,: 3.0.0:



(i for i in [1 , О, 2, . ", 'а'] if i) # эквивалентное генераторное выражение

26.З. Фильтр для проверки на "короткое замыкание"
(вычисления по короткой схеме)
Функции f1 lter (Python 3.х) и if1 lter (Python 2.х) возвращают генератор, таким образом они
могут быть очень удобны при создании тестов на "короткое замыкание" типа ог или and:
Версия Python 2.х ;:,: 2.0.1:
# не рекомендуется для реального использования, но позволяет сократить пример:
from itertools import if1 lter as fi lter
Версия Python 2.х ;:,: 2. 6.1:
from future_builtins import f1 lter
Чтобы найти первый элемент, который меньше 100:
car_shop = [('Toyota', 1 ООО), ('rectangular tire', 80), ('Porsche', 5000)]
def f1nd_something_smaller_than(name_value_tuple):
print('Check {О}, {1 }$'.format(*name_value_tuple)
return name_value_tuple[1 ] < 1 00
next(f1 lter(find_something_smal ler_than, car_shop))
# Вывод: Check Toyota, 1 000$
#
Check rectangular tire, 80$
# Результат: ('rectangular tire', 80)
Функция next дает следующий (в данном случае первый) элемент и, следовательно, явля­
ется причиной "короткого замыкания".

26.4. Дополняющая функция: filterfalse, ifilterfalse
В модуле itertools есть дополняющая функция для fi lter:
Версия Python 2.х ;:,: 2.0.1:
# не рекомендуется для реального использования, но позволяет сохранить пример и для
Python 2.х, и для Python 3.х
from itertools import if1lterfalse as f1 lterfalse
Версия Python 3.х ;:,: 3.0.0:
from itertools import f1 lterfalse
которая работает точно так же, как f1 lter у генератора, но сохраняет только элементы, которые имеют ложное значение (False):
# Использование без функции (None):
list(fi lterfalse(None, [1 , О, 2, . ", 'а'])) # отбрасывает 1 , 2, 'а'
# Результат: [О,

□. "]



# Использование с функцией
names = ['Fred', 'Wilma', 'Вагпеу']
def long_name(name):
return len(name) > 5

27. 1 . Самые большие и самые маленькие предметы в коллекции

1 53

l ist(f1 lterfalse(long_name, names))
# Результат: ['Fred', 'Wilma']
# "Короткое замы кание":
car_shop = [('Тоуоtа', 1 ООО), ('rectangular tire', 80), ('Porsche', 5000)]
def find_something_smal ler_than(name_value_tuple):
print('Check {О}, {1 }$'.format(*name_value_tuple)
return name_value_tuple[1 ] < 1 00
next(f1 lterfalse(f1nd_something_smal ler_than, car_shop))
# В ы вести: Check Toyota, 1 000$
# Результат: ('Тоуоtа', 1 ООО)
# Испол ьзование эквивалентного генератора:
car_shop = [('Toyota', 1 ООО), ('rectangular tire', 80), ('Porsche', 5000)]
generator = (саг fог саг in car_shop if not car[1 ] < 1 00)
next(generator)

Глава 2 7 . Модуль "Heapq"
27. 1 . Самые большие и самые маленькие предметы в коллекции

Для нахождения наибольших элементов в коллекции в модуле heapq есть функция
nlargest, которой передаются два аргумента: первый - количество элементов, которые мы хо­

тим получить, второй - имя коллекции:
import heapq

numbers = [1 , 4, 2, 1 00, 20, 50, 32, 200, 1 50, 8]
print(heapq.nlargest(4, numbers)) # [200, 1 50, 1 00, 50]

Аналогично для нахождения наименьших элементов в коллекции мы используем функ­
цию nsmallest:
print(heapq.nsmallest(4, numbers)) # [1 , 2, 4, 8]

Для сложных структур данных функции nlargest и nsmallest принимают дополнительный
аргумент (ключевой параметр). В следующем примере показано использование свойства
age для получения самых старых и самых молодых людей из словаря people:
people = [
{'f1rstname': 'John', 'lastname': 'Doe', 'age': 30},
{'f1rstname': 'Jапе', 'lastname': 'Doe', 'age': 25},
{'f1rstname': 'Janie', 'lastname': 'Doe', 'age': 1 О},
{'f1rstname': 'Jane', 'lastname': 'Roe', 'age': 22},
{'f1rstname': 'Johnny', 'lastname': 'Doe', 'age': 1 2},
{'f1rstname': 'John', 'lastname': 'Roe', 'age': 45}
oldest = heapq.nlargest(2, people, key=lambda s: s['age'])
print(oldest)
# Результат: [{'f1rstname': 'John', 'age': 45, 'lastname': 'Roe'}, {'f1rstname': 'John', 'age': 30, 'lastname':
'Doe'}]
youngest = heapq.nsmallest(2, people, key=lambda s: s['age'])
print(youngest)
# Результат: [{'f1rstname': 'Janie', 'age': 1 О, 'lastname': 'Doe'}, {'f1rstname': 'Johnny', 'age': 1 2, 'lastname': 'Doe'}]

1 54

Глава 28. Кортеж (tuple)

27 .2. Наименьший элемент в коллекции

Самым интересным свойством heap является то, что его наименьший элемент всегда является первым элементом: heap[0].
import heapq
numbers = [1 О, 4, 2, 1 00, 20, 50, 32, 200, 1 50, 8]
heapq.heapify(numbers)
print(numbers)
# Результат: [2, 4, 1 О, 1 00, 8, 50, 32, 200, 1 50, 20]
heapq. heappop(numbers) # 2
print(numbers)
# Результат: [4, 8, 1 О, 1 00, 20, 50, 32, 200, 1 50]
heapq.heappop(numbers) # 4
print(numbers)
# Результат: [8, 20, 1 О, 1 00, 1 50, 50, 32, 200]

Глава 28. Кортеж (tuple)
Кортеж - это неизменяемый список значений. Кортежи являются одним из самых про­
стых и распространенных типов коллекций в Python и могут быть созданы с помощью опе­
ратора запятой (value = 1 , 2, 3).

28. 1 . Кортеж

Синтаксически кортеж представляет собой список значений, разделенных запятыми:

t = 'а', 'Ь', 'с', 'd', 'е'

Хотя это и необязательно, обычно принято заключать кортеж в круглые скобки:

t =('а', 'Ь', 'с', 'd', 'е')

Создание пустого кортежа при помощи круглых скобок:

о

tO =
type(tO)

#

Чтобы создать кортеж с одним элементом, необходимо добавить заключительную запя­
тую:
t1 = 'а',
type(t1 )

#

Обратите внимание, что одиночное значение в скобках не является кортежем:
t2 =('а')
type(t2)

#

Для создания одноэлементного кортежа необходимо использовать запятую в конце:
t2 =('а',)
type(t2)

#

Обратите внимание, что для одноэлементных кортежей рекомендуется (см. в руковод­
стве РЕР8 про использование закрывающих запятых) использовать круглые скобки. Кроме
того, после запятой не должно быть пробелов (см. РЕР8 о пробельных символах).

28.2. Кортежи являются неизменяемыми

1 55

# нотация одобрена РЕРВ
t2 =('а',)
t2 = 'а',
# эта нотация не рекомендуется РЕРВ
# эта нотация не рекомендуется РЕРВ
t2 =('а', )
Другим способом создания кортежа является встроенная функция tuple.
t = tuple('lupins')
#('1', 'u', 'р', 'i', 'п', 's')
print(t)
t = tuple(range(3))
# (О, 1, 2)
print(t)

Эти примеры основаны на материалах из книги "Think Python" Аллена В. Дауни.

28.2. Кортежи являются неизменяемыми

Одно из основных различий между списками (l ist) и кортежами (tuple) в Python заключа­
ется в том, что кортежи являются неизменяемыми, то есть после инициализации кортежа
в него нельзя добавлять или изменять элементы. Например:
»> t = (1 , 4, 9)
»> t[O] = 2
Traceback (most recent call last):
File "", line 1, in
TypeError: 'tuple' object does not support item assignment

Аналогично кортежи не имеют методов .append и .extend, как у списков. Использование
оператора += возможно, но при этом изменяется привязка переменной, а не сам кортеж:
>» t = (1 , 2)
>>> q = t
»> t += (3, 4)
>>> t
(1 , 2, 3, 4) #output
>>> q
(1 , 2) #output

Будьте осторожны при размещении изменяемых объектов, таких как списки, внутри кортежей. Это может привести к очень запутанным результатам при их изменении. Например:
»> t = (1, 2, 3, [1, 2, 3])
(1, 2, 3, [1, 2, 3]) #output
»> t[3] += [4, 5]

Это вызовет ошибку и повлечет изменение содержимого списка внутри кортежа:
TypeError: 'tuple' object does not support item assignment
>>> t
(1, 2, 3, [1, 2, 3, 4, 5])

Вы можете использовать оператор += для "добавления" в кортеж - это создает новый кор­
теж с новым элементом, который вы "добавили", и присваивается его текущей переменной;
старый кортеж не изменяется, но заменяется!
Это позволяет избежать преобразования в список и из списка, но работает медленно и яв­
ляется плохой практикой, особенно если вы собираетесь "добавлять" несколько раз.

28.З. Упаковка и распаковка кортежей

Кортежи в Python - это значения, разделенные запятыми. Окружающие круглые скобки
для ввода кортежей необязательны, поэтому два примера
а = 1 , 2, 3
# а - кортеж (1 , 2, 3)
и
а = (1 , 2, 3)
# а - кортеж (1 , 2, 3)
эквивалентны. Присваивание а = 1 , 2, 3 также называется упаковкой, поскольку оно объе­
диняет (упаковывает) значения в кортеж.

1 56

Глава 28. Кортеж (tuple)

Обратите внимание, что кортеж с одним значением также является кортежем. Чтобы
указать Python, что переменная является кортежем, а не одним значением, можно исполь­
зовать запятую в конце:
а= 1
а = 1,

# а имеет значение 1
# а - это кортеж (1 ,)

Запятая нужна также, если вы используете скобки
а = (1 ,) # а - это кортеж (1 ,)
а = (1 ) # а имеет значение 1 и не является кортежем

Для распаковки значений из кортежа и выполнения множественного присваивания используйте
# распаковка или множественное присваивание
х, у, z = (1 , 2, 3)
# х == 1
# у == 2
# z == 3

Символ _ может быть использован в качестве имени одноразовой ("выбрасываемой") переменной, если нужны только некоторые элементы кортежа, выступая в роли заполнителя:
а = 1 , 2, 3, 4
_, х, у, _ = а
# х == 2
# у == 3

Одноэлементные кортежи:
х, 1 , # х - это означение 1
=

х

=

1,

# х - это кортеж (1 ,)

В Python 3 целевая переменная с префиксом * может быть использована в качестве универсальной переменной (catch-all variaЬle):
first, *more, last = (1 , 2, 3, 4, 5)
#f1rst == 1
#more == [2, 3, 4]
#last == 5

28.4. Встроенные функции кортежей

Кортежи поддерживают следующие встроенные функции:

Сравнение
Если элементы имеют одинаковый тип, Python выполняет сравнение и возвращает результат. Если элементы разного типа, то проверяется, являются ли они числами.
• Если числа, то выполняется сравнение.
• Если один из элементов является числом, то возвращается другой элемент.
• В противном случае типы сортируются в алфавитном порядке.
Если мы дошли до конца одного из списков, то более длинный список "больше". Если оба
списка одинаковы, то возвращается О.
tuple1 = ('а', 'Ь', 'с', 'd', 'е')
tuple2 = ('1 ','2','3')
tuple3 = ('а', 'Ь', 'с', 'd', 'е')
cmp(tuple1 , tuple2)

#1

cmp(tuple2, tuple1 )

# -1

cmp(tuple1 , tuple3)



28.5. Кортежи являются поэлементно хешируемыми и сравниваем ыми

1 57

Длm1а кортежа

Функция len возвращает общую длину кортежа

len(tuple1 )
#5

Максимальное значение кортежа

Функция max возвращает элемент кортежа с максимальным значением

max(tuple1 )
max(tuple2)

#'е'
# '3'

Минимальное значение кортежа

Функция min возвращает элемент из кортежа с минимальным значением

min(tuple1 )
min(tuple2)

# 'а'
# '1 '

Преобразование сШiска в кортеж

Встроенная функция tuple преобразует список в кортеж
l ist = [1 ,2,3,4,5]
tuple(list)
»>Out: (1 , 2, 3, 4, 5)

Конкатенация кортежей

Используйте + для конкатенации двух кортежей
tuple1 + tuple2
>>>Out:('a', 'Ь', 'с', 'd', 'е', '1 ', '2\ 'З')

28.5. Кортежи являются поэлементно хешируемыми
и сравниваемыми
hash( (1 , 2) )
# ok
hash(( □, {"hello"}) # не подходит, поскольку списки и множества не хешируемы

Таким образом, кортеж можно поместить внутри набора (set) или в качестве ключа в сло­
варь (dict) только тогда, когда это можно сделать с каждым из его элементов.
{ (1 , 2) }
{(□, {"hello"}) )

#ок
# не ПОДХОДИТ

28.6. Индексирование кортежей

х = (1 , 2, 3)
х[О] # 1
х[1 ] # 2
х[2] # 3
х[3] # lndexError: индекс кортежа выходит за пределы диапазона

Индексирование с отрицательными числами будет начинаться с последнего элемента
как -1:
х[-1 ]
х[-2]
х[-3]
х[-4]

#3
#2
#1
# lndexError: индекс кортежа выходит за предел ы диапазона

Индексирование диапазона элементов:
print(x[:-1 ])
print(x[-1 :])
print(x[1 :3])

# (1 , 2)
# (3,)
# (2, 3)

1 58

Глава 29. Основы ввода и вы вода данных

28. 7. Реверсирование элементов
Обратим элементы в кортеже:
colors = "геd", "gгееп", "Ыuе"
rev = colors[::-1 ]
# rev: ("Ы uе", "gгееп", "геd")
colors = rev
# colors: ("Ыuе", "green", "геd")
Также можно использовать функцию reversed (дает итерируемый объект, который преобразуется в кортеж):
rev = tuple(reversed(colors))
# rev: ("Ы uе", "green", "red")
colors = rev
# colors: ("Ыuе", "gгееп", "геd")

Гла ва 2 9 . О сно в ы ввода и в ы вода
данны х
29. 1 . Использование функции вывода "print"

Версия Python З.х � 3.0:

В Python 3 функциональность вывода на экран представлена в виде функции:

print('This string will Ье displayed in the output")
# This string wil l Ье displayed in the output
print(''You сап print \n escape characters too.")
# You сап print escape characters too.

Версия Python 2.х � 2.3:

В Python 2 print изначально был оператором, как показано ниже:

print "This string wil l Ье displayed in the output"
# This string wil l Ье displayed in the output
print ''You сап print \n escape characters too."
# You сап print escape characters too.

Примечание: использование from _future_ import print_function в Python 2 позволит поль­
зователям использовать функцию printO так же, как и в коде Python 3. Это доступно только
для версии Python 2.6 и выше.

29.2. Ввод из файла
Входные данные также могут быть считаны из файлов. Файлы можно открывать с помо­
щью встроенной функции ореп. Использование синтаксиса with as (назы­
ваемого "менеджером контекста") позволяет очень просто использовать функцию ореп и по­
лучать доступ к файлу:
with open('somef1le.txt', 'г') as fi leobj:
# напишите здесь код, используя f1 leobj
Это гарантирует, что при выходе из блока выполнение кода автоматически закроет файл.
Файлы могут быть открыты в разных режимах. В приведенном выше примере файл открывается только для чтения. Чтобы открыть существующий файл только для чтения, ис­
пользуйте г. Если вы хотите открыть файл для двоичного чтения, используйте гЬ. Для добав-

29.2. Ввод из файла

1 59

ления данных в существующий файл используйте а. Используйте w, чтобы создать файл или
перезаписать существующий файл с тем же именем. С помощью г+ можно открыть файл как
для чтения, так и для записи. Первым аргументом ореп() является имя файла, вторым - ре­
жим. Если параметр mode оставить пустым, то по умолчанию он будет равен г.
# создадим файл примера:
with open('shoppinglist.txt', 'w') as f1leobj:
f1leobj.write('tomato\npasta\ngarlic')
with open('shoppinglist.txt', 'г') as f1leobj:
# тот метод создает список, в котором каждая строка
# файла является элементом списка
lines = fileobj.readlines()
print(lines)
# ['tomato\n', 'pasta\n', 'garlic']
with open('shoppinglist.txt', 'г') as f1leobj:
# здесь мы считываем все содержимое в одну строку:
content = fileobj.read()
# получаем список строк, как и в предыдущем примере:
lines = content.split('\n')
print(lines)
# ['tomato', 'pasta', 'garlic']

Если размер файла небольшой, то можно безопасно считать все содержимое файла в па­
мять. Если же файл очень большой, то часто лучше считывать построчно или по частям,
а входные данные обрабатывать в том же цикле. Для этого:
with open('shoppinglist.txt', 'г') as f1leobj:
# this method reads line Ьу line:
lines =
fог line in f1leobj:
lines.append(line.strip())



При чтении файлов учитывайте характерные для операционной системы символы пере­
вода строки. Хотя fог line in f1leobj автоматически удаляет их, всегда безопасно вызывать strip(),
как показано выше.
Открытые файлы (f1leobj в приведенных выше примерах) всегда указывают на определен­
ное место в файле. Когда файл открыт впервые, дескриптор (указатель) указывает на самое
начало файла, на позицию о. Дескриптор файла может отображать текущее положение при
помощи команды tel1:
fileobj = open('shoppinglist.txt', 'г')
pos = f1leobj.tell()
print('We аге at %u.' % pos) # We аге at О.

После прочтения всего содержимого позиция будет указана в конце файла:
content = fileobj.read()
end = f1leobj.tell()
print('This f1le was %u characters long.' % end)
# This f1le was 22 characters long.
fileobj.close()

Позиция может быть установлена в любое необходимое положение:
fileobj = open('shoppinglist.txt', 'г')
fileobj.seek(7)
pos = f1leobj.tell()
print('We аге at character #%u.' % pos)

1 60

Глава 29. Основы ввода и вы вода данных

Кроме того, во время данного вызова можно прочитать любую длину из содержимого
файла во время данного вызова. Для этого необходимо передать аргумент для read(). Когда
read() вызывается без аргументов, будет прочитано все содержимое файла до конца. Если пе­
редать аргумент, то будет прочитано указанное количество байтов или символов, в зависи­
мости от режима (rb и r соответственно):
# считы вает следующие 4 сим вола
# начиная с текущей позиции
next4 = fileobj.read(4)
# что у нас есть?
print(next4) # 'abcd'
# где мы сейчас н аходимся?
pos = fi leobj.tel l()
print('We are at %u.' % pos) # Мы находимся на позиции 1 1 , так как были на 7, и прочитали 4
символа
fi leobj.close()

Продемонстрируем разницу между символами и байтами:
with open('shopping list.txt', 'г') as f1 leobj:
print(type(f1leobj.read())) #
with open('shopping list.txt', 'rb') as f1 leobj:
print(type(f1 leobj.read())) #

29.З. Чтение из stdin

Программы на Python могут читать из конвейеров Unix. Приведем простой пример чте­
ния из std in:
import sys
for l ine in sys.stdin:
print(line)

Имейте в виду, что sys.stdin представляет собой поток. Это означает, что цикл for завер­
шится только после завершения потока.
Теперь вы можете направить вывод другой программы в вашу программу на Python сле­
дующим образом:
$ cat myf1le I python myprogram.py
В этом примере cat myf1 le может быть любой командой Unix, которая выводит в stdout.
В качестве альтернативы можно использовать модуль f1leinput:
import f1 leinput
for l ine in f1 leinput.input():
process(l ine)

29.4. Использование функций input() и raw_input()

Версия Python 2.х ;:,: 2.3:
Функция raw_input будет ждать, пока пользователь введет текст, а затем вернет результат
в виде строки.
foo = raw_input("Пoмecтитe сюда сообщение, запрашивающее ввод у пользователя")
В приведенном выше примере foo

будет хранить все введенные пользователем данные.

29.5. Функция запроса числа у пользователя

1 61

Версия Python 3.х � 3.0:
Функция input будет ждать, пока пользователь введет текст, а затем вернет результат в
виде строки.
foo = iпрut("Поместите сюда сообщение, запрашивающее ввод данных у пользователя")

В приведенном выше примере foo будет хранить все введенные пользователем данные.

29.5. Функция запроса числа у пользователя
def input_number(msg, err_msg=None):
while True:
try:
return float(raw_input(msg))
except ValueError:
if err_msg is not None:
print(err_msg)
def input_number(msg, err_msg=None):
while True:
tгу:
return float(input(msg))
except ValueError:
if err_msg is not None:
print(err_msg)

Чтобы использовать эту функцию:
user_number = iпрut_пumЬег("введите число:", "это не число!")

или, если не нужно сообщение об ошибке:

user_number = iпрut_пumЬег("введите число:")

29.6. Печать строки без новой строки в конце

Версия Python 2.х � 2.3:
В Python 2.х, чтобы продолжить строку с помощью print, завершите оператор print запятой.
При этом автоматически добавляется пробел.
print "Hello,", print "World!" # Hel lo, World !

Версия Python 3.х � 3.0:
В Python З.х функция print имеет необязательный концевой параметр - то, что она выводит в конце заданной строки. По умолчанию это символ новой строки:
print("Hel lo, ", end="\n")
print("World!")
# Hel lo, # World !

Но можно передать и другие символы:
print("Hel lo, ", end="")
print("World!")
# Hel lo, World!
print("Hel lo, ", end="")
print("World!")
# Hel lo, World!
print("Hel lo, ", end="BREAK")
print("World!")
# Hel lo, BREAKWorld!

1 62

Глава 30. Ввод/вы вод файлов и папок

Если требуется больший контроль над выводом, можно использовать метод sys.stdout.

write:

import sys
sys.stdout.write("Hel lo, ")
sys.stdout.write("World !")
# Hello, World!

Глава 30. Ввод/вывод файлов и папок
Параметр

П одро бн о сти

filename
(имя файла)

путь к вашему файлу или, если файл находится в рабочем катало­
rе, имя файла вашеrо файла

access_mode
(режим доступа)

строковое значение, определяющее способ открытия файла

buffering
(буферизация)

целочисленное значение, используемое для необязательной буфе­
ризации строк

Коrда речь идет о хранении, чтении или передаче данных, работа с файлами операцион­
ной системы одновременно необходима и леrка для Python. В отличие от друrих языков, Python
упрощает работу с файлами, требуя только команды для открытия, чтения/записи и закрытия.
Далее объясняется, как Python может взаимодействовать с файлами в операционной системе.

3 0 . 1 . Режимы работы с файлами

Существует несколько режимов, в которых можно открыть файл, определяемых параме­
тром mode. К ним относятся:
• 'г' - режим чтения. Устанавливается по умолчанию. Позволяет только читать file,
но не изменять его. При использовании этого режима файл должен существовать.
• 'w' - режим записи. Создает новый файл, если тот не существует, в противном
случае сотрет файл и разрешит запись в него.
• 'а' - режим добавления. В этом режиме данные записываются в конец файла.
При этом файл не стирается, и для этого режима он должен существовать.

'гЬ' - режим чтения в двоичном формате. Он аналогичен r, за исключением
того, что чтение принудительно осуществляется в двоичном режиме. Также
выбирается по умолчанию.

'г+' - режим чтения и записи одновременно. Позволяет одновременно читать
и записывать в файлы без использования г и w.
• 'гЬ+' - режим чтения и записи в двоичном формате. То же самое, что и г+, за
исключением того, что данные передаются в двоичном виде.

'wb' - режим записи в двоичном формате. То же, что и w, за исключением того, что
данные передаются в двоичном виде.
• 'w+' - режим записи и чтения. То же самое, что и г+, но если файл не существует,
то создается новый. В противном случае файл перезаписывается.

'wb+' - режим записи и чтения в двоичном режиме. То же, что и w+, но данные
передаются в двоичном виде.
• 'аЬ' - добавление в двоичном режиме. Аналогично а, за исключением того, что
данные представлены в двоичном виде.

30.1 . Режи м ы работы с файла м и



1 63

'а+' - режим добавления и чтения. Аналогичен w+, поскольку создает новый файл,
если тот не существует. В противном случае указатель переходит в конец файла,
если он существует.
'аЬ+' - режим добавления и чтения в двоичном формате. То же, что и а+, за
исключением того, что данные передаются в двоичном виде.

with open(f1lename, 'r') as f:
f.read()
with open(f1lename, 'w') as f:
f.write(f1ledata)
with open(f1lename, 'а') as f:
f. write('\\n' + newdata)

Чтение
Запись
Создает файл
Стирает файл
Начальное
положение

г

г+






х
х
х

Начало

w

w+

а

а+

х
х

















Начало

Начало

Начало

Конец

Конец

х

х
х

х

В Python З добавлен новый режим создания эксклюзивов (exclusive creation), чтобы слу­
чайно не усечь или не перезаписать существующий файл.
• 'х' - открыть для эксклюзивного создания; если файл уже существует, будет
выдана ошибка FileExistsError.
• 'хЬ' - открыть для записи в режиме эксклюзивного создания в двоичном формате.
То же, что и х, только данные в двоичном формате.
• 'х+' - режим чтения и записи. Аналогичен w+, поскольку создаст новый file, если
file не существует. В противном случае будет выдана ошибка FileExistsError.
• 'хЬ+' - режим записи и чтения. То же самое, что и х+, но данные в двоичном виде.

Чтение
Запись
Создает файл
Стирает файл
Начальное положение

х

х+








Начало

Начало

х
х

Это позволяет писать открытый код в более "питоновском" стиле:
Версия Python 3.х � 3.3:
try:
with open("fname", "г") as fout:
# Работа с открыты м файлом
except FileExistsError:
# Обработка ошибок происходит здесь

х

1 64

Глава 30. Ввод/вы вод файлов и папок

Для Python 2:

Версия Python 2.х ::с: 2.0:
import os.path
if os.path.isf1 le(fname):
with open("fname", "w") as fout:
# Работа с открытым файлом
else:
# Обработка ошибок происходит здесь

30.2. Построчное чтение file

Простейший способ итерации по строке файла:

with open('myfile.txt', 'г') as fp:
for line in fp:
print(line)

Метод readline() позволяет более детально управлять построчной итерацией. Пример
ниже эквивалентен приведенному выше:
with open('myfile.txt', 'г') as fp:
while True:
cur_line = fp.read line()
# Если результатом является пустая строка
if cur_line == ":
# Мы достигли конца файла
break
print(cur_line)

Итератирование при помощи циклов for и readline () вместе считается плохой практикой.
Чаще метод readlines() используется для хранения итерируемой коллекции строк файла:
with open("myf1le.txt", "г") as fp:
lines = fp.readlines()
for i in range(len(lines)):
print("Line " + str(i) + ": " + line)

Это выведет на экран следующее:
Line О: hello
Line 1: world

30.З. Итерация файлов (рекурсивно)

Для итерации всех файлов, включая вложенные каталоги, используйте метод os.walk:

import os
for root, folders, files in os.walk(root_dir):
for fi lename in f1les:
print root, f1 lename

В параметре root_dir можно указать "." для запуска из текущего каталога или любой дру­
гой стартовый путь.
Версия Python З.х ::с: 3.5:

Если вы также хотите получить информацию о файле, вы можете использовать более эф­
фективный метод os.scandir следующим образом:
for entry in os.scandir(path):
if not entry.name.startswith('.') and entry.is_f1le():
print(entry.name)

30.4. Получение полного содержимого файла

1 65

30.4. Получение полного содержимого файла

Предпочтительным методом ввода-вывода файлов является использование ключевого
слова with. Это гарантирует, что дескриптор файла будет закрыт после завершения чтения
или записи.
with open('myf1 le.txt') as in_file:
content = in_fi le.read()
print(content)

или, чтобы закрыть файл вручную, можно отказаться от использования with и просто вызвать close:
in_f1le = open('myf1 le.txt', 'г')
content = in_fi le.read()
print(content)
in_f1 le.close()

Следует помнить, что без использования инструкции with вы можете случайно оста­
вить открытым файл в случае возникновения непредвиденного исключения, как в приме­
ре ниже:
in_f1 le = open('myf1 le.txt', 'г')
raise Exception("oops")
in_f1le.close() # Это никогда не будет вызвано

30.5. Запись в файл
with open('myf1le.txt', 'w') as f:
f. write("Line 1 ")
f.write("Line 2")
f.write("Line 3")
f.write("Line 4")

Если вы откроете myf1 le.txt, то вы увидите, что его содержимое имеет следующий вид:
Line 1Line 2Line ЗLine 4

Python не добавляет разрывы строк автоматически, это нужно сделать вручную:
with open('myf1le.txt', 'w') as f:
f. write("Line 1 \n")
f.write("Line 2\п")
f.write("Line 3\п")
f.write("Line 4\n")
Line 1
Line 2
Line 3
Line 4

Не используйте метод os. l inesep в качестве ограничителя строк при записи файлов, от­
крытых в текстовом режиме (по умолчанию); вместо этого используйте \п. Если необходимо
указать кодировку, достаточно добавить параметр encoding в функцию open:
with open('my_f1 le.txt', 'w', encoding='utf-8') as f:
f. write('utf-8 text')

Также можно использовать для записи в файл оператор print. Механика этого процесса от­
личается в Python 2 и Python 3, но концепция одна и та же: вы можете взять вывод, который
бьш бы выведен на экран, и отправить его в файл.

1 66

Глава 30. Ввод/вы вод файлов и папок

Версия Python 3.х ;:,: 3.0:
with open('fred.txt', 'w') as outf1le:
s = "l'm Not Dead Yet!"
print(s)
# запись в stdout
print(s, f1le = outf1le)
# запись в outf1le
# Примеч ание : возможно указать параметр file и выводить на экран,
# убедившись, что файл завершается значением None либо напрямую, либо через переменную
myf1le = None
# запись в stdout
print(s, f1le = myf1le)
# запись в stdout
print(s, f1le = None)

В Python 2 вы бы сделали что-то вроде:
Версия Python 2.х ;:,: 2.0:
outfile = open('fred.txt', 'w')
s = "l'm Not Dead Yet!"
print s
# запись в stdout
print » outf1le, s
# запись в outf1le
В отличие от функции записи функция print автоматически добавляет разрывы строк.

30.6. Проверка наличия файла или пути

Примените стиль кодирования EAFP и используйте метод try для открытия:

import еггпо
try:
with open(path) as f:
# Файл существует
except IOError as е:
# Вызвать исключение, если не ENOENT (нет такого файла или каталога)
if е.еггпо != errno.ENOENT:
raise
# нет такого файла или каталога

Это также позволит избежать т. н. "состояния гонки", если другой процесс удаляет файл
между его проверкой и использованием. Это состояние гонки может произойти в следую­
щих случаях:
• Использование модуля os:
import os
os.path.isfile('/path/to/some/f1le.txt')

Версия Python 3.х ;:,: 3.4:
• Использование pathlib:
import pathlib
path = pathlib. Path('/path/to/some/f1le.txt')
if path.is_file():

Чтобы проверить, существует данный путь или нет, вы можете выполнить описанную
выше процедуру EAFP или проверить путь явно:
import os
path = "/home/myFiles/directory1 "
if os.path.exists(path):
# Выпол нить что-либо

30.7. Произвольны й доступ к файлам с помощью mmap

1 67

30. 7. Произвольный доступ к файлам
с помощью mmap
Использование модуля mmap позволяет пользователю произвольно обращаться к ме­
стам в файле путем сопоставления (мэппинга) в памяти. Это альтернатива использованию
обычных операций с файлами.
import mmap
with open('fi lename.ext', 'г') as fd:
# О: отображение всего файла
mm = mmap.mmap(fd .f1 leno(), О)
# вы вести сим вол ы с индексами с 5 по 1 О
print mm[S:1 О]
# вы вести строку, начинающуюся с текущей позиции mm
print mm.readline()
# записать символ в 5-й индекс
mm[S] = 'а'
# вернуть позицию mm к началу файла
mm.seek(O)
# закрыть объект mmap
mm.close()

30.8. Замена текста в файле
import fi leinput
replacements = {'Search 1 ': 'Replace1 ',
'Search2': 'Replace2'}
for line in f1 leinput.input('f1 lename.txt', inplace = True):
for search_for in replacements:
replace_with = replacements[search_for]
line = line.replace(search_for, replace_with)
pri nt(I i пе, end =")

30.9. Проверка того, что файл пуст
»>
»>

import os
os.stat(path_to_f1 le).st_size

==

О

или же
import os
»> os.path.getsize(path_to_f1le) > О

»>

Тем не менее оба способа будут генерировать исключение, если файл не существует. Что­
бы избежать такой ошибки, сделайте следующее:
import os
def is_empty_fi le(fpath):
return os.path.isf1 le(fpath) and os.path.getsize(fpath) > О
который вернет логическое значение bool.

1 68

Глава 31 . Модуль os.path

30. 1 О. Чтение файла в диапазоне строк

Предположим, что вы хотите выполнить итерацию только междУ некоторыми конкрет­
ными строками файла. Для этого вы можете использовать модуль itertools.
import itertools
with open('myfi le.tx1', 'г') as f:
for line in itertools.islice(f, 1 2, 30) :
# сделать здесь что-то

Будут прочитаны строки с 13 по 20, так как в Python индексация начинается с О. Поэтому
строка 1 индексируется как О. Также можно прочитать некоторые дополнительные строки,
используя ключевое слово next().
Когда вы используете файл-объект как итеративный, не применяйте здесь инструкцию
read line(), поскольку эти два метода не должны использоваться вместе.

30. 1 1 . Копирование дерева каталогов
import shutil
source = '//1 92.1 68.1 .2/Daily Reports'
destination = 'D:\\Reports\\Today'
shutil.copytree(source, destination)

Каталог назначения не должен существовать перед использованием этой функции.

30. 1 2. Копирование содержимого файла в другой файл
with open(input_f1 le, 'г') as in_fi le, open(output_f1 le, 'w') as out_f1 le:
for l ine in in_f1 le:
out_file.write(l ine)
• Используем модуль shutil :
import shutil
shutil.copyf1le(src, dst)

Глава 31 . Модуль os.path
Этот модуль реализует несколько полезных функций для работы с именами путей. Пара­
метры пути могут передаваться в виде строк или байтов. Приложениям рекомендуется пред­
ставлять имена как строки символов (Unicode).

31 . 1 . Объединение путей

Чтобы соединить два или более компонентов пути, импортируйте в Python модуль os, а
затем выполните следующее:
import os
os.path.join('a', 'Ь', 'с')

Преимущество использования os.path заключается в том, что он позволяет коду оставать­
ся совместимым во всех операционных системах, поскольку в нем используется раздели­
тель, подходящий для платформы, на которой он работает.
Например, результатом этой команды в Windows будет:
»> os.path.join('a', 'Ь', 'с')
'а\Ь\с'

В Unix:
>» os.path.join('a', 'Ь', 'с')
'а/Ыс'

31 .2. Управление компонентами пути

1 69

3 1 .2. Управление компонентами пути
Чтобы отделить компонент от пути, используйте:
р = os. path.join(os.getcwdO, 'foo.txt')
р
'/Users/csaftoiu/tmp/foo.txt'
»> os. path.dirname(p)
'/Users/csaftoiu/tmp'
»> os. path. basename(p)
'foo.txt'
»> os. path.split(os.getcwd())
('/Users/csaftoiu/tmp', 'foo.txt')
»> os. path.splitext(os.path.basename(p))
('foo', '.txt')
»>
>>>

3 1 .3. Получение родител ьского каталога
os.path.abspath(os.path.join(PATH_TO_G ET_THE_PARENT, os.pardir))

31 .4. Проверка существования пути
Чтобы проверить, существует ли заданный путь, используйте:
path = '/home/john/temp'
os.path.exists(path)
# это возвращает false, если путь не существует или является неработающей символической
ссыл кой

31 .5. Проверка того, я вляется ли данный путь каталогом,
файлом, символической ссыл кой, точкой монтирования
Для проверки того, является ли заданный путь каталогом:
dirname = '/home/john/python'
os.path.isdir(dirname)

Чтобы проверить, является ли данный путь файлом:
filename = dirname + 'main. py'
os.path.isf1le(f1lename)

Чтобы проверить, является ли данный путь символической ссьmкой:
symlink = dirname + 'some_sym_link'
os.path.islink(symlink)

Чтобы проверить, является ли данный путь точкой монтирования:
mount_path = '/home'
os.path.ismount(mount_path)

3 1 .6. Абсолютн ый путь из относительного пути
Используйте функцию os.path.abspath:
os.getcwd() '/Users/csaftoiu/tmp'
os. path.abspath('foo') '/Users/csaftoiu/tmp/foo'
»> os. path.abspath(' . ./foo') '/Users/csaftoiu/foo'
»> os. path.abspath('/foo') '/foo'
»>
»>

1 70

Глава 32. Итерируемые типы данных и итераторы

Глава 3 2. Итерируем ые типы данных
и итераторы
32. 1 . Итератор (iterator), итерируемый объект (iteraЫe)
и генератор (generator )
Итерируемый объект (iteraЬle) - это объект, который может возвращать итератор
(iterator). Точнее, любой объект, имеющий метод iter и возвращающий итератор, является
итерируемым. Также это может быть объект, реализующий метод _getitem_. Этот метод мо­
жет принимать индексы (начиная с нуля) и выдавать ошибку lndexError, если индексы стано­
вятся недействительными.
Класс str в Python является примером итерируемого объекта, в котором реализован ме­
тод _getitem_.
Итератор - это объект, который при вызове next(*object*) на некотором объекте выда­
ет следующее значение в последовательности. Более того, любой объект, реализующий
метод __next_, является итератором. После исчерпания итератора возникает сообщение
Stop lteration; повторное использование итератора в этой точке невозможно.

Итерируемые классы

Итерируемые классы определяют методы _iter_ и _next_. Пример итерируемоrо класса:

class MylteraЫe:
def iter (self):
return self
def next (self):
# код
#Классический итерируем ы й объект (iteraЫe) в старых версиях Python, _getitem_ все еще
поддерживается . . .
class MySequence:
def getitem (self, index):
if (condition):
raise lndexError
return (item)
#Можно получить простой экземпляр "итератора" с помощью iter(MySequence())
Попробуем инстантировать (создать экземпляр) абстрактного класса из модуля collec­
tions, чтобы лучше разобраться в этом. Пример:

Версия Python 2.х � 2.3
import collections
>» collections. lterator()
»> TypeError: Cant instantiate abstract class lterator with abstract methods next

Версия Python З.х � 3.0

»> TypeError: Cant instantiate abstract class lterator with abstract methods _next_

Чтобы обеспечить совместимость итерируемых классов Python 3 и Python 2, выполните
следующие действия:

Версия Python 2.х � 2.3
class MylteraЫe(object): # или col lections. lterator, что рекомендовано . . . .
d e f _iter_(self):
return self

32.2. Извлечение значений по одному

1 71

def next(self): #code
_next_ = next
Оба они теперь являются итераторами и по ним можно проходить циклом:
ех1 = MylteraЬleClass()
ех2 = MySequence()
fог (item) in (ех1 ): #code
fог (item) in (ех2): #code
Использование генер атор ов - это простой способ создания итераторов. Генератор - это
итератор, по которому можно итерировать.

32.2. Извлечение значений по одному
Начните со встроенной функции iter() для получения итер атор а по итерируемому объек­
ту и используйте функцию next() для получения элементов одного за другим, до возникнове­
ния сигнала Stoplteration, означающего конец итерации:
s = {1 , 2}
i = iter(s)
а = next(i)
Ь = next(i)
с = next(i)

# или список, или генератор, или даже итератор
# получить итератор
#а= 1
#Ь=2
# вызы вает Stoplteration

32.3. Итерирование по всему итерируемому
s = {1 , 2, 3}

# получить каждый элемент в s
fог а in s:
print а # в ы водит 1 , затем 2, затем 3
# копирование в список
11 = l ist(s) # 11 = [1 , 2, 3]
# использование генератора списка
12 = [а * 2 fог а in s if а > 2] # 12 = [б]

32.4. Проверка только одного элемента в итерируемом
С помощью распаковки извлеките первый элемент и убедитесь, что он единственный:
а,

=

itегаЫе

def foo():
yield 1
а,

=

foo()

nums = [1 , 2, 3]
а, = nums

#а=1
# ValueError: слишком много значений для распаковки

32.5. Что может быть итерируемым

Итерируемым (IteraЫe) может бьrгь любой объект, в котором элементы принимаются по од­
ному и в направлении только вперед. Встроенные коллекции Python являются итерируемыми:
[1 , 2, 3]

(1 , 2, 3)

# список (list), итерация по элементам
# кортеж (tuple)

Глава 33. Функции

1 72
{1 , 2, З}
{1 : 2, 3: 4}

# набор (set)
# словарь (dict),

итерация по ключам

Генераторы возвращают итерируемые объекты:
# foo еще не итерируемый ...
def foo():
yield 1
res = foo()

# . . . но

res уже итерируемый

32.6. В итератор нельзя входить повторно!
def gen():
yield 1
itегаЫе = gen()
for а in iteraЫe:
print а
# Каким был первый элемент в итерируемом? Теперь его не получить.
# Можно только получить новый итератор
gen()

Глава 33 . Функции
П ара м етр

Подроб ности

arg1 , ... , argN

Регулярные аргументы

*args

Неименованные позиционные аргументы

kw1 , ... , kwN

Только именованные аргументы (Keyword-only arguments, аргументы
только для ключевых слов)

**kwargs

Остальные именованные аргументы (аргументы ключевых слов)

Функции в Python представляют собой организованный, многократно используемый и
модульный код для выполнения набора определенных действий. Функции упрощают про­
цесс кодирования, предотвращают использование избыточной логики и делают код более
понятным. В данной главе описывается объявление и использование функций в Python.
В языке Python имеется множество встроенных функций, таких как print(), input(), len().
Можно также создавать собственные функции для выполнения более специфических за­
дач - пользовательские функции.

33. 1 . Создание и вызов простых функций

Использование инструкции def является наиболее распространенным способом опреде­
ления функции в Python. Этот оператор является так называемым составным выра:ж:ением
с одним предложением и имеет следующий синтаксис:
def function_name(parameters):
statement(s)
function_name - это идентификатор функции. Так как определение функции является ис­
полняемым, его исполнение связывает имя функции с объектом функции, который может
быть вызван позже при помощи идентификатора.

33. 1 . Со з д ание и вызов простых функций

1 73

parameters - это необязательный список идентификаторов, которые при вызове функции
привязываются к значениям, передаваемым в качестве аргументов. Функция может иметь
произвольное количество аргументов, которые разделяются запятыми.
statement(s) - или тело функции - непустая последовательность операторов, выполняе­
мых при каждом вызове функции. Тело функции не может быть пустым, так же как и любой
блок с отступом.
Вот пример простого определения функции, назначение которой состоит в том, чтобы
при каждом ее вызове выводить Hello:
def greetO:
print("Hello")
Теперь вызовем определенную функцию greet():
greet()
# Резул ьтат: Hello
Еще один пример определения функции, которая принимает один-единственный аргу­
мент и выводит на экран переданное значение при каждом вызове:
def greet_two(greeting):
print(greeting)
После этого необходимо вызвать функцию greet_two() с аргументом:
greet_two("Howdy")
# Howdy
Также вы можете задать для этого аргумента функции значение по умолчанию:
def greet_two(greeting="Howdy"):
print(greeting)
Теперь вы можете вызывать функцию без указания значения:
greet_two()
# Howdy
В отличие от многих других языков вам не нужно явно объявлять тип возвращаемого
значения функции. Функции Python могут возвращать значения любого типа с помощью
ключевого слова return. Одна функция может возвращать любое количество разных типов!
def many_types(x):
if х < О:
return "Hello!"
else:
return О
print(many_types(1 ))
print(many_types(-1 ))
# Резул ьтат:

о

Hello!
Пока это правильно обрабатывается вызывающей стороной, это совершенно правиль­
ный код Python.
Функция, которая достигает конца исполнения без оператора возврата, всегда будет воз­
вращать значение None:
def do_nothing():
pass
print(do_nothing())
# None

1 74

Глава 33. Функции

Как упоминалось ранее, определение функции должно иметь тело функции, т. е. непу­
стую последовательность операторов. Поэтому в данном примере в теле функции использу­
ется оператор pass, который является нулевой операцией - когда он выполняется, то ничего
не происходит. Это полезно в качестве заполнителя, когда синтаксически требуется опера­
тор, но код выполнять не нужно.

33.2. Определение функции с произвольным числом аргументов
Произвольное количество позиционных аргументов:

Определение функции, способной принимать произвольное число аргументов, можно
осуществить, предварительно обозначив один из аргументов символом *.
def func(*args):
# args будет представлять собой кортеж, содержащий все переданные значения
for i in args:
print(i)
func(1 , 2, 3) # Вызов с 3 аргументами
# Результат:
1
#
2
#
3
list_of_arg_values = [1 , 2, 3]
func(*list_of_arg_values) # Вызов со списком значений, * расширяет список
# Результат:
1
#
2
#
3
funcO # Вызов без аргументов
# Нет результата

Нельзя указать значение по умолчанию для args, например func(*args = [1 , 2, 3]) вызовет
синтаксическую ошибку (даже не будет компилироваться).
Нельзя предоставить аргументы по имени при вызове функции, например func(*args = [1 ,
2, 3]) приведет к ошибке TypeError.
Но если у вас уже есть аргументы в массиве (или любом другом итерируемом объекте),
можно вызвать свою функцию следующим образом: func(*my_stuff).
К этим аргументам (*args) можно обращаться по индексу, например args[0] вернет пер­
вый аргумент.
Произвольное количество именованных аргумеmов (аргументов ключевых слов)

Вы можете взять произвольное количество аргументов с именем, обозначая аргумент
в определении двумя * перед ним:
def func(**kwargs):
# kwargs будет представлять собой словарь, содержащий имена в качестве ключей
# и значения в качестве значений
for name, value in kwargs. items():
print(name, value)
func(val ue1 = 1 , value2 = 2, value3 = 3) # Вызов с тремя аргументами
# Результат: val ue1
1
#
value2
2
3
value3
#
func()
# Нет результата

# Вызов без аргументов

33.2. Определение функции с произвольны м числом аргументов
my_dict = {'foo': 1 , 'Ьаг': 2}
func(**my_dict)
# Резул ьтат:
foo 1
#
Ьаг 2

1 75

# Вызов со словарем

Вы не можете предоставить аргументы без имен, например func(1 , 2, 3) вызовет TypeError.
kwargs - это простой собственный (нативный) словарь языка Python. Например,
args['value1 '] даст значение для аргумента value1 . Предварительно убедитесь, что такой аргу­
мент существует, иначе будет выдана ошибка KeyError.
Предупреждение

Порядок в определении имеет значение. Первыми должны быть позиционные и имено­
ванные аргументы (необходимые аргументы). Затем идут произвольные аргументы *arg
(необязательные). Затем следуют аргументы только именованные (необходимые). Затем
произвольное юпочевое слово **kwargs (необязательно).
#
I-позиционные-I-необязател ьные-I--только-именованные-I-необязател ьные-I
def func(arg 1 , arg2=1 О , *args, kwarg 1 , kwarg2=2, **kwargs):
pass


должен быть предоставлен, в противном случае возникает TypeError.
Он может быть задан как позиционный (func(1 О)) или именованный

arg 1

(func(arg1 =1 О)).






kwarg 1 также должен быть указан, но он может быть предоставлен только
в качестве именованного аргумента: func(kwarg1 =1 О).
arg2 и kwarg2 являются необязательными. Если значение должно быть изменено,
применяются те же правила для arg 1 (либо позиционный, либо именованный),
либо kwarg 1 (только именованный).
*args перехватывает дополнительные позиционные параметры. Но следует
учитывать, что arg 1 и arg2 должны быть позиционными для передачи аргументов
в *args: func(1 , 1 , 1 , 1 ).

**kwargs перехватывает все дополнительные именованные параметры. В этом
случае любой параметр, который не является arg 1 , arg2, kwarg1 или kwarg2.
Например: func(kwarg3=1 О).
• В Python 3 вы можете использовать только *, чтобы указать, что все последующие
аргументы должны быть указаны как именованные. Например, функция math.
isclose в Python версии 3.5 и выше определяется с помощью def math. isclose (а, Ь,
*, rel_tol=1 е-09, abs_tol=0.0) , что означает, что первые два аргумента могут быть
заданы позиционно, но необязательные третий и четвертый параметры могут
быть заданы только как именованные аргументы.
Python 2.х не поддерживает только именованные параметры. Такое поведение можно
эмулировать с помощью kwargs:


def func(arg 1 , arg2=1 О, **kwargs):
try:
kwarg 1 = kwargs.pop("kwarg1 ")
except KeyError:
raise TypeError("missing required keyword-only argument: 'kwarg1 "')
kwarg2 = kwargs.pop("kwarg2", 2)
# тело функции . . .

Примечание о присвоении имен

Соглашение об именовании необязательных позиционных аргументов args и необяза­
тельных именованных аргументов kwargs - это просто соглашение, можно использовать лю­
бые имена, но полезно следовать этому соглашению, чтобы другие понимали, что вы делае­
те, или даже вы сами впоследствии, так что, пожалуйста, делайте это.

1 76

Глава 33. Функции

Примечание об уникальности

Любая функция может быть определена с одним или несколькими *args и одним или
несколькими **kwargs, но не с более чем одним из них. При этом *args должен быть послед­
ним позиционным аргументом, а **kwargs должен быть последним параметром. Попытка
использовать более одного из них приведет к исключению Syntax Еггог.
Примечание о вложенных функциях с необязательными аргументами

Такие функции можно вложить друг в друга, и обычно принято удалять те элементы, ко­
торые код уже обработал, но если вы передаете параметры, то вам необходимо передать не­
обязательные позиционные аргументы с префиксом * и необязательные именованные ар­
гументы с префиксом **, иначе args будут передаются как список или кортеж, а kwargs - как
один словарь, например:
def fn(**kwargs):
print(kwargs)
f1 (**kwargs)
def f1 (**kwargs):
print(len(kwargs))
fn(a = 1 , Ь = 2)

# Результат:
# {'а': 1 , 'Ь': 2}
#2

33 .З. Л ямбда-функции (встроенные/анонимные)

Ключевое слово lambda создает встроенную функцию (inline function), содержащую одно
выражение. Значение этого выражения и есть то, что возвращает функция при вызове.
Рассмотрим функцию:
def greeting():
return "Hello"
которая при вызове:
print(greeting())
выводит:
Hello
Это можно записать в виде лямбда-функции следующим образом:
greeLme = lambda: "Hello"
См. примечание в конце этого раздела о присвоении лямбда-функций переменным. Как прави­
ло, этого делать не следует.

Это создает встроенную функцию с именем greet_me, которая возвращает Hello. Обратите
внимание, что вы не пишете return при создании такой функции с лямбдой. Значение после
двоеточия возвращается автоматически.
После присвоения переменной она может использоваться как обычная функция:
pri nt(g reet_me())
выводит:
Hello
Лямбда-функция также может принимать аргументы:
strip_and_upper_case = lambda s: s.strip().upper()
strip_and_upper_case(" Hello ")

1 77

33.3. Лямбда-функции (встроенные/анонимные)

что возвращает строку:
H ELLO

Такие функции могут принимать произвольное количество аргументов и именованных
аргументов, как обычные функции.
greeting = lambda х, *args, **kwargs: print(x, args, kwargs)
greeting('hel lo', 'world', world='world')

что выводит:
hello ('world',) {'world': 'world'}

Обычно это удобно использовать для коротких функций, которые удобно определять
в том месте, где они вызываются (что обычно с sorted, fi lter и map).
Например, эта строка сортирует список строк, игнорируя их регистр и пробелы в начале
и в конце:
sorted( [" foo ", " bAR", "BaZ
# Резул ьтат:
# [' bAR', 'BaZ ', ' foo 1

'1 , key=lambda s: s.strip().upper())

Сортировка списка с игнорированием пробелов:
sorted( [" foo ", " bAR", "BaZ
# Резул ьтат:
# ['BaZ ', ' bAR', ' foo 1

'1 , key=lambda s: s.strip())

Примеры с функцией map:
sorted( map( lambda s: s.strip().upper(), [" foo ", "
# Резул ьтат:
# ['BAR', 'BAZ', 'FOO']
sorted( map( lambda s: s.strip(), [" foo ", "
# Резул ьтат:
# ['BaZ', 'bAR', 'foo']

bAR", "BaZ '1))

bAR", "BaZ '1))

Примеры с числовыми списками:
my_list = [3, -4, -2, 5, 1 , 7]
sorted( my_list, key=lambda х: abs(x))
# Резул ьтат:
# [1 , -2, 3, -4, 5, 7]
l ist( filter( lambda х: х>О, my_list))
# Резул ьтат:
# [3, 5, 1 , 7]
l ist( map( lambda х: abs(x), my_list))
# Резул ьтат:
[3, 4, 2, 5, 1 , 7]

Можно вызвать другие функции (с аргументами или без) внутри лямбда-функции.
def foo(msg):
print(msg)
greet = lambda х = "hello world": foo(x)
greet()

Выведет:
hello world

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

1 78

Глава 33. Функции

Примечание
Имейте в виду, что РЕР8 (официальное руководство по стилю Python) не рекомендует назначать лямбды для переменных (как было в первых двух примерах):
В сегда используйте инструкцию def вместо оператора присваивания, который привязывает
лямбда-выражение непосредственно к идентификатору.
Правильно:
def f(x): return 2*х
Неправильно:
f = lambda х: 2*х
Первая форма означает, что именем результирующего объ екта функции является конкретное
f вместо общего . Это более полезно для отслеживания и представления строк в це­
лом. Использование оператора присваивания исключает единственное преимущество, которое
лямбда-выражение может предлагать в явном выражении def (т. е. то, что оно может быть встро­
ено в большее выражение).

33.4. Создание функции с необязательными ар гу ментами

Необязательные аргументы можно отключить, присвоив (с помощью = ) аргументу-име­
ни значение по умолчанию:
def make(action = ' nothing '):
return action
Вызов этой функции возможен тремя различными способами:
make("fun")
# Выв од: fun
make(action = "sleep")
# Вы вод: sleep
# Аргумент я в ляется необяз ател ьн ы м , поэтому функция буд ет испол ьзовать зна ч ен ие
# по умолч а нию, если а ргумент не переда н.
make()
# Выв од: nothing
Предупреждение

С изменяемыми типами данных (list, dict, set и т. д.), если они заданы в качестве атрибута по
умолчанию, следует обращаться осторожно. Любое изменение аргумента по умолчанию меняет
его навсегда. См. главу "Определение функции с необязательными изменяемыми аргументами".

33.5. Определение функции с необязательными
изменяем ыми ар гу ментами

Существует проблема, связанная с использованием необязательных аргументов с изме­
няемым типом по умолчанию (описана в разделе "Определение функции с необязатель­
ными аргументами"), которая потенциально может привести к неожиданному поведению.
Пояснение
Проблема возникает потому, что аргументы по умолчанию инициализируются ОДШI раз,
в момент определения функции, а не (как во многих других языках) при ее вызове. Значения
по умолчанию хранятся в переменной-члене _defaults_ объекта функции.
def f(a, Ь = 42, с = □):
pass
print(f._defaults_)
# Резул ьтат: (42, D )

33.6. Передача аргументов и изменяемость

1 79

Для неизменяемых типов (см. главу "Передача аргументов и изменяемость") это не про­
блема, потому что нет способа изменить эту переменную; ее можно только переназначить,
оставив исходное значение без изменений. Следовательно, впоследствии гарантированно
сохраняется одинаковое значение по умолчанию. Однако для изменяемого типа исходное
значение может измениться при обращении к его различным функциям. Поэтому для по­
следующих вызовов функции не гарантируется сохранение первоначального значения по
умолчанию.
def append(elem, to = □):
to.append(elem)
# Этот вызов append() изменяет переменную по умолчанию "to"
return to
append(1 )

# Результат: [1 ]

append(2)

# Добавляет ее к внутреннему списку

append(3, □)

# Использование нового созданного списка дает ожидаемы й результат

# Результат: [1 , 2]
# Результат: [3]

# Повторны й вызов без аргумента снова добавит ее во внутренний список

append(4)

# Результат: [1 , 2, 4]
Примечание. Некоторые IDE, такие как PyCharm, выдают предупреждение, если в качестве атри­
бута по умолчанию указан изменяемый тип.

Решение
Если вы хотите, чтобы аргумент по умолчанию всегда был тем, который вы указываете в
определении функции, то всегда следует использовать неизменяемый тип в качестве аргу­
мента по умолчанию.
Часто в случае, когда изменяемый тип необходим по умолчанию, выход в том, чтобы ис­
пользовать значение None в качестве аргумента по умолчанию, а затем присваивать факти­
ческое значение по умолчанию переменной аргумента, если оно равно None.
def append(elem, to = None):
if to is None:
to = □
to.append(elem)
return to

33.6. Передача ар гументов и изменяемость

Сначала немного терминологии:
• аргумеm (фактический параметр): фактическая переменная, передаваемая
функции;
• параметр (формальный параметр): принимающая переменная, которая
используется в функции.
В Python аргументы передаются по присваиванию (в отличие от других языков, где аргу­
менты могут передаваться по значению/ссылке/указателю).
• Изменение параметра приводит к изменению аргумента (если тип аргумента
является изменяемым).
def foo(x):
х[О] = 9
print(x)

# здесь х - параметр
# это изменяет список, помеченны й как х, так и у

1 80

Глава 33. Функции

у = [4, 5, б]

# вызов foo с у в качестве аргумента
# Вывод: [9, 5, б] # изменен список, помеченный х

foo(y)

print(y)

# Вы вод: [9, 5, 6] # список, помеченны й у, изменен тоже



Переназначение параметра не приведет к переназначению аргумента.

def foo(x):

# здесь х - параметр, при вызове foo(y) мы присваиваем у значение х
# это изменяет список, помеченный как х, так и у
# х теперь маркирует другой список (на у это не влияет)
# это изменяет список х, а не список у

у = [4, 5, б]

# у - аргумент, х - параметр
# представим, что мы написали "х = у", теперь переходим к строке 1

х[О] = 9
х = [1 , 2, 3]
х[2] = 8

foo(y)

у
# Вывод: [9, 5, 6]

В Python мы не присваиваем фактически значения переменным, а привязываем
(т. е. назначаем, присоединяем) переменные (которые считаются именами) к объектам .
• Неизменяемые: целые числа, строки, кортежи и т. д. Все операции создают копии.
• Изменяемые : списки, словари, наборы и т. д. Операции могут быть
как изменяющими, так и не изменяющими.

х = [3, 1 , 9]
у=х

x.append(5)
x.sort()

# изменяет список, помеченны й х и у, причем и х, и у привязаны к [3, 1 , 9]
# изменяет список, помеченны й х и у (сортировка на месте)
# не изменяет список (создает копию только для х, но не для у)
# z - это х ([1 , 3, 9, 4])
# изменяет список, помеченный х и z (используется функция extend)
# не изменяет список (создает копию только для х)

х = х + [4]
z=x
х += [б]
х = sorted(x)
х
# Результат: [1 , 3, 4, 5, 6, 9]
у
# Результат: [1 , 3, 5, 9]
z
# Результат: [1 , 3, 5, 9, 4, 6]

33 . 7. Возврат значений из функций

Функции могут возвращать (return) значение, которое можно использовать напрямую:
def give_me_f1ve():
return 5

pri nt(g ive_me_f1ve())

# Результат: 5

# Вы вести возвращаемое значение

или сохранить значение для последующего использования:
num = give_me_five()
print(num)
# Вывести сохраненное возвращаемое значение

# Результат: 5

или использовать этозначение для любых операций:
print(give_me_f1ve() + 1 О)

# Результат: 1 5

1 81

33.8. Замы кание (closure)

Если в функции встречается оператор return, то происходит немедленный выход из функ­
ции, и последующие операции не оцениваются:
def give_me_another_f1ve():
return 5
print(This statement will not Ье printed. Ever.')
print(give_me_another_f1ve())
# Результат: 5

Вы также можете возвращать несколько значений (в виде кортежа):
def give_me_two_f1ves():
return 5, 5

# Возвращает два значения 5

f1rst, second = give_me_two_fives()
print(first)
# Результат: 5
pri nt(second)
# Результат: 5

Функция без оператора return неявно возвращает значение None. Аналогично функция
с оператором return, но без возвращаемого значения или переменной возвращает None.

33.8. Замыкание (closure)

Замыкания в Python создаются вызовами функций. Вызов makelnc создает привязку для х,
на которую ссылается функция inc. Каждый вызов makelnc создает новый экземпляр этой
функции, но каждый экземпляр имеет ссылку на разную х.
def makelnc(x):
def inc(y):
# х "присоединен" к определению inc
return у + х
return inc
incOne = makel nc(1 )
incFive = makel nc(S)
inc0ne(5)
#6
incFive(S)
#10

Обратите внимание, что в то время как при обычном замыкании вложенная функция
полностью наследует все переменные из окружающей ее среды, в этой конструкции вло­
женная функция имеет доступ только на чтение к унаследованным переменным, но не мо­
жет назначать их:
def makelnc(x):
def inc(y):
# увеличение х не разрешено
х += у
return х
return inc
incOne = makel nc(1 )
inc0ne(5)
# UnboundlocalError: локальная переменная 'х' упоминается перед присваиванием

1 82

Глава 33. Функции

В Python 3 появилась инструкция nonlocal для реализации полного закрытия с вложен­
ными функциями.
def makel nc(x):
def inc(y):
# теперь можно присвоить значение х
nonlocal х
х += у
return х
return inc
incOne = makel nc(1 )
inc0ne(5)
#6

33. 9. Принудительное использование именованных
параметров

Все параметры, указанные после первой звездочки в сигнатуре функции, являются толь­
ко именованными.
def f(*a, Ь):
pass
f(1 , 2, 3)
# ТуреЕггог: f() отсутствует 1 обязательный именованны й аргумент: 'Ь'

В Python 3 можно поместить одну звездочку в подпись функции, чтобы гарантировать,
что остальные аргументы могут передаваться только с использованием именованных аргу­
ментов.
def f(a, Ь, *, с):
pass

f(1 , 2, 3)
# ТуреЕггог: f() принимает 2 позиционных аргумента, но было задано 3
f(1 , 2, с = З)
# Ошибка отсутствует

33. 1 О. Вложенные функции

Функции в Python являются объектами первого класса. Они могут быть определены в лю­
бой области видимости.
def f1 bonacci(n):
def step(a,b):
return Ь, а+Ь
а, Ь = О, 1

for i in range(n):
а, Ь = step(a, Ь)
return а

Функции, захватывающие свою область видимости, могут передаваться, как и любые
другие объекты.
def make_adder(n):
def adder(x):
return n + х
return adder

33.1 1 . Предел рекурсии

1 83

add5 = make_adder(5)
add6 = make_adder(6)
add5(1 О)
#Результат: 1 5
add6(1 О)
#Резул ьтат: 1 6
def repeatedly_apply(func, n, х):
for i in range(n):
х = func(x)
return х
repeated ly_apply(add5, 5, 1 )
#Результат: 26

ЗЗ. 1 1 . Предел рекурсии

Существует ограничение на глубину возможной рекурсии, которое зависит от реализа­
ции Python. При достижении этого предела выдается исключение RuntimeError:
def cursing(depth):
try:
cursing(depth + 1 )
# фактически рекурсия
except RuntimeError as RE:
print('I recursed {} times!'.format(depth))
cursing(O)
# Резул ьтат: 1 recursed 1 083 times!

Можно изменить предел глубины рекурсии, используя метод sys.setrecu rsionlimit(limit),
и проверить этот предел на sys.getrecursionl imit().
sys.setrecursionl imit(2000)
cursing(O)
# Резул ьтат: 1 recursed 1 997 times!

Начиная с Python 3.5 исключением является RecursionError, производное из RuntimeError.

ЗЗ. 1 2. Рекурсивная лямбда-функция с использованием
присваиваемой переменной

Один из методов создания рекурсивных лямбда-функций заключается в присвоении
функции переменной и последующем обращении к этой переменной в самой функции. В ка­
честве примера можно привести рекурсивное вычисление факториала числа, как показано
в следующем коде:
lambda_factorial = lambda i:1 if i==O else i*lambda_factorial(i-1 )
print(lambda_factorial(4)) # 4 * 3 * 2 * 1 = 1 2 * 2 = 24

Описание кода

Лямбда-функции через присваивание переменной передается значение (4), которое она
оценивает и возвращает 1, если оно равно О, иначе возвращается текущее значение (i)* дру­
гого вычисления с помощью лямбда-функции значения -1 (i-1 ). Это продолжается до тех пор,
пока переданное значение не уменьшится до О (return 1 ). Процесс можно визуализировать
следующим образом:

1 84

Глава 33. Функции

Final Answer 24 (4 * 6)

-

lambda_factorial(4)
i == 4
Lambda_factoriaI(4-1 )
j

lambda_factoriaI(3)
i == 3
Lambda_factorial(3-1 )

return 6 (2 * 3)

t__

r

return 2 (2 * 1 )
-

lambda_factoriaI(2)
i == 2
Lambda_factorial(2-1 )

t..

'

return 1 (1 * 1 )
-

lambda_factorial(1 )
i == 1
Lambda_factorial(1 -1 )

l
return 1 (if i == О)

lambda_factorial(O)
i == о
return 1

33 . 1 3. Рекурсивные функции

Рекурсивная функция - это функция, которая вызывает сама себя в своем определе­
нии. Например, математическая функция факториала, определяемая как factorial(n) =
n*(n-1 )*(n-2)* . . . *3*2*1 , может быть запрограммирована как:
def factorial(n):
#n ДОЛЖНО быть цел ы м Ч И СЛОМ
if n == О:
return 1
else:
return n*factoria l(n-1 )

что даст следующие результаты:
factoria l (O)
#результат 1
factorial(1 )
#результат 1
factoria l (2)
#результат 2
factoria I (3)
#результат 6

как и ожидалось.
Обратите внимание, что эта функция рекурсивна, потому что второй return factorial(n-1 ),
где функция вызывает себя же в своем определении.

33.1 4. Определение функции с аргументами

1 85

Некоторые рекурсивные функции могут быть реализованы с использованием лямбда­
функций, к примеру функция факториала:
factorial = lambda п: 1 if п == О else n*factorial(n-1 )

Функция выводит то же, что и выше.

33. 1 4. Определение функции с аргументами

Аргументы указываются в круглых скобках после имени функции:

def divide(dividend, divisor): # Имена функции и ее аргументов
# Аргументы доступны по имени в теле функции
print(dividend / d ivisor)

Имя функции и ее список аргументов называются сигнатурой функции. Каждый имено­
ванный аргумент является фактически локальной переменной функции.
При вызове функции дайте значения для аргументов, указав их по порядку:
divide(1 О, 2}
# результат: 5

Также их можно указать в любом порядке, используя имена из определения функции:
divide(divisor = 2, d ividend = 1 О}
# результат: 5

33 . 1 5. Распаковка итерируемых объектов и словарей

Функции позволяют задавать такие типы параметров: позиционные, переменные пози­
ционные, именованные (keyword args, kwargs). Приведем наглядное и краткое использова­
ние разных типов.
def unpacking(a, Ь, с = 45, d = бО, *args, **kwargs):
print(a, Ь, с, d, args, kwargs)
»> unpacking(1 , 2}
1 2 45 60 () {}
»> unpacking(1 , 2, 3, 4)
1 2 3 4 (} {}
»> unpacking(1 , 2, с = 3, d = 4}
1 2 3 4 (} {}
»> unpacking(1 , 2, d = 4, с = 3}
1 2 3 4 (} {}
»> pair = (3,)
»> unpacking(1 , 2, *pair, d = 4}
1 2 3 4 (} {}
»> unpacking(1 , 2, d = 4, *pair)
1 2 3 4 (} {}
»> unpacking(1 , 2, *pair, с = 3}
Traceback (most recent call last):
File "", line 1, in
ТуреЕггог: unpacking() got multiple val ues for argument 'с'
»> unpacking(1 , 2, с = 3, *pair)
Traceback (most recent call last):
File "", line 1, in
ТуреЕггог: unpacking() got multiple val ues for argument 'с'
»> args_list = [3]
»> unpacking(1 , 2, *args_list, d = 4}

1 86
1 2 3 4 () {}
»> unpacking(1 , 2, d = 4, *args_l ist)
1 2 3 4 () {}
»> unpacking(1 , 2, с = 3, *args_l ist)
Traceback (most recent cal l last):
File "", line 1 , in
ТуреЕггог: unpacking() got multiple values for argument 'с'
»> unpacking(1 , 2, *args_list, с = 3)
Traceback (most recent cal l last):
File "", line 1 , in
ТуреЕггог: unpacking() got multiple values for argument 'с'
»> pair = (3, 4)
>» unpacking(1 , 2, *pair)
1 2 3 4 () {}
>» unpacking( 1 , 2, 3, 4, *pair)
1 2 3 4 (3, 4) {}
>» unpacking(1 , 2, d = 4, *pair)
Traceback (most recent cal l last):
File "", line 1 , in
ТуреЕггог: unpacking() got multiple values for argument 'd'
»> unpacking(1 , 2, *pair, d = 4)
Traceback (most recent cal l last):
File "", line 1 , in
ТуреЕггог: unpacking() got multiple values for argument 'd'
»> args_l ist = [3, 4]
>» unpacking(1 , 2, *args_list)
1 2 3 4 () {}
>» unpacking(1 , 2, 3, 4, *args_list)
1 2 3 4 (3, 4) {}
>» unpacking(1 , 2, d = 4, *args_l ist)
Traceback (most recent cal l last):
File "", line 1 , in
ТуреЕггог: unpacking() got multiple values for argument 'd'
>» unpacking(1 , 2, *args_list, d = 4)
Traceback (most recent cal l last):
File "", line 1 , in
ТуреЕггог: unpacking() got multiple values for argument 'd'
>» arg_dict = {'с':3, 'd':4}
»> unpacking(1 , 2, **arg_dict)
1 2 3 4 () {}
»> arg_dict = {'d':4, 'с':3}
»> unpacking(1 , 2, **arg_dict)
1 2 3 4 () {}
»> arg_dict = {'с':3, 'd':4, 'not_a_parameter': 75}
»> unpacking(1 , 2, **arg_dict)
1 2 3 4 () {'not_a_parameter': 75}
»> unpacking(1 , 2, *pair, **arg_dict)
Traceback (most recent cal l last):
File "", line 1 , in
ТуреЕггог: unpacking() got multiple values for argument 'd'
»> unpacking(1 , 2, 3, 4, **arg_dict)

Глава 33. Функции

33. 1 6. Определение функции с нескол ькими аргументами

1 87

Traceback (most recent call last):
File "", line 1, in
TypeError: unpacking() got multiple values for argument 'd'
# Позиционные аргументы имеют приоритет перед любыми другими формами передачи
аргументов
»> unpacking(1 , 2, *arg_dict, с= З)
1 2 3 4 () {'not_a_parameter': 75}
»> unpacking(1 , 2, 3, **arg_dict, с= З)
Traceback (most recent call last):
File "", line 1, in
TypeError: unpacking() got multiple values for argument 'с'

ЗЗ. 1 6. Определение функции с несколькими аргументами

В функцию можно передавать любое количество аргументов, единственные жесткие
правила - имя каждого аргумента должно быть уникальным, а необязательные аргументы
должны находиться после обязательных:
def func(value1 , value2, optionalvalue = 1 О):
return '{О} {1 } {2}'.format(value1 , value2, optionalvalue1 )

При вызове функции вы можете либо указать каждый именованный аргумент без имени, но порядок важен:
print(func(1 , 'а', 1 00))
# Результат: 1 а 1 00
print(func('abc', 1 4))
# аЬс 1 4 1 0

или можно комбинировать передачу аргументов с именем и без него. Тогда те, у кого есть
имя, должны следовать за аргументами без имени, но порядок аргументов с именем не име­
ет значения:
print(func('This', optionalvalue = 'StackOverflow Documentation', value2 = 'is'))
# Результат: This is StackOverflow Documentation

Глава 34. Создание функций
со списочны ми аргументами
3 4. 1 . Функция и вызов

Списки в качестве аргументов - это просто еще одна переменная:

def func(mylist):
for item in mylist:
print(item)

и может передаваться в самом вызове функции:
func([1 ,2,3,5,7])
1

2
3
5

7

1 88

Глава 35. Функциональное программирование в Python

или в качестве переменной:
alist = ['a','b','c','d']
func(alist)
а

ь

с

d

Глава 3 5. Функциональное
програм мирование в Python
Функциональное программирование позволяет разложить задачу н а набор функций.
В идеале функции только принимают входные данные и производят выходные данные и не
имеют никакого внутреннего состояния, которое влияло бы на выход, производимый при
заданном входном сигнале. Ниже рассматриваются функциональные техники, общие для
многих языков, такие как lambda, map, reduce.

35. 1 . Лямбда-функция

Это анонимная встроенная функция, определенная с помощью лямбда-выражения. Па­
раметры лямбда-функции указываются слева от двоеточия. Справа от двоеточия указывает­
ся тело функции. Результат выполнения тела функции возвращается неявно.
s = lambda х:х*х
s(2) =>4

35.2. Функция map

Функция map принимает функцию и коллекцию предметов. Она создает новую пустую
коллекцию, запускает функцию для каждого элемента в исходной коллекции и вставляет
каждое возвращаемое значение в новую коллекцию. Возвращает новую коллекцию. Приве­
дем простой пример такой функции, которая принимает список имен и возвращает список
длин этих имен:
name_lengths = map(len, ["Магу", "lsla", "Sam"])
print(name_lengths) =>[4, 4, 3]

35.3. Функция reduce

Принимает функцию и коллекцию предметов. Возвращает значение, которое создается
путем объединения элементов. Приведем пример простого использования этой функции,
которая возвращает сумму всех предметов в коллекции.
total = reduce(lambda а, х: а + х, [О, 1 , 2, 3, 4])
print(total) =>1 О

35.4. Функция filter

Принимает функцию и коллекцию. Возвращает коллекцию, состоящую из всех элемен­
тов, для которых функция вернула значение True.
arr=[l ,2,3,4,5,6]
[i for i in fi lter(lambda х:х>4,агг)]

# в ы водит [5,6]

1 89

36. 1 . Возведение в степень

Глава 36. Частичные функции
П одробн ости

Параметр
х

возводимое в степень число
показатель степени
функция, которая будет специализируемой

у
raise (возведение
в степень)

Как вы, вероятно, знаете, в объектно-ориентированном программировании специали­
зация абстрактного класса и его использование - это практика, о которой следует помнить
при написании кода. Что если бы вы могли определить абстрактную функцию и специали­
зировать эту функцию для создания различных ее версий? Это можно представить как свое­
го рода наследование функций, когда привязываются конкретные параметры, чтобы сделать
их подходящими для конкретного сценария использования.

36. 1 . Возведение в степень

Давайте предположим, что мы хотим возвести х в степень у. Вы бы написали это как:

def raise_power(x, у):
return x'k'l>> foo = 'Ьаг'
»> f'Foo is {foo}'
'Foo is bar'

Это также работает с более продвинутыми строками формата, включая выравнивание
и точечную нотацию.
»> f'{foo: л7s}'
• bar •

Примечание: f" не обозначает конкретный тип типа Ь" для bytes или u " для unicode
в Python 2. Формирование применяется немедленно, в результате чего получается обычная
строка.
Строки формата также могут быть вложенными:
»> price = 478.23
»> f"{f'${price:0.2f}':*>20s}"
'*************$478.23'

Выражения в f-строке оцениваются в порядке слева направо. Это можно обнаружить
только в том случае, если выражения имеют побочные эффекты:
»> def fn(I, incr):
result = 1[0]
... 1[0] += incr
... return result
»> 1st = [О]
»> f' {fn(lst,2)} {fn(lst,3)}'
· о 2·
»> f' {fn(lst,2)} {fn(lst,3)}'
'5 7'
»> 1st
[1 0]

40.4. Форматирование чисел с плавающей точкой
»> '{0:.0f}'.format(42.1 2345)
'42'
»> '{0:.1 f}'.format(42. 1 2345)
'42. 1 '

21 8

Глава 40. Форматирование строк

>» '{0:.3f}'.format(42.1 2345)
'42.1 23'
»> '{0:. 5f}'.format(42.1 2345)
'42.1 2345'
»> '{0:.7f}'.format(42.1 2345)
'42.1 234500'

То же самое относится и к другим способам ссылки:
»> '{:.3f}'.format(42. 1 2345)
'42.1 23'
»> '{answer:.3f}'.format(answer=42. 1 2345)
'42.1 23'

Числа с плавающей точкой также могут быть отформатированы в научной нотации или
в процентах:
»> '{0:.3e}'.format(42.1 2345)
'4.21 2е+01 '
»> '{0:.0%}'.format(42. 1 2345)
'421 2%'

Также можно комбинировать обозначения {О} и {name}. Это особенно полезно, если вы хотите округлить все переменные до заданного числа десятичных знаков:
>» s = 'Hello'
»> а, Ь, с = 1 . 1 2345, 2.34567, 34.5678
>» digits = 2
»> '{О}! {1 :.{n}f}, {2:.{n}f}, {3:.{n}f}'.format(s, а, Ь, с, n=digits)
'Hello! 1 . 1 2, 2.35, 34. 57'

40.5. Именованные заполнители

Строки формата могут содержать именованные заполнители, которые интерполируются
с помощью именованных аргументов для format.
Использова1П1е словаря (Python 2.х)
»> data = {'f1rst': 'Hodor', 'last': 'Hodor!'}
»> '{first} {last}'.format(**data)
'Hodor Hodor!'

Использова1П1е словаря (Python 3.2+)
»> '{first} {last}'.format_map(data)
'Hodor Hodor!'

Метод str.formaLmap позволяет использовать словари без их предварительной распаков­
ки. Также класс data (который может быть пользовательским) используется вместо только
что заполненного dict.
Без словаря
>» '{first} {last}'.format(f1 rst='Hodor', last='Hodor!')
'Hodor Hodor!'

40.6. Форматирование строк с использованием типа datetime

21 9

40.6. Форматирование строк с использованием типа datetime
Любой класс может создать свой собственный синтаксис форматирования строк с помо­
щью метода _format_. В стандартной библиотеке Python для этого удобно использовать тип
datetime, в котором можно использовать коды форматирования, подобные strftime, непосред­
ственно в str.format:
» > from datetime import datetime
» > 'Северная Америка: {dt:%m/%d/%Y}. ISO: {dt:%Y-%m-%d}.'.format(dt=datetime.now())
'Северная Америка: 07/21 /201 6. 1S0: 201 6-07-21 .'
Полный список списков форматов даты и времени можно найти в официальной доку­
ментации.

40. 7. Форматирование числовых значений
Метод .foгmat() может интерпретировать число в различных форматах, например:
» > '{:c}'.format(65)
'А'
» > '{:d}'.format(0x0a)
'1 О'
» > '{:n}'.format(0x0a)
'1 О'

# сим вол Юникода
# основание 1 О
# основание 1 О с использованием текущего языкового стандарта
# для разделителей

Форматирование целых чисел по разным основаниям (шестнадцатеричное, восьмеричное, двоичное)
» > '{0:x}'.format(1 О) # основание 1 6, строчные буквы - шестнадцатеричная система

'а'

» > '{0:X}'.format(1 О) # основание 1 6, верхний регистр - шестнадцатеричная система
'А'
» > '{:o}'.format(1 О) # основание 8 - восьмеричное
'1 2'
» > '{:b}'.format(1 О) # основание 2 - двоичное
'1 01 О'
» > '{0:#Ь}, {О:#о}, {0:#x}'.format(42) # С префиксом
'ОЫ 01 01 О, Оо52, Ох2а'
» > '8 Ьit: {О:08Ь}; Тhгее bytes: {0:0бx}'.format(42) # С нулевы м заполнением
'8 Ьit: 001 01 01 О; Тhгее bytes: 00002а'
Используйте форматирование для преобразования кортежа RGB со значениями с плавающей точкой в шестнадцатеричную строку координат цвета:
» > г, g, Ь = (1 .0, 0.4, О.О)
» > '#{:02X}{:02X}{:02X}'.format(int(255 * г), int(255 * g), int(255 * Ь))
'#FFббОО'
Преобразовывать можно только целые числа:
» > '{:x}'.format(42.0)
ТгасеЬасk (most гесепt call last):
File " ", line 1 , in
ValueError: Unknown format code 'х' fог object of type 'float'

40.8. Вложенное форматирование
Некоторые форматы могут принимать дополнительные параметры, например, ширину
форматируемой строки или выравнивание:
» > '{:. > 1 0}'.format('foo')
' . . . . . . .foo'

220

Глава 40. Форматирование строк

Они также могут предоставляться как параметры для format путем вложения большего
количества {} внутри {}:
» > '{:. > {}}'.format ('foo', 1 О}
' . . . . . . .foo'
'{:{}{}{}}'.format('foo', '*', •л•, 1 5}
'******foo******'

В последнем примере строка форматирования '{:{}{}{}}' изменена на '{:*л 1 5}' (т. е. "выровнять
по центру и заполнить * на общую длину 15"), перед применением ее к реальной строке 'foo',
которая должна быть отформатирована таким образом. Это может быть полезно в случаях,
когда параметры неизвестны заранее, например для выравнивания табличных данных:
data = ["а", "ЬЬЬЬЬЬЬ", "ссс"]
m = max(map(len, data))
» > for d in data:
... print('{: > {}}'.format(d, m))
>>>



а

ььььььь
ссс

40. 9. Форматирование с помощью функций Getitem и Getattr

Любая структура данных, поддерживающая функцию _getitem_, может форматировать
свою вложенную структуру:
person = {'f1rst': 'A rthur', 'last': 'Dent'}
'{p[fi rst]} {p[last]}' .format(p= person)
# 'A rthur Dent'

Доступ к объектным атрибутам можно получить с помощью getattr():

class Person(object):
first = 'Zaphod'
last = 'ВееЫеЬгох'

'{p.fi rst} {p.last}'.format(p= Person())
# 'Zaphod ВееЫеЬгох'

40. 1 О. Комбинированное заполнение и усечение строк

Допустим, вы хотите вывести переменные в столбец из 3 символов. Обратите внимание:
удвоение { и } экранирует их.
5 = ,,,,,,

pad
{{:3}}

:{а:3}:

truncate
{{:.3}}

:{е:.3}:

comЬined
{{: > 3.3}}
{{:3.3}}
{{:3.3}}
{{:3.3}}

:{а: > 3.3}:
:{а:3.3}:
:{с:3.3}:
:{е:3.3}:

print (s.format(a="1 "*1 , с="3"*3, е="5"*5}}

Результат:
pad
{:3}

:1

40. 1 1 . Пользовательское форматирование класса
truncate
{: .3}

:55 5 :

comblned
{:>3.3}
{:3.3}
{:3.3}

1:
:1
:333:

{:3 .3}

221

:555:

40. 1 1 . Пользовательское форматирование класса

Примечание: все приведеююе ниже относится как к методу str. format, так и к функпии format. В
приведенном далее тексте они взаимозаменяемы.
Для каждого значения, передаваемого в функцию format, Python ищет метод _formaL
для этого аргумента. Поэтому ваш собственный класс может иметь свой собственный ме­
тод _format_ для определения того, как функция format будет отображать и форматировать
ваш класс и его атрибуты. Это отличается от метода _str_, так как в методе _format_ мож­
но учитывать язык форматирования, включая выравнивание, ширину полей и т. д., и даже
(при желании) реализовать свои собственные спецификации формата и расширения языка
форматирования.
object._format_(self, format_spec)

Например:

# П ример на Python 2, но может быть легко применен к Python 3
class Example(object):
def _iniL(self,a,b,c):
self.a, self.b, self.c = а,Ь,с
def _formaL(self, format_spec):
'"'" Реализовать специальную семантику для спецификатора формата 's' '""'
# Отклонить все, что не является s
if format_spec[-1 ] != 's':
raise Val ueError('{} спецификатор формата не понят для этого объекта', format_spec[:-1 ])
# Выходные данные в данном примере будут иметь вид (,,)
raw = "(" + ",".join([str(self.a), str(self.b), str(self.c)]) + ") "
# Соблюдать язык форматирования, используя встроенный формат строк
# Поскольку мы знаем, что исходный format_spec заканчивается на 's',
# мы можем воспользоваться методом str.format со строковым аргументом,
# который мы создали выше
return "{r:{f}}".format( r=raw, f=format_spec )
inst = Example(1 ,2,3)
print "{0:>20s}".format( inst )
(1 ,2,3)
# вывод :
# Обратите внимание, как соблюдены выравнивание по правому краю и ширина поля 20.

Замечания:
Если у вашего пользовательского класса нет настраиваемого метода _format_ и экземпляр клас­
са передается в функпию format, Python 2 всегда будет использовать возвращаемое значение ме­
тода _str_ или метод _герг_ чтобы определить, что печатать (и если они не существуют, то по
умолчанию будет использоваться герг), и для форматирования потребуется использовать специ­
фикатор формата s. В Python 3, чтобы передать ваш пользовательский класс в функцию format,
вам нужно будет определить метод _formaL в вашем пользовательском классе.

222

Глава 41 . Строковые методы

Глава 41 . Строковые методы

41 . 1 . Изменение регистра букв в строке
Тип string в Python предоставляет множество функций, которые работают с регистром
букв в строке. К ним относятся:
• str.casefold


str.upper



str. lower



str.capitalize



str.title
• str.swapcase
С uniсоdе-строками (по умолчанию в Python 3) эти операции не являются отображаемы­
ми 1: 1 или обратимыми. Большинство из этих операций предназначены только для показа,
а не для нормализации.

Версия Python З.х ;с: 3.3
str.casefoldQ
Метод str.casefold создает создает строку в нижнем регистре (из строчных букв), пригод­
ную для сравнения без учета регистра. Действует более "агрессивно", чем str. lower, и может
изменять строки, которые уже находятся в нижнем регистре, или увеличивать длину строк,
не предназначен для отображения.
"XBr".casefoldQ
# 'xsso'
"XBГ. lower()
# 'х Вс;'
Преобразования, происходящие при действиях с регистрами, определяются консорциу­
мом Unicode в файле CaseFolding.txt на их веб-сайте.
str.upper()
Метод str. upper берет каждый символ в строке и преобразует его в свой эквивалент верхнего регистра (заглавных букв), например:
"This is а 'string'.".upper()
# 'TH I S IS А 'STRING'."
str. lower()
Метод str. lower делает обратное; он принимает каждый символ в строке и преобразует его
в его нижний регистр:
"This IS а 'string'.".lower()
# "this is а 'string'."
str.capitalize()
Метод str.capitalize возвращает "капитализированную" версию строки, т. е. делает первый
символ верхним регистром, а остальные ниже (как в предложениях):
"this 1s А 'String'.".capitalize() # Первы й символ заглавны ми буквами, остальные - строчны ми
# 'This is а 'string'."
str.title()
Метод str.title возвращает "заголовочную" версию строки, то есть каждая буква в начале
слова будет в верхнем регистре, а все остальные - в нижнем регистре:

41 .2. str.translate: замена символов в строке

223

"this 1s а 'String .title(}
# 'This 1s А 'String "'
"'

str.swapcase()
Метод str.swapcase возвращает новый строковый объект, в котором все символы нижнего
регистра меняются местами с символами верхнего регистра, а все символы верхнего реги­
стра - с символами нижнего:
"this iS А STRi NG".swapcase() # Поменяем регистр каждого символа
# "TH IS 1s а strlng"

Использование методов класса str

Стоит отметить, что эти методы могут вызываться либо на строковых объектах (как пока­
зано выше), либо как метод класса класса str (с явным вызовом - str.upper и т. д.).
str.upper{'This is а 'string "')
# "TH IS IS А 'STRI NG "'

Это наиболее полезно при применении одного из этих методов ко многим строкам сразу,
к примеру, в функции map.
map(str.upper,['These","are","some","'strings"']}
# [TH ESE', 'ARE', 'SOM E', '"STRINGS"']

41 .2. str.translate: замена сим волов в строке

Python поддерживает метод translate для типа данных str, который позволяет указать
так называемую таблицу замены (translation tаЫе), которая используется для замены симво­
лов, а также символы, которые должны быть удалены в процессе работы.
str.translate(taЫe[, deletechars]}

Параметр

Описание

таблица поиска, которая определяет соответствие одного символа другому.
tаЫе
список символов, которые должны быть удалены из строки.
deletechars
Метод maketrans (str.maketrans в Python 3 и string.maketrans в Python 2) позволяет сгенерировать таблицу замены.
»> translation_taЫe = str.maketrans("aeiou", "1 2345"}
»> my_string = 'This is а string!"
»> translated = my_string.translate(translation_taЫe)
ThЗs 3s 1 strЗng!'

Метод translate возвращает строку, которая является копией исходной строки с произве­
денными заменами. Если требуется удалить только символы, можно установить аргумент
tаЫе в None.
»> 'this syntax is very useful'.translate(None, 'aeiou')
'ths syntx s vry sfl'

41 .З. str.format и f-strings: форматирование значений в строку

Язык Python предоставляет возможность интерполяции и форматирования строк с помо­
щью функции str.format, появившейся в версии 2.6, и с помощью нового типа форматирован­
ных строковых литералов f-strings, появившихся в версии 3.6.
Даны следующие переменные:
i = 10
1 .5
s = "foo"

f=

1 = ['а', 1 , 2]

d = {'а': 1 , 2: 'foo'}

224

Глава 41 . Строковые методы

Следующие утверждения эквивалентны:
"1 О 1 . 5 foo ['а', 1 , 2] {'а': 1 , 2: 'foo'}"
»> "{} {} {} {} {}".format(i, f, s, 1, d)
>» str.format("{} {} {} {} {}", i, f, s, 1, d)
»> "{О} {1 } {2} {З} {4}".format(i, f, s, 1, d)
»> "{0:d} {1 :0.1 f} {2} {З! г} {4!r}".format(i, f, s, 1, d)
>» "{i:d} {f:0.1 f} {s} {l!r} {d! r}".format(i = i, f= f, s =s, 1 = 1, d = d)
»> f"{i} {f} {s} {1} {d}"
»> f"{i:d} {f:0.1 f} {s} {l ! r} {d !r}"

Для справки: Python также поддерживает квалификаторы в стиле С для форматирования
строк. Приведенные ниже примеры эквивалентны приведенным выше, но версии с исполь­
зованием str.format являются предпочтительными из-за преимуществ гибкости, согласован­
ности обозначений и расширяемости:
"%d %0.1 f %s %г %г" % (i, f, s, 1, d)
"%(i)d %(f)0.1 f %(s)s %(1)г %(d)r" % dict(i = i, f = f, s =s, 1 = 1, d = d)

Фигурные скобки, используемые для интерполяции в str.format, также могут быть прону­
мерованы для уменьшения дублирования при форматировании строк. Например, следую­
щие варианты являются эквивалентными:
"1 am from Australia. I love cupcakes from Australia!"
»> "1 am from {}. I love cupcakes from {}!".format("Australia", "Australia")
>» "1 am from {О}. I love cupcakes from {0}!".format("Australia")

Хотя официальная документация на Python, как обычно, достаточно основательна, мож­
но обратиться к сайту pyformat.info, где есть большой набор примеров с подробными объяс­
нениями.
Символы { и } могут быть экранированы следующим образом:
"{'а': 5, ' Ь': б}"

»> "{{'{}': {}, '{}': {}}}".format("a", 5, "Ь", 6)

»> f"{{'{'a'}': {5}, '{'Ь'}': {б}}"

См. главу "Форматирование строк" для получения дополнительной информации. Функ­
ция str.format() была предложена в РЕР 3101, а f-strings - в РЕР 498.

41 .4. Полезные константы модуля string

Модуль string языка Python предоставляет константы для операций, связанных со строка­
ми. Чтобы воспользоваться ими, импортируйте модуль string :
»> import string
string.ascii_letters :
Конкатенация ascii_lowercase и ascii_uppercase:
»> string.ascii_letters
'abcdefghijklmnopqrstuvwxyzABCDEFG H IJ KLM NOPQRSTUVWXYZ'
string.ascii_lowercase:

Содержит все символы ASCII нижнего регистра:

>» string.ascii_lowercase
'abcdefghijklmnopqrstuvwxyz'
string.ascii_uppercase:

Содержит все символы ASCII верхнего регистра:

»> string.ascii_uppercase
'ABCDEFGH IJKLMNOPQRSTUVWXYZ'

225

41 .5. Удаление ненужных ведущих и завершающих символов из строки
string.digits:

Содержит все десятичные знаковые символы:

»> string.digits
'01 23456789'
string. hexdigits:

Содержит все шестнадцатеричные символы цифр:

»> string.hexdigits
'01 23456789abcdefABCDEF'
string.octaldigits:

Содержит все восьмеричные символы цифр:

»> string.octaldigits
'01 234567'
string.punctuation:

Содержит все символы, которые считаются символами пунктуации в локализации С:

»> string.punctuation
'!"#$%&\'()*+,-./:;?@[\\] Л-'{1}~'
string.whitespace:

Содержит все символы ASCII, считающиеся пробелами:

»> string.whitespace
' \t\n\r\x0b\x0c'

В скриптовом режиме print(string.whitespace) будет печатать фактические символы, ис­
пользуйте str для получения строки, возвращенной выше.
string.printaЫe:

Содержит все символы, которые считаются пригодными для
string.digits, string.ascii_letters, string.punctuation и string.whitespace.

печати; это комбинация

»> string.printaЫe
'01 23456789abcdefghijklmnopqrstuvwxyzABCDEFGH IJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;?@
[\\] л_'{1}~ \t\n\r\x0b\x0c'

41 .5. Удаление ненужных ведущих и завершающих символов
из строки

Для удаления из строки ведущих и завершающих символов предусмотрены три метода:
str.strip, str.rstrip и str. lstrip. Все три метода имеют одинаковую сигнатуру и все три возвраща­

ют новый объект string с удаленными ненужными символами.
str.strip([chars])
Метод str.strip работает с заданной

строкой и удаляет (обрезает) все ведущие и завершаю­
щие символы, содержащиеся в аргументе chars; если chars не предоставлены или имеют зна­
чение None, все пробельные символы удаляются по умолчанию. Например:
»> " строка с ведущими и завершающими пробел ьными символами
'строка с ведущими и завершающими пробел ьны ми сим волами'

".strip()

Если заданы chars в качестве аргумента, то все содержащиеся символы удаляются из
строки, которая возвращается. Например:
»> "»> а Python prompt".strip('> ') # удаляет сим вол '>' и сим вол пробела
'а Python prompt'
str.rstrip([chars]) и str.lstrip([chars])

226

Глава 41 . Строковые методы

Эти методы имеют сходную семантику и аргументы с str.strip(), различие заключается
в направлении, с которого они начинают действовать. Метод str.rstrip() начинает с конца
строки, str.lstrip() - с начала строки.
Используем str.rstrip:
spacious string
spacious string'

»> "

'

".rstrip()

Используем str.lstrip :
>» " spacious string
'spacious string

".rstrip()

41 .б. Реверсирование строки

Строка может быть перевернута с помощью встроенной функции reversed(), которая принимает строку и возвращает итератор в обратном порядке.
>» reversed('hello')

»> [char for char in reversed('hello')]

['о', '1', '1', 'е', 'h']

Функция reversed() может быть "завернута" в вызов ".join() для создания строки из итератора.
»> ".join(reversed('hello'))
'olleh'

Хотя использование reversed() может быть более понятным для начинающих пользовате­
лей Python, использование расширенного среза с шагом -1 выполняется быстрее и является
более кратким. Реализуем его в виде функции:
»> def reversed_string(main_string):

return main_string[::-1 ]

»> reversed_string('hello')
'olleh'

41 . 7. Разбиение строки по разделителю на список строк
str.split(sep=None, maxsplit=- 1 )

Метод str.split принимает строку и возвращает список подстрок исходной строки. Пове­
дение зависит от того, предоставлен или исключен аргумент sep. Если sep не предоставлен
или равен None, то разбиение происходит везде, где есть пробелы. Однако ведущие и завер­
шающие пробелы игнорируются, а несколько последовательных символов пробела обраба­
тываются как один символ пробела:
»> ''Тhis is а sentence.".split()

['Тhis', 'is', 'а', 'sentence.']
>» " This is

а sentence. ".split()
['Тhis', 'is', 'а', 'sentence.']
>» "

D

".split()

Параметр sep может использоваться для определения строки-разделителя. Исходная
строка разделяется в том месте, где встречается разделитель, а сам разделитель отбрасыва­
ется. Несколько последовательных разделителей не рассматриваются так же, как одно вхож­
дение, а приводят к созданию пустых строк.
»> ''Тhis is а sentence.".split(' ')

['Тhis', 'is', 'а', 'sentence.']

41 .8. Замена всех вхождений одной подстроки на другую подстроку
»> "Earth,Stars,Sun,Moon".split(',')

227

['Earth', 'Stars', 'Sun', 'Мооп']

»> " This is

а sentence. ".split(' ')

[", 'T his', 'is', ", ", ", 'а', 'sentence.', ", "]

»> "This is а sentence.".split('e')

['Тhis is а s', 'пt', 'пс', '.']

»> ''Тhis is а sentence.".split('en')

['Тhis is а s', 't', 'се.']

По умолчанию используется разделение на каждое вхождение разделителя, однако па­
раметр maxsplit ограничивает количество разделителей. Значение по умолчанию -1 означа­
ет отсутствие ограничений:
»> "This is а sentence.".split('e', maxsplit=O)

['This is а sentence.']

»> "This is а sentence.''.split('e', maxsplit= 1 )

['Тhis i s а s', 'пtепсе.']

»> "This is а sentence.".split('e', maxsplit=2)

['This is а s', 'nt', 'псе.']

»> "This is а sentence.''.split('e', maxsplit=-1 )

['Тhis is а s', 'пt', 'пс', '.']

str.rsplit(sep=None, maxsplit=- 1 )
Метод str. rsplit ("right split", "разделение справа") отличается о т str.split ("left split", "разделение слева") при указании maxsplit. Разбиение начинается с конца строки, а не с начала:
»> "This is а sentence.".rsplit('e', maxsplit= 1 )

['This i s а sentenc', '.']

»> "This is а sentence.''.rsplit('e', maxsplit=2)

['Тhis is а sent', 'пс', '.']

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

41 .8. Замена всех вхождений одной подстроки на другую
подстроку

Тип str в Python также имеет метод замены вхождений одной подстроки на другую под­
строку в данной строке. Для более сложных случаев можно использовать метод re.sub.

str. replace(old, newL count]) :
Метод str.replace принимает два аргумента old и new, содержащих old подстроку, которая
должна быть заменена new подстрокой. Необязательный аргумент count указывает количе­
ство замен, которые должны быть произведены. Например, чтобы заменить 'foo' на 'spam'
в следующей строке, мы можем вызвать str.replace с old = 'foo' и new = 'spam':
»> "Make sure to foo your sentence.".replace('foo', 'spam')
"Make sure to spam your sentence.''

Если заданная строка содержит несколько примеров, которые соответствуют аргументу
old, то все вхождения заменяются значением, указанным в new:

228

Глава 41 . Строковые методы

>» "lt сап foo multiple examples of foo if you want.".replace('foo', 'spam')
"lt сап spam multiple examples of spam if you want."

если, конечно, мы не зададим значение для count. В этом случае вхождения count будут
заменены:
»> """lt сап foo multiple examples of foo if you want, \
... or you сап limit the foo with the third argument.""".replace('foo', 'spam', 1 )
'lt сап spam multiple examples of foo i f you want, о г you сап limit the foo with the third argument.'

41 . 9. Проверка содержимого строк

Тип str в Python также имеет ряд методов, которые могут быть использованы для оценки
содержимого строки. Это str.isalpha, str.isdigit, str.isalnum, str.isspace. Проверка регистра может
быть выполнена с помощью методов str.isupper, str.islower и str.istitle.
str.isalpha

Этот метод не принимает никаких аргументов и возвращает True, если все символы
в данной строке являются буквенными (алфавитными), например:
»> "Hello World".isalpha()
False
>» "Hello2World".isalpha()
False
»> "HelloWorld!".isalpha()
False
»> "HelloWorld".isalpha()
True

# содержит пробел
# содержит число
# содержит знаки

Как крайний случай пустая строка при использовании '"'.isalpha() оценивается как False.
str.isupper , str.islower , str.istitle

Эти методы проверяют регистр в заданной строке.
если все символы в заданной строке имеют верх-

str.isupper - метод, возвращающий True,
ний регистр, и False противном случае.
>» "HeLLO WORLD".isupper()
False
»> "HELLO WORLD".isupper()
True
»> '"'.isupper()
False

И наоборот, str.islower - это метод, который возвращает True, если все символы в данной
строке являются строчными, и False в противном случае.
»> "Hello world".islower()
False
>» "hello world".islower()
True
>» "".islower()
False
str.istitle возвращает значение True, если заданная строка "капитализирована", то есть каждое слово начинается с символа верхнего регистра, за которым следуют строчные буквы.
»> "hello world".istitle()
False
»> "Hello world".istitle()
False
»> "Hello World".istitle()
True
»> "".istitle()
False

229

41 .9. Проверка содержимого строк
str.isdecimal , str.isdigit , str.isnumeric

str.isdecimal возвращает, является ли данная строка последовательностью десятичных
цифр, пригодной для представления десятичного числа.
str.isdigit включает цифры, не имеющие формы, пригодной для представления десятично­
го числа, например надстрочные цифры.
str.isnumeric включает любые числовые значения, даже если они не являются цифрами,
например значения вне диапазона 0-9.
isdecimal

isdigit

isnumeric

1 2345

True

True

True

?2??5

True

True

True

?23????

False

True

True

??

False

False

True

Five

False

False

False

Строки байтов (bytes в Python 3, str в Python 2) поддерживают только функцию isdigit, кото­
рая проверяет только наличие основных АSСП-цифр. Как и в случае с str.isal pha, пустая стро­
ка оценивается как False.
str.isalnum

Это комбинация из str.isalpha и str.isnumeric, в частности, принимает значение True, если
все символы в данной строке являются буквенно-цифровыми , то есть они состоят из буквен­
ных (алфавитных) или числовых символов:
»> "Hello2World".isalnum()
True
»> "HelloWorld".isalnum()
True
»> "201 6".isalnum()
True
»> "Hello World".isalnum () # содержит пробел
False
str.isspace

Оценивается как True, если строка содержит только пробельные символы.
»> "\t\r\n".isspace()
True
»> " ".isspace()
True

Иногда строка выглядит "пустой", но мы не знаем, связано ли это с тем, что она содержит
пробельные символы или вообще ничего не содержит.
»> "".isspace()

False

Для этого случая нам необходима дополнительная проверка:

>>> my_str = "

»> my_str.isspace()

False

»> my_str.isspace() ог not my_str

True

Но самый короткий способ проверить, является ли строка пустой или содержит пробель­
ные символы - использовать функцию strip (без аргументов она удаляет все ведущие и завер­
шающие пробельные символы):
»> not my_str.strip()
True

230

Глава 41 . Строковые методы

41 . 1 О. Проверка содержания подстроки в строке

В Python интуитивно понятна проверка того, содержит ли строка заданную подстроку.
Для этого достаточно воспользоваться оператором in:
>» "foo" in "foo. baz.bar"
True

Примечание: при проверке пустой строки всегда будет получен результат True:
>>> "'' in "test"

True

41 . 1 1 . Объединение списка строк в одну строку

Строка может быть использована в качестве разделителя для объединения списка строк
в одну строку с помощью метода join(). Например, можно создать строку, в которой каждый
элемент списка отделяется пробелом.
>>> " ".join(["once","upon","a","time"])

"once upon а time"

В следующем примере элементы строки разделяются тремя дефисами.
>>> "--".join(["once", "upon", "а", "time"])

41 . 1 2. Подсчет количества вхождений подстроки в строке

Для подсчета количества вхождений подстроки в другую строку существует один метод -

str.count.

str.count(subL startL end]])

Метод str.count возвращает целочисленное int значение, указывающее на количество не­
пересекающихся вхождений подстроки sub в другую строку. Необязательные аргументы start
и end указывают начальные и конечные позиции, между которыми будет производиться по­
иск. По умолчанию start = О и end = len(str), то есть поиск будет производиться во всей строке:
>» s = "She sells seashells Ьу the seashore."
>» s.count("sh")
2
»> s.count("se")
3
»> s.count("sea")
2
>» s.count("seashells")
1

Задавая разные значения для start и end, мы можем получить более локализованный по­
иск и подсчет, например, если start равно 13, то вызов:
»> s.count("sea", start)
1

эквивалентен:
»> t = s[start:]
»> t.count("sea")

1

41 . 1 3. Сравнение строк без учета регистра

Сравнение строк без учета регистра кажется чем-то тривиальным, но это не так. В этом
разделе рассматриваются только строки в Unicode (в Python 3 - по умолчанию). Отметим,
что Python 2 может иметь незначительные недостатки по сравнению с Python 3 - работа
с Unicode в последнем гораздо более полная.

41 . 1 3. Сравнение строк без учета регистра

231

Прежде всего следует отметить, что преобразования для снятия регистра в Unicode не являются тривиальными. Существует текст, для которого
text.lower() != text.upper().lower(), such as "1!,":
»> "l!,".lower()

·r.,·

»> "l!,".upper().lower()

'ss'

Но, допустим, вы хотели сравнить "BUSSE" и "Bufse" без учета регистра. Или также хотите
сравнить "BUSSE" и "BUBE" равными - с новой формой заглавного символа. Рекомендуемым
способом является использование casefold:
Версия Python З.х :::: 3.3:
»> help(str.casefold}
Help оп method_descriptor:
casefold(...)
S.casefold() -> str
Return а version of S suitaЫe for caseless comparisons.
(Возвращает версию S, пригодную для сравнений без учета регистра)

Если casefold недоступен, выполнение .upper().lower() помогает (но только в некоторой
степени).
Теперь рассмотрим работу с ударениями. Вы, вероятно, думаете, что "е" == "е", но это не
так:
>>> "е" ==
False

"е"

Это потому, что на самом деле в первом случае мы имеем дело с буквой с циркумфлексом,
а во втором - с буквой с комбинированным циркумфлексным ударением:
»> import unicodedata
»> [unicodedata.name(char) for char in "е"]
['LATIN SMALL LЕТТЕR Е WITH C I RCUM FLEX']
»> [unicodedata.name(char) for char in "е"]
['LAТIN SMALL LЕТТЕR Е', 'CO M B I N I N G C I RCUM FLEX ACCENT]

Самый простой способ решения этой проблемы - unicoded ata.normal ize. Скорее всего,
вы хотите использовать нормализацию NFKD, но проверяйте документацию. Затем вы­
полните:
»> unicodedata.normal ize("N FKD", "е") == unicodedata.normalize("N FKD", "е")
True

В довершение это же с применением функций:
import unicodedata
def normalize_caseless(text):
return unicodedata.normalize("N FKD", text.casefold()}
def caseless_equal{left, right}:
return normalize_caseless(left) == normalize_caseless(right}

232

Глава 41 . Строковые м етоды

41 . 1 4. Выравнивание строк

Python предоставляет функции для выравнивания строк, позволяющие использовать за­
полнение текстом, что значительно упрощает выравнивание. Ниже приведен пример ис­
пользования функций str.ljust и str. rjust:
interstates_lengths = {
5: (1 381 , 2222),
1 9: (63, 1 02),
40: (2555, 41 1 2),
93: {1 89,305),

}

for road, length in interstates_lengths.items():
miles,kms = length
print('{} -> {} mi. ({} km.)'.format(str(road).rjust(4), str(miles).ljust(4), str(kms).ljust(4)))
40 -> 2555 mi. (41 1 2 km.)
1 9 -> 63 mi. (1 02 km.)
5 -> 1 381 mi. (2222 km.)
93 -> 1 89 mi. (305 km.)

Функции ljust и rjust очень похожи. Обе имеют параметр width и необязательный пара­
метр f1llchar. Любая строка, созданная этими функциями, имеет длину не менее параметра
width, который был передан в функцию. Если длина строки превышает ширину width, то она
не усекается. Аргумент f1llchar, в качестве которого по умолчанию используется символ про­
бела ' ', должен быть одним символом, а не многосимвольной строкой.
Функция ljust заполняет конец вызываемой строки символом fillchar до тех пор, пока дли­
на строки не станет равной ширине. Функция rjust аналогичным образом заполняет начало
строки. Таким образом, 1 и г в названиях этих функций означают сторону, с которой исходная
строка, а не символ-заполнитель, располагается в выходной строке.

41 . 1 5. Проверка начальных и конечных символов строки

Для проверки начала и конца строки в Python можно использовать методы str.startswith()
и str.endswith().
str.startswith(prefix(, start(, end)))

Как следует из названия, str.startswith используется для проверки того, начинается ли заданная строка с символов, заданных в pref1x.
»> s = 'This is а test string"
>» s.startswith("Т'')
True
>» s.startswith("Thi")
True
>» s.startswith("thi")
False
Необязательные аргументы start и end задают начальную и конечную точки, с которых
будет начинаться и заканчиваться тестирование. В следующем примере при указании зна­
чения start, равного 2, поиск строки будет производиться с позиции 2:
»> s.startswith("is", 2)
True
Это дает True, так как s[2] == 'i' и s[3] == 's'. Вы также можете проверить, начинается ли кортеж (tuple) с любой из множества строк:
»> s.startswith(('This', That'))
True
»> s.startswith (('ab', 'Ьс'))
False

41 .1 6. Преобразование между строковыми или байтовыми данными и символами Unicode

233

str.endswith(prefixL startL end]])

Метод str.endswith полностью аналогичен str.startswith, с той лишь разницей, что ищет
не начальные, а конечные символы. Например, чтобы проверить, заканчивается ли строка
символом полной остановки, т. е. точкой, можно написать:
»> s = "this ends in а full stop."
»> s.endswith('.')
Тгuе
»> s.endswith('!')
False

Как и в случае с staгtswith,в качестве завершающей последовательности может использоваться более одного символа:
»> s.endswith('stop.')
Тгuе
»> s.endswith('Stop.')
False

Вы также можете проверить, заканчивается ли кортеж какой-либо из множества строк.
»> s.endswith(('.', 'something'))
Тгuе
»> s.endswith (('ab', 'Ьс'})
False

41 . 1 6. Преобразование между строковы ми или байтовы ми
данными и сим волами Unicode

Содержимое файлов и сетевых сообщений может представлять собой закодированные
символы. Для правильного отображения их часто необходимо преобразовать в Unicode.
В Python 2 может потребоваться преобразование строковых данных в символы Unicode.
По умолчанию (", "" и т. д.) - это АSСП-строка, причем любые значения, выходящие за пре­
делы АSСП-диапазона, отображаются как экранированные значения. Строки Unicode имеют
вид u " (или u '"', и т. д.).
Версия Python 2.х � 2.3:
# В ы получаете "© аЬс" в кодировке UTF-8 из файла, сети или другого источника данных
s = '\хс2\ха9 аЬс'
s[O]
type(s)

# s - это массив байтов, а не строка символов
# Не знает, что оригинал был UTF-8,
# форма строковых литералов по умолчанию в Python 2
# '\хс2' - бессмысленны й байт (без контекста, например, кодировки)
# str - несмотря на то, что он не является полезным,
# без знания кодировки

# u'\xa9 аЬс'
# Теперь у нас есть строка Unicode, которая может быть
# прочитана как UTF-8 и вы ведена на печать правил ьно
# В Python 2 строковые литерал ы Unicode нуждаются в ведущем сим воле u
# str.decode преобразует строку, которая может содержать экранированные байты,
# в Unicode-cтpoкy

u = s.decode('utf-8'}

u[O]
type(u)

# u'\xa9' - сим вол Unicode 'Знак копирайта' (U+OOA9} '©'
# Unicode

u.encode('utf-8'}

# '\хс2\ха9 аЬс'
# unicode.encode выдает строку с экранированными байтами для
# нe-ASCI I символов

234

Глава 42. Использование циклов внутри функций

В Python З может возникнуть необходимость преобразования массивов байтов (называ­
емых "байтовыми литералами") в строки символов Unicode. По умолчанию используется
строка Unicode, и литералы байтовых строк теперь должны вводиться как Ь", ь·· и т. д. Бай­
товый литерал вернет True на isinstance(some_val, byte), предполагая, что some_val - это строка,
которая может быть закодирована в байтовом виде.
Версия Python З.х � 3.0:
# В ы получаете "© аЬс" в кодировке UTF-8 из файла или сети
s = Ь'\хс2\ха9 аЬс'
# s - массив байтов, а не символов
# В Python 3 строковый литерал по умолчанию имеет формат Unicode;
# для литералов байтовых массивов требуется ведущий сим вол Ь
# Ь'\хс2' - бессмысленный байт (без контекста, например, кодировки)
s[O]
# bytes - теперь, когда массивы байтов стали явны ми, Python может
type(s)
# показать это.
u = s.decode('utf-8')

# '© аЬс' на терминале Unicode
# bytes.decode преобразует массив байтов в строку,
# (которая в Python 3 будет иметь вид Unicode)

u[O]
type(u)

# '\u00a9' - сим вол Unicode 'Знак копирайта' (U+OOA9) '©'
# str
# Строковы м литералом по умолчанию в Python 3
# является UTF-8 Unicode
# Ь'\хс2\ха9 аЬс'
# str.encode создает массив байтов, отображающий байты
# диапазона ASCI I как неэкранированные символы

u.encode('utf-8')

Глава 42. Испол ьзование ци клов
внутри фун кций
В Python функция будет возвращена, как только выполнение перейдет к оператору воз­
врата.

42. 1 . Оператор возврата внутри цикла в функции
В данном примере функция вернется, как только значение переменной станет равным 1.
def func(params):
for value in params:
print ('Got value {}'.format(value))
if value == 1 :
# Возвращает из функции, как только значение становится равным 1
print ("»» Got 1 ")
return
print ("Still looping")
return "Couldn't find 1 "
func([S, 3, 1 , 2, 8, 9])

43.1 . Им порт модуля

235

результат:
Got value 5
Still looping
Got value З
Still looping
Got value 1
»» Got 1

Глава 43 . Импорт модулей
43. 1 . Импорт модуля

Используйте оператор import:

»> import random
»> print(random.randint(1 , 1 О))
4
import module импортирует модуль и затем позволяет ссылаться на его объекты - напри­
мер, значения, функции и классы - с помощью синтаксиса module.name. В приведенном
выше примере импортируется модуль random, который содержит функцию randint. Поэтому,
импортировав random, можно вызвать функцию randint с помощью random.randint. Вы можете
импортировать модуль и присвоить ему другое имя:
>» import random as rn
»> print(rn.randint(1 , 1 О))
4

Если ваш файл на Python main.py находится в той же папке, что и custom.py, вы можете
импортировать его следующим образом:
import custom

Также возможен импорт функции из модуля:

»> from math import sin
»> sin(1 )
0.841 4709848078965

Для импорта специальных функций глубже в модуль точечный оператор может исполь­
зоваться только в левой части от ключевого слова import:
from urllib.request import urlopen

В Python есть два способа вызова функции с верхнего уровня. Один из них - import, а дру­
гой - from. Первый способ следует использовать, когда есть вероятность столкновения имен.
Допустим, у нас есть файлы hello.py и world.py, в которых есть одна и та же функция с именем
function. Тогда оператор import будет работать хорошо.
from hello import function
from world import function
function() #будет в ызвана функция из world. А не функция hello

В общем случае import предоставляет пространство имен.
import hello
import world
hello.function() # будет вызвана исключительно функция из hello
world .function() # будет вызвана исключительно функция из world

236

Глава 43. Импорт модулей

Но если вы уверены, что во всем вашем проекте не может быть такого, чтобы одно и то
же имя функции вы использовали неоднократно, используйте оператор from. В одной строке
может быть выполнено несколько операций импорта:
»> # Множественные модул и
»> import time, sockets, random
>» # Множественные функции
>» from math import sin, cos, tan
>» # Множественные константы
»> from math import pi, е
»> print(pi)
3.1 41 592653589793
»> print(cos (45))
О.525321 98881 77297
»> print(time.time())
1 482807222.724041 7

Приведенные выше ключевые слова и синтаксис могут также использоваться в комбинациях:
>» from url l ib.request import urlopen as geturl, pathname2url as path2url, getproxies
>» from math import factorial as fact, gamma, atan as arctan
»> import random.randint, time, sys
»> print(time.time())
1 482807222.724041 7
»> print(arctan(60))
1 .5541 31 203080956
»> filepath = "/dogs/jumping poodle (december).png"
»> print(path2url(filepath))
/dogs/jumping%20poodle%20%28december%29.png

43.2. Специальная переменная _all_

Модули могут иметь специальную переменную _al l_ для ограничения того, какие пере­
менные импортируются при использовании from mymodu le import *.
Дан следующий модуль:
# mymodu le.py
_al l_ = ['imported_by_star']
imported_by_star = 42
not_imported_by_star = 21

При использовании функции from mymodu le import * импортируется только

imported_by_star:

»> from mymodule import *
»> imported_by_star
42
»> noLimported_by_star
Traceback (most recent call last):
File "", line 1 , in
NameError: name 'not_imported_by_star' is not def1ned

Однако not_imported_by_star может быть импортирован явным образом:
»> from mymodule import not_imported_by_star
>» noLimported_by_star

43.3. Им порт модулей из произвольного места файловой системы

43.3. Импорт модулей из произвольного места файловой
системы

237

Если вы хотите импортировать модуль, который еще не существует ни как встроенный
модуль в стандартной библиотеке Python (Python Standard Library), ни как побочный пакет,
вы можете добавить в sys.path путь к каталогу, в котором находится ваш модуль. Это может
быть полезно, если установлено несколько сред Python.
import sys
sys.path.append("/path/to/directory/containing/your/module")
import mymodule

Важно, чтобы в этом случае добавлялся путь к каталогу, в котором находится mymodule,
а не путь к самому модулю.

43.4. Импорт всех имен из модуля
from module_name import *

Например:

from math import *
sqrt(2)
# вместо math.sqrt(2)
# вместо math.ceil(2.7)
ceil(2.7)

При этом в глобальное пространство имен будут импортированы все имена, заданные в мо­
дуле math, за исключением имен, начинающихся с символа подчеркивания (который указыва­
ет на то, что автор считает это имя предназначенным только для внутреннего использования).
ПредупреждеШiе: Если функция с таким же именем уже была определена или импор­
тирована, то она будет перезаписана. Практически всегда рекомендуется импортировать
только конкретные имена from math import sqrt, ceil :
def sqrt(num):
print("Я не знаю, чему равен квадратны й корень из {}.".format(num))
sqrt(4)
# Вы вод: Я не знаю, чему равен квадратный корень из 4.
from math import *
sqrt(4)
# Вы вод: 2.0

Импорт со звездочками разрешен только на уровне модуля. Попытки выполнить их
в определениях классов или функций приводят к ошибке SyntaxError.
def f():
from math import *
class A:
from math import *
SyntaxError: import * only allowed at module level

43.5. Программный импорт

Версия Python 2.х � 2. 7:
Чтобы импортировать модуль через вызов функции, используйте модуль importlib (вхо­
дит в состав Python начиная с версии 2. 7):
import importlib
random = importlib.import_module("random")

Функция importlib.import_module() также будет импортировать подмодуль пакета напрямую:
collections_abc = importlib.import_module("collections.abc")
Для старых версий Python используйте модуль imp.

238

Глава 43. Импорт модулей

Версия Python 2.х :,; 2. 7:
Для выполнения программного импорта используйте функции imp.f1nd_module и imp. load_
module.

import imp, sys
def import_module(name):
fp, pathname, description = imp.find_module(name)
tгу:
return imp. load_module(name, fp, pathname, description)
final ly:
if fp:
fp.close()

Не используйте _import_() для программного импорта модулей! Существуют тонкие де­
тали, связанные с sys.modules, аргументом fromlist и т. д., которые легко упустить из виду
и которые importlib.imporLmodule() решает за вас.

43.6. Правила РЕР8 для импорта

Некоторые рекомендации по стилю РЕР8 для импорта.
1. Импорт должен быть на отдельных строках:

from math import sq rt, ceil
from math import sq rt
from math import ceil

# Не рекомендуется
# Рекомендуется

2. Располагайте операторы импорта в верхней части модуля в следующем порядке:
о Импорт стандартных библиотек
о
Импорт от сторонних производителей
о Конкретный импорт локальных приложений/бибилиотек
3. Следует избегать импорта с подстановочными знаками, поскольку это приводит к пу­
танице в именах в текущем пространстве имен. Если вы выполянете from module import *, то
может быть неясно, происходит ли конкретное имя в коде из module или нет. Это вдвойне
верно, если у вас есть несколько утверждений типа from module import *.
4. Избегайте использования относительного импорта, вместо него используйте явный
импорт.

43. 7. Импорт конкретных имен из модуля

Вместо импорта всего модуля можно импортировать только определенные, конкретные
имена:
from random import randint
print(randint(1 , 1 О))

# Синтаксис "from MODULENAM E import NAM E1 L NAM E2L ... ]]"
# Резул ьтат: 5

from random необходим, поскольку интерпретатор Python должен знать, из какого ресурса
ему следует импортировать функцию или класс, а import randint указывает на саму функцию
или класс. Еще один пример (аналогичный приведенному выше):
from math import pi
print(pi)
# Резул ьтат: 3 . 1 41 59265359

Следующий пример приведет к ошибке, поскольку мы не импортировали модуль:
random.randrange(1 , 1 О) # работает, тол ько если "import random" запускался ранее

Выведет:

NameError: name 'random' is not defined

43.8. Им порт субмодулей

239

Интерпретатор Python не понимает, что вы имеете в виду под словом random. Его необхо­
димо объявить, добавив, например import random:
import random
random. randrange(1 , 1 О)

43.8. Импорт субмодулей
from module.submod u le import function

Это импортирует function из modu le.submodu le.

43.9. Повторный импорт модуля

При работе с интерактивным интерпретатором может потребоваться перезагрузка моду­
ля. Это может быть полезно, если вы редактируете модуль и хотите импортировать самую
новую версию, или если вы провели манкипатчинг какого-то элемента существующего мо­
дуля и хотите вернуть изменения. Обратите внимание, что для возврата нельзя просто им­
портировать модуль заново:
import math
math.pi = 3
print(math.pi) # 3
import math
print(math.pi) # 3

Это происходит потому, что интерпретатор регистрирует каждый импортируемый мо­
дуль. И когда вы пытаетесь повторно импортировать модуль, интерпретатор видит его в рее­
стре и ничего не делает. Поэтому сложный способ повторного импорта - использовать import
после удаления соответствующего элемента из реестра:
print(math.pi) # 3
import sys
if 'math' in sys.modules: # Есть ли модул ь "math" в реестре?
del sys.modules['math'] # Если да, то удалите его.
import math
print(math.pi)
# 3. 1 41 592653589793

Но есть и более простой и понятный путь.
Python 2
Используем функцию reload:
Python 2.х версии ;;с: 2.3:
import math
math.pi = 3
print(math.pi) # 3
reload(math)
print(math.pi) # 3. 1 41 592653589793

Python 3
Функция reload перемещена в importlib:
Python 3.х версии ;;с: 3.0:
import math
math.pi = 3
print(math.pi) # 3
from importlib import reload
reload(math)
print(math.pi) # 3. 1 41 592653589793

240

Гл ава 44. Р азница между модулем и пакето м

43. 1 О. Функция _im p ort_()

Функция _import_() может быть использована для импорта модулей в случае, когда их
имена известны только во время выполнения программы:
if user_input == "os":
os = _import_("os")
# эквивалентно import os
Эта функция также может быть использована для указания пути к модулю:
mod = _import_(r"C:/path/to/file/anywhere/on/computer/module.py")

Глава 44. Разница между модулем
и пакетом
44. 1 . Модули

Модуль - это отдельный элемент языка Python, который может быть импортирован. Ис­
пользование модуля выглядит следующим образом:
module.py
def hi():
print("Hello world!")
my_script.py
import module
module.hi()
в интерпретаторе:
»> from module import hi
»> hi()
# Hello world!

44.2. Пакеты

Пакет состоит из нескольких файлов (или модулей) Python и даже может включать би­
блиотеки, написанные на языках С или С++. Вместо того чтобы быть одним файлом, он пред­
ставляет собой целую структуру папок, которая может выглядеть следующим образом:
Папка package
• _init_.py
• dog.py
• hi.py
_init_.py
from package.dog import woof
from package.hi import hi
dog.py
def woof():
print("W00F!!!")

45. 1 . Округление: round, floor, ceil, trunc

241

hi.py
def hi{}:
print("Hello world!")
Все пакеты Python должны содержать файл _init_. py. Когда вы импортируете пакет в
свой скрипт (сценарий) при помощи import package, будет запущен сценарий _init_.py, кото­
рый предоставит вам доступ ко всем функциям пакета. В приведенном случае он позволяет
использовать функции package.hi и package.woof.

Глава 45. Математический модул ь math
45. 1 . Округление: round, floor, ceil, trunc
Помимо встроенной функции round математический модуль предоставляет функции
floor, ceil и trunc.
х = 1 . 55
у = -1 .55
# округлить до ближайшего целого числа
round(x)
#2
# -2
round(y)
# второй аргумент указывает, до скол ьких знаков после запятой следует округлять
# (по умолчанию О)
# 1 .6
round(x, 1 )
round(y, 1 )
# -1 .6
# math - это модул ь, поэтому сначала импортируйте его, а затем используйте
import math
# получить наибол ьшее целое число, меньшее х
math. floor(x)
#1
math.floor(y)
# -2
# получить наименьшее целое число, бол ьшее х
math.ceil(x)
#2
math.ceil(y)
# -1
# отбросить дробную часть х
math.trunc(x)
# 1 , э квивалентно math.floor для положительных чисел
# -1 , эквивалентно math.ceil для отрицател ьных чисел
math.trunc(y)

Версия Python 2.х � 2. 7:

floor, ceil, trunc и round всегда возвращают число типа float.

round(1 .3) # 1 .0
round всегда округляет половинные значения "в сторону от ноля".
round(0.5) # 1 .0
round(1 . 5) # 2.0

Версия Python З.х � 3.0:

floor, ceil, trunc всегда возвращают целочисленное значение, а round возвращает целочис­
ленное значение, если вызывается с одним аргументом.

242

Глава 45. Математический модуль math

round(1 .3) # 1
round(1 .33, 1 ) # 1 .3

round округляет в сторону ближайшего четного числа. Это исправляет смещение в сторо­
ну больших чисел при выполнении большого количества вычислений.
round(0.5) # О
round(1 .5) # 2

Внимание!
Как и в любом другом представлении с плавающей точкой, некоторые дроби не могут
быть представлены точно. Это может привести к неожиданному поведению при округлении.
round(2.675, 2) # 2.67, не 2.68!

Предупреждение о функциях floor, trunc и целочисленном делении отрицательных
чисел
В языке Python (а также в С++ и Java) отрицательные числа округляются от нуля. Пример:
»> math.floor(-1 .7)
-2.0
»> -5 // 2
-3

45.2. Тригонометрия
Вычисление длины гипотенузы
math.hypot(2, 4) # Просто сокращение для SquareRoot(2*2 + 4*2)
# Результат: 4.4721 3 595499958

Преобразование градусов в радианы и обратно
Все функции модуля math предполагают использование радиан, поэтому необходимо
преобразовать градусы в радианы:
# П реобразование 45 градусов в радианы
math.radians(45)
# Результат: 0.7853981 633974483

Все результаты обратных тригонометрических функций возвращают результат в радиа­
нах, поэтому может потребоваться обратное преобразование в градусы:
math.degrees(math.asin(1 ))
# Результат: 90.0

# П реобразование результата в градусы

Синус, косинус, тангенс и обратные функции
# Синус и арксинус:
math.sin(math.pi / 2)
# Результат: 1 .0
math.sin(math.radians(90))
# Результат: 1 .0

# Синус 90 градусов

math.asin(1 )
# Результат: 1 .5707963267948966 # "= pi / 2"
math.asin(1 ) / math.pi
# Результат: 0.5
# Косинус и арккосинус:
math.cos(math.pi / 2)
# Результат: 6.1 23233995736766е-1 7
# Почти ноль, но не совсем, потому что "pi" - число типа float с ограниченной точностью!

45.3. Функция pow для быстрого возведения в степень

243

math.acos{1 )
# Результат: О.О
# Тангенс и арктангенс:
math.tan(math.pi/2)
# Результат: 1 .6331 2393531 9537е+1 6
# Очень бол ьшое, но не бесконечное число, потому что "pi" - число типа float
# с ограниченной точностью

Версия Python З.х :::: 3.5:
math.atan(math.inf)
# Результат: 1 .5707963267948966 # Это просто "pi / 2"
math.atan{float('inf'))
# Результат: 1 .5707963267948966 # Это просто "pi / 2"
Помимо math.atan существует также двухаргументная функция math.atan2, которая вы­
числяет правильный квадрант и позволяет избежать ловушек, связанных с делением на
ноль:
math.atan2{1 , 2) # Эквивалентно "math.atan{1 /2)"
# Результат: О.4636476090008061 # ::: 26.57 градусов, 1 -й квадрант
math.atan2{-1 , -2) # Не равно "math.atan{-1 /-2)" == "math.atan{1 /2)"
# Результат: -2. 677945044588987 # ::: -1 53.43 градусов (или 206.57 градусов), 3-й квадрант
math.atan2{1 , О) # math.atan{1 /0) вызовет ошибку ZeroDivisionError
# Результат: 1 . 5707963267948966 # Это просто "pi / 2"

ГШiерболические синус, косинус и тангенс
# Функция гиперболического синуса
math.sinh{math.pi) # = 1 1 .548739357257746
math.asinh{1 ) # = 0.881 37358701 9 5429
# Функция гиперболического косинуса
math.cosh{math.pi) # = 1 1 .59 1 953275521 51 9
math.acosh{1 ) # = О.О
# Функция гиперболического тангенса
math.tanh{math. pi) # = О.99627207622075
math.atanh{0.5) # = О.5493061 443340549

45.З. Функция pow для быстрого возведения
в степень
Используем модуль timeit из командной строки:
> python -m timeit 'for х in xrange{50000): Ь = х*3'

1 О loops, best of 3: 51 .2 msec per loop

> python -m timeit 'from math import pow' 'for х in xrange{50000): Ь = pow(x,3)'

1 00 loops, best of 3: 9.1 5 msec per loop

Встроенный оператор * часто бывает полезен, но если производительность важна,
то используйте math.pow. При этом следует учитывать, что pow возвращает данные типа
float, даже если аргументы являются целыми числами:
> from math import pow
> pow{5,5)

31 25.0

244

Глава 45. Математический модуль math

45.4. Бесконечность и NaN ("не число")
Во всех версиях Python мы можем представить бесконечность и NaN ("не число", "not
а number") следующим образом:
pos_inf = float('inf')
neg_inf = float('-inf')
not_a_num = float('nan')

# положительная бесконечность
# отрицател ьная бесконечность
# NaN ("не число")

В Python 3.5 и выше мы также можем использовать задекларированные константы
math.inf и math.nan:

Версия Python 3.х � 3.5
pos_inf = math. inf
neg_inf = -math.inf
not_a_num = math.nan
Строковые представления отображаются в виде inf и -inf и пап:
pos_inf, neg_inf, not_a_num
# Результат: (inf, -inf, пап)
С помощью метода isinf мы можем провести проверку на положительную или отрицательную бесконечность:
math.isinf(pos_inf)
# Результат: True
math.isinf(neg_inf)
# Результат: True
М ы можем провести проверку на положительную или отрицательную бесконечность путем
пря мого сравнения:
pos_inf == float('inf') # или == math.inf в Python 3.5+
# Резул ьтат: True
neg_inf == float('-inf') # или == -math.inf в Python 3.5+
# Результат: True
neg_inf == pos_inf
# Результат: False
Python 3.2 и выше также позволяет проверять конечность:

Версия Python 3.х � 3.2
math.isf1nite(pos_inf)
# Результат: False
math.isf1nite(0.0)
# Результат: True
Операторы сравнения работают, как и ожидалось, для положител ьной и отрицательной
бесконечности:
import sys
sys.float_info.max
# Результат: 1 .7976931 3486231 57е+308 (это зависит от системы)
pos_inf > sys.floaLinfo.max
# Результат: True

45.4. Бесконечность и NaN ("не число")

245

neg_inf < -sys.float_info.max
# Результат: True

Но если арифметическое выражение выдает значение, превышающее максимум, который может быть представлен в виде числа типа float, то оно становится бесконечностью:
pos_inf == sys.float_info.max * 1 .0000001
# Результат: True
neg_inf == -sys.float_info. max * 1 .0000001
# Результат: True

Однако деление на ноль не дает результата, равного бесконечности (или отрицательной
бесконечности), а вызывает исключение ZeroDivisionError.
try:
х = 1 .0 / о.о
print(x)
except ZeroDivisionError:
ргint("Деление на ноль")
# Результат: Деление на нол ь

Арифметические операции над бесконечностью дают в результате бесконечность, или
иногда NaN:
-5.0 * pos_inf == neg_inf
# Результат: True
-5.0 * neg_inf == pos_inf
# Результат: True
pos_inf * neg_inf == neg_inf
# Результат: True
О.О * pos_inf
# Результат: пап
О.О * neg_inf
# Результат: пап
pos_inf / pos_inf
# Результат: пап

NaN никогда не равно ничему, даже самому себе. Мы можем проверить это с помощью
метода isnan:
not_a_num == not_a_num
# Результат: False
math.isnan(not_a_num)
Результат: True

NaN всегда сравнивается как "не равно", но никогда не меньше или больше:
not_a_num != 5.0 # или любое случайное значение
# Результат: True
not_a_num > 5.0 ог not_a_num < 5.0 ог not_a_num == 5.0
# Результат: False

Глава 45. Математический модуль math

246

Арифметические операции над NaN всегда дают NaN. Это касается и умножения на -1 :
"отрицательного NaN" не существует.
5.0 * not_a_num
# Результат: пап
float('-nan')
# Результат: пап

Python 3.х Version � 3.5
-math.nan
# Результат: пап
Существует одно тонкое различие между старыми flоаt-версиями NaN и бесконечности и
константами математической библиотеки math в Python 3.5+:

Версия Python 3.х � 3.5:
math.inf is math.inf, math.nan is math.nan
# Результат: (True, True)
float('inf') is float('inf'), float('nan') is float('nan')
# Результат: (False, False)

45.5. Логарифмы

math.log(x) вычисляет натуральный (по основанию е) логарифм от х.

math.log(math.e)
math.log(1 )
math. log(1 00)

# 1 .0
# О.О
# 4.6051 701 85988092

math. log может терять точность при вычислении чисел, близких к 1, из-за ограничений
чисел типа float. Для точного вычисления логарифмов, близких к 1, следует использовать
math.log1 р, который вычисляет натуральный логарифм от 1 плюс аргумент:
math.log(1 + 1 е-20) # О.О
math.log1 р(1 е-20) # 1 е-20
math.log1 О может быть использован для вычисления логарифмов по основанию 10:
math.log1 0(1 О) # 1 .0

Версия Python 2.х � 2.3.0

При использовании двух аргументов math. log(x, base) выдает логарифм х по заданному основанию (т.е. log(x) / log(base)).
math.log(1 00, 1 О) # 2.0
math. log(27, 3) # 3.0
math.log(1 , 1 О) # О.О

45.6. Константы

Модули math включают в себя две часто используемые математические константы.
• math.pi - математическая константа числа "пи"
• math.e - математическая константа е (основание натурального логарифма)

»> from math import pi, е
»> pi
3.1 41 592653589793

247

45.7. Мнимые числа
>>> е
2.71 8281 828459045
>>>

В Python 3.5 и выше имеются константы для бесконечности и NaN ("не числа"). Более ста­
рый синтаксис передачи строки в float() по-прежнему работает.
Версия Python З.х � 3.5:
math.inf == float('inf')
# Результат: True
-math.inf == float('-inf')
# Результат: True
# NaN никогда не сравнивается ни с чем, даже с самим собой
math.nan == float('nan')
# Результат: False

45. 7. Мнимые числа

Мнимые числа в Python обозначаются символом "j" или "J", стоящим после заданного
числа.
1j
1j * 1j

# Эквивалентно квадратному корню из -1 .
# = (-1 +0j)

45.8. Копирование знаков

В Python 2.6 и выше функция math.copysign(x, у) возвращает х со знаком у. Возвращаемое
значение всегда имеет тип float.
Версия Python 2.х � 2.6
math.copysign(-2, 3)
math.copysign(3, -3)
math.copysign(4, 1 4.2)
math.copysign(1 , -О.О)

# 2.0
# -3.0
# 4.0
# -1 .0, на платформе, поддерживающей "знаковый нол ь"

45.9. Комплексные числа и модуль cmath

Модуль cmath аналогичен модулю math, но определяет функции, подходящие для ком­
плексной плоскости. Прежде всего комплексные числа - это числовой тип, который являет­
ся частью самого языка Python, а не предоставляется библиотечным классом. Поэтому нам
не нужно выполнять import cmath для обычных арифметических выражений. Обратите вни­
мание, что мы используем j (или J), а не i.
z = 1 + 3j

Следует использовать 1 j, поскольку j будет именем переменной, а не числовым литералом.
1j * 1 j
Результат: (-1 +0j)

1j ** 1 j
# Результат: (О.207879576350761 93+0j) # "i to the i " == math.e ** -(math.pi/2)

Мы имеем действительную (real) и мнимую (imag) части, а также комплексную сопряжен­
ную (conjugate):

248

Глава 45. Математический модуль math

# действительная часть и мнимая часть имеют тип float
z.real, z.imag
# Результат: (1 .0, 3.0)
z.conjugate()
# Результат: (1 -3j) # z.conjugate() == z.real - z.imag * 1 j

Встроенные функции abs и complex также являются частью самого языка и не требуют
импорта:
abs(1 + 1 j)
# Результат: 1 .41 421 3 5623730951 # квадратный корень из 2
complex(1 )
# Результат: (1 +0j)
complex(imag=1 )
# Результат: (1j)
complex(1 , 1 )
# Результат: ( 1 +1 j)
Функция complex может принимать строку, но в ней не может быть пробелов:
complex('1 + 1 j')
# Результат: (1 +1 j)
complex('1 + 1 j')
# Exception: ValueError: complex() arg is а malformed string (complex() arg - неправильно
сформированная строка)

Но для большинства функций модуль все же нужен, например для sqrt:
import cmath
cmath.sqrt(-1 )
# Результат: 1 j

Естественно, поведение sqrt различно для комплексных и вещественных чисел. Для не­
комплексных при применении модуля math квадратный корень из отрицательного числа
вызывает исключение:
import math
math.sqrt(-1 )
# Exception: ValueError: math domain еггог (ошибка математической области (домена))

Предусмотрены функции для преобразования в полярные координаты и обратно:
cmath.polar(1 + 1 j)
# Результат: (1 .41 421 3 5623730951 , 0.7853981 633974483) # == (sqrt(1 + 1 ), atan2(1 , 1 ))
abs(1 + 1 j), cmath.phase(1 + 1 j)
# Результат: (1 .41 421 3 5623730951 , 0.7853981 633974483) # аналогично предыдущему
cmath.rect(math.sqrt(2), math.atan(1 ))
# Результат: (1 .0000000000000002+1 .0000000000000002j)

Математическая область комплексного анализа выходит за рамки данного примера, но
многие функции в комплексной плоскости имеют "срез ветви", обычно вдоль действитель­
ной или мнимой оси. Большинство современных платформ поддерживают "подписанный
ноль", как указано в IEEE 754, что обеспечивает непрерывность этих функций по обе сторо­
ны от среза ветви. Следующий пример взят из документации по языку Python:
cmath.phase(complex(-1 .0, О.О))
# Результат: 3.1 41 592653589793

45.9. Комплексные числа и модуль cmath

249

cmath.phase(complex(-1 .O, -О.О))
# Результат: -3. 1 41 592653589793
Модуль cmath также предоставляет множество функций, имеющих прямые аналоги из
модуля math. Кроме sqrt существуют комплексные версии ехр, log, log1 О, тригонометриче­
ских функций и их инверсий (sin, cos, tan, asin, acos, atan), а также гиперболических функций
и их инверсий (sinh, cosh, tanh, asinh, acosh, atanh). Заметим, однако, что комплексный аналог
math.atan2, двухаргументной формы арктангенса, отсутствует.
cmath.log(1 + 1 j)
# Результат: (О.34657359O27997264+O.7853981 633974483j)
cmath.exp(1 j * cmath.pi)
# Результат: (-1 +1 .2246467991 473532е-1 бj) # е для i pi == -1 , в пределах ошибки округления
Константы pi и е предоставляются Python. Обратите внимание, что они имеют тип дан­
ных float, а не complex.
type(cmath.pi)
# Результат:
Модуль cmath также предоставляет комплексные версии isinf и (для Python 3.2+) isf1nite
(см. главу "Бесконечность и NaN ("не число")"). Комплексное число считается бесконечным,
если либо его действительная часть, либо мнимая часть являются бесконечными.
cmath.isinf(complex(float('inf'), О.О))
# Результат: True
Аналогично модуль cmath предоставляет комплескную версию isnan. (См. главу "Беско­
нечность и NaN ("не число")"). Комплексное число считается "не числом", если либо его дей­
ствительная часть, либо мнимая часть "не число".
cmath.isnan(O.O, float('nan'))
# Результат: True
Обратите внимание, что в cmath аналог для констант math. inf и math.nan отсутствует (на­
чиная с Python 3.5 и выше):

Версия Python 3.х � 3.5:
cmath.isinf(complex(O. O, math.inf))
# Результат: True
cmath.isnan(complex(math.nan, О.О))
# Результат: True
cmath.inf
# Exception: AttributeError: module 'cmath' has по attribute 'inf' (модуль 'cmath' не имеет атрибута
'inf')
В Python 3.5 и выше в модулях cmath и math есть метод isclose.

Версия Python 3.х � 3.5:
z = cmath.rect(*cmath. polar(1 +1 j))
z
# Результат: (1 .OOOOOOOOOOOOOOO2+1 .OOOOOOOOOOOOOOO2j)
cmath.isclose(z, 1 +1 j)
# True

250

Глава 46. Комплексная математика

Глава 4 6. Комплексная математика

46. 1 . Расширенная комплексная арифметика

Модуль cmath включает допотштельные функции для работы с комплексными числами.

import cmath

Данный модуль позволяет вычислять фазу комплексного числа в радианах:
# Комплексное число
z = 2+3j
cmath.phase(z) # О.982793723247329

Он позволяет преобразовывать комплексные числа между декартовым (прямоугольным
или тригонометрическим) и полярным представлениями:
cmath.polar(z)
# (3.605551 275463989, О.982793723247329)
cmath.rect(2, cmath.pi/2) # (0+2j)

Модуль содержит версию для работы с комплексными числами для:
• экспоненциальных и логарифмических функции (как обычно, log - натуральный
логарифм, log1 О - десятичный логарифм):
cmath.exp(z)
cmath.log(z)
cmath. log 1 0(-1 00)



# (1 .2824746787307684+0. 982793723247329j)
# (2+1 .364376353841 841 2j)

квадратных корней:
# (1 .6741 492280355401 +0.89 59774761 298381j)

cmath.sqrt(z)



# (-7.31 51 1 0094901 1 03+1 .0427436562359045j)

тригонометрических функций и обратных к ним:

cmath.sin(z)
# (9. 1 544991 469 1 1 43-4.1 68906959966565j)
cmath.cos(z)
# (-4. 1 89625690968807-9 . 1 09227893755337j)
cmath.tan(z)
# (-0.003764025641 504249+1 .0032386273 5361j)
cmath.asin(z)
# (О.570652784321 0994+1 . 9833870299 1 65355j)
cmath.acos(z)
# (1 .0001 435424737972-1 .98338702991 65355j)
cmath.atan(z)
# (1 .409921 0495965755+0.22907268296853878j)
cmath.sin(z)*2 + cmath.cos(z)**2 # (1 +0j)



гиперболических функций и обратных к ним:

cmath.sinh(z)
# (-3 . 59056458998578+0.530921 08624851 97j)
cmath.cosh(z)
# (-3 .72454550491 53224+0. 51 1 8225699873846j)
cmath.tanh(z)
# (О. 9653858790221 33-0.00988437503832249 5j)
cmath.asinh(z)
# (0.570652784321 0994+1 . 9833870299 1 65355j)
cmath.acosh(z)
# (1 . 98338702991 65355+1 .0001 435424737972j)
cmath.atanh(z)
# (0. 1 4694666622552977+1 .3389725222944935j)
cmath.cosh(z)*2 - cmath.sin(z)**2 # (1 +0j)
cmath.cosh((0+1 j)*z) - cmath.cos(z) # 0j

46.2. Основы комплексной арифметики

В Python имеется встроенная поддержка комплексной арифметики. Мнимая единица
обозначается j:
z = 2+3j
w = 1 -7j

# Комплексное число
# Еще одно комплексное число

Комплексные числа можно складывать, вычитать, умножать, делить и возводить в степень:
z+w
z-w

# (3-4j)
# (1 +1 0j)

47. 1 . collections.Counter
z*w
z/w
z**3

251

# (23-1 1 j}

# (-0.38+0.34j}
# (-46+9j}

Python также может выделять действительную и мнимую части комплексных чисел, вы­
числять их абсолютное значение и сопряженность:
z.real
z.imag
abs(z)
z.conjugate()

# 2.0
# 3.0
# 3.605551 275463989
# (2-3j}

Глава 4 7 . Модуль col lections
Встроенный пакет collections предоставляет несколько специализированных, гибких ти­
пов коллекций, которые отличаются высокой производительностью и являются альтерна­
тивой общим типам коллекций dict, list, tuple и set. Модуль также определяет абстрактные
базовые классы, описывающие различные виды функциональности коллекций (например,
M utaЬleSet и ltemsView).

47. 1 . collections.Counter

Counter - это подкласс, позволяющий легко подсчитывать объекты. В нем имеются слу­
жебные методы для работы с частотой встречаемости объектов, которые вы подсчитываете.
import collections
counts = collections.Counter([1 ,2,3]}

Приведенный выше код создает объект counts, который содержит частоты всех элемен­
тов, передаваемых в конструктор. В данном примере значение Counter({1 : 1 , 2: 1 , 3: 1 }).
Примеры конструкторов
Счетчик букв
»> collections.Counter('Happy Birthday')

Counter({'a': 2, 'р': 2, 'у': 2, 'i': 1 , 'г': 1 , 'В': 1 , ' ': 1 , 'Н': 1 , 'd': 1 , 'h': 1 , 't': 1 })

Счетчик слов
»> collections.Counter('I am Sam Sam I am That Sam-I-am That Sam-I-am! 1 do not like that Sam-lam'.
split())
Counter({'I': 3, 'Sam': 2, 'Sam-I-am': 2, 'Тhat': 2, 'am': 2, 'do': 1 , 'Sam-I-am!': 1 , 'that': 1 , 'поt': 1 , 'like': 1 })

Рецепты
»> с = collections.Counter({'a': 4, 'Ь': 2, 'с': -2, 'd': О}}
Получение количества отдельных элементов:
»> с['а']
4

Установка количества отдельных элементов:
»> с['с'] = -3
>>> с

Counter({'a': 4, 'Ь': 2, 'd': О, 'с': -3}}

Получение общего количества элементов в счетчике (4 + 2 + О - 3):
»> sum(c. itervalues()) # отрицательные числа учитываются !

3

Глава 47. Модуль collections

252

Получить элементы (сохраняются только те, которые имеют положительный счетчик):
»> l ist(c.elements())

['а', 'а', 'а', 'а', 'Ь', 'Ь']

Удаление ключей с О или отрицательным значением:
»> с - collections.Counter()
Counter({'a': 4, 'Ь': 2})

Удалить все:
»> c.clear()

>>> с
Counter()

Добавить/удалить отдельные элементы:
»> c.update({'a': 3, 'Ь':3})
»> c.update({'a': 2, 'с':2})
>>>

с
Counter({'a': 5, 'Ь': 3, 'с': 2})
»> c.suЬtract({'a': 3, 'Ь': 3, 'с': 3})
>>> с
Counter({'a': 2, 'Ь': О, 'с': -1 })

# добавляет к существующим, устанавливает, если их нет

# вычитает (допускаются отрицательные значения)

47.2. collections.OrderedDict

Порядок ключей в словарях Python произвольный: он не зависит от порядка их добавления. Например:
»> d = {'foo': 5, 'Ьаг': 6}
»> print(d)

{'foo': 5, 'Ьаг': 6}
»> d ['baz'] = 7
»> pri nt( а)
{'baz': 7, 'foo': 5, 'Ьаг': 6}
>» d ['foobar'] = 8
»> print(a)
{'baz': 7, 'foo': 5, 'Ьаг': 6, 'foobar': 8}

(Произвольное упорядочивание, подразумеваемое выше, означает, что при использова­
нии приведенного кода можно получить разные результаты по сравнению с тем, что пока­
зано здесь).
Порядок появления ключей соответствует порядку их итерации, например в цикле
for. Класс collections.Ordered Dict предоставляет объекты словарей, сохраняющие порядок
следования ключей. Упорядоченные словари (Ordered Dicts) могут быть созданы, как по­
казано ниже, с помощью серии упорядоченных элементов (здесь - список кортежей пар
"ключ-значение"):
»> from collections import Ordered Dict
»> d = Ordered D ict([('foo', 5), ('Ьаг', 6)])
>» print(d)

Ordered Dict([('foo', 5), ('Ьаг', 6)])
»> d ['baz'] = 7
»> pri nt( d)
Ordered Dict([('foo', 5), ('Ьаг', 6), ('baz', 7)])
»> d ['foobar'] = 8
»> pri nt( d)
Ordered Dict([('foo', 5), ('Ьаг', 6), ('baz', 7), ('foobar', 8)])

253

47.3. collections.defaultdict

Или мы можем создать пустой Ordered Dict и затем добавить в него элементы:
»> о = Ordered Dict()
»> o['key1 '] = "value1 "
»> o['key2'] = "value2"
»> print(o)
Ordered Dict([('key1 ', 'value1 '), ('key2', 'value2')])

Итерация в Ordered Dict позволяет получить доступ к ключам в том порядке, в котором
они бьши добавлены. Что произойдет, если мы присвоим новое значение существующему
ключу?
»> d['foo'] = 4
»> print( d)
Ordered Dict([('foo', 4), ('Ьаг', 6), ('baz', 7), ('fооЬаг', 8)])

Ключ сохраняет свое первоначальное место в Ordered Dict.

47.3. collections.defaultdict

collections.defaultdict(default_factory) возвращает подкласс dict, имеющий значение по умол­
чанию для отсутствующих ключей. Аргументом должна быть функция, возвращающая зна­
чение по умолчанию при вызове без аргументов. Если ничего не передано, то по умолчанию
возвращается значение None.
»> state_capitals = collections.defaultdict(str)
>» state_capitals
defaultdict(, {})

Этот код возвращает ссылку на defaultdict, который создаст строковый объект с помощью
своего метода default_factory.
Типичным применением defaultdict является использование в качестве default_factory од­
ного из встроенных типов, таких как str, int, list или dict, так как они возвращают пустые
типы при вызове без аргументов:
»> str()

и

»> int()

о



»> list

Вызов defaultdict с несуществующим ключом не приводит к ошибке, как это бьшо бы в
обычном словаре.
»> state_capitals['A laska']

и

»> state_capitals
defaultdict(, {'Alaska': "})
Другой пример, с типом данных int:
»> fruiLcounts = defaultdict(int)
»> fruiLcounts['apple'] += 2
»> fruiLcounts
default_dict(int, {'apple': 2})
»> fruiLcounts['Ьanana']

о

# Ошибки возникать не должны
# Ошибки возникать не должны

# Создается новый ключ
»> fruiLcounts
default_dict(int, {'apple': 2, 'banana': О})

Обычные словарные методы работают со словарем по умолчанию:
»> state_capitals['Alabama'] = 'Montgomery'
»> state_capitals
defaultdict(, {'Alabama': 'Montgomery', 'Alaska': "})

254

Глава 47. Модуль collections

Использование list в качестве defaulLfactory приведет к созданию списка для каждого нового ключа.
»> s = [('N C', 'Raleigh'), ('VI( 'Richmond'), ('WI( 'Seattle'), ('NC', 'Asheville')]
»> dd = col lections.defaultdict(l ist)
»> for k, v in s:
... dd[k].append(v)
>» dd
defaultdict(,
{VA': ['Richmond'],
'NC': ['Raleigh', 'Asheville'],
'WN: ['Seattle']})

47.4. collections.namedtuple

Создайте новый тип Person с помощью namedtuple следующим образом:

Person = namedtuple('Person', ['age', 'height', 'name'])

Второй аргумент представляет собой список атрибутов, которыми будет обладать кор­
теж. Эти атрибуты также можно перечислить в виде строки, разделенной пробелом или за­
пятой:
Person = namedtuple('Person', 'age, height, name')

или

Person = namedtuple('Person', 'age height name')

После определения именованный кортеж может быть инстанцирован путем вызова объ­
екта с необходимыми параметрами, например:
dave = Person(З0, 1 78, 'Dave')

Также могут использоваться именованные аргументы:

jack = Person(age=З0, height= 1 78, name='Jack S.')

Теперь можно получить доступ к атрибутам именованного кортежа:

printUack.age) # 30
printUack.name) # 'Jack S.'

Первым аргументом конструктора именованного кортежа (в нашем примере 'Person') яв­
ляется имя типа - typename. Обычно для конструктора и имени типа используется одно и то
же слово, но они могут быть разными:
Human = namedtuple('Person', 'age, height, name')
dave = Human(З0, 1 78, 'Dave')
print(dave) # yields: Person(age=З0, height=1 78, name='Dave')

47.5. collections.deque

Возвращает новый объект deque, инициализированный слева направо (с помощью функ­
ции append()) с данными из итерируемого объекта. Если итерируемый объект не указан, то
новый deque будет пустым.
Deque - это обобщение стеков и очередей (название является сокращением от "douЫe­
ended queue", "двусторонняя очередь"). Поддерживаются потокобезопасные, экономящие па­
мять добавления и удаления с любой стороны deque с примерно одинаковой производитель­
ностью 0(1) в любом направлении. Хотя списочные объекты поддерживают аналогичные
операции, они оптимизированы для быстрых операций с фиксированной длиной и несут
O(n) затрат на перемещение памяти для операций рор(О) и insert(0, v), которые изменяют как
размер, так и положение базового представления данных.
Начиная с версии Python 2.4, если параметр maxlen не задан или равен None, то deque
может расти до произвольной длины. В противном случае deque ограничивается заданной

255

47.5. collections.deque

максимальной длиной. После заполнения deque ограниченной длины при добавлении
новых элементов соответствующее количество элементов удаляется с противополож­
ного конца. Дескрипторы ограниченной длины обеспечивают функциональность, ана­
логичную хвостовому фильтру (tail filter) в Unix. Они также полезны для отслеживания
транзакций и других массивов данных, где интерес представляет только самая послед­
няя активность.
»> from collections import deque
# создать нов ы й deque с тремя элементами
»> d = deque('ghi')
»> for elem in d:
# итерация по элементам deque
... print elem.upper()
G

н
1

»> d.append('j')
»> d.appendleft('f')
»> d
deque(['f', 'g', 'h', 'i', 'j'])

# добавить новую запись в правую часть
# добавить новую запись в левую часть
# показать представление deque

»> d.pop()

# вернуть и удалить крайний правы й элемент

т»> d.popleft()

'f'
»> list(d)
['g', 'h', 'i']
»> d[O]
'g'
>» d[-1 ]
'i'

# вернуть и удалить крайний левы й элемент
# перечислить содержимое deque
# заглянуть (peek) в крайний лев ы й элемент
# заглянуть (peek) в крайний правы й элемент

»> list(reversed(d))
[Т, 'h\ 'g']
»> 'h' in d
True
»> d.extend('jkl')
>>> d
deque(['g', 'h', 'i', 'j', 'k', '1'])

# перечислить содержимое deque в обратном порядке

»> d.rotate(1 )
»> d
deque(['I', 'g', 'h', 'i', 'j', ' k'])
»> d.rotate(-1 )
»> d
deque(['g', 'h', 'i', 'j', 'k', '1'])

# правы й поворот

# выполнить поиск в deque
# добавить несколько элементов сразу

# левы й поворот

»> deque(reversed(d))
# создать нов ы й deque в обратном порядке
deque(['I', 'k', 'j', 'i', 'h', 'g'])
»> d.clear()
# очистить deque
»> d.pop()
# невозможно выполнить операцию выталкивания (рор) из пустого deque
Traceback (most recent call last):
File "", line 1 , in -toplevel­
d.pop()
lndexError: рор from an empty deque
»> d.extendleft('abc')
>>> d
deque(['c', 'Ь', 'а'])

# extendleft() меняет порядок ввода

256

Глава 47. Модуль collections

47.6. collections.ChainMap

Инструмент управления поиском в словарях ChainMap появился в версии Python 3.3.
Он возвращает новый объект ChainMap при заданном количестве map. Этот объект объе­
диняет несколько словарей или других отображений для создания единого, обновляемого
представления.
ChainMaps полезны для управления вложенными контекстами и оверлеями. Примером
в мире Python может служить реализация класса Context в шаблонизаторе Django. Он поле­
зен для быстрого связывания нескольких отображений, чтобы результат можно было рас­
сматривать как единое целое. Зачастую это гораздо быстрее, чем создавать новый словарь
и выполнять несколько вызовов update().
В любом случае, когда имеется цепочка значений поиска, можно использовать ChainMap.
Примером может служить наличие как значений, задаваемых пользователем, так и слова­
ря значений по умолчанию. Другой пример - карты параметров POST и GET, используемые в
веб-приложениях, например Django или Flask. С помощью ChainMap можно получить комби­
нированное представление двух разных словарей.
Список параметров maps упорядочен от первого поиска до последнего поиска. При по­
иске последовательно перебираются все нижележащие отображения, пока не будет найден
ключ. В отличие от этого записи, обновления и удаления выполняются только на первом
отображении.
import collections
# определим два словаря, в которых хотя бы некоторые ключи пересекаются
dict1 = {'apple': 1 , 'banana': 2}
dict2 = {'coconut': 1 , 'date': 1 , 'apple': З}
# создадим две ChainMap с разным упорядочи ванием словарей
comblned_dict = collections.ChainMap(dict1, dict2)
reverse_ordered_dict = collections.ChainMap(dict2, dict1 )
Обратите внимание на влияние порядка нахождения первого значения на последующий
поиск:
for k, v in comblned_dict.items():
print(k, v)
date 1
apple 1
banana 2
coconut 1
for k, v in reverse_ordered_dict.items():
print(k, v)
date 1
apple 3
banana 2
coconut 1

48. 1 . Функция ltemgetter

Глава 48. Модуль operator

257

48. 1 . Функция ltemgetter
Группировка пар "ключ-значение" словаря по значению с помощью itemgetter:
from itertools import groupby
from operator import itemgetter
adict = {'а': 1 , 'Ь': 5, 'с': 1 }
dict((i, dict(v)) for i, v in groupby(adict.items(), itemgetter(1 )))
# Резул ьтат: {1 : {'а': 1, 'с': 1 }, 5: {'Ь': 5}}
что эквивалентно (но быстрее) использования лямбда-функции такого типа:
dict((i, dict(v)) for i, v in groupby(adict.items(), lambda х: х[1 ]))
Сортировка списка кортежей по второму элементу, сначала первый элемент как вторичный:
alist_of_tuples = [(5,2), (1 ,3), (2,2)]
sorted(alisLof_tuples, key=itemgetter(1 ,0))
# Резул ьтат: [(2, 2), (5, 2), (1 , 3)]

48.2. operator как альтернатива инфиксному оператору
Для каждого инфиксного оператора, например "+", существует орегаtог-функция (для "+"
это operator.add):
1 +1
# Резул ьтат: 2
from operator import add
add(1 , 1 )
# Резул ьтат: 2
Несмотря на то, что в основной документации указано, что для арифметических операторов допускается только числовой ввод, возможно нижеследующее:
from operator import mul
mul('a', 1 О)
# Резул ьтат: 'аааааааааа'
mu1([3], 3)
# Резул ьтат: [3, 3, 3]

48.З. Methodcaller
Вместо нижеприведенной лямбда-функции, которая вызывает метод явно:
alist = ['wolf', 'sheep', 'duck']
l ist(f1 lter(lambda х: x.startswith('d'), alist))
# Резул ьтат: ['duck']

# Сохранять только элементы, начинающиеся с 'd'

можно использовать оператор-функцию, которая делает то же самое:
from operator import methodcal ler
l ist(f1 lter(methodcaller('startswith', 'd'), al ist)) # Делает то же самое, но быстрее.
# Резул ьтат: ['duck']

258

Глава 49. Модул ь JSON

Глава 49. Модул ь JSON

49. 1 . Хранение данных в файле

Следующий фрагмент кода кодирует данные, хранящиеся в d, в формат JSON и сохраняет
их в файл (замените параметр fi lename на реальное имя файла).
import json
d={
'foo': 'Ьаг',
'alice': 1 ,
'wonderland': [1 , 2, 3]
with open(f1 lename, 'w') as f:
json.dump(d, f)

49.2. Получение данных из файла

Следующий фрагмент кода открывает закодированный в формате JSON файл (замените
параметр f1 lename на реальное имя файла) и возвращает объект, который хранится в этом
файле.
import json
with open(f1 lename, 'г') as f:
d = json. load(f)

49.З. Форматирование вывода JSON
Допустим, у нас есть следующие данные:

>>>

data = {"cats": [{"name": "Tubbs", "соlог": "white"}, {"name": "Реррег", "соlог":"Ыасk"}]}

Просто "сброс" в JSОN-формате не дает ничего особенного:

»> printQson.dumps(data))
{"cats": [{"name": 'Tubbs", "соlог": "white"}, {"name": "Реррег", "соlог": "Ыасk"}]}

Установка отступов для получения более красивого результата
Если мы хотим вывести данные в более эстетичном виде, мы можем задать размер от­
ступа:
»>

{

printQson.dumps(data, indent=2))

"cats": [
{
"name": 'Tubbs",
"color": "white"
},
{
"name": "Реррег",
"color": "Ыасk"

Сортировка ключей по алфавиту для получения последовательного вывода
По умолчанию порядок ключей в выводе не определен. Мы можем расположить их в ал­
фавитном порядке, чтобы всегда получать один и тот же результат:

49.4. "load" и "loads", "dump" и "dumps"

259

»> printQson.dumps(data, sorLkeys = True))
{"cats": [{"color": "white", "name": 'Tubbs"}, {"color": "Ыасk", "name": "Реррег"}]}

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

Можно избавиться от лишних пробелов, что делается путем установки строк-разделите­
лей, отличных от стандартных ', ' и ': ':
»>printQson.dumps(data, separators = (';, ':')))
{"cats":[{"name":"Tubbs","color":"white"},{"name":"Pepper","color":"black"}]}

49.4. "load" и "loads", "dump" и "dumps"

Модуль json содержит функции для чтения строк в Unicode и записи в них, а также для
чтения из файлов и записи в них. Они отличаются друг от друга наличием в названии функ­
ции концевой буквы "-s". В этих примерах мы используем строковый объект ввода-вывода,
но те же функции применимы для любого файлового объекта.
Здесь мы используем строковые функции:
import json
data = {u"foo": u"bar", u"baz": □}
json_string = json.dumps(data)
# u'{"foo": "Ьаг", "baz": □}'
json.loadsQson_string)
# {u"foo": u"bar", u"baz": □}

А здесь мы используем функции для файлов:
import json
from io import String lO
json_f1 le = String lO()
data = {u"foo": u"bar", u"baz": □}
json.dump(data, json_f1le)
json_f1 le.seek(0)
# Возврат к началу файла перед чтением
json_f1 le_content = json_f1le.read()
# u'{"foo": "Ьаг", "baz'':

0}'

json_f1 le.seek(0)
# Возврат к началу файла перед чтением
json.loadQson_f1 le)
# {u"foo": u"bar", u"baz": □}

Как видно, основное отличие заключается в том, что при дампе JSОN-данных необходимо
передавать в функцию дескриптор файла, а не перехватывать возвращаемое значение. Так­
же стоит отметить, что во избежание повреждения данных перед чтением или записью не­
обходимо обращаться к началу файла. При открытии файла курсор помещается в позицию О,
поэтому приведенное ниже описание также будет работать:
import json
json_f1 le_path = './data.json'
data = {u"foo": u"bar", u"baz": □}
with openQson_fi le_path, 'w') as json_file:
json.dump(data, json_f1le)
with openQson_fi le_path) as json_fi le:
json_file_content = json_fi le. read()
# u'{"foo": "Ьаг", "baz": □}'
with openQson_fi le_path) as json_fi le:
json. loadQson_f1 le)
# {u"foo": u"bar", u"baz": □}

260

Глава 49. Модуль JSON

Наличие обоих способов работы с JSОN-данными позволяет удобно работать с форматами, основанными на JSON, такими как PySpark's json-per-line:
# loading from а fi le
data = Uson.loads(l ine) for line in open(fi le_path).splitlines()]
# dumping to а f1 le
with open(f1 le_path, 'w') as json_fi le:
for item in data:
json.dump(item, json_f1 le)
json_f1 le.write('\n')

49.5. Вызов "json.tool" из командной строки для эстетичного
вывода JSON
Имеется некоторый JSОN-файл типа "foo.json":

{"foo": {"Ьаг": {"baz": 1 }}}

Мы можем вызвать модуль непосредственно из командной строки (передав в качестве
аргумента имя файла), чтобы вывести ero в эстетичном виде:
$ python -m json.tool foo.json
{
"foo": {
"Ьаг": {
"baz": 1

Модуль также будет принимать входные данные из STDOUT, поэтому (в командном языке
Bash) мы можем сделать то же самое:
$ cat foo.json I python -m json.tool

49.6. JSОN-кодирование пользовательских объектов
Если мы попробуем сделать следующее:

import json
from datetime import datetime
data = {'datetime': datetime(201 6, 9, 26, 4, 44, О)}
printUson.dumps(data))

мы получаем ошибку ТypeError: datetime.datetime(2016, 9, 26, 4, 44) is not JSON serializaЬle.
Чтобы правильно сериализовать объект даты-времени (datetime), нам необходимо написать
собственный код для его преобразования:
class DatetimeJSON EncoderUson.JSON Encoder):
def default(self, obj):
try:
return obj.isoformat()
except AttributeError:
# obj не имеет метода isoformat; пусть встроенный кодировщик JSON справится с этой
задачей
return super(DatetimeJSON Encoder, self).default(obj)

и затем использовать этот класс кодировщика вместо json.dumps:
encoder = DatetimeJSON Encoder()
print(encoder.encode(data))
# prints {"datetime": "201 6-09-26Т04:44:ОО"}

49.7. Создание JSON из словаря Python

261

49.7. Создание JSON из словаря Python
import json
d={
'foo': 'Ьаг',
'alice': 1 ,
'wonderland': [1 , 2, 3]
}
json.dumps(d)

Этот фрагмент кода вернет следующий результат:

'{"wonderland": [1 , 2, 3], "foo": "Ьаг", "alice": 1 }'

49.8. Создание словаря Python из JSON
import json
s = '{"wonderland": [1 , 2, 3], "foo": "Ьаг", "alice": 1 }'
json. loads(s)

Этот фрагмент кода вернет следующий результат:
{u'alice': 1 , u'foo': u'Ьаг', u'wonderland': [1 , 2, 31}

Глава 50. Модуль SqliteЗ
50. 1 . SqliteЗ не требует отдельного серверного процесса

Модуль sqlite3 был написан Герхардом Херингом. Для использования модуля необходимо
сначала создать объект Connection, представляющий базу данных. Здесь данные будут хра­
ниться в файле example.db:
import sqlite3
conn = sqlite3.connect('example.db')

Можно также указать специальное имя :memory: для создания базы данных в оператив­
ной памяти. После создания объекта Connection можно создать объект Cursor и вызвать его
метод execute() для выполнения SQL-команд:
с = conn.cursor()
# Создать таблицу
c.execute("'CREATE TABLE stocks
(date text, trans text, symbol text, qty real, price real) "')
# Вставить строку данных
c.execute("INSERT I NTO stocks VALUES ('2006-01 -05','BUY','RHAТ', 1 00,35. 1 4)")
# Сохранить (зафиксировать) изменения
conn.commit()
# Мы также можем закрыть соединение, если закончили с ним работать.
# Убедитесь, что все изменения были зафиксированы, иначе они будут потерян ы .
conn.close()

50.2. Получение значений из базы данных и обработка ошибок

Рассмотрим получение значений из базы данных SQLiteЗ. Выведем значения строк, воз­
вращаемых запросом select:
import sqlite3
conn = sqlite3.connect('example.db')

Глава 51 . Модуль os

262

с = conn.cursor()
c.execute("SELECT * from taЬle_name where id=cust_id")
for row in с:
print row # получится список

Для получения одного совпадения используется метод fetchone():
print c.fetchone()
Для получения нескольких строк используйте метод fetchall():
=
a c.fetchall() # это аналогично методу list(cursor), использовавшемуся ранее
for row in а:
print row
Обработка ошибок может быть выполнена с помощью встроенной функции sqliteЗ. Error:
try:
#SQL код
except sqliteЗ.Error as е:
print "Ап error occurred:", e.args[0]

Глава 51 . Модуль os
Параметр

П одро бн ости

path

Путь к файлу. Разделитель пути может быть определен с помощью os.path.sep.

mode

Необходимое разрешение, в восьмеричном исчислении (например 0700)

Этот модуль обеспечивает переносимый способ использования функциональности, за­
висящей от операционной системы.

5 1 . 1 . makedirs - рекурсивное создание каталогов
Дана локальная директория со следующим содержимым:
L_ dir1
1----- subdir1
L subdir2
Мы хотим создать те же подкаталоги subdir1 , subdir2 в новом каталоге dir2, который еще
не существует.
import os
os.makedirs("./dir2/subdir1 ")
os.makedirs("./dir2/subdir2")
В результате вы полнения этой операции:
��ubdir1
L subdir2
dir2
1----- subdir1
L subdir2
Каталог dir2 создается в первый раз только тогда, когда он нужен для создания subdir1 .
Если бы вместо этого мы использовали os.mkdir, то возникло бы исключение, поскольку dir2
еще не существовало бы.

51 .2. Создание каталога

263

os.mkdir("./dir2/subdir1 ")
OSError: [Errno 2] No such f1le ог directory: './dir2/subdir1 '
os.makedir "не понравится", если целевой каталог уже существует. Если мы запустим его
снова:
OSError: [Errno 1 7] File exists: './dir2/subdir1 '
Однако это можно легко исправить, перехватив исключение и проверив, что каталог бьш
создан.
try:
os.makedirs("./dir2/subdir1 ")
except OSError:
if not os.path.isdir("./dir2/subdir1 "):
raise
try:
os.makedirs("./dir2/subdir2")
except OSError:
if not os.path.isdir("./dir2/subdir2"):
raise

51 .2. Создание каталога
os.mkdir('newdir')
Если необходимо указать права доступа, можно использовать необязательный аргумент
mode:
os.mkdir('newdir', mode=0700)

51 .З. Получение текущего каталога
Используйте функцию os.getcwdO:
print(os.getcwd())

51 .4. Определение операционной системы

Модуль os предоставляет интерфейс для определения типа операционной системы, на
которой в данный момент работает код.
os.name
В Python 3 это может вернуть одно из следующих значений:
• posix
• nt




се

java

51 .5. Удаление каталога

Удаление каталога по адресу path:
os.rmdir(path)
Не следует использовать os.remove() для удаления каталога. Эта функция предназначена
для файлов, и ее использование для каталогов приведет к ошибке OSError.

51 .6. Следование по симлинку (POSIX)

Иногда требуется определить цель симлинка. Для этого используется os.readlink:
pri nt(os. read li n k(path_to_syml ink))

264

Глава 52. Модуль locale

51 .7. Изменение разрешений файла
os.chmod(path, mode)

где mode - желаемое разрешение, в восьмеричном исчислении.

Глава 52. Модуль locale
52. 1 . Форматирование валюты в долларах США
с использованием модуля locale
import locale
locale.setlocale(locale.LC_ALL, ")
Результат[2]: 'English_United States.1 252'
locale.currency(762559748.49)
Результат[3]: '$762559748.49'
locale.currency(762559748.49, grouping=True)
Результат[4]: '$762,559,748.49'

Глава 5 3 . Модуль ltertools
53. 1 . Метод комбинаций в модуле ltertools

itertools.comblnations вернет генератор последовательности k-комбинаций списка.
Другими словами, он возвращает генератор кортежей всех возможных k-образных ком­
бинаций входного списка.
Например, если у вас есть список:
а = [1 ,2,3,4,5]
Ь = list(itertools.comblnations(a, 2))
print Ь

Результат:
[(1 , 2), (1 , 3), (1 , 4), (1 , 5), (2, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 5)]

Вышеприведенный результат представляет собой генератор, преобразованный в список
кортежей всех возможных парных комбинаций входного списка а.
Вы также можете найти все тройные комбинации:
а = [1 ,2,3,4,5]
Ь = list(itertools.comblnations(a, 3))
print Ь

Результат:
[(1 , 2, 3), (1 , 2, 4), (1 , 2, 5), (1 , 3, 4),
(1 , 3, 5), (1 , 4, 5), (2, 3, 4), (2, 3, 5),
(2, 4, 5), (3, 4, 5)]

53.2. itertools.dropwhile

265

53.2. itertools.dropwhile
Функция itertools.dropwhile позволяет брать элементы и з последовательности после того,
как условие впервые принимает значение False.
def is_even(x):
return х % 2 == О
1st = [О, 2, 4, 1 2, 1 8, 1 3, 1 4, 22, 23, 44]
result = l ist(itertools.dropwhile(is_even, lst))
print(result)
Это дает результаты [13, 14, 22, 23, 44] .
Заметим, что первым числом, нарушающим предикат (т. е. функцию, возвращающую бу­
лево значение) is_even, является 13. Все элементы до этого отбрасываются.
Результат, выдаваемый dropwhile, аналогичен результату, полученному из приведенно­
го ниже кода.
def dropwhi le(predicate, itегаЫе):
itегаЫе = iter(iteraЫe)
fог х in itегаЫе:
if not predicate(x):
yield х
Ьгеаk
fог х in itегаЫе:
yield х
Конкатенация результатов, полученных с помощью takewhile и dropwhile, дает исходный
итерируемый объект.
result = l ist(iteгtools.takewhi le(is_even, lst)) + list(iteгtools.dropwhile(is_even, lst))

53.3. Использование zip_longest для двух итераторов до тех пор,
пока они оба не будут исчерпаны
Подобно встроенной функции zip(), функция iteгtools.zip_longest продолжит итерировать
до конца более короткого из двух итерируемых объектов.
from itertools import zip_longest
а = [i fог i in range(S)] # Длина равна 5
Ь = ['а', 'Ь', 'с', 'd', 'е', 'f', 'g'] # Длина равна 7
fог i in zip_longest(a, Ь):
х, у = i # Обратите внимание, что zip_longest возвращает значения в виде кортежа
print(x, у)
Необязательный аргумент fi llvalue (по умолчанию это ") может быть передан следующим
образом:
fог i in zip_longest(a, Ь, fi l lvalue='Hogwash!'):
х, у = i # Обратите внимание, что zip_longest возвращает значения в виде кортежа
print(x, у)
В

Python 2.6 и 2.7 эта функция называется itertools. izip_longest.

53.4. Получение среза генератора
Функция iteгtools.islice позволяет получить срез генератора:
# возвращает генератор
resu lts = fetch_paged_results()
# Необходимо получить только первые 20 результатов
limit = 20
fог data in itertools. islice(results, limit):
print(data)

266

Глава 53. Модуль ltertools

Обычно нельзя получить срез генератора:
def gen():
n=0
while n < 20:
n += 1
yield n
for part in gen()[:3]:
print(part)

Результатом будет:
Traceback (most recent call last):
File "gen.py", line 6, in
for рагt in gen()[:3]:
ТуреЕггог: 'generator' object is not subscriptaЫe

Следующий код работает:
import itertools
def gen():
n=0
while n < 20:
n += 1
yield n
for рагt in itertools.islice(gen(), 3):
print(part)

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

start, stop и step:

itertools.islice(iteraЫe, 1, 30, 3)

53.5. Группировка элементов из итерируемого объекта
с помощью функции

Начнем с итерируемого переменного, которое необходимо сгруппировать:

1st

=

[("а", 5, 6), ("Ь", 2, 4), ("а", 2, 5), ("с", 2, 6)]

Сформируем сгруппированный генератор, группируя по второму элементу в каждом кор­
теже:
def testGroupBy(lst):
groups = itertools.groupby(lst, key = lambda х: х[1 ])
for key, group in groups:
print(key, list(group))
testGroupBy(lst)
# 5 [('а', 5, 6)]
# 2 [('Ь', 2, 4), ('а', 2, 5), ('с', 2, 6)]

Группируются только группы последовательных элементов. Перед вызовом GroupBy мо­
жет потребоваться сортировка по одному и тому же ключу. Например (последний элемент
изменен):
1st = [("а", 5, 6), ("Ь", 2, 4), ("а", 2, 5), ("с", 5, 6)]
testGroupBy(lst)

53.6. itertools.takewhile

267

# 5 [('а', 5, 6)]

# 2 [('Ь', 2, 4), ('а', 2, 5)]
# 5 [('с', 5, 6)]

Группа, возвращаемая GroupBy, является итератором, который будет недействителен до
следующей итерации. Например, следующий пример не будет работать, если вы хотите, что­
бы группы были отсортированы по ключу. Группа 5 будет пустой, так как при извлечении
группы 2 группа 5 станет недействительной.
1st = [("а", 5, 6), ("Ь", 2, 4), ("а", 2, 5), ("с", 2, 6)]
groups = iteгtools.groupby(lst, key=lambda х: х[1 ])
fог key, group in sorted(groups):
print(key, list(group))
# 2 [('с', 2, 6)]
#5



Чтобы правильно выполнить сортировку, перед сортировкой создайте список из итератора.
groups = itertools.groupby(lst, key=lambda х: х[1 ])
fог key, group in sorted((key, list(group)) fог key, group in groups):
print(key, list(group))
# 2 [('Ь', 2, 4), ('а', 2, 5), ('с', 2, 6)]
# 5 [('а', 5, 6)]

53.6. itertools.takewhile

Функция itertools.takewhile позволяет брать элементы из последовательности до тех пор,
пока условие впервые не примет значение False.
def is_even(x):
return х % 2 == О
1st = [О, 2, 4, 1 2, 1 8, 1 3, 1 4, 22, 23, 44]
result = list(iteгtools.takewhile(is_even, lst))
print(result)
В результате получается [О, 2, 4, 1 2, 1 8].

Заметим, что первым числом, нарушающим предикат (т. е. функцию, возвращающую бу­
лево значение) is_even, является 13. Как только takewhile встречает значение, дающее False
для данного предиката, он выходит. Результат, выдаваемый takewhile, похож на результат, по­
лученный из приведенного ниже кода.
def takewhile(predicate, itегаЫе):
fог х in itегаЫе:
if predicate(x):
yield х
else:
Ьгеаk
Примечание: конкатенация результатов, полученных с помощью takewhile и dropwhile,
дает исходный итерируемый объект.
result = list(itertools.takewhile(is_even, lst)) + list(itertools.dropwhile(is_even, lst))

53. 7. itertools.permutations

Функция itertools.permutations возвращает генератор с последовательными перестановками элементов итерируемого множества длиной г.
а = [1 ,2,3]
list(iteгtools.permutations(a))
# [(1 , 2, 3), (1 , 3, 2), (2, 1 , 3), (2, 3, 1 ), (3, 1 , 2), (3, 2, 1 )]

268

Глава 53. Модул ь ltertools

list(itertools.permutations(a, 2))
[(1 , 2), (1 , 3), (2, 1 ), (2, 3), (3, 1 ), (3, 2)]

Если список а имеет дублирующиеся элементы, то и результирующие перестановки бу­
дут иметь дублирующиеся элементы, для получения уникальных перестановок можно ис­
пользовать set:
а = [1 ,2,1 ]
list(itertools.permutations(a))
# [(1 , 2, 1 ), (1 , 1 , 2), (2, 1 , 1 ), (2, 1 , 1 ), (1 , 1 , 2), (1 , 2, 1 )]
set(itertools.permutations(a))
# {(1 , 1 , 2), (1 , 2, 1 ), (2, 1 , 1 )}

53.8. itertools.repeat

Повторим что-либо n раз:

>» import itertools
»> for i in itertools.repeat('eщe-и-eщe', 3):
... print(i)
еще-и-еще
еще-и-еще
еще-и-еще

53.9. Получение накопленной суммы чисел в итерируемом
объекте

Версия Python 3.х ::с: 3.2:

accumulate выдает кумулятивную сумму (или произведение) чисел.

»> import itertools as it
»> import operator
»> l ist(it.accumulate([1 ,2,3,4,5]))
[1 , 3, 6, 1 О, 1 5]
»> l ist(it.accumulate([1 ,2,3,4,5], func=operator.mul))
[1 , 2, 6, 24, 1 20]

53. 1 О. Циклический переход по элементам итератора
cycle - это бесконечный итератор.

»> import itertools as it
>» it.cycle('ABCD')
A B C D A B C D A B C D ...

Поэтому во избежание образования замкнутого цикла при его использовании необходимо указывать границы. Пример:
>» # Итерация по каждому элементу цикла для фиксированного диапазона
»> cycle_iterator = it.cycle('abc1 23')
»> [next(cycle_iterator) for i in range(O, 1 О)]
['а', 'Ь', 'с', ' 1 ', '2', 'З ', 'а', 'Ь', 'с', ' 1 ']

53. 1 1 . itertools.product

Эта функция позволяет выполнять итерацию по декартову произведению списка итери­
руемых переменных. Например:
for х, у in itertools.product(xrange(1 О), xrange(1 О)):
print х, у

53. 1 2. itertools.count

269

эквивалентно
for х in xrange(1 О):
for у in xrange(1 О):
print х, у
Как и все функции Python, принимающие переменное число аргументов, мы можем пе­
редать список в itertools.product для распаковки с помощью оператора *. Таким образом,
its = [xrange(1 О)] * 2
for х,у in itertools.product(*its):
print х, у
дает те же результаты, что и оба предыдущих примера:
»> from itertools import product
»> а=[1 ,2,3,4]
»> Ь=['а','Ь','с']
»> product(a,b)
< itertools.product object at 0x000000000271 2F78>
»> for i in product(a,b):
print i
(1 , 'а')

(1 , 'Ь')
(1 , 'с')
(2, 'а')

(2, 'Ь')
(2, 'с')
(3, 'а')

(3, 'Ь')
(3, 'с')
(4, 'а')

(4, 'Ь')
(4, 'с')

53. 1 2. itertools.count
Введение :

Эта простая функция генерирует бесконечный ряд чисел. Например:

for number in itertools.count():
if number > 20:
break
print(number)
Обратите внимание, что мы должны прерваться, иначе он будет выводить вечно! Результат:
0 1 2 3 4 5 6 7 8 9 10

Arguments:

Функция count() принимает два аргумента: start and step:
for number in itertools.count(start=1 О, step=4):
print(number)
if number > 20:
break
Результат:
1 О 1 4 1 8 22

270

Глава 54. Модуль Asyncio

53. 1 3. Объединение нескольких итераторов в цепочку
С помощью itertools.chain можно создать один генератор, который будет последовательно
выдавать значения из нескольких генераторов.
from itertools import chain
а = (х for х in ['1 ', '2', '3', '4'])
Ь = (х for х in ['х', 'у', 'z'])
' '.join(chain(a, Ь))
Результатом будет:
'1 2 3 4 х у z'
В качестве альтернативного конструктора можно использовать метод класса chain.from_
iteraЫe, который принимает в качестве единственного параметра итерируемый объект из
итерируемых объектов. Для получения того же результата, что и выше, используйте:
' '.join(chain.from_iteraЫe([a,b])
Хотя chain может принимать произвольное число аргументов, chain.from_iteraЫe - един­
ственный способ выстроить цепочку из бесконечного числа итерируемых объектов.

Глава 54. Модуль Asyncio
54. 1 . Синтаксис корутин (сопрограмм) и делегирования
До выхода версии Python 3.5 модуль asyncio использовал генераторы для имитации асин­
хронных вызовов и поэтому имел синтаксис, отличающийся от синтаксиса текущего рели­
за Python 3.5.

Версия Python 3.х ::с: 3.5:

В Python 3.5 появились ключевые слова async и await. Обратите внимание на отсутствие
круглых скобок вокруг вызова await func().
import asyncio
async def main():
print(await func())
async def func():
# Выполнить трудоемкие действия ...
return "Hello, world !"
if _name_ == "_main_":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Версия Python 3.х ::с: 3.3 < 3.5:

До версии Python 3.5 для определения корутин использовался декоратор @аsуnсiо.соrоutinе.
Для делегирования генератора использовался выход из выражения. Обратите внимание на
круглые скобки вокруг yield from func().
import asyncio
@asyncio.coroutine
def main():
print((yield from func()))

54.2. Асинхронные исполнители (Executors)

271

@asyncio.coroutine
def func():
# Выполнить трудоемкие действия...
return "Hello, world!"
if _name_ == "_main_":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Версия Python З.х � 3.5:

Приведем пример, показывающий, как можно асинхронно выполнять две функции:

import asyncio
async def сог1 ():
print("cor1 start")
for i in range(1 О):
await asyncio.sleep(1 .5)
print("cor1 ", i)
async def сог2():
print("cor2 start")
for i in range(1 5):
await asyncio.sleep(1 )
print("cor2", i)
loop = asyncio.get_event_loop()
cors = asyncio.wait([cor1 (), сог2()])
loop.run_until_complete(cors)

54.2. Асинхронные исполнители (Executors)

Примечание: используется синтаксис async/await языка Python 3.5+

asyncio поддерживает использование объектов Executor, находящихся в concurrent. futures,
для асинхронного планирования задач. В циклах событий имеется функция run_in_executor(),
которая принимает объект Executor, вызываемую переменную (CallaЫe) и параметры вызы­
ваемой переменной.
Планирование задачи для исполнителя:
import asyncio
from concurrent.futures import Thread Pool Executor
def func(a, Ь):
# Выполнить трудоемкие действия . . .
return а + Ь
async def main(loop):
executor = Thread PoolExecutor()
result = await loop.run_in_executor(executor, func, "Hel lo,", " world !")
print(result)
if _name_ == "_main_":
loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))
Каждый цикл событий также имеет слот Executor "по умолчанию", который может быть
назначен исполнителю. Для назначения исполнителя и планирования задач из цикла ис­
пользуется метод set_defau lt_executor().
import asyncio
from concurrent.futures import Thread Pool Executor

272

Глава 54. Модуль Asyncio

def func(a, Ь):
# Выполнить трудоемкие действия . . .
return а + Ь
async def main(loop):
# Примечание: использование 'None' в качестве первого параметра обозначает Executor
# 'по умолчанию·.
result = await loop.run_in_executor(None, func, "Hello,", " world !")
print(resu lt)
if _name_ == "_main_":
loop = asyncio.get_event_loopQ
loop.set_default_executor(Thread Pool Executor())
loop.run_unti l_complete(main(loop))
В concurrent.futures существует два основных типа исполнителей: Thread Pool Executor
и ProcessPoolExecutor. Thread Pool Executor содержит пул потоков, который может быть либо
задан вручную в конструкторе, либо по умолчанию равен количеству ядер на машине, ум­
ноженному на 5. Thread Pool Executor использует пул потоков для выполнения назначенных
ему задач и, как правило, лучше справляется с операциями, связанными с процессором, а не
с операциями ввода-вывода. Это отличает ero от ProcessPool Executor, который порождает
новый процесс для каждой назначенной ему задачи. ProcessPool Executor может принимать
только задачи и параметры, которые относятся к катеrории picklaЫe (т. е. преобразуются
в поток байтов и обратно). Наиболее распространенныt non-picklaЫe задачи - это методы
объектов. Если необходимо запланировать метод объекта в качестве задачи в исполнителе,
необходимо использовать Thread Pool Executor.

54.З. Использование UVLoop
uvloop - это реализация asyncio.AbstractEventloop, основанная на библиотеке libuv (исполь­
зуется в nodejs). Она совместима с 99% функций модуля asyncio и работает гораздо быстрее,
чем традиционный asyncio. Eventloop. В настоящее время uvloop недоступен для Windows,
установите его с помощью pip install uvloop.
import asyncio
import uvloop
if _name_ == "_main_":
asyncio.set_evenLloop(uvloop.new_evenLloop())
# Выполня йте свои процессы здесь . . .
Можно также изменить фабрику циклов событий, установив в uvloop значение Eventloop­
Policy.
import asyncio
import uvloop
if _name_ == "_main_":
asyncio.set_evenLloop_policy(uvloop. EventloopPolicy())
loop = asyncio.new_event_loop()

54.4. Примитив синхронизации: Event
Концеш:�;ия

Используйте Event для синхронизации планирования нескольких корутин (сопрограмм).
Проще говоря, событие (Event) - это как стартовый выстрел в беге: оно позволяет бегунам
выйти на старт.

Пример
# event trigger function (функция запуска события)
def trigger(event):

273

54.5. Простой Websocket
print('EVENT SЕТ')
event.setO # пробуждение ожидающих корутин
# потребители событий
async def consumer_a(event):
consumer_name = 'Consumer А'
print('{} waiting'.format(consumer_name))
await event.wait()
print('{} triggered'.format(consumer_name))
async def consumer_b(event):
consumer_name = 'Consumer В'
print('{} waiting'.format(consumer_name))
await event.wait()
print('{} triggered'.format(consumer_name))
# событие
event = asyncio. Event()
# обернуть корутины в одно будущее
main_future = asyncio.wait([consumer_a(event),
consumer_Ь(event)])
# цикл событий
event_loop = asyncio.get_event_loop()
event_loop.cal l_later(0. 1 , functools.partial(trigger, event)) # срабатывание события через О, 1 с
# завершить main_future
done, pending = event_loop.run_until_complete(main_future)

Результат:
Consumer В waiting
Consumer А waiting
EVENТ SET
Consumer В triggered
Consumer А triggered

54.5. Простой Websocket

Создадим простой echo websocket с использованием модуля asyncio. Для соединения
с сервером и отправки/получ ения сообщений определим корутины. Обмен сообщениями
в websocket происходит в главной корутине main, которая запускается циклом событий.
import asyncio
import aiohttp

session = aiohttp.ClientSession()
class EchoWebsocket:

# работает с менеджером контекста

async def connect(self):
self.websocket = await session.ws_connect("wss://echo.websocket.org")
async def send(self, message):
self.websocket.send_str(message)
async def receive(self):
result = (await self.websocket. receive())
return resu lt.data
async def main():
echo = EchoWebsocket()

274
await echo.connect()
await echo.send("Hello World!")
print(await echo.receive())

Глава 55. Модуль Random

# "Hello World!"

if _name_ == '_main_':

# главный цикл

loop = asyncio.get_event_loop()
loop.run_unti l_complete(main())

54.6. Распространенные заблуждения, связанные с модулем
asyncio

Вероятно, самое распространенное заблуждение об asyncio заключается в том, что он
будто бы позволяет выполнять любые задачи параллельно, обходя блокировку GIL (global
interpreter lock) и, следовательно, параллельно (в отдельных потоках) позволяет выполнять
блокирующие задания. Это не так!
Модуль asyncio и библиотеки, созданные для совместной работы с asyncio, построены
на основе корутин. Обратите внимание на asyncio.sleep в примерах выше. Это пример не­
блокирующей корутины, которая ждет "в фоновом режиме" и передает управление обрат­
но вызывающей функции (при вызове с помощью await). time.sleep - пример блокирующей
функции. Выполнение программы просто остановится и вернется только после завершения
time.sleep. Реальным примером может служить библиотека requests, состоящая только из
блокирующих функций. При вызове любой из ее функций в рамках asyncio параллельность
отсутствует. aiohttp, напротив, создавалась с учетом требований asyncio. Ее корутины будут
выполняться параллельно.
• Если у вас есть длительные задачи, привязанные к процессору, которые вы
хотели бы выполнять параллельно, то asyncio не для вас. Для этого вам нужны
потоки (threads) или многопроцессорная обработка (multiprocessing).
• Если у вас есть задания, связанные с вводом-выводом (IO), вы можете запускать
их параллельно с помощью asyncio.

Глава 55. Модул ь Random
55. 1 . Создание случайного пароля пользователя

Для создания случайного пароля пользователя мы можем использовать символы, пред­
ставленные в модуле string. В частности, punctuation - для знаков препинания, ascii_letters ДЛЯ букв и digits - ДЛЯ цифр:
from string import punctuation, ascii_letters, digits

Затем мы можем объединить все эти символы в имя с названием symbols:
symbols = ascii_letters + digits + punctuation
Удалив любой из них, можно получить пул символов с меньшим количеством элемен­
тов. После этого мы можем использовать random.SystemRandom для генерации пароля. Для
пароля длиной в 10 символов:
secure_random = random.SystemRandom()
password = "".join(secure_random.choice(symbols) for i in range(1 О))
print(password) # •л@g;J?] М бе'

Отметим, что другие корутины (подпрограммы), которые сразу же становятся доступны­
ми в модуле random, такие как random.choice, random.randint и т. д., не подходят для крипто­
графических целей. "За кулисами" этих процедур скрывается использование генератора
псвевдослучайных чисел Mersenne Twister PRNG, который не удовлетворяет требованиям

55.2. Создание криптографически защищенных случайных чисел

275

CSPRNG (Криптографически стойкого генератора псевдослучайных чисел). Поэтому, в част­
ности, не следует использовать ни одну из них для генерации паролей, которые вы планиру­
ете использовать. Всегда используйте экземпляр SystemRandom, как показано выше.

Версия Python З.х � 3.6
Начиная с Python 3.6 доступен модуль secrets, который использует криптографически без­
опасную функциональность. Цитируя официальную документацию, для создания "десяти­
символьного буквенно-цифрового пароля, содержащего как минимум один строчный сим­
вол, как минимум один прописной символ и не менее трех цифр", можно сделать следующее:
import string
alphabet = string.ascii_letters + string.digits
while True:
password = ".join(choice(alphabet) for i in range(1О))
if (any(c.islowerO for с in password)
and any(c.isupper() for с in password)
and sum(c.isdigit() for с in password) >= 3):
break

55.2. Создание криптографически защищенных случайных чисел

По умолчанию модуль random в Python использует для генерации случайных чисел гене­
ратор псвевдослучайных чисел Mersenne Twister PRNG, который, хотя и подходит для таких
областей, как моделирование, не удовлетворяет требованиям безопасности в более слож­
ных средах. Для создания криптографически защищенного псевдослучайного числа мож­
но использовать SystemRandom, который, используя os.urandom, способен выступать в роли
криптографически защищенного генератора псевдослучайных чисел, CPRNG.
Самый простой способ его использования заключается в инициализации класса System­
Random. Предоставляемые методы аналогичны методам, экспортируемым модулем random.
from random import SystemRandom
secure_rand_gen = SystemRandom()

Для создания случайной последовательности из 10 чисел типа int в диапазоне [О, 20] мож­
но просто вызвать randrange() :
print([secure_rand_gen.randrange(1О) for i in range(1О)])
# [9, 6, 9, 2, 2, 3, 8, О, 9, 9]

Для создания случайного целого числа в заданном диапазоне можно использовать randint:
print(secure_rand_gen.randint(0, 20))
#5

Подходы для всех остальных методов аналогичны. Интерфейс точно такой же, меняется
только генератор чисел, лежащий в основе. Для получения криптографически защищенных
случайных байтов можно также напрямую использовать метод os.urandom.

55.З. Случайности и последовательности: перемешивание,
выбор и выборка
import random

shuffie()

Вы можете использовать random.shuffle() для перемешивания/рандомизации элементов
в изменяемой и индексируемой последовательности. Например, списка:
laughs = ["H i", "Но", "Не"]
random.shuffle(laughs)
# Перемешивает на месте! Не делайте: laughs = random.shuffle(laughs)
print(laughs)
# В ы вод: ["Не", "H i", "Но"] # Результат может отличаться!

276

Глава 55. Модул ь Random

choice()
Берет случайный элемент из произвольной последовательности:
print(random.choice(laughs})
# В ы вод: Не

# Резул ьтат может отличаться !

sample()
Как и в случае с choice, он берет случайные элементы из произвольной последовательно­
сти, но можно указать их количество:
# I-последовател ьность-I-numЬег--I
print(random.sample(
laughs
1
)) # Взять один элемент
# В ы вод: ['Но']
# Резул ьтат может отличаться !

он не будет брать один и тот же элемент дважды:
print(random.sample(laughs, З}}
# В ы вод: ['Но', 'Не', 'Hi']
print(random.sample(laughs, 4))

# Возьмем 3 случайных элемента из последовател ьности
# Резул ьтат может отличаться !
# Возьмем 4 случайных элемента из последовател ьности

ValueError: Sample larger than population (выборка больше количества элементов )

55.4. Создание чисел типов int и float: randint, randrange, random
и uniform
import random

randint()
Возвращает случайное целое число в диапазоне от х до у (включительно):
random.randint(x, у)

Например, получим случайное число от 1 до 8:

random.randint(1, 8} # Резул ьтат: 8

randrange()

У random. randrange тот же синтаксис, что и у range, но, в

нее значение не включается:

отличие от random. randint, послед-

random.randrange(100} # Случайное целое число от О до 99
random.randrange(20, 50} # Случайное целое число от 20 до 49
random.rangrange(1О, 20, З} # Случайное целое число от 1О до 19 с шагом 3 (1О, 13, 16 и 19}
:JUUUU

100 ООО последовател ьных вызовов
-,----,-----,---�=:::z:::
========;-i

1

1

25000

- ra ndom . ra n d range( l , 9 , 2 )
1
- ra ndom . ra n d i nt(l,8)

20000
:s;
ctl

� 15000
,:;
10000

5000

З

4

5

б

Выпавшее число

9

55.5. Воспроизводимые случайные числа: методы Seed и State

277

random

Возвращает случайное число с плавающей точкой, в диапазоне от О до 1:

random.random() # Результат: О.6648609321 53063 1 7

uniform

Возвращает случайное число с плавающей точкой, между х и у (включительно):
random. uniform(1 , 8) # Результат: 3.726062641 7301 08

55.5. Воспроизводимые случайные числа: методы Seed и State

При задании конкретного значения seed (случайного начального значения) создается се­
рия случайных чисел:
random.seed(5)
print(random.randrange(0, 1 О))
# Результат: 9
print(random.randrange(0, 1 О))
# Результат: 4

# Создать фиксированное состояние
# Получить случайное целое число от О до 9

Сброс Seed снова создаст ту же самую "случайную" последовательность:
random.seed(5) # Сбросить модуль random в то же фиксированное состояние
print(random.randrange(0, 1 О))
# Результат: 9
print(random.randrange(0, 1 О))
# Результат: 4

Поскольку seed зафиксировано, результаты всегда равны 9 и 4. Если наличие конкретных
чисел не требуется, а требуется только, чтобы значения были одинаковыми, то можно также
просто использовать getstate и setstate для возврата в предыдущее состояние:
save_state = random.getstate()
print(random.randrange(0, 1 О))
# Результат: 5
print(random.randrange(0, 1 О))
# Результат: 8

# Получить текущее состояние

random.setstate(save_state)
print(random.randrange(0, 1 О))
# Результат: 5
print(random.randrange(0, 1 О))
# Результат: 8

# Сброс в текущее состояние

Чтобы создать псевдослучайную последовательность, используйте seed со значением

None:

random.seed(None)

или вызовите метод seed без аргументов:

random.seed()

55.6. Случайное двоичное решение
import random
probabll ity = 0.3
if random.random() < probaЬil ity:
ргiпt("Решение с вероятностью 0.3")
else:
ргiпt("Решение с вероятностью 0.7")

278

Глава 56. Модул ь Functools

Глава 56. Модуль Functools

56. 1 . Функция partial

Частичная функция partial создает частичное применение функции из друтой функции.
Она используется для привязки значений к некоторым аргументам функции (или именован­
ных аргументов) и создания вызываемой функции без уже заданных аргументов.
»>
»>
»>


from functools import partial
unhex = partial(int, base=1 6)
unhex._doc_ = 'Convert base1 6 string to int'
unhex('callaЫe')

33901 55550

partial(), как следует из названия, позволяет частично оценить функцию. Рассмотрим сле­
дующий пример:
ln [2]: from functools import partial
ln [3]: def f(a, Ь, с, х):
... : return 1 ООО*а + 1 ОО*Ь + 1 О*с + х
ln [4]: g = partial (f, 1 , 1 , 1 )
ln [5]: print g(2)
1112

При создании g функция f, принимающая четыре аргумента (а, Ь, с, х), также частично
оценивается для первых трех аргументов - а, Ь, с. Оценка f завершается при вызове g, g(2), ко­
торый передает f четвертый аргумент. Один из способов представления partial - это сдвиго­
вый регистр, вставляющий в некоторую функцию по одному аргументу за раз. partial удобен
в тех случаях, когда данные поступают в виде потока и мы не можем передать более одного
аргумента.

56.2. cmp_to_key

В Python изменились методы сортировки, которые принимают функцию ключа. Эти
функции принимают значение и возвращают ключ, который используется для сортировки
массивов.
Старые функции сравнения принимали два значения и возвращали -1 , о или +1 , если
первый аргумент бьm меньше, равен или больше второго аргумента соответственно. Это
несовместимо с новой функцией key. Вот тут-то и приходит на помощь functools.cmp_to_key:
»> import functools
»> import locale
»> sorted(["A", "S", "F", "D"], key=functools.cmp_to_key(locale.strcol l))
[ 'А', 'D', 'F', 'S ']

56.3. @lru_cache

Декоратор @lru_cache можно использовать для обертывания дорогой, требующей боль­
ших вычислений функции кэшем Least Recently Used (с наименьшим количеством послед­
них использованных данных). Это позволяет мемоизировать вызовы функций, так что
последующие вызовы с теми же параметрами могут возвращаться мгновенно, а не вычис­
ляться заново.
@l ru_cache(maxsize=None) # Безграничный кэш
def f1 bonacci(n):
if n < 2:

56.4. @total ordering

279

return n
return fibonacci(n-1 ) + fibonacci(n-2)
»> f1bonacci(1 5)
В приведенном примере значение f1bonacci(З) вычисляется только один раз, тогда как
если бы у fibonacci не было LRU-кэша, f1bonacci(З) вычислялось бы до 230 раз. Следовательно,
@lru_cache особенно хорошо подходит для рекурсивных функций или динамического про­
граммирования, когда ресурсоемкая функция может быть вызвана несколько раз с одними
и теми же параметрами.
@lru_cache имеет два аргумента:
• maxsize: Количество сохраняемых вызовов. Когда количество уникальных
вызовов превысит maxsize, LRU-кэш удалит "наименее недавно использованные"
вызовы.
• typed (добавлено в версии 3.3): это флаг для определения принадлежности
эквивалентных аргументов разных типов к разным записям кэша (т. е. считаются
ли 3 и 3.0 разными аргументами)
Мы также можем видеть статистику кэша:
»> f1b.cache_info()
Cachelnfo(hits = 1 3, misses = 1 6, maxsize = None, currsize = 1 6)
Примечание: поскольку @lru_cache использует словари для кэширования результатов,
все параметры для функции должны быть хешируемыми, чтобы кэш работал.

56.4. @total_ordering

Когда мы хотим создать упорядоченный класс, обычно нам необходимо определить ме­
тоды _eq()_, _lt_(), _le_(), _gt_() и _ge_().
Декоратор total_ordering, примененный к классу, позволяет определить _eq_() и только
один из _IL(), _le_(), _gL() и _ge_(), при этом допускаются все операции упорядочива­
ния класса.
@total_ordering
class Employee:

def _eq_(self, other):
return ((self.surname, self.name) = = (other.surname, other.name))
def _IL(self, other):
return ((self.surname, self.name) < (other.surname, other.name))
Декоратор использует сочетание предоставленных методов и алгебраических операций
для получения других методов сравнения. Например, если мы определим _lt_() и _eq()_
и хотим получить _gt_(), можно просто проверить not _IL() and not _eq()_.
Примечание: функция total_ordering доступна только начиная с версии Python 2.7.

56.5. reduce

В Python 3.х функция reduce, о которой уже упоминалось, бьmа удалена из встроенных мо­
дулей и теперь должна импортироваться из functools.
from functools import reduce
def factorial(n):
return reduce(lambda а, Ь: (а*Ь), range(1 , n+1 ))

280

Глава 57. Модуль dis

Глава 57. Модуль dis

57 . 1 . Что такое байт-код Python?
Python является гибридным интерпретатором. При выполнении программы он сначала
ассемблирует ее в байт-код, который затем может быть запущен в интерпретаторе Python
(также называемом виртуалы-юй машиной Python). Модуль dis из стандартной библиотеки
позволяет сделать байт-код Python человекочитаемым путем дизассемблирования классов,
методов, функций и объектов кода.
»> def hello():
print "Hel lo, World"
»> dis.dis(hel lo)
2
О LOAD_CONST 1 ('Hello, World')
3 PRINT_ITEM
4 PRINT _N EWLI N E
5 LOAD_CONST О (None)
8 RETURN_VALU E
Интерпретатор Python основан на стеке и использует систему first-in last-out ("первым
пришел - последним ушел").
Каждый код операции (опкод) в языке ассемблера Python (байт-код) забирает из стека
определенное количество элементов и возвращает в стек определенное количество элемен­
тов. Если в стеке не хватает элементов для выполнения опкода, то интерпретатор Python за­
вершает работу, возможно, без сообщения об ошибке.

57 .2. Константы в модуле dis
EXTENDED_ARG = 1 45 # Все оnкоды, превышающие это значение, имеют 2 операнда
HAVE_дRGUM ENT = 90 # Все оnкоды, превышающие это значение, имеют хотя бы 1 операнд
cmp_op

= ('', используемы ми в продуктах Adobe
Байтоподобн ы й объект, содержащий сим волы,
которые необходимо игнорировать в кодировке
Байтоподобн ы й объект
Если значение pad равно True, то перед
кодированием байты будут дозаполнен ы до
значений, кратных 4
Байтоподобн ы й объект

Кодировка Base 64 представляет собой общую схему кодирования двоичных данных
в строковый формат ASCII с использованием разновидности кодирования radix 64. Модуль
base64 является частью стандартной библиотеки, то есть устанавливается вместе с Python.
Понимание байтов и строк очень важно для данной темы. В этой главе объясняется, как ис­
пользовать различные возможности и основания систем счисления модуля base64.

58 . 1 . Кодирование и декодирование Base64

Чтобы включить модуль base64 в свой скрипт, необходимо сначала его импортировать:

import Ьаsеб4

Для работы функций кодирования и декодирования base64 требуется байтоподобный объ­
ект. Чтобы перевести нашу строку в байты, мы должны закодировать ее с помощью встроен­
ной в Python функции. Чаще всего используется кодировка UTF-8, однако полный список этих
стандартных кодировок (включая языки с разными символами) можно найти в официаль­
ной документации по Python. Ниже приведен пример кодирования строки в байты:
s = "Hello World!"
Ь = s.encode("UTF-8")

Вывод последней строки будет таким:
b'Hello World!'

Префикс Ь используется для обозначения того, что значение является байтовым объек­
том. Для Ваsе64-кодирования этих байтов мы используем функцию base64.b64encode() :

58.2. Кодирование и декодирование Base32

283

import base64
s = "Hel lo World!"
Ь = s.encode("UTF-8")
е = base64.b64encode(b)
print(e)
Этот код выведет результат:
b'SGVsbG8gV29ybGQh'
который по-прежнему является байтовым объектом. Чтобы получить из этих байтов
строку, мы можем использовать метод Python decode(), применив кодировку UTF-8:
import base64
s = "Hel lo World!"
Ь = s.encode("UTF-8")
е = base64.b64encode(b)
s1 = e.decode("UTF-8")
print(s1 )
На выходе получаем:
SGVsbG8gV29ybGQh
Если бы мы хотели закодировать строку, а затем декодировать, то могли бы использовать
метод base64.b64decode():
import base64
# Создание строки
s = "Hel lo World!"
# Кодирование строки в байты
Ь = s.encode("UTF-8")
# Ваsе64-кодирование байтов
е = base64.b64encode(b)
# Декодирование байтов Base64 в строку
s1 = e.decode("UTF-8")
# Вы вод строки в кодировке Base64
print("Base64 Encoded:", s1 )
# Кодирование строки в кодировке Base64 в байты
Ы = s1 .encode("UTF-8")
# Декодирование байтов в формате Base64
d = base64.b64decode(Ы )
# Декодирование байтов в строку
s2 = d.decode("UTF-8")
print(s2)
Как и следовало ожидать, результатом будет исходная строка:
Base64 Encoded: SGVsbG8gV29ybGQh
Hello World!

58.2. Кодирование и декодирование Base32
Модуль base64 также включает в себя функции кодирования и декодирования для Base32.
Эти функции очень похожи на функции Base64:
import base64
# Создание строки
s = "Hel lo World!"
# Кодирование строки в байты
Ь = s.encode("UTF-8")
# Ваsе32-кодирование байтов
е = base64.b32encode(b)
# Декодирование байтов Base32 в строку
s1 = e.decode("UTF-8")
# Вы вод строки в кодировке Base32

284

Глава 58. Модуль base64

print("Base32 Encoded:", s1 )
# Кодирование строки в кодировке Base32 в байты
Ь1 = s1 .encode("UTF-8"}
# Декодирование байтов Base32
d = base64. b32decode(Ь1 )
# Декодирование байтов в строку
s2 = d.decode("UTF-8"}
print(s2}
В результате будет получено:
Base32 Encoded: JBSWYЗDPEBLW64TMMQQQ====
Hello World!

58.З. Кодирование и декодирование Base1 6
Модуль Ьаsе64 также включает функции кодирования и декодирования для Base16, чаще
всего называемой шестнадцатеричной системой. Эти функции очень похожи на функции
Base64 и Base32:
import base64
# Создание строки
s = "Hello World!"
# Кодирование строки в байты
Ь = s.encode("UTF-8"}
# Base1 6-кодирование байтов
е = Ьаsе64.Ь1 бencode(b}
# Декодирование байтов Base1 6 в строку
s1 = e.decode("UTF-8"}
# Вывод строки в кодировке Base1 6
print("Base1 6 Encoded:", s1 )
# Кодирование строки в кодировке Base1 6 в байты
Ь1 = s1 .encode("UTF-8"}
# Декодирование байтов Base1 6
d = Ьаsе64.Ь1 бdесоdе(Ь1 )
# Декодирование байтов в строку
s2 = d.decode("UTF-8"}
print(s2}
В результате будет получено:
Base16 Encoded: 48656C6C6F20576F726C6421
Hello World!

58.4. Кодирование и декодирование ASCll85

Компания Adobe создала собственную кодировку ASCII85, которая похожа н а Base85, но
имеет отличия. Эта кодировка часто используется в файлах Adobe PDF. Эти функции появи­
лись в Python версии 3.4. В остальном функции base64.a85encode(} и base64.a85encode(} ана­
логичны предыдущим:
import base64
# Создание строки
s = "Hello World!"
# Кодирование строки в байты
Ь = s.encode("UTF-8"}
# ASCll85 кодирование байтов
е = base64.a85encode(b}
# Декодирование байтов ASCll85 в строку
s1 = e.decode("UTF-8"}
# Вывод строки в кодировке ASCll85
print("ASCl l85 Encoded:", s1 }
# Кодирование строки в кодировке ASCll85 в байты

58.5. Кодирование и декодирование Base85

285

Ы = s1 .encode("UTF-8")
# Декодирование байтов ASCll85
d = base64.a85decode(Ы )
# Декодирование байтов в строку
s2 = d.decode("UTF-8")
print(s2)
В результате будет получено:
ASCII85 Encoded: 87cURD]i,"Ebo80
Hello World!

58.5. Кодирование и декодирование Base85
Как и функции Base64, Base32 и Baselб, функции кодирования и декодирования Base85
являются base64.b85encode() и base64.b85decode():
import base64
# Соэдание строки
s = "Hello World!"
# Кодирование строки в байты
Ь = s.encode("UTF-8")
# Base85 Кодирование байтов
е = base64. b85encode(b)
# Декодирование байтов Base8 5 в строку
s1 = e.decode("UTF-8")
# Печать строки в кодировке Base85
print("Base85Encoded:", s1 )
# Кодирование строки в кодировке Base85 в байты
Ы = s1 .encode("UTF-8")
# Декодирование байтов Base85
d = base64.b85decode(Ы )
# Декодирование байтов в строку
s2 = d.decode("UTF-8")
print(s2)
В результате будет получено:
Base85 Encoded: NM&qnZy;BlaOfo A Nf
Hello World!

Глава 59. Модуль Queue
Модуль Queue реализует очереди с несколькими производителями и потребителями.
Он особенно полезен при многопоточном программировании, когда необходимо обеспечить
безопасный обмен информацией между несколькими потоками. Модуль Queue предостав­
ляет три типа очередей: 1. Queue 2. LifoQueue 3. PriorityQueue Exception, которая может быть:
1) полной (очередь переполнена) или 2) пустой (очередь опустошена).

59. 1 . Простой пример
from Queue import Queue
question_queue = Queue()
for х in range(1 , 1 О):
temp_dict = ('key', х)
question_queue.put(temp_dict)

286

Глава 60. Модуль Deque

while(not question_queue.empty()):
item = question_queue.get()
print(str(item))

Результат:
('key', 1 )
('key', 2)
('key', 3)
('key', 4)
('key', 5)
('key', 6)
('key', 7)
('key', 8)
('key', 9)

Глава 60. Модуль Deque
Параметр

П одробн о ст и

iteraЫe

Создает deque с начальными элементами, скопированными из другого
итерируемого объекта

maxlen

Ограничивает размер deque, вытесняя старые элементы по мере добав­
ления новых

60. 1 . Базовое использование deque
Основные методы, которые полезно использовать в этом классе - popleft и append left.
from col lections import deque
d = deque([1 , 2, 3])
р = d.popleft()
# р = 1 , d = deque([2, 3])
d.append left(5) # d = deque([5, 2, 3])

60.2. Доступные методы в deque
Создание пустого объекта deque:
dl = deque() # deque(D) создание пустого deque

Создание deque с некоторыми элементами:
dl = deque([1 , 2, 3, 4]) # deque([1 , 2, 3, 4])

Добавление элемента в deque:

dl.append(5) # deque([1 , 2, 3, 4, 5])

Добавление элемента в левую часть deque:

dl.append left(O) # deque([O, 1 , 2, 3, 4, 5])

Добавление списка элементов в deque:

dl.extend([б, 7]) # deque([O, 1 , 2, 3, 4, 5, б, 7])

60.3. Ограничение размера deque

287

Добавление списка элементов с левой стороны:
dl.extendleft([-2, -1 ]) # deque([-1 , -2, О, 1 , 2, 3, 4, 5, 6, 7])

Использование .рор() приведет к удалению элемента из правой части:

dl.pop() # 7 = > deque([-1 , -2, О, 1 , 2, 3, 4, 5, 6])

Использование элемента .popleft() для удаления элемента с левой стороны:

dl.popleft() # -1 deque([-2, О, 1 , 2, 3, 4, 5, 6])

Удалить элемент по его значению:

dl.remove(1 ) # deque([-2, О, 2, 3, 4, 5, 6])

Обратный порядок элементов в deque:

dl.reverse() # deq ue([6, 5, 4, 3, 2, О, -2])

60.З. Ограничение размера deque

Используйте параметр maxlen при создании deque для ограничения размера:

from collections import deque
d = deque(maxlen = 3)
# вмещает только 3 элемента
# deque([1 ])
d.append(1 )
d.append(2)
# deque([1 , 2])
d.append(3)
# deque([1 , 2, 3])
# deque([2, 3, 4]) (1 удалена, так как maxlen равен 3)
d.append(4)

60.4. Поиск по ширине (Breadth First Search)

Deque - единственная структура данных Python, поддерживающая быстрыми операции
очереди. (Заметим, что queue.Queue обычно не подходит, поскольку он предназначен для вза­
имодействия между потоками.) Основным вариантом использования Queue является поиск
по ширине.
from collections import deque
def Ьfs(graph, root):
distances = {}
d istances[root] = О
q = deque([root])
while q :
# Сам ы м стары м (но ещ е н е посещенным) узлом будет крайний левый узел.
current = q.popleft()
for neighbor in graph[current]:
if neighbor not in d istances:
distances[neighbor] = distances[current] + 1
# Когда видим новый узел, то добавляем его в правую часть очереди.
q.append(neighbor)
return d istances

Допустим, у нас есть простой направленный граф:
graph

=

{1 :[2,3], 2: [4], 3: [4,5], 4:[3,5], 5: □}

Теперь мы можем находить расстояния от некоторого начального положения:

»> Ьfs(graph, 1 )
{1 : 0, 2: 1 , 3: 1 , 4: 2, 5: 2}
»> Ьfs(graph, 3)
{3: 0, 4: 1 , 5: 1 }

Глава 61 . Модул ь Webbrowser

288

Глава 61 . Модуль Webbrowser
Параметр
webbrowser.open()

Подробности

url

URL-aдpec для открытия в веб-браузере

new

О открывает URL в существующей вкладке, 1 открывает в новом
окне, 2 открывает в новой вкладке

autoraise

если установлено значение True, то окно будет перемещаться
поверх друrих окон

webbrowser.open_new()
url

URL-aдpec для открытия в веб-браузере

webbrowser.open_new_tab()
url

URL-aдpec для открытия в веб-браузере

webbrowser.get()
using

браузер для использования

webbrowser.register()
url

URL-aдpec для открытия в веб-браузере

constructor

путь к исполняемому браузеру

instance

Экземпляр веб-браузера, возвращаемый из метода webbrowser.get()

Согласно стандартной документации Python, модуль webbrowser предоставляет высоко­
уровневый интерфейс, позволяющий отображать пользователям WеЬ-документы. В данной
теме рассказывается и демонстрируется правильное использование модуля webbrowser.

61 . 1 . Открытие U RL-aдpeca браузером по умолчанию
Чтобы просто открыть URL, используйте метод webbrowser.open():
import webbrowser
webbrowser.open("http://stackoverflow.com")
Если в данный момент открыто окно браузера, то метод откроет новую вкладку по ука­
занному URL. Если окно не открыто, метод откроет браузер по умолчанию операционной
системы и перейдет на URL, указанный в параметре. Метод open поддерживает следующие
параметры:

url - URL-aдpec для открытия в веб-браузере (строка) [обязательный]
• new - при значении параметра О открывается в существующей вкладке,
1 -открывается в новом окне, 2 - открывается в новой вкладке (целое число)
[по умолчанию О]
• autoraise - если установлено значение True, то окно будет перемещаться поверх
окон друrих приложений (булево значение) [по умолчанию False]
Обратите внимание, что аргументы new и autoraise редко работают, так как большинство
современных браузеров отказываются от этих команд. Webbrowser также может попытаться
открыть URL в новом окне с помощью метода open_new:
import webbrowser
webbrowser.open_new("http://stackoverflow.com")

61 .2. Открытие URL-aдpeca с помощью различных браузеров

289

Этот метод обычно игнорируется современными браузерами, и URL обычно открывается
в новой вкладке. Открытие новой вкладки может быть опробовано модулем с помощью ме­
тода open_new_tab:
import webbrowser
webbrowser.open_new_tab("http://stackoverflow.com")

61 .2. Открытие URL-aдpeca с помощью различных браузеров

Модуль webbrowser также поддерживает различные браузеры с помощью методов
и get(). Метод get используется для создания контроллера браузера по заданному
пути к исполняемому файлу, а метод register - для прикрепления этих исполянемых файлов
к заданным типам браузеров для последующего использования, обычно при использовании
нескольких типов браузеров.
register()

import webbrowser
ff_path = webbrowser.get("C:/Program Files/Mozilla Firefox/f1 refox.exe")
ff = webbrowser.get(ff_path)
ff.open("http://stackoverflow.com/")
Registering а browser type:
import webbrowser
ff_path = webbrowser.get("C:/Program Files/Mozilla Firefox/f1 refox.exe")
ff = webbrowser.get(ff_path)
webbrowser.register('f1 refox', None, ff)
# Теперь для ссыл ки на испол ьзование Firefox в будущем можно испол ьзовать следующее
webbrowser.get('f1refox').open("https://stackoverflow.com/")

Глава 62. tkinter
Tkinter - самая популярная в Python библиотека графического интерфейса пользовате­
ля (GUI). В этой главе рассказывается о правильном использовании этой библиотеки и ее
возможностях.

62. 1 . Менеджеры геометрии

В Тkinter существует три механизма управления геометрией: place, pack и grid. Менеджер
place использует абсолютные пиксельные координаты. Менеджер pack размещает виджеты
в одну из 4 сторон. Новые виджеты размещаются рядом с существующими. Менеджер grid

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

place
Наиболее распространенными именованными аргументами для widget.place являются
следующие:
• х, абсолютная х-координата виджета
• у, абсолютная у-координата виджета
• height, абсолютная высота виджета
• width, абсолютная ширина виджета
Пример кода с использованием place:
class PlaceExample(Frame):
def _iniL(self,master):
Frame._init_(self,master)

290

Глава 62. tkinter

self.grid()
top_text = Label(master,text ="Этo находится сверху в начале координат")
#top_text.pack()
top_text.place(x = 0,y = 0,height = 50,width = 200)
bottom_right_text = Label(master,text = "Этo находится в позиции 200,400")
#top_text.pack()
bottom_right_text.place(x = 200,y = 400,height = 50,width = 200)
# Spawn Window
if _name_ = = "_main_":
root = Tk()
place_frame = PlaceExample(root)
place_frame.mainloop()

Pack

widget.pack может принимать следующие именованные аргументы:
• expand, заполнять ли пространство, оставленное родительским объектом


f1 II, заполнять ли все пространство (NONE (по умолчанию), Х, У, или ВОТН)



side, относительно которой будет производиться упаковка (ТОР (по умолчанию),
LEFT, или RIGHT)

ваттом,

Grid

Наиболее часто используемые именованные аргументы widget.grid следующие:

row, строка виджета (по умолчанию наименьшая незанятая)


rowspan, количество строк, которые охватывает виджет (по умолчанию 1)



col umn, столбец виджета (по умолчанию О)



col umnspan, количество колонок, которые охватывает виджет (по умолчанию 1)



sticky, куда поместить виджет, если ячейка сетки больше него (комбинация из
N,NE,E,SE,S,SW,W,NW)
Строки и столбцы имеют нулевой индекс. Строки увеличиваются вниз, а столбцы - впра­
во. Пример кода с использованием grid:
from tkinter import *
class Grid Example(Frame):
def _init_(self,master):
Frame._init_(self,master)
self.grid()
top_text = Label(self,text = "Этoт текст появляется слева вверху")
top_text.grid() # Положение по умолчанию О, О
bottom_text = Label(self,text = "Этoт текст появляется слева внизу")
bottom_text.grid() # Положение по умолчанию 1, О
right_text = Label(self,text = "Этoт текст появляется справа и охватывает обе строки",
wraplength = 100)
# Положение 0,1
# Rowspan означает, что фактическое положение [0-1],1
righLtext.grid(row = 0,column= 1,rowspan = 2)
# Spawn Window
if _name_=="_main_":
root=Tk()
grid_frame=GridExample(root)
grid_frame.mainloop()

Никогда не используйте pack и grid в одном фрейме ! Это приведет приложение к вза­
имоблокировке!

62.2. Минимальное приложение tkinter

291

62.2. Минимальное приложение tkinter
tkinter - это набор инструментов для создания графического интерфейса, который пред­
ставляет собой обертку вокруг библиотеки графического интерфейса Tk/Tcl GUI и входит в
состав Python. Следующий код создает новое окно с помощью tkinter и помещает некоторый
текст в тело окна.
import tkinter as tk
# Окно графического интерфейса является подклассом базового объекта tkinter Frame
class HelloWorld Frame(tk. Frame):
def _iniL(self, master):
# Вызов конструктора суперкласса
tk. Frame._init_(self, master)
# Помещение фрей ма в главное окно
self.grid()
# Создать текстовое поле с текстом "Hello World"
hello = tk. Label(self, text = "Hello World! This label сап hold strings!")
# Поместить текстовое поле во фрей м
hello.grid(row = 0, column = 0)
# Spawn window
if _name_ == "_main_":
# Создать объект главного окна
root = tk. Tk()
# Установить заголовок окна
root.title("Hello World!")
# Инстанцировать объект HelloWorld Frame
hello_frame = HelloWorld Frame(root)
# Запустить графический интерфейс
hello_frame.mainloop()

Глава 6 3 . Модуль pyautogui
pyautogui - это модуль, предназначенный для управления мышью и клавиатурой.
В основном этот модуль используется для автоматизации задач щелчка мыши и нажа­
тия клавиш клавиатуры. Для мыши координаты экрана (0,0) начинаются с левого верх­
него угла. Если вы не можете управлять мышью, то быстро переместите курсор мыши в
левый верхний угол, это перехватит управление мышью и клавиатурой у Python и вер­
нет его вам.

6 3 . 1 . Функции мыши
Вот некоторые и з полезных функций мыши для управления ею.
size() #указывает размер экрана
position() # возврат текущего положения м ы ши
moveTo(200,0,duration = 1 .5) #переместить курсор в позицию (200,0) с задержкой в 1 ,5 секунды
moveRel() #переместить курсор относительно текущей позиции
click(337,46) #щелкнет м ы шью на упомянутой позиции
dragRel() #перетащит м ы шь относительно позиции
pyautogui.displayMousePosition() # выдает текущее положение м ы ши, но должно выполняться
в терминале

292

Глава 64. Индексация и нарезка

63.2. Функции клавиатуры

Вот некоторые из полезных функций клавиатуры для автоматизации нажатия клавиш.

typewrite(" ) #это в ы ведет строку на экране, на котором сфокусировано текущее окно
typewrite(['a','b','left','left','X','Y'])
pyautogui.KEYBOARD_KEYS #получить список всех клавиш_клавиатуры
pyautogui.hotkey('ctrl','o') #для комбинации клавиш для ввода

63.3. Распознавание скриншотов и изображений

Эти функции помогут вам сделать снимок экрана, а также сопоставить изображение с ча­
стью экрана .
.screenshot('c:\\path')
.locateOnScreen('c:\\path')
locateCenterOnScreen('c:\\path')

#получить скриншот
#поиск этого изображения на экране и получение координат
#получить координаты для изображения на экране

Глава 64. Индексация и нарезка
Параметр

П одро б ности

obj

Объект, из которого вы хотите извлечь "подобъект"
Индекс obj, с которого должен начинаться подобъект (следует пом­
нить, что Python имеет нулевую индексацию, то есть первый элемент
obj имеет индекс О). Если это значение опущено, то по умолчанию оно
равно О.
(неполный) индекс obj, на котором должен заканчиваться подобъект.
Если это значение опущено, то по умолчанию оно равно len(obj).
Позволяет выбрать только каждый элемент с шагом step. Если опуще­
но, по умолчанию принимается значение 1.

start

stop
step

64. 1 . Базовая нарезка

Для любого итерируемого объекта (например строки, списка и т. д.) Python позволяет на­
резать и вернуть подстроку или подсписок его данных. Формат для нарезки:
iterable_name[start:stop:step]

где

фрагмента. По умолчанию равен О (индекс первого
элемента)
• stop - последний индекс фрагмента. По умолчанию равен len(iteraЫe)

step - размер шага (лучше пояснен на примерах ниже)
Примеры:


start - первый индекс

а = "abcdef"
# "abcdef"
а
# То же, что и а[:] или а[::], поскольку для всех трех индексов используются
# значения по умолчанию
а[-1 ]
# "f"
а[:]
# "abcdef"
а[::]
# "abcdef"

64.2. Реверсирование объекта

293

# "def" (от и ндекса 3 до конца (по умолчанию равен размеру итери руемого объекта))
а[З:]
# "abcd" (от начала (по умолчанию О} до позиции 4 (искл ючая))
а[:4]
а[2:4] # "cd" (от позиции 2 до позиции 4 (исключая))
Кроме того, может быть использован любой из вышеперечисленных способов с задан­
ным размером шага:
а[::2] # "асе" (каждый 2-й элемент)
а[1 :4:2] # "bd" (от и ндекса 1 до и ндекса 4 (исключая), каждый 2-й элемент
Индексы могут быть отрицательными, в этом случае они вычисляются с конца последо­
вательности:
а[:-1 ] # "abcde" (от и ндекса О (по умолчанию) до 2-го последнего элемента
# (последний элемент -1 ))
а[:-2] # "abcd" (от и ндекса О (по умолчанию) до 3-го последнего элемента
# (последний элемент -2}}
а[-1 :] # "f" (от последнего элемента до конца (по умолчан и ю len())
Размер шага может быть и отрицательным, в этом случае нарезка будет выполнять ите­
рации по списку в обратном порядке:
а[З:1 :-1 ] # "dc" (от и ндекса 2 до None (по умолчанию), в обратном порядке)
Эта конструкция полезна для инвертирования итерируемого объекта
а[::-1 ]

# "fedcba" (от последнего элемента (по умолчанию len()-1 } к первому,
# в обратном порядке(-1 ))

Обратите внимание, что для отрицательных шагов end_index по умолчанию равен None.
a[5:None:-1 ]
а[5:О:-1 ]

# "fedcba" (это экви валентно а[::-1 ]}
# "fedcb" (от последнего элемента (и ндекс 5) до второго элемента (и ндекс 1 }

64.2. Реверсирование объекта

С помощью срезов можно очень просто перевернуть строку, список или кортеж (или, по
сути, любой объект коллекции, реализующий срез с параметром step). Здесь приведен при­
мер инвертирования строки, хотя это в равной степени относится и к другим типам, пере­
численным выше:
s = 'reverse me!'
s[::-1 ] # '!em esrever'
Кратко рассмотрим синтаксис. [::-1 ] означает, что срез должен быть от начала до конца
строки (так как start и end опущены), а шаг -1 означает, что он должен двигаться по строке в
обратном направлении.

64.З. Назначение среза

Python позволяет назначать новые срезы для замены старых срезов списка за одну опера­
цию. Это означает, что если у вас есть список, то вы можете заменить несколько его членов
в одном назначении:
1st = [1 , 2, З]
lst[1 :З] = [4, 5]
print(lst) # Результат: [1 , 4, 5]
Назначение не должно совпадать по размеру, поэтому, если вы хотите заменить старый
срез новым, отличающимся по размеру, вы можете это сделать:
1st = [1 , 2, 3, 4, 5]
lst[1 :4] = [б]
print(lst) # Результат: [1 , 6, 5]

294

Глава 64. Индексация и нарезка

Также можно использовать известный синтаксис нарезки для выполнения таких действий, как замена всего списка:
1st = [1 , 2, 3]
lst[:] = [4, 5, б]
print(lst) # Результат: [4, 5, б]

или только двух последних элементов списка:
1st = [1 , 2, 3]
lst[-2:] = [4, 5, б]
print(lst) # Результат: [1 , 4, 5, 6]

64.4. Создание неглубокой копии массива

Быстрый способ создания копии массива (в отличие от присвоения переменной другой
ссьшки на исходный массив) заключается в следующем:
arr[:]

Рассмотрим синтаксис. [:] означает, что значения start, end и slice опущены. По умолчанию
они равны О, len(arr) и 1 соответственно, то есть запрашиваемый нами подмассив будет со­
держать все элементы arr с начала и до самого конца. На практике это выглядит так:
arr = ['а', 'Ь', 'с']
сору = arr[:]
arr.append ('d')
print(arr) # ['а', 'Ь', 'с', 'd']
print(copy) # ['а', 'Ь', 'с']

Как видно, arr.append('d') добавила d в arr, но копия (сору) осталась неизменной! Заметим,
что при этом выполняется неглубокое копирование, идентичное функции arr.copy().

64.5. Индексация пользовательских классов:
_getitem_, _setitem_ и _delitem_
class M ulti lndexing list:
def _init_(self, value):
self.value = value
def _repr_(self):
return repr(self.value)
def _getitem_(self, item):
if isinstance(item, (int, slice)):
return self._class_(self.value[item])
return [self.value[i] for i in item]
def _setitem_(self, item, value):
if isinstance(item, int):
self.value[item] = value
elif isinstance(item, slice):
raise ValueError('Cannot interpret slice with multiindexing')
else:
for i in item:
if isinstance(i, slice):
raise ValueError('Cannot interpret slice with multiindexing')
self.value[i] = value

64.6. Базовая индексация

295

def _delitem_(self, item):
if isinstance(item, int):
del self.value[item]
elif isinstance(item, slice):
del self.value[item]
else:
if any(isinstance(elem, slice) for elem in item):
raise ValueError('Cannot interpret slice with multiindexing')
item = sorted(item, reverse = True)
for elem in item:
del self.value[elem]

Это позволяет осуществлять нарезку и индексацию для доступа к элементам:
а

=

а

Mu ltilndexing List([1 ,2,3,4,5,6,7,8])

# Резул ьтат: [1 , 2, 3, 4, 5, 6, 7, 8]
а[1 ,5,2,6, 1 ]
# Резул ьтат: [2, 6, 3, 7, 2]
а[4, 1 , 5:, 2, ::2]
# Резул ьтат: [5, 2, [6, 7, 8], 3, [1 , 3, 5, 71]
#
41 1 +---50:--12-1----::2----- > 'а'

По индексу 1 можно получить доступ ко второму элементу списка, по индексу 2 - к третьему и т. д.:
print(arr[1 ])

>> 'Ь'

print(arr[2])

Для доступа к элементам из конца списка можно также использовать отрицательные
индексы. Например, индекс -1 позволяет получить последний элемент списка, а индекс -2 предпоследний элемент списка:
print(arr[-1 ])
'd'
print(arr[-2])

>>

>> 'с'

296

Глава 65. Построение графиков с помощью Matplotlib

Если попытаться обратиться к индексу, которого нет в списке, то будет выдана ошибка
lndexError:
print агг[б]
Traceback (most recent call last):
File "", line 1 , in .
lndexError: l ist index out of range (индекс списка выходит за пределы диапазона)

Глава 65. Построение графиков
с помощью Matplotlib
Matplotlib (https://matplotlib.org/) - это библиотека для двумерного черчения, основанная
на NumPy.

65. 1 . Графики с общей осью х, но разными осями у:
использование twinx()

В данном примере мы построим на одном графике синусоиду и гиперболическую синусоиду с общей осью х и разными осями у. Для этого используется команда twinx().
import numpy as пр
import matplotlib.pyplot as plt
х = np. linspace(O, 2.0*np.pi, 1 01 )
у = np.sin(x)
z = np.sinh(x)
# отделить объект f1gure и объект axes
# от объекта plotting
fig, ах1 = plt.subplots()
# Дублирование осей с другой осью у
# и той же осью х
ах2 = ах1 .twinx() # ах2 и ах1 будут иметь общую ось х и разн ые оси у
# построить кривые на осях 1 и 2
curve1 , = ах1 .plot(x, у, label = "sin", color = 'r') curve2,

=

ax2.plot(x, z, label = "sinh", color = 'b')

# Составить список кривых для доступа к параметрам в кривых
кривые = [кривая1 , кривая2]
# добавить легенду по осям 1 или 2 объекта.
# обычно достаточно одной команды
# ах1 . legend() # не будет отображаться легенда ах2
# ax2. legend() # не будет отображаться легенда ах1
ах1 .legend(curves, [curve.get_label() for curve in curves])
# ax2. legend(curves, [curve.get_label() for curve in curves]) # также допустимо
# Глобальн ые свойства фигуры
plt.title("Гpaфик синусоиды и гиперболической синусоиды")
plt.show()

297

65.2. Графики с общей осью у, но разными осями х: использование функции twiny()
График синусоиды и гиперболической синусоиды
1.00
0.75
0.5 0
0. 2 5
0. 00
-0.2 5
-0 . 5 0
-0 . 7 5
-1.00

о

1

2

3

4

5

6

65.2. Графики с общей осью у, но разными осями х:
использование функции twiny()
В данном примере с помощью метода twinyO демонстрируется график с кривыми, имею­
щими общую ось у, но разные оси х. Кроме того, на график добавлены некоторые дополни­
тельные функции, такие как заголовок, легенда, метки, сетки, отметки на осях и цвета.
import numpy as пр
import matplotlib.pyplot as plt
у = np.linspace(0, 2.0*np.pi, 1 01 )
х 1 = np.sin(y)
х2 = np.sinh(y)
# значения для создания отметок на осях х и у
ynumbers = np.linspace(0, 7, 1 5)
xnumbers1 = np.linspace(-1 , 1 , 1 1 )
xnumbers2 = np.linspace(0, 300, 7)
# отделить объект f1gure и объект axes
# от объекта plotting
f1g, ах1 = plt.subplotsO
# Дублирование осей с другой осью х
# и той же осью у
ах2 = ах1 .twiny() # ах2 и ах1 будут иметь общую ось у и разные оси х
# построить кривые на осях 1 и 2
curve1 , = ах1 .plot(x1 , у, label="sin", color='r')
curve2, = ax2.plot(x2, у, label="sinh", color='b')

298

Глава 65. Построение графиков с помощью Matplotlib

# Составить список кривых для доступа к параметрам в кривых
curves = [curve1 , curve2]
# добавить легенду по ося м 1 или 2 объекта
# обычно достаточно одной команды
# ах1 . legend() # не будет отображаться легенда ах2
# ax2.legend() # не будет отображаться легенда ах1
# ах1 . legend(curves, [cu rve.get_label(} for curve in curves])
ax2.legend(curves, [curve.get_label(} for curve in curves]) # также допустимо
# метки оси х через оси
ах1 .sеt_хlаЬеl("Величина", color = curve1 .geLcolor())
ах2.sеt_хlаЬеl("Величина", color = curve2.geLcolor())
# метка оси у по ося м
ах1 .sеt_уlаЬеl("Угол/Значение", color = curve1 .get_color())
# ах2.sеt_уlаЬеl("Величина", color = curve2.get_color()) # не работает
# в ах2 нет управления свойствами по оси у
# метки на оси у - сделать их также цветны ми
ах1 .tick_params(axis = 'y', colors = curve1 .geLcolor())
# ax2.tick_params(axis = 'y', colors = curve2.get_color()) # не работает
# в ах2 нет управления свойствами по оси у
# метки на оси х через оси
ах1 .tick_params(axis = 'x', colors = curve1 .get_color())
ax2.tick_params(axis = 'x', colors = curve2.get_color())
# установить метки по оси х
ах1 .set_xticks(xnumbers 1 )
ax2.set_xticks(xnumbers2)
# установить метки по оси у
ах1 .set_yticks(ynumbers)
# ax2.set_yticks(ynumbers) # также работает
# Сетки по ося м 1 # используйте это, если ось 1 используется для
# определения свойств общей оси х
# ах1 .grid(color = curve1 .geLcolor())
# Для создания сеток с использованием осей 2
ах1 .grid(color = curve2.get_color())
ax2.grid(color = curve2.get_color())
ах1 .xaxis.grid(False)
# Глобальные свойства фигуры
plt.title('Тpaфик синусоиды и гиперболической синусоиды")
plt.show()

299

65.3. Простой график в Matplotlib

7.0
6.5
6.0
5.5

о

[

- si n 1
si nh

:=

::t:
ф

::t:

1

L.---



5.0
ф

График синусоиды и гиперболической
синусоиды
250
50
Величина

4. 5
4. 0

-------

�г--__

3.5

---L---v---

3.0
2.5

------

1.5
0. 5
О. О
- 1 . 0 -0.8 -0.6 -0.4 -0.2

О.О
0.2
Величина

--r---....

__,�

-----

1.0

-

1--...

2.0

300

0.4

0.6

0.8

65.З. Простой график в Matplotlib

1.0

Данный пример ИJШюстрирует создание простой синусоиды с помощью Matplotlib.

import numpy as пр
import matplotlib.pyplot as plt
# угол, изменяющийся в пределах от О до 2*pi
х = np. l inspace(O, 2.0*np.pi, 1 01 )
у = np.sin(x) # функция синуса
plt.plot(x, у)
plt.show()
1.00
0.75
0.50
0.25
0.00
-0 . 2 5
-0 . 5 0
-0 . 7 5
-1.00

о

l

2

3

4

5

6

300

Глава 65. Построение графиков с помощью Matplotlib

65.4. Добавление дополнительных функций к простому графику:
названия осей, заголовок, метки осей, сетка и легенда

В этом примере мы берем график синусоиды и добавляем к нему заголовок, названия
осей, метки осей, сетку и легенду.
import numpy as пр
import matplotlib. pyplot as plt
х = np.linspace(O, 2.0*np.pi, 1 01 )
у = np.sin(x)
# значения для создания меток по осям х и у
xnumbers = np.linspace(O, 7, 1 5)
ynumbers = np.linspace(-1 , 1 , 1 1 )
plt.plot(x, у, color='r', label='sin') # г - красный цвет
plt.xlabel("Yroл в радианах")
рlt.уlаЬеl("Величина")
plt.title("Гpaфик некоторой тригонометрической функции")
plt.xticks(xnumbers)
plt.yticks(ynumbers) plt.legend()
plt.grid()
plt.axis([O, 6.5, -1 . 1 , 1 . 1 ]) # [xstart, xend, ystart, yend]
plt.show()

График некоторой тригонометрической
функции

Jv

1.0
0.8

'

0.4

a::i

si n

J-

\

0. 6

rn
::t:
:s:

:s:

1
_

0. 2

о. о

\

-0 . 2
-0 . 4
-0 . 6

/

\

/

//

V

-0 . 8



-1.0

о.о

0.5

1.0

1.5

2.0

2.5

3.0

3. 5

4. 0

Угол в радианах

4. 5

5.0

5.5

6.0

6.5

301

65.5. Создание нескольких кривых на одном графике путем наложения, аналогично MATLAB

65.5. Создание нескольких кривых на одном графике путем
наложения, аналогично MATLAB

В данном примере синусоида и косинусоида построены на одном графике путем наложения.
# Хорошо подходит для графиков с близкими значения ми х, у
# Использование одной команды plot и legend
import numpy as пр
import matplotlib.pyplot as plt
х = np.linspace(0, 2.0*np. pi, 101)
у = np.sin(x)
z = np.cos(x)
# значения для создания меток по осям х и у
xnumbers = np.linspace(0, 7, 15)
ynumbers = np.linspace(-1, 1, 11)
plt.plot(x, у, 'г', х, z, 'g') # r, g - красный, зеленый цвет
plt.xlabel("Yгoл в радианах")
рlt.уlаЬеl("Величина")
plt.title('Tpaфик некоторых тригонометрических функций")
plt.xticks(xnumbers)
plt.yticks(ynumbers)
plt.legend(['cинyc', 'косинус'])
plt.grid()
plt.axis([0, 6.5, -1.1, 1.1]) # [xstart, xend, ystart, yend]
plt.show()
График некоторых тригонометрических функций

1. О
0. 8

ro
I
:s:
J
:s:
@

a::i

�--'х·-·__

-+---а:--"-+---+/

�.........,.,-+�
"'\
------+---+--+----+--/-+-#//
_----+-_
1 _..,,

0 . 6 �--, ---+-+-------+----+-+--+----+---1-+----+--<
;
/
\
1'\ ----+-----+---+----+-----1--F--+-----+------!
J
-+----+-----+-+0. 4 �-----l-f-�
/
;
/
\
\
/-1-1-----+----+-- ---1
0. 2 �-�---+--+-+----+----- Ь -> с; с ->а; }
import pydotplus
graph_a = pydotpl us.graph_from_d0Lf1 le('demo.dot')
graph_a.write_svg('test.svg') # создать граф в формате svg
Вы получите файл в фомате *.svg (ScalaЬle Vector Graphics) следующего вида:

66.2. PyGraphviz
PyGraphviz можно получить из Python Package Index по адресу http://pypi.python.org/pypi/
pygraphviz или установить его с помощью следующей команды:
pip install pygraphviz
Будет сделана попытка найти и установить подходящую версию, соответствующую ва­
шей операционной системе и версии Python.
Установить версию для разработки (на github.com) можно с помощью:
pip install git://github.com/pygraphviz/pygraphviz.git#egg=pygraphviz
Загрузите граф, определенный в файле DOT.
• Предполагается, что файл имеет формат DOT. Он будет загружен, разобран
(парсирован), затем будет возвращен класс Dot, представляющий граф. Например,
простой demo.dot:

304

Глава 67. Генераторы

digraph demol { а -> Ь -> с; с -> а ; }
import pygraphviz as pgv
G = pgv.AGraph("demo.dot")
G.draw('test', format='svg', prog='dot')
Вы получите файл в формате *.svg (ScalaЬle Vector Graphics), такой же, как и в предыду­
щей главе.

Гла ва 67 . Генератор ы
Генераторы - это "ленивые" итераторы, создаваемые функциями-генераторами (с ис­
пользованием yield) или выражениями-генераторами (с использованием (an_expression for х
in an_iterator)).

67. 1 . Введение

Выражения-генераторы аналогичны генераторам (comprehensions) списков, словарей
и множеств, но заключены в круглые скобки. Скобки не обязательно должны присутство­
вать, если они используются в качестве единственного аргумента вызова функции.
expression = (х**2 for х in range(1 О))
Получено 10 первых совершенных квадратов, включая О (в котором х = О).
Функции-генераторы похожи на обычные функции, за исключением того, что в их теле
имеются один или несколько операторов yield. Такие функции не могут возвращать (return)
никаких значений (однако пустые возвраты допускаются, если необходимо остановить ге­
нератор раньше времени).
def function():
for х in range(1 О):
yield х**2
Эта генераторная функция эквивалентна предыдущему генераторному выражению, она
выводит то же самое.
Примечание: все выражения-генераторы имеют свои эквивалентные функции, но не на­
оборот.
Генераторное выражение может быть использовано без скобок, если в противном случае
обе скобки повторялись бы:
sum(i for i in range(1 О) if i % 2 == О) #Результат: 20
#Результат: True или False в зависимости от foo
any(x = О for х in foo)
type(a > Ь for а in foo if а % 2 == 1 ) #Результат:
Вместо:
sum((i for i in range(1 О) if i % 2 == О))
any((x = О for х in foo))
type((a > Ь for а in foo if а % 2 == 1 ))
Но не:
fooFunction(i for i in range(1 О) if i % 2 == O,foo,bar)
return х = О for х in foo
barFunction(baz, а > Ь for а in foo if а % 2 == 1 )
При вызове функции-генератора создается объект-генератор, над которым в дальней­
шем можно выполнять итерации. В отличие от других типов итераторов, объекты-генерато­
ры могут быть обойдены только один раз.

67. 1 . Введен ие

305

g1 = function()
print(g 1 ) # Out:
Обратите внимание, что тело генератора выполняется не сразу: при вызове функции
function() в приведенном примере она сразу возвращает объект генератора, не выполняя
даже первого оператора print. Это позволяет генераторам занимать меньше памяти, чем
функции, возвращающие список, соответственно можно создавать генераторы, выдающие
бесконечно длинные последовательности. По этой причине генераторы часто используются
в науке о данных и других контекстах, связанных с большими объемами данных. Еще одним
преимуществом является то, что другой код может сразу использовать значения, выданные
генератором, не дожидаясь получения полной последовательности.
Однако если необходимо использовать значения, полученные генератором, более одно­
го раза, и если их генерация обходится дороже хранения, то, возможно, лучше хранить по­
лученные значения в виде списка, чем заново генерировать последовательность. Более под­
робная информация приведена ниже в подтеме "Сброс генератора".
Обычно объект-генератор используется в цикле или в любой функции, требующей ите­
рируемого объекта:
for x in g 1 :
рrint("Получено", х)
# Output:
# Получено О
# Получено 1
# Получено 4
# Получено 9
# Получено 1 6
# Получено 25
# Получено 36
# Получено 49
# Получено 64
# Получено 81
arr1 = list(g 1 )
# arr1 = , так как в цикле выше уже были использованы все значения
g2 = function()
arr2 = list(g2) # arr2 = [О, 1 , 4, 9, 1 6, 25, 36, 49, 64, 8 1 ]



Поскольку объекты-генераторы являются итераторами, их можно перебирать вручную
с помощью функции next(). При этом при каждом последующем вызове будут возвращаться
поочередно все полученные значения.
Кстати, каждый раз, когда вы вызываете next() на генераторе, Python выполняет опера­
торы в теле функции генератора, пока не дойдет до следующего оператора yield. В этот мо­
мент он возвращает аргумент команды yield и запоминает точку, в которой это произошло.
Повторный вызов next() возобновит выполнение с этой точки и будет продолжаться до сле­
дующего оператора yield.
Если Python доходит до конца генераторной функции, не встречая больше yield, возникает исключение Stoplteration (это нормально, все итераторы ведут себя таким образом).
g3 = function()
а = next(g3) # а становится О
Ь = next(g3) # Ь становится 1
с = next(g3) # с становится 2
j = next(g3) # Возни кает Stoplteration, j остается неоп ределенной
Отметим, что в Python 2 объекты генераторов использовали метод .next(), который можно
бьmо использовать для ручного итерационного перебора получаемых значений. В Python 3
этот метод бьm заменен стандартным для всех итераторов ._next_().

306

Глава 67. Генераторы

Сброс генератора
Помните, что перебирать объекты, созданные генератором, можно только один раз. Если
вы уже перебирали объекты в скрипте (сценарии), то любая дальнейшая попытка сделать
это приведет к результату None.
Если необходимо использовать объекты, созданные генератором, более одного раза, можно
либо определить генераторную функцию повторно и использовать ее во второй раз, или при
первом использовании можно сохранить вывод функции-генератора в списке. Повторное со­
здание функции-генератора будет хорошим вариантом, если вы имеете дело с большими объ­
емами данных, и хранение списка всех элементов данных займет много места на диске. И на­
оборот, если первоначальная генерация элементов требует больших затрат, лучше хранить
сгенерированные элементы в виде списка, чтобы можно бьто использовать их повторно.

67.2. Бесконечные последовательности

Генераторы можно использовать для представления бесконечных последовательностей:
def integers_starting_from(n):
while True:
yield n
n += 1
natural_numbers = integers_starting_from(1 )

Бесконечная последовательность чисел, как приведенная выше, также может быть сге­
нерирована с помощью itertools.count. Приведенный выше код можно переписать следую­
щим образом:
natural_numbers = itertools.count(1 )
Для создания новых генераторов можно использовать генераторные концепции
(generator comprehensions) на бесконечных генераторах:
multiples_of_two = (х * 2 for х in natural_numbers)
multiples_of_three = (х for х in natural_numbers if х % 3 = = О)
Следует помнить, что передача бесконечного генератора в любую функцию, которая по­
пытается использовать генератор целиком, приведет к плачевным последствиям:
list(multiples_of_two) # н и когда не завершится или вызовет ошибку, специфичную для ОС
Вместо этого используйте генераторы списков или наборов (list/set comprehensions), ука­
зывая range (или xrange для Python < 3.0):
firsLf1ve_multiples_of_three = [next(multiples_of_three) for _ in range(5)]
# [3, б, 9, 1 2, 1 5]
или используйте itertools.islice() для разбиения итератора на подмножества:
from itertools import islice
multiples_of_four = (х * 4 for х in integers_starting_from(1 ))
f1rsLf1ve_multiples_of_four = list(islice(multiples_of_four, 5))
# [4, 8, 1 2, 1 б, 20]
Обратите внимание, что исходный генератор тоже обновляется, как и все остальные генераторы, исходящие из того же "корня":
next(natural_numbers) # дает 1 б
next(multiples_of_two) # дает 34
next(multiples_of_four) # дает 24
Итерация бесконечной последовательности также может быть выполнена с помощью
цикла for. Обязательно включите в него условный оператор break, чтобы цикл в конце кон­
цов завершился:
for idx, number in enumerate(multiplies_of_two):
print(number)

67.3. Отправка объектов генератору

307

if idx == 9:
Ьгеаk # остановиться после выполнения первых 1 О умножений н а два

Классический пример - числа Фибоначчи

import itertools

def fi bonacci():
а, Ь = 1, 1
while True:
yield а
а, Ь = Ь, а + Ь
f1rst_ten_f1bs = list(itertools.islice(f1bonacci(), 1 О))
# [1 , 1 , 2, 3, 5, 8, 1 3, 21 , 34, 55]
def nth_f1 b(n):
return next(itertools. islice(f1 bonacci(), п - 1 , п))
ninety_nineth_f1b = nth_f1 b(99) # 3 542248481 79261 9 1 5075

67.З. Отправка объектов генератору

Помимо получения значений от генератора можно отправить объект генератору с по­
мощью метода send() .
def accumulator():
total = О
value = None
while True:
# получить отправленное значение
val ue = yield total
if value is None: Ьгеаk
# агрегирование значений
total += val ue
generator = accumulator()
# продвигаться до первого "yield"
next(generator) # О
# с этого момента генератор агрегирует значения
generator.send(1 )
#1
generator.send(1 О)
#11
#111
generator.send(1 00)
# ...
# Вызов next(generator) эквивалентен вызову generator.send(None)
next(generator) # Stoplteration

Здесь происходит следующее:
• Когда вы сначала вызываете next(generator), программа выполняется до первого
оператора yield и возвращает значение total в этой точке, которое равно О.
Выполнение генератора приостанавливается в этой точке.
• Когда вы вызываете generator.send(x), интерпретатор принимает аргумент х
и делает его возвращаемым значением последнего оператора yield, которому
присваивается value. Затем генератор выполняется как обычно, до тех пор, пока
он не выдает следующее значение.
• Когда вы наконец вызываете next(generator), программа рассматривает это так, как
будто вы отправляете None в генератор. В этом примере используется None как
специальное значение, чтобы указать генератору остановиться.

308

Глава 67. Генераторы

67.4. Предоставление всех значений из другого итерируемого
объекта
Версия Python З.х ::с: 3.3

Используйте yield from, если вы хотите получить все значения из другого итерируемого
объекта:
def foob{x):
yield from range(x * 2)
yield from range{2)
list{foob{5)) # [О, 1 , 2, 3, 4, 5, 6, 7, 8, 9, О, 1 ]
Это работает и с генераторами.
def f1 Ьto(n):
а, Ь = 1 , 1
while True:
if а >= п: break
yield а
а, Ь = Ь, а + Ь
def usefibO:
yield from f1Ьto{1 О)
yield from f1Ьto{20)
list(usef1b{)) # [1 , 1 , 2, 3, 5, 8, 1 , 1 , 2, 3, 5, 8, 1 3]

67.5. Итерация

Объект генератора поддерживает протокол итератора. То есть он предоставляет метод
nextO (_next_() в Python 3.х, который используется для пошагового выполнения, а его метод
_iter_ возвращает сам себя. Это означает, что генератор может быть использован в любой
языковой конструкции, поддерживающей общие итерируемые объекты.
# частичная реал изация xrange() в Python 2.х
def xrange(n):
i=о
while i < п:
yield i

i += 1

# зацикл и вание
for i in xrange{1 О):
print(i) # выводит значения О, 1 , ... , 9
# распаковка
а, Ь, с = xrange{3) # О, 1 , 2
# построение списка
1 = list(xrange{1 О)) # [О, 1, ... , 9]

67.6. Функция next()
Встроенная функция next() представляет собой удобную обертку, которая может быть ис­
пользована для получения значения из любого итератора (в том числе и генератора итера­
торов) и для предоставления значения по умолчанию в случае, если итератор исчерпан.
def nums():
yield 1

67.7. Корутины (сопрограммы)

309

yield 2
yield 3
generator = nums()
next(generator,
next(generator,
next(generator,
next(generator,
next(generator,
# ...

None)
Nопе)
None)
None)
None)

#1
#2
#3
# None
# None

Синтаксис следующий: next(iteratorL default]). Если итератор заканчивается и было переда­
но значение по умолчанию, то оно возвращается. Если значение по умолчанию не бьmо пе­
редано, то выдается сообщение Stoplteration.

67.7. Корутины (сопрограммы)

Генераторы могут использоваться для реализации корутин (сопрограмм):

# создание и продвижение генератора до первого yield
def coroutine(func):
def start(*args,**kwargs):
сг = func(*args,**kwargs)
next(cr)
return сг
return start
# пример корутины
@coroutine
def adder(sum = О):
while True:
х = yield sum
sum += х
# пример использования
s = adder()
s.send(1 ) # 1
s.send(2) # 3

Корутины широко используются для реализации машин состояний, так как в первую
очередь они удобны для создания однометодных процедур, требующих для своей работы
учета состояния. Они действуют в существующем состоянии и возвращают значение, полу­
ченное по завершении операции.

67.8. Рефакторинг кода построения списков

Предположим, у вас есть сложный код, который создает и возвращает список, начиная
с пустого списка и многократно добавляя к нему:
def create():
result = D
# программ ная логика здесь ...
result.append(val ue) # возможно в нескольких местах
# еще логика...
return result # возможно в нескольких местах
values = create()

Если замена внутренней логики на генератор списка нецелесообразна, можно превра­
тить всю функцию в генератор на месте, а затем собрать результаты:
def create_gen():
# программ ная логика ...

31 0

Глава 67. Генераторы

yield val ue
# еще програм мная логика
return # не требуется, если в конце функции, конечно
values = list(create_gen())
Если логика рекурсивна, используйте yield from, чтобы включить все значения из рекур­
сивного вызова в "уплощенный" результат:
def preorder_traversal {node):
yield node.val ue
for child in node.children:
yield from preorder_traversal{child)

67.9. Использование оператора yield с рекурсией: рекурсивное
перечисление всех файлов в каталоге
Сначала импортируйте библиотеки, которые работают с файлами:
from os import listdir
from os.path import isf1 le, join, exists
Вспомогательная функция для чтения только файлов из каталога:
def get_f1 les(path):
for file in listdir(path):
ful l_path = join(path, fi le)
if isf1 le{fu l l_path):
if exists(fu l l_path):
yield full_path
Другая вспомогательная функция для получения только подкаталогов:
def get_directories(path):
for directory in listdir{path):
ful l_path = join(path, directory)
if not isf1le{ful l_path):
if exists(fu l l_path):
yield full_path
Теперь используйте эти функции для рекурсивного получения всех файлов в каталоге
и во всех его подкаталогах (с использованием генераторов):
def get_f1 les_recursive{directory):
for file in get_fi les{directory):
yield f1 le
for subdirectory in get_directories{directory):
for f1 le in get_fi les_recursive(subdirectory): # здесь рекурсивны й вызов
yield f1 le
Эту функцию можно упростить, используя yield from:
def get_fi les_recursive{directory):
yield from get_f1 les{directory)
for subdirectory in get_directories{directory):
yield from get_f1 les_recursive(subdirectory)

6 7 . 1 О. Генераторные выражения
Можно создавать генераторы итераторов, используя следующий синтаксис:

generator = (i * 2 for i in гапgе{З))
next(generator) # О
next(generator) # 2
next(generator) # 4
next(generator) # вызы вает Stoplteration

67. 1 1 . Использование генератора для получения чисел Фибоначчи

31 1

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

sum(i -1г1< 2 for i in range(4)) # о л 2 + 1 л 2 + 2л 2 + 3 л 2 = О + 1 + 4 + 9 = 1 4
Кроме того, вы сэкономите память, поскольку вместо загрузки всего списка, по которому
выполняется итерация ([О, 1 , 2, З] в этом примере), генератор позволяет Python использовать
значения по мере необходимости.

67 . 1 1 . Использование генератора для получения чисел
Фибоначчи

Практический пример использования генератора состоит в том, чтобы перебирать зна­
чения бесконечного ряда. Вот пример нахождения первых десяти членов последовательно­
сти Фибоначчи:
def fi b(a = O, Ь = 1 ):
""" Генератор, выдающий числа Фибоначчи, 'а' и 'Ь' - начальные значения"""
while True:
yield а
а, Ь = Ь, а + Ь
f = f1b()
print(', '.join(str(next(f)) for _ in range(1 О)))

о, 1, 1, 2, 3, 5, 8, 13, 21, 34

67. 1 2. Поиск

Функция next полезна даже без итерации. Передача выражения генератора для next - это
быстрый способ поиска первого вхождения элемента, удовлетворяющего некоторому преди­
кату. Процедурный код наподобие
def fi nd_and_transform(sequence, predicate, func):
for element in sequence:
if predicate(element):
return func(element)
raise ValueError
item = fi nd_and_transform(my_sequence, my_predicate, my_func)
можно заменить на:

item = next(my_func(x) for х in my_sequence if my_predicate(x))
# Stoplteration будет вызвано, есл и нет совпадений; это исключение может быть
# перехвачено и п реобразовано, если это необходимо.
Для этой цели желательно создать псевдоним (alias), например f1 rst
оболочку для преобразования исключения:
def fi rst(generator):
tгу:
return next(generator)
except Stoplteration:
raise ValueError

=

next, или функцию­

67. 1 3. Параллельный итерационный перебор генераторов

Для параллельного перебора нескольких генераторов используйте встроенную функцию zip:

for х, у in zip(a,b):
print(x,y)

Глава 68. Фун кция reduce

31 2
Результаты:



Зz

В Python 2 вместо этого следует использовать itertools.izip. Здесь мы таюке видим, чго все функ­
ции zip дают кортежи. Обратите внимающ что zip прекратит итерацию, как только в одном из
итерируемых объектов закончится количество элементов. Если вы хотите выполнять итерацию
столько, сколько длится самый длинный итерируемый объект, используйте itertools.zip_longestO.

Глава 68. Функция reduce
Параметр

П одро бн ости

function

функция, используемая для уменьшения итерируемого объекта (должна
принимать два аргумента) (только позиционно)

itегаЫе

итерируемый объект, который будет уменьшен (только позиционно)

initial izer

начальное значение уменьшения (необязательно, только позиционно)

68. 1 . Обзор

# импорт не требуется
from functools import reduce # ... но его можно загрузить из модуля functools

from functools import reduce # обязател ьно
reduce уменьшает итерируемый объект путем многократного применения функции
к очередному элементу itегаЫе и суммарному результату на данный момент.
def add(s1 , s2):
return s1 + s2

asequence = [1 , 2, З]
reduce(add, asequence) # экви валентно: add(add(1 ,2),3)

# Результат: б

В этом примере мы создали собственную функцию add. Однако в Python имеется стандартная эквивалентная функция в модуле орегаtог:
import орегаtог
reduce(operator.add, asequence)
# Результат: б

В reduce также может быть передано начальное значение:

reduce(add, asequence, 1 О)

# Результат: 1 б

68.2. Использование reduce

def multiply(s 1 , s2):
print('{arg1 } * {arg2} = {res}'.format(arg1 = s1 ,
arg2 = s2,
res = s1 *s2))
return s1 * s2
asequence = [1 , 2, З]

68.3. Накопительны й продукт

313

При наличии инициализатора ( i nitia l i zer) функция запускается путем применения его
к инициализатору и первому итерируемому элементу:
cump rod = reduce (mul ti ply, ase qu e nc e, 5)
# Результат: 5 * 1 = 5
5*2= 10
#
#
1 0 * 3 = 30
pri nt (cump rod)
# Резул ьтат: 30

Без параметра in itial i zer использование re duc e начинается с применения функции к первым двум элементам списка:
cump rod = reduce (mul ti ply, ase qu e nc e)
# Резул ьтат: 1 * 2 = 2
2*3=6
#
p ri nt (cump rod)
# Резул ьтат: б

68.3. Накопительный продукт
impo rt орегаtог
re duc e (op erat o r.mul, [1 О, 5, -3])
# Результат: -1 50

68.4. Вариант без "короткого замыкания" функций any/all

Функция re duce не прервет итерацию до того, как итерируемый объект ( itегаЫе) будет
полностью пройден, поэтому его можно использовать для создания функции апу() или a ll{)
без "короткого замыкания":
impo rt орегаtог
# "a ll" без короткого замыкания
re duc e (op erat o r. a nd_ [F al se, Tr u e, Tr u e, Tr u e]) # = Fa l se
# "апу" без короткого замыкания
re duc e (op erat o r.o r_ [Tr u e, Fa l se, F al se, F al se]) # = Tr u e

Глава 69. Функция map
П ара м етр

Подроб ности

funct ion

функция, используемая для отображения (должна принимать столь­
ко параметров, сколько итерируемых объектов) (только позиционно)

itera Ы e

функция, которая применяется к каждому элементу итерируемого
объекта (только позиционно)
как ite ra Ы e, но применяется сколько угодно раз (необязательно, толь­
ко позиционно)

*a dditi on al_iteraЫes

69.1 . Базовое использование map, itertools.imap и future_builtins.map

Функция m a p является самой простой среди встроенных в Python, используемых для
функционального программирования. m a p() применяет указанную функцию к каждому эле­
менту в итерируемом объекте:
n a m es = ['F re d', 'W ilm a', 'B ar n ey']

31 4

Глава 69. Функция map

Версия Python 3.х ;:,: 3.0:
map(len, names) # map в Python 3.х - это класс; его экземпляры итерируемы
# Результат:
Совместимая с Python 3 map включена в модуль future_builtins:

Версия Python 2.х ;:,: 2. 6:
# содержит map(), совместимую с Python 3.х
from future_builtins import map
# см. ниже
map(len, names)
# Результат:
В качестве альтернативы в Python 2 можно использовать imap из itertools для получения
генератора.

Версия Python 2.х ;:,: 2.3
map(len, names)
# Результат: [4, 5, 6]

# map() возвращает список

from itertools import imap
# itertools.imap() возвращает генератор
imap(len, names)
# Результат:
Результат может быть явно преобразован в list (список), чтобы убрать различия между
Python 2 и З:
list(map(len, names))
# Результат: [4, 5, б]
map() можно заменить эквивалентным генератором списка или выражением генератора:
[len(item) for item in names] # э квивалентно Python 2.х map()
# Результат: [4, 5, 6]
(len(item) for item in names) # эквивалентно Python 3.х map()
# Результат:

69.2. Сопоставление каждого значения в итерируемом объекте
К примеру, можно принять абсолютное значение каждого элемента:
list(map(abs, (1 , -1 , 2, -2, 3, -3))) # вызов ' list' в Python 2.х не нужен
# Результат: [1 , 1 , 2, 2, 3, 3]
Анонимная функция также поддерживает отображение (mapping) списка:
map(lambda х:х*2, [1 , 2, 3, 4, 5])
# Результат: [2, 4, 6, 8, 1 О]
или преобразование десятичных значений в проценты:
def to_percent(num):
return num * 1 00
list(map(to_percent, [0.95, 0.75, 1 .01 , 0.1 ]))
# Результат: [95.0, 75.0, 1 01 .0, 1 О.О]
или конвертации валют (с учетом обменного курса):
from functools import partial
from орегаtог import mul

69.3. Отображение (mapping) значений различных итерируемых объектов

31 5

rate = 0.9 # фиктивны й обменный курс, 1 доллар = 0,9 евро
dollars = {'under_my_bed': 1 ООО,
'jeans': 45,
'bank': 5000}
sum(map(partial(mul, rate), dollars.values()))
# Результат: 5440. 5
functools. partial - удобный способ исправить параметры функций, чтобы их можно бьшо
использовать с map вместо использования lambda или создания настраиваемых функций.

69.З. Отображение (mapping) значений различных итерируемых
объектов
Например, вычислим среднее значение каждого i элемента из множества итерируемых
объектов:
def average(*args):
return float(sum(args)) / len(args) # приведение к float - обязател ьно тол ько для Python 2.х
measurement1 = [1 00, 1 1 1 , 99, 97]
measurement2 = [1 02, 1 1 7, 9 1 , 1 02]
measurementЗ = [1 04, 1 02, 95, 1 01 ]
l ist(map(average, measurement1 , measurement2, measurementЗ))
# Результат: [1 02.0, 1 1 О.О, 95.0, 1 00.0]
Существуют разные требования, если более чем один итерируемый объект передается
в map в зависимости от версии Python:
• Функция должна принимать столько параметров, сколько есть итерируемых
объектов:
def median_of_three(a, Ь, с):
return sorted((a, Ь, с))[1 ]
list(map(median_of_three, measurement1 , measurement2))

TypeError: median_of_three О missing 1 required positional argument: ' с' (в median_of_three О отсут­
ствует 1 требуемый позиционный аргумент: 'с')
list(map(median_of_three, measurement1 , measurement2, measurementЗ, measurementЗ))

TypeError: median_of_three() takes 3 positional arguments but 4 were given (median_of_three О при­
нимает 3 позиционных аргумента, но даны 4)

Версия Python 2.х � 2.0.1


map: отображение повторяется до тех пор, пока один итерируемый объект еще не
полностью исчерпан, но принимает значение None из полностью исчерпанных
итераций:

import operator
measurement1 = [1 00, 1 1 1 , 99, 97]
measurement2 = [1 02, 1 1 7]
# Вычислить разность между элементами
l ist(map(operator.sub, measurement1 , measurement2))

TypeError: unsupported operand type(s) for -: 'int' and 'NoneТype' (неподдерживаемый тип операн­
дов для -: 'int' и 'NoneType')

31 6

Глава 69. Функция map


itertools.imap и future_builtins.map: отображение останавливается, как только один
из итерируемых объектов останавливается:

import operator
from itertools import imap
measurement1 = [1 00, 1 1 1 , 99, 97]
measurement2 = [1 02, 1 1 7]
# Вычислить разность между элементами
list(imap(operator.sub, measurement1 , measurement2))
# Результат: [-2, -6]
list(imap(operator.sub, measurement2, measurement1 ))
# Результат: [2, 6]
Версия Python З.х � 3.0.0



отображение останавливается, как только один из итерируемых объектов
останавливается:

import орегаtог
measurement1 = [1 00, 1 1 1 , 99, 97]
measurement2 = [1 02, 1 1 7]
# Вычислить разность между элементами
list(map(operator.sub, measurement1 , measurement2))
# Результат: [-2, -6]
list(map(operator.sub, measurement2, measurement1 ))
# Результат: [2, 6]

69.4. Транспонирование с помощью map: использование "None"
в качестве аргумента функции (только для Python 2.х)
from itertools import imap
from future_builtins import map as fmap # Разное имя, чтобы подчеркнуть различия
image = [[1 , 2, 3],

[4, 5, 6],

[7, 8, 91]
list(map(None, *image))
# Результат: [(1 , 4, 7), (2, 5, 8), (3, 6, 9)]
list(fmap(None, *image))
# Результат: [(1 , 4, 7), (2, 5, 8), (3, 6, 9)]
list(imap(None, *image))
# Результат: [(1 , 4, 7), (2, 5, 8), (3, 6, 9)]
image2 = [[1 , 2, 3],
[4, 5],
[7, 8, 91]
list(map(None, *image2))
# Результат: [(1 , 4, 7), (2, 5, 8), (3, None, 9)]
list(fmap(None, *image2))
# Результат: [(1 , 4, 7), (2, 5, 8)]
list(imap(None, *image2))
# Результат: [(1 , 4, 7), (2, 5, 8)]

# Заполнить отсутствующие значения значением None
# игнорировать столбцы с отсутствующими
# значениями

69.5. Последовательное и параллельное отображение

317

Версия Python З.х � 3.0.0:
list(map(None, *image))
TypeError: 'NoneТype' object is not callaЬle (объект "NoneТype" не может быть вызван)
Но есть обходное решение для получения аналогичных результатов:
def conv_to_list(*args):
return list(args)
list(map(conv_to_list, *image))
# Резул ьтат: [[1 , 4, 7], [2, 5, 8], [3, 6, 9]]

69.5. Последовательное и параллельное отображение
map () - это встроенная функция, что означает, что она доступна повсюду, без необходи­
мости использовать оператор import. Она доступна везде, как print (). В примере 5 необходимо
использовать оператор импорта (import pprint), прежде чем появится возможность использо­
вать функцию pretty print. Таким образом, pprint не является встроенной функцией.

Последовательное отображение

В этом случае каждый аргумент итерируемого объекта передается в качестве аргумента функ­
ции отображеIШЯ в порядке возрастаIШЯ. Эта ситуация возникает, когда у нас есть только один
итерируемый объект для отображеIШЯ, а для функции отображения требуется один аргумент.
Пример 1.
insects = ['fly', 'ant', 'beetle', 'cankerworm']
f = lambda х: х + ' is an insect'
print(list(map(f, insects))) # функция, определяемая f, выполняется над кажды м элементом
итерируемого объекта insects
Результат:
['fly is an insect', 'ant is an insect', 'beetle is an insect', 'cankerworm is an insect']
Пример 2.
print(list(map(len, insects))) # функция len выполняется для каждого элемента списка insects
Результат:
[3, 3, 6, 1 0]

Параллельное отображе1П1е

В этом случае каждый аргумент функции сопоставления выталкивается из всех итериру­
емых объектов (по одному от каждого итерируемого) параллельно. Таким образом, количе­
ство предоставленных итерируемых объектов должно соответствовать количеству аргумен­
тов, требуемых функцией.
carnivores = ['lion', 'tiger', 'leopard', 'arctic fox']
herblvores = ['african buffalo', 'moose', 'okapi', 'parakeet']
omnivores = ['chicken', 'dove', 'mouse', 'pig']
def animals(w, х, у, z):
return '{О}, {1 }, {2}, and {3} ARE ALL AN I MALS'.format(w.title(), х, у, z)
Пример 3.
# Слишком много аргументов
# здесь можно заметить, что map пытается передать в len по одному элементу из каждого из

31 8

Глава 70. Возведение в степень

# четырех итерируемых объектов. Это приводит к тому, что len "жалуется", будто
# его "кормят" слиш ком большим количеством аргументов
print(list(map(len, insects, carnivores, herblvores, omnivores)))
Результат:

ТypeError: len() takes exactly one argument (4 given) (len() принимает ровно один аргумент (дано 4))
Пример 4.
# Слиш ком мало аргументов
# здесь следует заметить, что map должен выполнять animal на отдельных элементах insects
# поочередно.
# Но animals "жалуются", когда
# приходит только один аргумент, тогда как ожидается четыре.
print(list(map(animals, insects)))
результат:

ТypeError: animals() missing 3 required positional arguments: 'х', 'у', and 'z' (animals() не хватает
3 требуемых позиционных аргументов: 'х', 'у', and 'z')
Пример 5.
# здесь map снабжает w, х, у, z одним значением из списка
import pprint
pprint.pprint(list(map(animals, insects, carnivores, herblvores, omnivores)))
результат:
['Fly, lion, african buffalo, and chicken ARE ALL ANI MALS',
'Ant, tiger, moose, and dove ARE ALL ANI MALS',
'Beetle, leopard, okapi, and mouse ARE ALL AN I MALS',
'Cankerworm, arctic fox, parakeet, and pig ARE ALL ANI MALS']

Глава 70. Возведение в степень
70. 1 . Возведение в степен ь с использованием встроенных
модулей: ** и pow()
Для возведения в степень можно использовать встроенную функцшо pow или оператор ,н,:
2 ,н, 3 # 8
pow(2, 3) # 8
Для большинства (в Python 2.х - для всех) арифметических операций тип результата бу­
дет соответствовать типу более широкого операнда. Это не так для ,н,; следующие случаи яв­
ляются исключениями из этого правила:
• Основание: int, показатель степени: int < О:
2 ,н, -3
# Out: 0.1 25 (result is а float)


Это справедливо и для Python З.х.



До версии Python 2.2.0 это приводило к ошибке ValueError.



Основание: int < О или float < О, показатель степени float != int

(-2) ** (0.5) # also (-2.) ** (0.5)
# Результат: (8.659560562354934е-1 7+1 .41 421 35623730951j) (результат типа complex)

70.2. Квадратн ый корень: math.sqrt () и cmath.sqrt

31 9

• До версии Python 3.0.0 это приводило к ошибке ValueError.
Модуль operator содержит две функции, эквивалентные **-оператору:
i mport operator
operator. pow(4, 2)
operator._pow_(4, 3)

# 16
# 64

или можно напрямую вызвать метод _pow_:
val 1 , val2 = 4, 2
val 1 ._pow_(val2) # 1 6
val2._rpow_(val 1 ) # 1 6
# in-place операция возведения в степень не поддерживается таки м и неизменяем ы м и
# классами, как int, float, complex:
# val 1 ._ipow_(val2)

70.2. Квадратный корень: math.sqrt() и cmath.sqrt

Модуль math содержит функцию math.sqrt(), которая позволяет вычислить квадратный
корень из любого числа (которое может быть преобразовано в тип float), причем результат
всегда будет иметь тип float:
i mport math
# 3.0
math.sqrt(9)
math.sqrt(1 1 . 1 1 )
# 3.3331 6666249979 1 8
math.sqrt(Decimal('6.25')) # 2.5
Функция math.sqrt() выдает ошибку ValueError, если результат будет комплексным числом
(complex):
math.sqrt(-1 О)
ValueError: math domain error (ошибка математической области)
Функция math.sqrt(x) работает быстрее, чем math.pow(x, 0.5) или х ** 0.5, но точность ре­
зультатов одинакова. Модуль cmath очень похож на модуль math, за исключением того, что
он может вычислять комплексные числа и все его результаты имеют вид а + Ьi. Он также мо­
жет использовать функцию .sqrt():
i mport cmath
cmath.sqrt(4) # 2+0j
cmath.sqrt(-4) # 2j
Здесь j - это квадратный корень из -1 . Все числа можно представить в виде а + Ьi, или, как
в данном случае, а + bj. а - это действительная часть числа, как 2 в 2+0j. Поскольку у него нет
мнимой части, Ь равно О. Ь представляет собой часть мнимой части числа, например 2 в 2j.
Поскольку в нем нет действительной части, 2j можно записать как о + 2j.

70.З. Возведение в степень по модулю:
функция pow() с тремя аргументами

При задании функции pow() с тремя аргументами pow(a, Ь , с ) выполняется возведение
в степень по модулю аЬ mod с:
pow(3, 4, 1 7) # 1 3
# экви валентное неоnти мизирован ное вы ражение:
3 ** 4 % 1 7 # 1 3

320

Глава 70. Возведение в степень

# шаги:
3 1'>> х = 3
>>> у = х ** 3
>>> у

27
»> z = у ** (1 .0 / 3)
>>> z
3.0
>>> z == х
True

324

Глава 71 . Поиск

Глава 71 . Поиск

71 . 1 . Поиск элемента
Все встроенные типы коллекций в Python реализуют способ проверки принадлежности
элементов с помощью функции in.

Список (List)

al ist = [О, 1 , 2, 3, 4, 5, 6, 7, 8, 9]
# Тгuе
5 in alist
# False
1 О in al ist

Кортеж (Tuple)

atuple = ('О', '1 ', '2', '3', '4')
# False
4 in atuple
# Тгuе
'4' in atuple

Строка (String)

astring = 'i am а string'
'а' in astring
# Тгuе
'am' in astring
# Тгuе
'1' in astring
# False

Набор (Set)

aset = {(1 О, 1 О), (20, 20), (30, 30)}
(1 О, 1 О) in aset # Тгuе
# False
1 О in aset

Словарь (Dict)

Случай с dict немного особенный: обычно in проверяет только ключи. Если вы хотите
искать в значениях, то необходимо указать это. То же самое, если вы хотите искать пары

"ключ-значение".

adict = {О: 'а', 1 : 'Ь', 2: 'с', 3:
1 in adict
'а' in adict
2 in adict. keys()
'а' in adict.values()
(О, 'а') in adict. items()

'd'}
# Тгuе - неявный поиск в ключах
# False
# Тгuе - явный поиск в ключах
# Тгuе - явный поиск в значениях
# Тгuе - явный поиск пар "ключ/значение"

71 .2. Поиск в пользовательских классах: _contains_ и _iter_
Для того чтобы разрешить использование in в пользовательских классах, класс должен
предоставить магический метод _contains_ или, если это невозможно, метод _iter_.
Предположим, имеется класс, содержащий список списков:
class Listlist:
def _init_(self, value):
self.value = value
# Создать набор всех значений для быстрого доступа
self.setofvalues = set(item fог suЫist in self.value fог item in suЫist)
def _iter_(self):
print('Using _iter_.')
# Генератор по всем элементам подсписка
return (item fог suЫist in self.value fог item in suЫist)
def _contains_(self, value):
print('Using _contains_.')

71 .3. Получение индекса для строк: str.index (), str.rindex() и str.find (), str.rfind ()

325

# Просто поиск, есть ли значение в наборе
return value in self.setofvalues
# Даже без использования набора можно использовать метод iter для проверки на содержание:
# return any(item == value for item in iter(self))

Использование проверки на принадлежность возможно при использовании in:
а = Listlist([[1 , 1 , 1 ],[О, 1 , 1 ],[1 ,5, 1 11)
1 0 in а # False
# Prints: Using _contains_.
5 in а # True
# Prints: Using _contains_.

даже после удаления метода _contains_:
del Listlist._contains_
5 in а # True
# Prints: Using _iter_.

Примечание: зацикливание in (как for i in а) всегда будет использовать _iter_, даже если
класс реализует метод _contains_.

71 .З. Получение индекса для строк:
str.index(), str.rindex() и str. find(), str.rfind()

Строковый тип данных также имеет метод index, но с более расширенными возможностя­
ми и дополнительной функцией str.f1nd. Для обоих вышеперечисленных существует допол­
нительный обратный метод.
astring = 'Hello on StackOverflow'
#4
astring. index('o')
astring. rindex('o')
# 20
astring.f1nd('o')
astring. rf1nd('o')

#4
# 20

Разница между index/rindex и f1nd/rf1nd заключается в том, что именно происходит, если
подстрока не найдена в строке:
astring.index('q')
astring.f1nd('q')

# ValueError: substring not found (подстрока не найдена)
# -1

Все эти методы допускают указание начального и конечного индекса:
astring. index('o',
astring. index('o',
astring. index('o',
astring. index('o',

5)
6)
5, 7)
5, 6)

#6
# 6 - начало включено
#6
# - конец не включен

ValueError: substring not found (подстрока не найдена)
astring. rindex('o', 20)
astring. rindex('o', 1 9)
astring. rindex('o', 4, 7)

# 20
# 20 - по-прежнему слева направо
#6

71 .4. Получение индексов в списках и кортежах:
list.index(), tuple.index()

Списки и кортежи поддерживают метод index для получения позиции элемента:

alist = [1 О, 1 6, 26, 5, 2, 1 9, 1 05, 26]
# поиск 1 6 в списке
alist. index(1 6) # 1

Глава 71 . Поиск

326
al ist[1 ]
al ist.index(1 5)

# 16

ValueError: 15 is not in list (15 не в crrn:cкe)

Но возвращается только позиция первого найденного элемента:
atuple = (1 О, 1 6, 26, 5, 2, 1 9, 1 05, 26)
atuple.index(26) # 2
# 26
atuple[2]
# 26 - тоже 26!
atuple[7]

71 .5. Поиск ключа или ключей для значения в словаре

В словарях (dict) нет встроенного метода поиска значения или ключа, поскольку словари
неупорядочены. Можно создать функцию, которая получает ключ (или ключи) для заданно­
го значения:
def getKeysForVal ue(dictionary, value):
foundkeys = D
for keys in dictionary:
if dictionary[key] == value:
foundkeys.append(key)
return found keys

Это также можно записать в виде эквивалентного генератора списка:

def getKeysForVal ueComp(d ictionary, value):
return [key for key in dictionary if dictionary[key] == value]

Если вам нужен только один найденный ключ:
def getOneKeyForVal ue(dictionary, value):
return next(key for key in dictionary if dictionary[key]

==

value)

Первые две функции возвращают список (list) всех ключей, имеющих указанное значение:
adict = {'а': 1 О, 'Ь': 20, 'с': 1 О}
getKeysForValue(adict, 1 О)
getKeysForValueComp(adict, 1 О)
getKeysForValueComp(adict, 20)
getKeysForValueComp(adict, 25)

# ['с', 'а'] - порядок случайный, с тем же успехом ['а', 'с']
# ['с', 'а']
# ['Ь']

getOneKeyForValue(adict, 1 О)
getOneKeyForValue(adict, 20)

# 'с' - в зависимости от обстоятельств это может быть и 'а'
# 'Ь'


Следующая функция возвращает только один ключ:

и вызвать Stoplteration Exception, если значения нет в словаре:
getOneKeyForValue(adict, 25)

Stoplteration

71 .б. Получение индекса в отсортированных
последовательностях: Ьisect.Ьisect_left()

Отсортированные последовательности позволяют использовать более быстрые алгорит­
мы поиска, такие как Ьisect.Ьisect_left() 1 :
import Ьisect
def index_sorted(sorted_seq, value):
""" Найти крайнее левое значение, точно равное х, или вызвать ошибку ValueError"'"'
i = Ьisect.Ьisect_left(sorted_seq, val ue)

71 .7. Поиск во вложенных последовательностях

327

if i != len(sorted_seq) and sorted_seq[i] == value:
return i
raise ValueError
alist = [i for i in range(1 , 1 00000, 3)] # Отсортированный список от 1 до 1 00000 с шагом 3
index_sorted(alist, 97285) # 32428
index_sorted(alist, 4) # 1
index_sorted(alist, 97286)
ValueError

Для очень больших отсортированных последовательностей увеличение скорости может
быть довольно значительным. В случае первого поиска примерно в 500 раз быстрее:
%timeit index_sorted(alist, 97285)
# 1 00000 циклов, луч ший из 3: 3 мкс на цикл
%timeit alist.index(97285)
# 1 ООО циклов, лучший из 3: 1 .58 мс на цикл

Хотя это немного медленнее, если элемент является одним из первых:
%timeit index_sorted(alist, 4)
# 1 00000 циклов, лучший из 3: 2.98 м кс на цикл
%timeit alist.index(4)
# 1 000000 loops, best of 3: 580 нс на цикл

71 . 7. Поиск во вложенных последовательностях
Поиск во вложенных последовательностях, таких как список кортежей, требует подхода,
подобного поиску ключей для значений в словаре (dict), но нуждается в специализирован­
ных функциях. Индекс крайней последовательности, если значение было найдено в после­
довательности:
def outer_index(nested_sequence, value):
return next(index for index, inner in enumerate(nested_sequence))
for item in inner
if item == value)
alist_of_tuples = [(4, 5, 6), (3, 1 , 'а'), (7, О, 4.3)]
outer_index(alist_of_tuples, 'а') # 1
outer_index(alist_of_tuples, 4.3) # 2

или индекс внешней и внутренней последовательности:
def outer_inner_index(nested_sequence, val ue):
return next((oindex, iindex) for oindex, inner in enumerate(nested_sequence)
for iindex, item in enumerate(inner)
if item == value)
outer_inner_index(alist_of_tuples, 'а') # (1 , 2)
alist_of_tuples[1 ][2] # 'а'
outer_inner_index(alist_of_tuples, 7) # (2, О)
alist_of_tuples[2][0] # 7
В общем случае (не всегда) использование next и генераторного выражения с условия­
ми для нахождения первого вхождения искомого значения является наиболее эффективным
подходом.

328

Глава 72. Сортировка, м и н и мум и максимум

Глава 72. Сортировка, минимум
и максимум
72. 1 . Упорядоченность в пользовательских классах
min, max и sorted требуют, чтобы объекты были упорядочены. Чтобы класс был упоря­
доченным, в нем должны быть определены все 6 методов _lt_ _gt_ _ge_ _ le_ _пе_
и _еq_:
class lntegerContainer(object):
def _init_(self, value):
self.value = value
def _repr_(self):
return "{}({})".format(self._class_._name_, self.value)
def _lt_(self, other):
print('{!r} - Test less than {!r}'.format(self, other))
return self.value < other.value
def _le_(self, other):
print('{!r} - Test less than or equal to {!r}'.format(self, other))
return self.value other.value
def _ge_(self, other):
print('{!r} - Test greater than or equal to {!r}'.format(self, other))
return self.value >= other.value
def _eq_(self, other):
print('{!r} - Test equal to {!r}'.format(self, other))
return self.value == other.value
def _ne_(self, other):
print('{!r} - Test not equal to {!r}'.format(self, other))
return self.value != other.value
Хотя реализация всех этих методов будет казаться ненужной, опускание некоторых из
них сделает ваш код подверженным ошибкам. Примеры:
al ist = [l ntegerContainer(S), l ntegerContainer(З),
l ntegerContainer(1 О), l ntegerContainer(7)

]

res = max(alist)
# Вы вод:
#
#
print(res)
# Вы вод:
res = min(al ist)
# Вы вод:
#
#
print(res)
# Вы вод:

lntegerContainer(З) - Test greater than lntegerContainer(S)
l ntegerContainer(1 О) - Test greater than lntegerContainer(S)
l ntegerContainer(7) - Test greater than lntegerContainer(1 О)
lntegerContainer(1 О)
lntegerContainer(З) - Test less than l ntegerContainer(S)
l ntegerContainer(1 О) - Test less than lntegerContainer(З)
l ntegerContainer(7) - Test less than l ntegerContainer(З)
lntegerContainer(З)

72. 1 . Упорядоченность в пользовательских классах

329

res = sorted(alist)
# В ы вод:
lntegerContainer(З) - Test less than lntegerContainer(S)
#
lntegerContainer(1 О) - Test less than lntegerContainer(З)
#
lntegerContainer(1 О) - Test less than lntegerContainer(S)
#
lntegerContainer(7) - Test less than lntegerContainer(S)
#
lntegerContainer(7) - Test less than lntegerContainer(1 О)
print(res)
# В ы вод:
[lntegerContainer(З), lntegerContainer(S), lntegerContainer(7), lntegerContainer(1 О)]
sorted с помощью reverse=True также использует _lt_:
res = sorted(alist, reverse = True)
# В ы вод:
lntegerContainer(1 О) - Test less than lntegerContainer(7)
#
lntegerContainer(З) - Test less than lntegerContainer(1 О)
#
lntegerContainer(З) - Test less than lntegerContainer(1 О)
#
lntegerContainer(З) - Test less than lntegerContainer(7)
#
lntegerContainer(S) - Test less than lntegerContainer(7)
#
lntegerContainer(S) - Test less than lntegerContainer(З)
print(res)
# В ы вод:
[lntegerContainer(1 О), lntegerContainer(7), lntegerContainer(S), lntegerContainer(З)]
Но sorted может использовать _gt_ вместо этого, если значение по умолчанию не реа­
лизовано:
del lntegerContainer._lL # The lntegerContainer бол ьше не реализует "less than"
res = min(alist)
# В ы вод:
#
#
print(res)
# В ы вод:

lntegerContainer(S) - Test greater than lntegerContainer(З)
lntegerContainer(З) - Test greater than lntegerContainer(1 О)
lntegerContainer(З) - Test greater than lntegerContainer(7)
lntegerContainer(З)

Методы сортировки вызовут TypeError, если не реализованы ни _IL, ни _gL :
del lntegerContainer._gt_ # The lntegerContainer бол ьше не реализует "greater then"
res = min(alist)
Tиn Error: unorderaЫe types: lntegerContainer () < lntegerContainer ()
Декоратор functools.total_ordering можно использовать для упрощения работы с этими ме­
тодами сравнения. Если вы декорируете свой класс при помощи total_ordering, вам нужно ре­
ализовать _eq_ , _ne_ и только один из методов _lt_, _le_, _ge_ или _gt_, а декоратор
сам заполнит остальные:
import functools
@functools.total_ordering
class lntegerContainer(object):
def _iniL(self, value):
self.val ue = value
def _repr_(self):
return "{}({})".format(self._class_._name� self.val ue)
def _IL(self, other):
print('{!r} - Test less than {!r}'.format(self, other))
return self.value < other.value
def _eq_(self, other):
print('{!r} - Test equal to {!r}'.format(self, other))
return self.value == other.value

330

Глава 72. Сортировка, м и н и мум и максимум

def _ne_(self, other):
print('{!r} - Test not equal to {!r}'.format(self, other))
return self.value ! = other.value
lntegerContainer(5) > lntegerContainer(б)
# В ы вод: lntegerContainer(5) - Test less than lntegerContainer(б)
# Возвращает: False
lntegerContainer(б) > lntegerContainer(5)
# Вывод: lntegerContainer(б) - Test less than lntegerContainer(5)
# Вывод: lntegerContainer(б) - Test equal to lntegerContainer(5)
# Возвращает: True

Обратите внимание, что теперь > (больше чем, greater than) вызывает метод меньше, чем
(less than), а в некоторых случаях даже метод _eq_. Это также означает, что если скорость

имеет большое значение, то вы должны реализовать каждый метод сравнения самостоя­
тельно.

72.2. Особый случай: словари
Получение минимального или максимального значения или использование функции
sorted зависит от итераций над объектом. В случае словаря итерация происходит только по
ключам:
adict = {'а': 3, 'Ь': 5, 'с': 1}
min(adict)
# Результат: 'а'
max(adict)
# Результат: 'с'
sorted(adict)
# Результат: ['а', 'Ь', 'с']
Чтобы сохранить структуру словаря, необходимо выполнить итерацию по . items() :
min(adict.items())
# Результат: ('а', 3)
max(adict.items())
# Результат: ('с', 1)
sorted(adict. items())
# Результат: [('а', 3), ('Ь', 5), ('с', 1)]
Для sorted можно создать упорядоченный Ordered Dict, чтобы сохранить сортировку
и структуру, подобную структуре словаря:
from col lections import Ordered Dict
Ordered Dict(sorted(adict.items()))
# Результат: Ordered Dict([('a', 3), (' Ь', 5), ('с', 1)])
res = Ordered Dict(sorted(adict. items()))
res['a']
# Результат: 3

По значению

Следует использовать ключевой аргумент:

min(adict.items(), key = lambda х: х[1])
# Результат: ('с', 1)
max(adict.items(), key = operator. itemgetter(1))
# Результат: ('Ь', 5)
sorted(adict. items(), key = operator.itemgetter(1), reverse = True)
# Результат: [('Ь', 5), ('а', 3), ('с', 1)]

72.3. Использование ключевого аргумента

331

72.З. Использование ключевого аргумента

Нахождение минимума/максимума последовательности последовательностей возможно следующим образом:
l isLof_tuples = [(О, 1 О}, (1 , 1 5), (2, 8}]
min(l ist_of_tuples)
# Резул ьтат: (О, 1 О}

но если вы хотите сортировать по конкретному элементу в каждой последовательности,
используйте ключевой аргумент:
min(l ist_of_tuples, key = lambda х: х[О]} # Сортировка по первому элементу
# Результат: (О, 1 О}
min(l ist_of_tuples, key = lambda х: х[1 ]} # Сортировка по второму элементу
# Результат: (2, 8}
sorted(list_of_tuples, key = lambda х: х[О]} # Сортировка по первому элементу (по возрастанию)
# Результат: [(О, 1 О}, (1 , 1 5), (2, 8}]
sorted(list_of_tuples, key = lambda х: х[1 ]} # Сортировка по первому элементу
# Результат: [(2, 8}, (О, 1 О}, (1 , 1 5}]
import орегаtог
# Модуль operator содержит эффективные альтернативы использованию ля мбда-функции
max(lisLof_tuples, key = operator. itemgetter(O}} # Сортировка по первому элементу
# Результат: (2, 8}
max(lisLof_tuples, key = operator. itemgetter(1 )) # Сортировка по второму элементу
# Результат: (1 , 1 5)
sorted(list_of_tuples, key = operator. itemgetter(O}, reverse = True) # Реверсивны й (убы вающий}
# Результат: [(2, 8}, (1 , 1 5), (О, 1 О}]
sorted(list_of_tuples, key = operator. itemgetter(1 }, reverse = True) # Reversed (убы вающий}
# Результат: [(1 , 1 5), (О, 1 О}, (2, 8}]

72.4. Аргумент по умолчанию для max, min
В

функции max или min нельзя передать пустую последовательность:

min(□)

ValueError: min() arg is an empty sequence (min() arg - пустая последовательность)

Однако в Python 3 можно передать ключевой аргумент default со значением, которое будет возвращено, если последовательность пуста, вместо того чтобы вызывать исключение:
max(□, default = 42}
# Результат: 42
max(□, default = O}
# Результат: О

72.5. Получение отсортированной последовательности
Использование одной последовательности:

sorted((7, 2, 1 , 5))
# Резул ьтат: [1 , 2, 5, 7]

# кортеж

sorted(['c', '!>:, 'Ь'])
# Резул ьтат: ['N, 'Ь', 'с'] .

# список

332

Глава 72. Сортировка, минимум и максимум

sorted({1 1 , 8, 1 })
# Результат: [1 , 8, 1 1 ]

# набор

sorted({'1 1 ': 5, '3': 2, '1 О': 1 5})
# Результат: ['1 О', '1 1 ', '3']

# словарь
# итерация тол ько по кл ючам

sorted('bdca')
# Результат: ['а';ь•;с';d'].

# строка

Результатом всегда является новый список, исходные данные остаются неизменными.

72.6. Извлечение N наибольших или N наименьших элементов
из итерируемого объекта

Чтобы найти некоторое количество (больше одного) наибольших или наименьших зна­
чений итерируемого объекта, можно воспользоваться функциями nlargest и nsmallest из мо­
дуля heapq :
import heapq
# получ ить 5 самых больших элементов из диапазона
heapq.nlargest(5, range(1 О))
# Результат: [9, 8, 7, 6, 5]
heapq.nsmallest(5, range(1 О))
# Результат: [О, 1 , 2, 3, 4]
Это гораздо удобнее, чем сортировать весь итерируемый объект, а затем делать срезы
с конца или начала. Внутри этих функций используется структура данных "приоритетной
очереди двоичной кучи" (Ьinary heap priority queue), что очень удобно для данного случая.
Подобно min, max и sorted, эти функции принимают необязательный ключевой аргумент, ко­
торый должен быть функцией, которая, получая элемент, возвращает его ключ сортировки.
Приведем код, который извлекает 1000 самых длинных строк из файла:
import heapq
with open(f1lename) as f:
longest_lines = heapq .nlargest(1 ООО, f, key=len)
Здесь мы открываем файл и передаем дескриптор файла f в функцию nlargest. Итерация
по файлу позволяет получить каждУю строчку файла в виде отдельной строки string; затем
nlargest передает каждый элемент (или строчку) в функцию len для определения ключа сор­
тировки.
Функция len, получая строку string, возвращает длину строчки в символах. Требуется хра­
нение только списка из 1000 наибольших на данный момент строчек, что можно противо­
поставить коду
longest_lines = sorted(f, key=len)[1 ООО:]
который должен будет держать в памяти весь файл.

72. 7. Получение минимального или максимального
из нескольких значений
min(7,2,1 ,5)
# Результат: 1

max(7,2, 1 ,5)
# Результат: 7

72.8. Минимум и максимум последовательности

333

72.8. Минимум и максимум последовательности
Получение минимума последовательности (итерируемого объекта) эквивалентно доступу к первому элементу отсортированной (sorted) последовательности:
min([2, 7, 5])
# Резул ьтат: 2
sorted([2, 7, 5])[0]
# Резул ьтат: 2

С максимумом немного сложнее, так как sorted сохраняет порядок, а max возвращает пер­
вое встретившееся значение. В случае отсутствия дубликатов максимальное значение сов­
падает с последним элементом отсортированного возврата:
max([2, 7, 5])
# Резул ьтат: 7
sorted([2, 7, 5]) [-1 ]
# Резул ьтат: 7

Но не в том случае, если имеется несколько элементов, которые оцениваются как имею­
щие максимальное значение:
class MyClass(object):
def _iniL(self, value, name):
self.value = value
self.name = name
def _IL(self, other):
return self.value < other.value
def _repr_(self):
return str(self.name)
sorted([MyClass(4, 'первы й'), MyClass(1 , 'второй'), MyClass(4, 'третий')])
# Резул ьтат: [второй, первый, третий]
max([MyClass(4, 'первы й'), MyClass(1 , 'второй'), MyClass(4, 'третий')])
# Резул ьтат: первый

Допускается использование любого итерируемого объекта, содержащего элементы, под­
держивающие операции < или >.

Глава 73 . П одс ч ет
73. 1 . Подсчет всех вхождений всех элементов в итерируемом
объекте: collections.Counter
from collections import Counter
с = Counter(["a", "Ь", "с", "d", "а", "Ь", "а", "с", "d"])

с
# Резул ьтат: Counter({'a': 3, 'Ь': 2, 'с': 2, 'd': 2})
с["а"]
# Резул ьтат: 3
с[7]
# нет в списке (7 встретилось О раз!)
# Резул ьтат: О

Можно использовать collections.Counter для любого итерируемого объекта и подсчет каж­
дого вхождения для каждого элемента.

334

Глава 73. Подсчет

ПримечаIШе: исключение составляет случай, когда задан словарь d ict или другой класс,
похожий на collections.Mapping, тогда они не будут подсчитаны, а будет создан счетчик
(Counter) с этими значениями:
Counter({"e": 2})
# Результат: Counter({"e": 2})
Counter({"e": "е"}) # предупреждение Счетчик не проверяет, что значения имеют тип int
# Результат: Counter({"e": "е"})

73.2. Получение наиболее часто встречающегося значения:
collections.Counter.most_common()
Подсчет ключей отображения (Mapping) при помощи collections.Counter невозможен, но
мы можем подсчитать значения:
from collections import Counter
adict = {'а': 5, 'Ь': 3, 'с': 5, 'd': 2, 'е':2, 'q': 5}
Counter(adict.values())
# Результат: Counter({2: 2, 3: 1 , 5: 3})
Наиболее распространенные элементы можно получить, используя метод most_common:
# Сортировка от наиболее часто встречающегося к наименее часто встречающемуся значению:
Counter(adict.values()). mosLcommon()
# Результат: [(5, 3), (2, 2), (3, 1 )]
# Получение наиболее часто встречающегося значения
Counter(adict. val ues() ). mosLcommon(1 )
# Результат: [(5, 3)]
# Получение двух наиболее частых значений
Counter(adict.values()). mosLcommon(2)
# Результат: [(5, 3), (2, 2)].

73.3. Подсчет количества вхождений элемента
в последовательность: list.count() и tuple.count()
al ist = [1 , 2, 3, 4, 1 , 2, 1 , 3, 4]
alist.count(1 )
# Результат: 3

atuple = ('Ьеаг', 'weasel', 'bear', 'frog')
atuple.count('bear')
# Результат: 2
atuple.count('fox')
# Результат: О

73.4. Подсчет вхождений подстроки в строку: str.count()
astring = 'thisisashorttext'
astring.count('t')
# Результат: 4
Это работает для подстрок длиной более одного символа:
astring.count('th')
# Результат: 1
astring.count('is')
# Результат: 2
astring.count('text')
# Результат: 1

73.5. Подсчет вхождений в numру-массиве

335

что было бы невозможно при использовании col lections.Counter, который подсчитывает
только одиночные символы:
from col lections import Counter
Counter(astring)
# Результат: Counter({'a': 1, 'е': 1, 'h': 2, 'i': 2, 'о': 1, 'г': 1, 's': 3, 't': 4, 'х': 1 })

73.5. Подсчет вхождений в numру- массиве
Для подсчета вхождений значения в numру-массиве можно использовать:
import numpy as пр
a = np.array([0,3,4,3,5,4,7])
»> print np.sum(a = = 3)
»>
»>

2

Логическое выражение создает массив, в котором все вхождения запрашиваемых зна­
чений равны 1, а все остальные равны нулю. Таким образом, суммирование этих значений
дает количество вхождений. Это работает для массивов любой формы и типа.
Для подсчета вхождений всех уникальных значений в numру-массиве можно исполь­
зовать методы unique и Ьincount. Unique автоматически "уплощает" многомерные массивы,
а Ьincount работает только с одномерными массивами, содержащими только положитель­
ные целые числа.
unique,counts = np.unique(a,return_counts = True)
print unique,counts # counts[i] равен вхождениям unique[i] в а
[0 3 4 5 71 [1 2 2 1 1 ]
»> Ьin_count = np.Ьincount(a)
»> print Ьin_count # Ьin_count[i] равен вхождениям i в а
[1 0 0 2 2 1 0 1 ]
»>
»>

Если ваши данные представляют собой numру-массивы, то, как правило, гораздо быстрее
использовать эти методы, чем преобразовывать данные в общие методы.

Глава 74. Функция print
74. 1 . Основы вы вода на экран
В Python 3 и выше print является функцией, а не ключевым словом.
print('hello world !')

# Результат: hello world!

foo = 1 bar = 'Ьаr' baz = 3.1 4
print(foo) # Результат: 1
print(bar) # Результат: bar
print(baz) # Результат: 3. 1 4
Также можно передать на печать несколько параметров:
print(foo, bar, baz)

# Результат: 1 bar 3. 1 4

Другой способ вывести множественные параметры - это использование символа +
print(str(foo) + " " + bar + " " + str(baz))

# Результат: 1 bar 3. 1 4

336

Глава 74. Функция print

Однако при использовании + для печати нескольких параметров следует обратить вни­
мание на то, что тип параметров должен быть одинаковым. Попытка вывести приведен­
ный выше пример без приведения к типу string приведет к ошибке, поскольку к строке "bar"
Python будет пытаться добавить цифру 1, а затем прибавить число 3.14.
# Неправильно:
# type: int str float
print(foo + Ьаг + baz)
# приведет к ошибке

Это связано с тем, что в первую очередь будет оцениваться содержимое:
print(4 + 5)
# Результат: 9
print("4" + "5"}
# Результат: 45
print([4] + [5]}
# Результат: [4, 5]

Использование знака + может быть очень полезным для пользователя при чтении вывода переменных. В приведенном ниже примере вывод очень легко читается:
import random
#указание python включить функцию для создания случайных чисел
randnum = random.randint(O, 1 2}
#создать случайное число от О до 1 2 и присвоить его переменной
ргiпt("Случайно сгенерированное число было - " + str(randnum))

С помощью параметра end можно запретить функции печати автоматически печатать
новую строку:
print("y этого нет новой строки в конце... ", end = "")
ргint("видите?"}
# Результат: здесь нет новой строки в конце . . . видите?

Если требуется запись в файл, это можно передать в качестве параметра f1le:
with open('my_f1le.txt', 'w+') as my_f1le:
print("этo идет в файл !", f1le = my_f1 le}

74.2. Параметры функции print

При помощи этой функции можно не только печатать текст. У print также есть несколько
параметров, которые помогут вам.
Аргумент sep: поместить строку между аргументами. К примеру, вам нужно вывести список слов, разделенных запятой или какой-либо друrой строкой:
»> print('apples';bananas', 'cherries', sep = ', ')
apple, bananas, cherries
»> print('apple';banana', 'cherries', sep = ', ')
apple, banana, cherries
>>>

Аргумент end: использование в конце чего-то кроме новой строки. Без этого аргумента
все функции print() пишут строку, а затем переходят к началу следующей строки. Вы може­
те изменить его так, чтобы он ничего не делал (использовать пустую строку "), или сделать
двойной интервал между абзацами, используя две новые строки.
»> print("
from docopt_dispatch import dispatch
@dispatch.on('-development')
def development(host, port, **kwargs):
print('in *development* mode')
@dispatch.on('-production')
def development(host, port, **kwargs):
print('in *production* mode')
@dispatch.on('items', 'add')
def items_add(item, **kwargs):
print('adding item ...')
@dispatch.on('items', 'delete')
def items_delete(item, **kwargs):
print('deleting item...')
if _name_ = = '_main_':
dispatch(_doc_)

Глава 8 4 . Библиотека подпроцессов
Параметр

П одр об н ости

args

Один исполняемый файл или последовательность исполняемых файлов
и аргументов - 'Is', ['ls', '-la']
Запускать под управлением оболочки? Оболочка по умолчанию - /Ьin/

shel l
cwd

sh нa POSIX.

Рабочий каталог дочернего процесса.

84. 1 . Больше возможностей с помощью Рореп

Использование subprocess.Popen дает более тонкий контроль над запущенными процес­
сами, чем subprocess.call.
Запуск подпроцесса
process = subprocess. Popen([r'C:\path\to\app.exe', 'arg1 ', '--flag', 'arg'])

Сигнатура функции Popen очень похожа на сигнатуру функции cal l, однако Popen возвра­
щается немедленно, а не дожидается завершения подпроцесса, как это делает cal l .
Ожидание завершения подпроцесса
process = subprocess.Popen([r'C:\path\to\app.exe', 'arg1 ', ·--flag', 'arg'])
process.waitO

84.2. Вызов внешних команд

367

Чтение вьmода из подпроцесса
process = subprocess.Popen([r'C:\path\to\app.exe'], stdout=subprocess.PI PE, stderr=subprocess.PI PE)
# Это будет блокироваться до завершения процесса
stdout, stderr = process.communicate()
print stdout
print stderr

Интерактивный доступ к запущенным подпроцессам

Вы можете читать и писать на stdin и stdout даже тогда, когда подпроцесс еще не завер­
шен. Это может быть полезно при автоматизации работы в другой программе.
Запись в подпроцесс
process = subprocess.Popen([r'C:\path\to\app.exe1, stdout = subprocess.PI PE, stdin = subprocess.PI PE)
process.stdin.write('line of input\n') # Запись входных дан н ых
line = process.stdout.readline() # Читаем строку из stdout
# Выполняем логику на прочитанной строке.

Однако если вам нужен только один набор входных и выходных данных, а не динамиче­
ское взаимодействие, то следует использовать communicate() вместо того, чтобы напрямую
обращаться к stdin и stdout.
Чтение потока из подпроцесса

В случае, если вы хотите увидеть вывод подпроцесса построчно, можно воспользоваться
следующим фрагментом кода:
process = subprocess.Popen(, stdout=subprocess.PIPE)
while process.poll() is None:
output_line = process.stdout.readline()

В случае, если в выходных данных подкоманды отсутствует символ EOL, приведенный
фрагмент не работает. Тогда можно прочитать вывод посимвольно следующим образом:
process = subprocess.Popen(, stdout=subprocess.PIPE)
while process.poll() is None:
output_line = process.stdout.read(1 )

Число 1, указанное в качестве аргумента метода read, указывает на то, что метод read дол­
жен считывать по одному символу за раз. Вы можете задать чтение любого количества сим­
волов, используя другое число. Отрицательное число или О указывает read читать как одну
строку до тех пор, пока не будет встречено EOF. В обоих приведенных фрагментах process.
poll() является None, пока подпроцесс не завершится. Это используется для выхода из цик­
ла, когда больше нет вывода для чтения. Аналогичная процедура может быть применена
и к stderr подпроцесса.

84.2. Вызов внешних команд

Наиболее простым вариантом является использование функции subprocess.call. В ка­
честве первого аргумента она принимает список. Первым элементом списка должно быть
внешнее приложение, которое вы хотите вызвать. Остальные элементы списка являются ар­
гументами, которые будут переданы этому приложению.
subprocess.call([r'C:\path\to\app.exe', 'arg 1 ', '--flag', 'агg'])

Для команд оболочки установите значение shell=True и предоставьте команду в виде стро­
ки, а не списка.
subprocess.call('echo "Hello, world "', shell=True)

Обратите внимание, что две приведенные выше команды возвращают только статус
выхода из подпроцесса. Кроме того, будьте внимательны при использовании shell=True, по-

368

Глава 85. Файл setup.py

скольку это создает проблемы с безопасностью. Если вы хотите иметь возможность по­
лучать стандартный вывод подпроцесса, то замените вызов subprocess.call на subprocess.
check__output.

84.З. Как создать аргумент списка команд

Метод подпроцесса, позволяющий выполнять команды, требует наличия команды в виде
списка (по крайней мере, при использовании shel l_mode=True).
Правила создания списка не всегда просты, особенно при работе со сложными команда­
ми. К счастью, существует очень полезный инструмент, позволяющий это сделать: shlex. Са­
мый простой способ создания списка для использования в качестве команды заключается
в следующем:
import shlex
cmd_to_subprocess = shlex.spl it(command_used_in_the_shell)

Простой пример:
import shlex
shlex.spl it('ls --color -I -t -г')
out: ['Is', '--color', '-1', '-t', '-r']

Глава 85. Файл setup.py
Параметр
name

Подробности
Название вашего дистрибутива

packages

Строка версии вашего дистрибутива
Список пакетов Python (то есть каталогов, содержащих модули) для
включения. Он может быть задан вручную, но обычно вместо этого ис­
пользуется вызов setuptools.f1nd_packagesO

py_modules

Список модулей Python верхнего уровня (то есть одиночных .ру файлов),
которые необходимо включить

version

85. 1 . Назначение файла setup.py

Сценарий установки является центром всей деятельности по сборке, распространению
и установке модулей с помощью Distutils. Его назначение - корректная установка программ­
ного обеспечения.
Если вы хотите распространять модуль с названием foo, содержащийся в файле foo. py, то
ваш сценарий установки может быть простым:
from distuti ls.core import setup
setup(name='foo',
version='1 .0',
ру_modu les= ['foo'],

Для создания исходного дистрибутива этого модуля необходимо создать установочный
скрипт setup.py, содержащий приведенный выше код, и выполнить эту команду из терми­
нала:

85.2. Использование метаданных системы управления версиями в файле setup.py

369

python setup.py sdist
sdist создаст архивный файл (например, tarball на Unix, ZI P на Windows), содержащий ваш
скрипт установки setup.py и ваш модуль foo.py. Архив будет иметь имя foo-1 .O.tar.gz (или .zip)
и распакуется в каталог foo-1 .о.
Если конечный пользователь захочет установить ваш модуль foo, ему достаточно загру­
зить foo-1 .O.tar.gz (или .zip), распаковать его и - из каталога foo-1.0 - запустить:
python setup.py install

85.2. Использование метаданных системы управления версиями
в файле setup.py
setuptools_scm - это официально утвержденный пакет, который может использовать ме­
таданные Git или Mercurial для определения номера версии вашего пакета, а также найти
Руthоn-пакеты и пакетные данные для включения в него.
from setuptools import setup, find_packages
setup(
setup_req uires=['setu ptools_scm'],
use_scm_version=True,
packages=f1nd_packages(),
include_package_data=True,
В данном примере используются обе возможности; чтобы использовать только метадан­
ные SCM для версии, замените вызов find_packages() своим списком пакетов или, чтобы ис­
пользовать только поиск пакетов, удалите use_scm_version = True.

85.3. Добавление скриптов командной строки в пакет Python
Скрипты командной строки внутри пакетов Python - обычное явление. Вы можете орга­
низовать свой пакет таким образом, чтобы при установке пакета пользователем скрипт бьш
доступен по его пути. Допустим, у вас есть пакет greetings, в котором есть сценарий команд­
ной строки hello_world.py.
greetings/
greetings/
_init_.py
hello_world.py
Вы можете запустить этот скрипт, выполнив команду:
python greetings/greetings/hello_world.py
Однако, возможно, вы хотите запустить его следующим образом:
hello_world.py
Этого можно добиться, добавив scripts в setup() в файле setup.py следующим образом:
from setuptools import setup
setup(
name='greetings',
scri pts=['hello_world.ру']
Когда вы установите пакет greetings сейчас, hello_world.py будет добавлен в ваш путь. Дру­
гой возможностью бьmо бы добавить точку входа:
entry_points={'console_scripts': ['greetings=greetings.hello_world:main']}
Таким образом, вам нужно просто запустить его как:
greetings

370

Глава 86. Рекурсия

85.4. Добавление параметров установки

Как видно из предыдущих примеров, основное использование этого скрипта заключает­
ся в следующем:
python setup.py install
Но есть и более широкие возможности, например, установить пакет и иметь возмож­
ность изменять код и тестировать его без необходимости повторной установки. Для этого
используется:
python setup.py develop
Если вы хотите выполнять специфические действия, например, компилировать доку­
ментацию Sphinx или собирать fortran-кoд, вы можете создать свою собственную опцию, на­
пример такую:
cmdclasses = dict()
class BuildSphinx(Command):
'"'" Build Sphinx documentation."""
description = 'Build Sphinx documentation'
user_options = D
def initialize_options(self):
pass
def f1nalize_options(self):
pass
def run(self):
import sphinx
sphinx.build_main(['setup.py', '-Ь', 'html', './doc', './doc/_build/html'])
sphinx.build_main(['setup.py', '-Ь', 'man', './doc', './doc/_build/man'])
cmdclasses['build_sphinx'] =BuildSphinx
setup(
cmdclass=cmdclasses,
initialize_options и f1nalize_options будут выполняться до и после функции run, как это следу­
ет из их названий. После этого вы сможете вызвать:
python setup.py build_sphinx

Глава 86. Рекурсия
86. 1 . Что, как и когда делать с рекурсией

Рекурсия возникает, когда вызов функции приводит к повторному вызову этой же функ­
ции до завершения вызова исходной функции. Например, рассмотрим известное математи­
ческое выражение х! (т.е. операцию факториала). Для всех неотрицательных целых чисел
операция факториала определяется следующим образом:
• Если число равно О, то ответом будет 1.
• В противном случае ответом будет это число, умноженное на факториал числа, на
единицу меньшего этого числа.

86. 1 . Что, как и когда делать с рекурсией

371

В языке Python реализация операции факториала может быть оформлена в виде функ­
ции следующим образом:
def factorial(n):
if п == О:
return 1
else:
return п * factorial(n - 1 )

Функции рекурсии иногда бывают очень сложны для понимания, поэтому рассмотрим
их пошагово. Рассмотрим выражение factorial(3). Это выражение и все вызовы функции соз­
дают новое окруже1mе. По сути, окружение - это таблица, в которой идентификаторы (на­
пример, n, factorial, print и т. д.) сопоставляются с соответствующими значениями. В любой мо­
мент времени с помощью функции localsO можно получить доступ к текущему окружению.
В первом вызове функции единственной локальной переменной, которая определяется, яв­
ляется п = 3. Поэтому вывод locals() покажет {'п': 3}. Поскольку п == 3, возвращаемое значение
становится п * factorial(n - 1 ).
На следующем этапе может возникнуть некоторая путаница. Глядя на наше новое выра­
жение, мы уже знаем, что такое п. Однако мы еще не знаем, что такое factorial(n - 1 ). Сначала
п - 1 оценивается в 2. Затем 2 передается в factorial как значение для п. Поскольку это новый
вызов функции, создается вторая среда для хранения этого нового п. Пусть А - первая среда,
а В - вторая среда. А по-прежнему существует и равно {'п': 3}, однако В (которая равна {'n': 2})
является текущим окружением. Если посмотреть на тело функции, то возвращаемое значе­
ние снова равно п * factorial(n - 1 ). Не оценивая это выражение, подставим его в исходное вы­
ражение возврата. При этом мы мысленно отбрасываем в, поэтому не забываем подставлять
п соответствующим образом (т.е. ссылки на п из В заменяются на п - 1 , в котором использует­
ся п из А). Теперь исходное выражение возврата становится п * ((п - 1 ) * factorial((n - 1 ) - 1 )). По­
тратьте секунду на то, чтобы убедиться, что вы понимаете, почему это так.
Теперь оценим часть factorial((n - 1 ) - 1 )). Так как в А п == 3, то в factorial мы передаем 1 . Сле­
довательно, мы создаем новое окружение С, которое равно {'п': 1 }. И снова возвращаемое зна­
чение равно п * factorial(n - 1 ). Поэтому заменим factorial((n - 1 ) - 1 )) "исходного" выражения
возврата аналогично тому, как мы корректировали исходное выражение возврата ранее. Те­
перь "исходное" выражение имеет вид п * ((п - 1 ) * ((п - 2) * factorial((n - 2) - 1 ))).
Почти все готово. Теперь нужно вычислить factorial((n - 2) - 1). На этот раз мы передаем О.
Поэтому эта оценка равна 1 . Теперь выполним последнюю подстановку. Теперь "исходное"
возвращаемое выражение имеет вид п * ((n - 1 ) * ((n - 2) * 1 )). Если вспомнить, что исходное вы­
ражение возврата оценивается по А, то выражение становится 3 * ((3 - 1 ) * ((3 - 2) * 1 )). Это, ко­
нечно же, равно б. Чтобы убедиться в правильности ответа, вспомним, что 3! == 3 * 2 * 1 == б.
Прежде чем читать дальше, убедитесь, что вы полностью понимаете концепцию окружений
и их применение к рекурсии.
Оператор if п == О: return 1 называется базовым. Это связано с тем, что в нем нет рекурсии.
Базовый случай (base case) абсолютно необходим. Без него вы столкнетесь с бесконечной ре­
курсией. При этом, если у вас есть хотя бы один базовый случай, вы можете иметь сколько
угодно случаев. Например, мы могли бы эквивалентно записать factorial в виде:
def factorial(n):
if п == О:
return 1
elif п == 1 :
return 1
else:
return п * factorial(n - 1 )

Возможны также случаи множественной рекурсии, но мы не будем их рассматривать,
так как они относительно редки и часто сложны для восприятия.
Также возможны "параллельные" вызовы рекурсивных функций. Например, рассмо­
трим последовательность Фибоначчи, которая определяется следующим образом:

372

Глава 86. Рекурсия

• Если число равно О, то ответ равен О.
• Если число равно 1, то ответ равен 1.
• В противном случае ответом будет сумма двух предыдущих чисел Фибоначчи.
Это можно определить следующим образом:
def f1b(n):
if n == О о г n == 1 :
return n
else:
return f1b(n - 2) + fib(n - 1 )

Не будем рассматривать эту функцию так подробно, как это было с factorial(З), но конеч­
ное возвращаемое значение f1b(S) эквивалентно следующему (синтаксически неверному)
выражению:
(

f1b((n - 2) - 2)
+
(
fib(((n - 2) - 1 ) - 2)
+
fib(((n - 2) - 1 ) - 1 )
)

)
+
(
(

)

fib(((n - 1 ) - 2) - 2)
+
fib(((n - 1 ) - 2) - 1 )

)
+
(
fib(((n - 1 ) - 1 ) - 2)
+
(
fi b((((n - 1 ) - 1 ) - 1 ) - 2)
+
fi b((((n - 1 ) - 1 ) - 1 ) - 1 )
)
)

Таким образом, получается (1 + (О + 1 )) + ((О + 1 ) + (1 + (О + 1 )), что, конечно же, равно 5. Те­
перь рассмотрим еще несколько словарных терминов:
• Хвостовой вызов (Tail call) - это просто вызов рекурсивной функции, который
является последней операцией, выполняемой перед возвратом значения.
Чтобы было понятно, return foo(n - 1 ) - это вызов хвоста, а return foo(n - 1 ) + 1 - нет
(поскольку сложение является последней операцией).
• Оптимизация хвостовых вызовов (Tail call optimization, ТСО) - это способ
автоматического сокращения рекурсии в рекурсивных функциях.
• Устранение хвостового вызова (Tail call elimination, ТСЕ) - это приведение
хвостового вызова к выражению, которое может быть вычислено без рекурсии.
ТСЕ является разновидностью ТСО.
Оптимизация хвостовых вызовов полезна по ряду причин:
• Интерпретатор может минимизировать объем памяти, занимаемый
окружениями. Поскольку ни один компьютер не обладает неограниченной

86.2. Задач а "исследования дере ва" с помощью рекурсии

373

памятью, чрезмерное количество рекурсивных вызовов функций приведет
к переполнению стека.
• Интерпретатор может уменьшить количество переключений кадров стека.
В Python по ряду причин не реализована ТСО. Поэтому для преодоления этого ограниче­
ния требуются другие методы. Выбор метода зависит от конкретного случая. При определен­
ной интуиции определения factorial и fib можно относительно легко преобразовать в итера­
тивный код следующим образом:
def factorial(n):
product = 1
while n > 1 :
product *= n
n -= 1
return product
def fib(n):
а, Ь = О, 1
while n > О:
а, Ь = Ь, а + Ь
n -= 1
return а

Обычно это наиболее эффективный способ ручного устранения рекурсии, но для более
сложных функций он может стать довольно сложным.
Другим полезным инструментом является декоратор l ru_cache в Python, который может
быть использован для сокращения числа избыточных вычислений.
Теперь вы имеете представление о том, как избежать рекурсии в Python, но когда следует
использовать рекурсию? Ответ - "не часто". Все рекурсивные функции могут быть реализо­
ваны итеративно. Просто нужно понять, как это сделать. Однако есть редкие случаи, когда
рекурсия не помешает. В Python рекурсия распространена в тех случаях, когда ожидаемые
входные данные не вызывают значительного количества вызовов рекурсивных функций.
Если рекурсия - это тема, которая вас интересует, то следует изучать функциональные
языки, такие как Scheme или Haskell. В этих языках рекурсия гораздо полезнее.
Обратите внимание, что приведенный пример для последовательности Фибоначчи, хотя
и хорошо показывает, как применять определение на языке Python и в дальнейшем исполь­
зовать lru cache, имеет не очень большое время работы, так как для каждого небазового слу­
чая выполняется 2 рекурсивных вызова. Количество обращений к функции растет экспонен­
циально до n.
Как ни странно, более рациональная реализация будет использовать линейную рекурсию:
def fib(n):
if n 4 + 3 -> 4 + 3 + 2 -> 4 + 3 + 2 + 1 -> 1 О )

86.4. Увеличение максимальной глубины рекурсии

375

тогда как цикл for работает строго вперед: ( 1 -> 1 + 2 -> 1 + 2 + З -> 1 + 2 + З + 4 -> 1 О ). Иногда
рекурсивное решение оказывается проще итеративного. Это наглядно видно при реализа­
ции реверсирования связного списка.

86.4. Увеличение максимальной глубины рекурсии

Существует предел глубины рекурсии, который зависит от реализации Python. Когда пре­
дел достигнут, возникает исключение RuntimeError:
RuntimeError: Maximum Recursion Depth Exceeded

Вот пример программы, которая вызовет эту ошибку:

def cursing(depth):
try:
cursing(depth + 1 ) # actual ly, re-cursing
except RuntimeError as RE:
print('Я рекурсировал {} paз!'.format(depth))
cursing(O)
# В ы вод: Я рекурсировал 1 083 раз!

Можно изменить предел глубины рекурсии, используя
sys.setrecursionl imit(l imit)

Проверить, каковы текущие параметры ограничения, можно, выполнив команду:

sys.getrecursionl imit()

Выполняя тот же метод с нашим новым пределом, получим

sys.setrecursionl imit(2000)
cursing(O)
# В ы вод: Я рекурсировал 1 997 раз!

Из Python 3.5 исключение представляет собой RecursionError, производное из RuntimeError.

86.5. Хвостовая рекурсия - плохая практика

Когда единственное, что возвращается из функции, - это рекурсивный вызов, это назы­
вается хвостовой рекурсией. Приведем пример обратного отсчета, написанный с использо­
ванием хвостовой рекурсии:
def countdown(n):
if n == О:
print "Blastoff!"
else:
print n
countdown(n-1 )

Любое вычисление, которое может быть выполнено с помощью итерации, может быть
выполнено и с помощью рекурсии. Вот версия f1nd_max, написанная с использованием хво­
стовой рекурсии:
def find_max(seq, max_so_far):
if not seq:
return max_so_far
if max_so_far < seq[O] :
return find_max(seq[1 :], seq[O])
else:
return find_max(seq[1 :], max_so_far)

Хвостовая рекурсия считается плохой практикой в Python, поскольку компилятор Python не
справляется с оптимизацией хвостовых рекурсивных вызовов. Рекурсивное решение в подоб­
ных случаях использует больше системных ресурсов, чем эквивалентное итеративное решение.

376

Глава 86. Рекурсия

86.б. Оптимизация хвостовой рекурсии
с помощью интроспекции стека
По умолчанию стек рекурсии в Python не может превышать 1000 кадров. Это можно изме­
нить, задав параметр sys.setrecursionl imit(1 5000), который работает быстрее, однако этот ме­
тод потребляет больше памяти. Вместо этого мы можем решить проблему хвостовой рекур­
сии с помощью интроспекции стека.
#!/usr/Ьin/env python2.4
# Эта программа демонстрирует декоратор Python, который реализует
# оптимизацию хвостовых вызовов (Tail cal l optim ization, ТСО)вызовов. Он
# делает это, бросая исключение, если он является своим прародителем, и перехватывая
# такие исключения для вызова стека.
import sys
class Tai l RecurseException:
def init (self, args, kwargs):
self.args = args
self.kwargs = kwargs
def tai l_call_optimized(g):
Эта функция украшает функцию оптимизации хвостовых вызовов. Для этого она бросает
исключение, если функция я вляется своим прародителем, и перехватывает такие исключения,
чтобы сымитировать оптимизацию хвостовых вызовов.
Эта функция не работает, если декорированная функция рекурсирует в нехвостовом контексте.
def func(*args, **kwargs):
f = sys._getframe()
if f.f_back and f.f_back.f_back and f.f_back.f_back.f_code
raise Tai l RecurseException(args, kwargs)
else:
while 1 :
try:
return g(*args, **kwargs)
except TailRecurseException, е:
args = e.args
kwargs = e.kwargs
func._doc_ = g._doc_
return func

==

f.f_code:

Для оптимизации рекурсивных функций мы можем использовать декоратор @tail_cal l_
Вот несколько распространенных примеров рекурсии
с использованием описанного выше декоратора:
Пример с факториалом:

optimized для вызова нашей функции.

@tail_cal l_optimized
def factorial(n, асс = 1 ):
"calculate а factorial"
if n == О:
return асс
return factorial(n-1 , n*acc)
print factorial(1 0000)
# ВЫ ВОДИТ очень большое Ч И СЛО,
# но не достигает предела рекурсии.

Пример Фибоначчи:
@tail_cal l_optimized
def f1 b(i, current = О, next = 1 ):
if i = = О:

87. 1 . Добавление типов в функцию

377

return current
else:
return fib(i - 1, next, current + next)
print f1b(1 0000)

# также вы водит очень бол ьшое число,
# но не достигает п редел а рекурсии.

Глава 87. Подсказки типов
8 7 . 1 . Добавление типов в функцию

Рассмотрим пример функции, которая принимает два аргумента и возвращает значение,
обозначающее их сумму:
def two_sum(a, Ь):
return а + Ь
Глядя на этот код, нельзя уверенно и без сомнений указать тип аргументов функции
two_sum. Он работает и в том, и в дРуrом случае, когда ему передаются значения int:
print(two_sum(2, 1 )) # 3
и со строками:
print(two_sum("a", "Ь")) # "аЬ"
и с другими значениями, такими как списки, кортежи и т. д.
В силу динамической природы типов Python, когда для данной операции применимо
множество типов, любая программа проверки типов не сможет обоснованно утверждать,
разрешен ли вызов данной функции или нет.
Чтобы помочь нашему средству проверки типов, мы можем дать ему подсказки в опре­
делении функции, указывающие на тип, который мы допускаем. Чтобы указать, что мы хо­
тим разрешить только типы int, мы можем изменить определение функции на следующее:
def two_sum(a: int, Ь: int):
return а + Ь
Аннотации следуют за именем аргумента с символом : в качестве разделителя.
Аналогично, чтобы указать, что разрешены только типы str, мы изменим нашу функцию,
чтобы указать это:
def two_sum(a: str, Ь: str):
return а + Ь
Кроме указания типа аргументов можно также указать возвращаемое значение вызова
функции. Для этого после закрывающей круглой скобки в списке аргументов, но перед сим­
волом : в конце объявления функции добавляется символ ->, за которым следует тип:
def two_sum(a: int, Ь: int) -> int:
return а + Ь
Теперь мы указали, что возвращаемое значение при вызове two_sum должно иметь тип
int. Аналогичным образом мы можем определить соответствующие значения для str, float,
list, set и других.
Хотя подсказки типов в основном используются программами проверки типов и IDE,
иногда может потребоваться их извлечение. Это можно сделать с помощью специального
атрибута _annotations_:
two_sum._annotations_
# {'а': , 'Ь': , 'return': }

378

Глава 87. Подсказки типов

87 .2. Функция NamedTu p le

Создание именованного кортежа с подсказками типа осуществляется с помощью функ­
ции NamedTuple из модуля typing:
import typing
Point = typing.NamedTuple('Point', [('х', int), ('у', int)])
Заметим, что имя результирующего типа является первым аргументом функции, но его
следует присвоить одноименной переменной, чтобы облегчить проверку типов.

87 . 3. Общие типы
typing.ТypeVar - это фабрика общих типов. Его основная задача - служить в качестве параметра/заместителя для аннотаций общих функций/классов/методов:
import typing
Т = typing.TypeVar{"Т'')
def get_f1rst_element{I: typing.Sequence[T]) -> Т:
'""' Получает первы й элемент последовательности."""
return 1[0]

87 .4. Переменные и атрибуты
Переменные аннотируются с помощью комментариев:

х = 3 # type: int
х = negate(x)
х = 'програм ма проверки типов (type-checker) может обнаружить эту ошибку'

Версия Python З.х � 3.6

Начиная с Python 3.6 появился новый синтаксис для аннотаций переменных. Приведен­
ный выше код может иметь вид
х: int = 3
В отличие от комментариев, можно также просто добавить подсказку типа к перемен­
ной, которая ранее не была объявлена, не задавая ей значения:
у: int
Кроме того, если они используются на уровне модуля или класса, то подсказки о типе
можно получить, используя typing.geLtype_hints{class_or_module):
class Foo:
х: int
у: str = 'аЬс'
print(typing.geLtype_hints{Foo))
# ChainMap({'x': , 'у': }, {})
Кроме того, доступ к ним можно получить с помощью _annotations_:
х: int
print(_annotations_)
# {'х': }
class С:
s: str
print{C._annotations_)
# {'s': }

87.5. Члены и методы классов

379

87 .5. Члены и методы классов
class A:
х = None # type: float
def _iniL(self, х: float) -> None:
self не должен быть аннотирован
init должен быть аннотирован для возврата None
self.x = х
@classmethod
def from_int(cls, х: int) -> 'М:
cls не должен быть аннотирован
Используйте прямую ссылку для обращения к текущему классу со строковым литералом 'А'
return cls(float(x))

Прямая ссылка (forward reference) на текущий класс необходима, поскольку аннотации
оцениваются при определении функции. Прямые ссылки также могут использоваться при
обращении к классу, при импортировании которого возникнет циклический импорт.

87 .6. Подсказки типов для именованных аргументов
def hel lo_world(greeting: str = 'Hello'):
print(greeting + ' world !')

Обратите внимание на пробелы вокруг знака равенства - это отличается от того, как
обычно оформляются именованные аргументы.

Глава 88. Исключения
Ошибки, обнаруженные в процессе выполнения, называются исключениями (exceptions)
и не являются безусловно фатальными. Большинство исключений не обрабатывается
программами; можно написать программы, обрабатывающие отдельные исключения.
В Python имеются специальные функции для работы с исключениями и логикой исключе­
ний. Кроме того, исключения имеют богатую иерархию типов, все они наследуются от типа
BaseException.

88. 1 . Перехват исключений

Для перехвата исключений используйте try...except:. Следует указывать исключение как
можно более точно:
try:
х= 5 / О
except ZeroDivisionError as е:
# 'е' - объект исключения
ргiпt("Получено деление на ноль! Исключение было:", е)
# обработать случай исключения

х=О

f1nally:
print "The END"
# запускается независимо от выполнения.

380

Глава 88. Исключения

Указанный класс исключений - в данном случае ZeroDivisionError - перехватывает лю­
бое исключение, относящееся к этому классу или к любому его подклассу. Например,
ZeroDivisionError является подклассом ArithmeticError:
>» ZeroDivisionError._bases_
( ,)

Таким образом, следующий код также перехватит ошибку ZeroDivisionError:
try:

5/0

except ArithmeticError:
ргint("Получена арифметическая ошибка")

88.2. Не перехватывайте все подряд!

Часто возникает соблазн перехватить каждое исключение:

try:
very_difficult_function()
except Exception:
# log / попытка повторного подкл ючения / благополуч н ы й выход
f1nally:
print "The END"
# запускается независимо от выполнения

и все, что включает в себя BaseException и все его дочерние элементы, включая Exception:
try:
even_more_diff1culLfunction()
except:
pass # сделать все необходимое

В большинстве случаев это плохая практика. Она может перехватить больше, чем пред­
полагалось, например, SystemExit, Keyboard lnterrupt и MemoryError - каждая из которых, как
правило, должна обрабатываться иначе, чем обычные системные или логические ошибки.
Это также означает, что нет четкого понимания того, что внутренний код может сделать не­
правильно и как правильно восстановиться после этого состояния. Если вы будете перехва­
тывать каждую ошибку, то не будете знать, какая ошибка произошла и как ее исправить.
Это чаще всего называют "маскировкой ошибок" (bug masking), и ее следует избегать.
Пусть вместо тихого сбоя или, что еще хуже, сбоя на более глубоком уровне выполнения
ваша программа аварийно завершается. (Представьте, что это транзакционная система.)
Обычно эти конструкции используются на самом внешнем уровне программы и фиксиру­
ют подробности ошибки, чтобы ее можно бьто устранить или обработать более конкретно.

88.3. Повторный вызов исключений

Иногда требуется перехватить исключение, чтобы проверить его, например, в целях про­
токолирования. После проверки вы хотите, чтобы исключение продолжало распространять­
ся, как и раньше. В этом случае достаточно использовать оператор raise без параметров.
try:

5/0

except ZeroDivisionError:
ргint("Получена ошибка")
raise

Однако следует помнить, что дальше в стеке вызывающей стороны исключение все рав­
но может быть перехвачено и как-то обработано. Готовый вывод в этом случае может быть
проблемным, поскольку он произойдет в любом случае (перехваченном или не перехвачен­
ном). Поэтому, возможно, лучше вызвать другое исключение, содержащее как ваш коммен­
тарий к ситуации, так и исходное исключение:

88.4. Перехват м ножественных исключений

381

try:
5/0
except ZeroDivisionError as е:
raise ZeroDivisionError("Пoлyчeнa ошибка", е)
Однако это имеет тот недостаток, что трассировка исключений сводится именно к этому
оператору raise, в то время как raise без аргумента сохраняет исходную трассировку исключе­
ний. В Python 3 можно сохранить исходный стек, используя синтаксис raise-from:
raise ZeroDivisionError("Пoлyчeнa ошибка") from е

88.4. Перехват множественных исключений

Существует несколько способов перехвата нескольких исключений.
Первый способ заключается в создании кортежа типов исключений, которые вы хотите
перехватывать и обрабатывать аналогичным образом. В данном примере код будет игнори­
ровать исключения КеуЕггог и AttributeError.
try:
d = {}
а = d[1 ]
Ь = d.non_existing_f1eld
except (КеуЕггог, AttributeError) as е:
ргiпt("Было перехвачено исключение КеуЕггог или AttributeError")
Если вы хотите обрабатывать разные исключения разными способами, то для каждого
типа можно выделить отдельный блок исключений. В данном примере мы по-прежнему пе­
рехватываем КеуЕггог и AttributeError, но обрабатываем эти исключения по-разному.
tгу:

d = {}
а = d[1 ]
Ь = d.non_existing_f1eld
except КеуЕггог as е:
ргiпt("Произошла ошибка КеуЕггог. Сообщение об ошибке:", е)
except AttributeError as е:
ргiпt("Произошла ошибка AttributeError. Сообщение об ошибке:", е)

88.5. Иерархия исключений

Обработка исключений происходит на основе иерархии исключений, определяемой
структурой наследования классов исключений. Например, IOError и OSError являются под­
классами EnvironmentError. Код, перехватывающий IOError, не будет перехватывать OSError. Од­
нако код, перехватывающий EnvironmentError, будет перехватывать и IOError, и OSError.
Иерархия встроенных исключений:
Python 2.х Версия � 2.3:

BaseException

+-- SystemExit
+-- Keyboardlnterrupt
+--

GeneratorExit

+-- Exception
+-- Stoplteration

StandardError
1 +-- BufferError
1 +-- ArithmeticError
1 1 +-- FloatingPointError
1 1 +- OverflowError
1 1 +-- ZeroDivisionError
1 +-- AssertionError
1 +-- AttributeError

+--

382
+-- EnvironmentError
1 +-- IOError
1 +- OSError
1 +-- WindowsError (Windows)
1 +- VMSError (VMS}
+-- EOFError
+-- lmportError
+-- LookupError
1 +- lndexError
1 +-- KeyError
+-- MemoryError
+-- NameError
1 +- Unbound local Error
+-- ReferenceError
+-- RuntimeError
1 +-- Notlmplemented Error
+-- SyntaxError
1 +-- lndentationError
1 +- TabError
+-- SystemError
+-- TypeError
+-- ValueError
+-- UnicodeError
+-- UnicodeDecodeError
+-- UnicodeEncodeError
+-- UnicodeTranslateError
+-- Warning
+- DeprecationWarning
+-- Pending DeprecationWarning
+- RuntimeWarning
+-- SyntaxWarning
+- UserWarning
+-- FutureWarning
+- lmportWarning
+-- UnicodeWarning
+- BytesWarning

Версия Python З.х :?: 3.0:
BaseException
+- System Exit
+-- Keyboard lnterrupt
+- GeneratorExit
+-- Exception
+- Stoplteration
+-- StopAsynclteration
+- ArithmeticError
1 +-- Floating PointError
1 +-- OverflowError
1 +-- ZeroDivisionError
+- AssertionError
+-- AttributeError
+- BufferError
+-- EOFError
+- lmportError
+-- LookupError
1 +-- 1 ndexError
1 +-- KeyError
+-- MemoryError
+-- NameError

Глава 88. Исключения

88.6. Else

383

1 +-- Unbound local Error
+-- OSError
+-- Blocking l OError
+-- ChildProcessError
+-- ConnectionError
1 +-- BrokenPipeError
1 +- ConnectionAborted Error
1 +- ConnectionRefusedError
1 +-- ConnectionResetError
+-- FileExistsError
+-- FileNotFoundError
+-- lnterrupted Error
+-- lsADirectoryError
+-- NotADirectoryError
+-- PermissionError
+-- ProcessLookupError
+-- ТimeoutError
+-- ReferenceError
+-- RuntimeError
1 +-- Notl mplemented Error
1 +-- RecursionError
+-- SyntaxError
1 +-- lndentationError
1 +-- TabError
+-- SystemError
+-- TypeError
+-- Val ueError
1 +-- UnicodeError
1 +-- UnicodeDecodeError
1 +-- UnicodeEncodeError
1 +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- lmportWarning
+-- UnicodeWarning
+-- BytesWarning
+-- ResourceWarning

88.6. Else
Код в блоке else будет выполняться только в том случае, если код в блоке try не вызвал ис­
ключений. Это полезно, если у вас есть код, который вы не хотите запускать, если возникло
исключение, но не хотите, чтобы исключения, вызванные этим кодом, были перехвачены.
Например:
try:
data = {1 : 'опе', 2: 'two'}
print(data[1 ])
except KeyError as е:
print('key not found')
else:
raise ValueError()
# Выходные данн ые: 1
# Выходные данн ые: ValueError

384

Глава 88. Исключения

Обратите внимание, что этот вид else: не может быть объединен с if, начинающим
еlsе-предложение с el if. Если у вас есть последующее if, то оно должно оставаться с отступом
ниже else:, как в примере:
try:
except ...:
else:
if ... :
elif ... :
else:

88. 7. Вызов исключений

Если ваш код встречает условие, которое он не знает, как обработать, например, непра­
вильный параметр, он должен вызвать соответствующее исключение.
def even_the_odds(odds):
if odds % 2 != 1 :
raise ValueError("He получено нечетное число")
return odds + 1

88.8. Создание пользовательских типов исключений
Создайте класс, наследующий от Exception:

class FooException(Exception):
pass
try:
raise FооЕхсерtiоп("введите описание здесь")
except FooException:
ргiпt("Возникло исключение FooException.")

или другой тип исключения:
class NegativeError(ValueError):
pass
def foo(x):
# функция, принимающая только положительные значения х
if х < О:
raise NegativeError("Heвoзмoжнo обрабаты вать отрицательные числа")
. . . # остальная часть тела функции
try:
result = foo(int(input("Bвeдитe целое положительное число: "))) # raw_input в Python 2.х
except NegativeError:
print("Bы ввели отрицательное число!")
else:
ргiпt("Результат: " + str(resu lt))

88. 9. Практические примеры обработки исключений
Ввод данных пользователем

Представьте, что вы хотите, чтобы пользователь ввел число с помощью функции input.
Вы хотите убедиться, что вводимые данные являются числом. Для этого можно использо­
вать try/except:

88.1 О. Исключения - это тоже объекты

385

Версия Python З.х � 3.0:
while True:
try:
nb = int(input('Bвeдитe число: '))
break
except ValueError:
print('Этo не число, попробуйте еще раз.')

Примечание: в Python 2.х вместо этого используется raw_input; функция input в Python 2.х
существует, но имеет другую семантику. В приведенном выше примере input также будет
принимать такие выражения, как 2 + 2, которые оцениваются в число. Если входные данные
не удалось преобразовать в целое число, то выдается ошибка ValueError. Ее можно перехва­
тить с помощью except. Если исключение не возникло, то break "выпрыгивает" из цикла. По­
сле завершения цикла nb содержит целое число.
Словари

Представьте, что вы выполняете итерацию по списку последовательных целых чисел
range(n), и у вас есть список словарей d, который содержит информацию о том, что нужно
делать, когда вы встречаете некоторые конкретные целые числа, например, пропускать сле­
дующие за d[i].
d = [{7: З}, {25: 9}, {38: 5}]
for i in range(len(d)):
do_stuff(i)
try:
dic = d[i]
i += dic[i]
except KeyError:
i += 1
При попытке получить значение из словаря для несуществующего ключа будет выдана
ошибка KeyError.

88. 1 О. Исключения - это тоже объекты

Исключения - это обычные объекты Python, наследующие от встроенного типа BaseEx­
ception. Скрипт на Python может использовать оператор raise для прерывания выполнения,
в результате чего Python выводит трассировку стека вызовов на данный момент и представ­
ление экземпляра исключения. Например,
»> def failing_function():
... raise ValueError('Example error!')
»> failing_function()
Traceback (most recent call last):
File "", line 1 , in
File "", line 2, in failing_function
ValueError: Example error!
в котором говорится, что нашей функцией failing_function(), которая была выполнена в ин­
терпретаторе, бьша вызвана ошибка ValueError с сообщением 'Example error!
Вызывающий код может выбрать способ обработки всех типов исключений, которые могут быть вызваны:
>>> try:
... failing_function()
... except ValueError:
... ргiпt('Обработка ошибки')
Обработка ош ибки

386

Глава 88. Исключения

Объекты исключений можно получить, присвоив их в части except. . . кода обработки исключений:
>» try:

... failing_function()
... except ValueError as е:
... ргint{'Перехваченное искл ючение', герг(е))
Перехваченное искл ючение ValueError{'Example еггог!',)
Полный список встроенных исключений Python вместе с их описанием можно найти
в официальной документации Python.

88. 1 1 . Выполнение кода очистки
с помощью f1nally

Иногда требуется, чтобы что-то происходило независимо от того, какое исключение про­
изошло, например, если необходимо очистить некоторые ресурсы. Блок f1nally предложения
tгу будет выполнен независимо от того, были ли вызваны какие-либо исключения.
resource = allocate_some_expensive_resource()
try:
do_stuff(resource)
except SomeException as е:
log_error(e)
raise # повторн ы й вызов ошибки
f1nally:
free_expensive_resource(resource)
Часто этот паттерн лучше обрабатывать с помощью менеджеров контекста (с использо­
ванием оператора with).

88. 1 2. Создание цепочек исключений с использованием
raise from

В процессе обработки исключения может возникнуть необходимость вызвать другое ис­
ключение. Например, при получении ошибки IOError во время чтения из файла вместо нее
можно вызвать ошибку, специфичную для приложения, чтобы показать ее пользователям
библиотеки.

Версия Python З.х :::: 3.0

Вы можете выстроить цепочку исключений, чтобы показать, как происходила обработ­
ка исключений:
>» try:

5/0

except ZeroDivisionError as е:
raise VаluеЕггог("Деление не удалось") from е
Traceback (most recent call last):
File "", line 2, in
ZeroDivisionError: division Ьу zero
Вышеуказан ное исключение я вилось непосредствен ной прич и ной следующего исключения:
Traceback (most recent call last):
File "", line 4, in
ValueError: Деление не удалось

89. 1 . Пользовательское исключение

Глава 89. Вызов пол ьзовател ьских
ошибок и искл ючений

387

В Python имеется множество встроенных исключений, которые заставляют вашу про­
грамму выдавать ошибку, если что-то в ней идет не так. Однако иногда может потребовать­
ся создание задуманных вами пользовательских исключений. Пользователь может опреде­
лить такие исключения, создав новый класс. Этот класс исключений должен быть прямо
или косвенно производным от класса Exception. Большинство встроенных исключений так­
же являются производными от этого класса.

89. 1 . Пользовател ьское исключение

Здесь мы создали пользовательское исключение CustomError, которое является производ­
ным от класса Exception. Это новое исключение может быть вызвано, как и другие исключе­
ния, с помощью оператора raise с необязательным сообщением об ошибке.
class Custom Error(Exception):
pass
х=1

if х == 1 :
raise Custom Error('Этo пользовательская ошибка')

Результат:
Traceback (most recent call last):
File "error_custom. py", line 8, in
raise CustomError('Этo пользовательская ошибка')
_main_.Custom Error: Это пользовательская ошибка

89.2. Перехват пользовательского исключения

В данном примере показано, как перехватить пользовательское исключение Exception:

class Custom Error(Exception):
pass

tгу:
raise CustomError('мoжeтe меня пой мать?')
except CustomError as е:
print ('Catched Custom Error :{}'.format(e))
except Exception as е:
print ('Generic exception: {}'.format(e))

Результат:

Catched CustomError: можете меня пой мать?

Глава 90. Искл ючения сообщества
В сообществе Stack Overflow участники часто задают вопросы об одних и тех же ошибках:

"lmportError: No module named '??????"' , "SyntaxError: invalid syntax" или "NameError: name '??' is not
def1ned". Попытаемся сократить количество таких вопросов.

90. 1 . Прочие ошибки
AssertError
Оператор assert существует практически во всех языках программирования. При выпол­
нении:
assert condition

388

Глава 90. Исключения сообщества

или:
assert condition, message

это эквивалентно следующему:

if _debug_:
if not condition: raise AssertionError(message)

Утверждения (assertions) могут содержать необязательное сообщение, и вы можете от­
ключить их, когда закончите отладку.
Примечание: встроенная переменная debug имеет значение True в обычных условиях,
False - при запросе оптимизации (опция командной строки -0). Присвоение переменной
debug является недопустимым. Значение встроенной переменной определяется при запуске
интерпретатора.
Keyboardlnterrupt
Ошибка, возникающая при нажатии пользователем клавиши прерывания, обычно
"Ctrl" + "С" или "del".
ZeroDivisionEпor
Вы пытались вычислить 1 /0, что дает неопределенный результат. Посмотрите этот пример:
Python 2.х версии � 2.0 :,; 2. 7:
div = float(raw_input("Дeлитeли: "))
for х in xrange(div+1 }: #включает само число и ноль
if div/x == div//x:
print х, "является делителем", div

Версия Python 3.х � 3.0:
div = iпt(iпрut("Делители: "))
for х in range(div+1 ): #включает само число и ноль
if div/x == div//x:
print(x, "я вляется делителем", d iv}

При этом возникает ошибка ZeroDivisionError, поскольку цикл for присваивает х это значе­
ние. Должно быть так:
Python 2.х версии � 2.0 :,; 2. 7:
div = float(raw_input("Дeлитeли: "))
for х in xrange(1 ,div+1 ): #включает само число, но не ноль
if div/x == div//x:
print х, "я вляется делителем", div

Версия Python 3.х � 3.0:
div = iпt(iпрut("Делители: "))
for х in range(1 ,div+1 }: ##включает само число, но не ноль
if div/x == div//x:
print(x, "я вляется делителем", d iv)

90.2. Ошибка "NameError. name '??' is not defined"

Возникает при попытке использования переменной, метода или функции, которые не
инициализированы (по крайней мере, не были инициализированы ранее). Другими слова­
ми, она возникает, когда запрашиваемое локальное или глобальное имя не найдено. Воз­
можно, вы неправильно указали имя объекта или забьши что-то импортировать. Также воз­
можно, что он находится в другой области видимости. Об этом мы расскажем в отдельных
примерах.

90.3. Ошибки типов данных

389

Не прописано в коде

Возможно, вы забьши провести инициализацию, особенно это касается констант:
foo # Эта переменная не определена
Ьаг() # Эта функция не определена

Возможно, определение произведено позже:
baz()
def baz():
pass

или не был осуществлен импорт:
#needs import math
def sqrt():
х = float(input('Value: "))
return math.sqrt(x)

Области видимости Python и правило LEGB

Так называемое правило LEGB говорит об областях видимости Python. Его название про­
исходит от различных областей, упорядоченных по соответствующим приоритетам:
Local --> Enclosed --> G lobal --> Built-in.



Local - Локальные: Переменные, не объявленные глобальными или
не назначенные в функции.
• Enclosing - Охватывающие: Переменные, определяемые в функции, которая
обернута внутри другой функции.
• Global - Глобальные: Переменные, объявленные глобальными, или назначенные
на верхнем уровне ячейки.
• Built-in - Встроенные: Переменные, предварительно назначенные в модуле
встроенных имен.
В качестве примера:
for i in range(4):
d=i*2
print(d)

Переменная d доступна, так как цикл for не помечает новую область видимости, но если
бы это произошло, то мы бы получили ошибку, и поведение цикла бьшо бы аналогичным:
def noaccess():
for i in range(4):
d=i*2
noaccess()
print(d)

NameError: имя 'd' не определено

90.З. Ошибки типов данных

Эти исключения возникают в тех случаях, когда тип некоторого объекта должен быть
другим.
ТуреЕпоr: [definition/method] принимает ? поз1ЩИонных аргументов, но было задано ?

Функция или метод бьши вызваны с большим (или меньшим) количеством аргументов,
чем те, которые она может принять.

390

Глава 90. Исключения сообщества

Пр имер
Если задано больше аргументов:
def foo(a): return а
foo(a,b,c,d) # Определены а, Ь, с, d

Если задано меньше аргументов:
def foo(a,b,c,d): return а += Ь + с + d
foo(a) # а определено
ПримечаIШе: если необходимо использовать неизвестное количество аргументов, мож­
но использовать *args или **kwargs.
ТypeError: неподдерживаемые типы операндов для [operand] : '???' и '???'

Некоторые типы не могут работать вместе, в зависимости от операнда.

Пр имер
Операнд + используется для конкатенации и сложения, но ни один из них нельзя исполь­
зовать для обоих типов. Например, попытка создать набор (set) путем конкатенации 'set1 • и
'tuple1 ' дает ошибку. Код:
set1 , tuple1 = {1 ,2}, (3,4)
а = set1 + tuple1

Некоторые типы (например, int и string) оба используют "+", но для разных целей:
Ь

=

400 + 'foo'

Но вы можете, например, добавить числа типа float к int:
d = 1 + 1 .0
ТypeError: '??' object is not iteraЬle/subscriptaЬle:

Для того чтобы объект был итерируемым, он может принимать последовательные ин­
дексы, начиная с нуля, до тех пор, пока индексы не перестанут быть действительными и не
возникнет ошибка lndexError (более технически: он должен иметь метод _iter_, который воз­
вращает _iterator_ или который определяет метод _getitem_, который делает то, о чем го­
ворилось ранее).
Пр имер
Здесь мы указываем, что Ьаг является нулевым элементом 1 . Бессмысленность:
foo = 1
Ьаг = foo[0]

Или другой вариант: for пытается для х установить amount[0], первый элемент итерируе­
мого объекта, но не может, так как amount - это целое число:
amount = 1 0
for х in amount: print(x)

ТypeError: '??' object is not callaЬle

Вы определили переменную и вызываете ее позже (как это делается с функцией или
методом).
Пр имер
foo = "notAFunction"
foo()

90.4. Синтаксическая ошибка в хорошем коде

391

90.4. Синтаксическая ошибка в хорошем коде
В подавляющем большинстве случаев ошибка SyntaxError, указывающая на строку, озна­
чает, что проблема есть в строке перед ней (в данном примере это пропущенная скобка):
def my_print():
х = (1 + 1
print(x)
Возвращает
File "", line 3
print(x)
л

SyntaxError: invalid syntax
Как видно из примера, наиболее распространенной причиной возникновения этой про­
блемы является несовпадение скобок или круглых скобок. В Python 3 есть одна существен­
ная оговорка для операторов печати:

Python 3.х Version � 3.0:
»> print "hello world"
File "", line 1
print "hello world"
л

Так как оператор print был заменен функцией print(), то:
print("hello world") # Обратите вниман ие, что это работает и в Ру2, и в РуЗ

90.5. Ошибки отступа (или ошибки синтаксиса отступов)
В большинстве других языков отступы не обязательны, но в Python (и других языках: ран­
ние версии FORTRAN, Makefiles, Whitespace (эзотерический язык) и т. д.) это не так, что может
сбить с толку, если вы привыкли к другому языку, если вы копировали код из примера в свой
собственный, или просто если вы новичок.

IndentationError/SyntaxError: unexpected indent (неожиданный отступ)

Это исключение возникает, когда уровень отступа увеличивается без причины.
В примере ниже нет причин для повышения уровня отступа:

Версия Python 2.х � 2.0 :,:; 2. 7:
print "This line is ok"
print 'This line isn't ok"

Версия Python 3.х � 3.0:
print('This line is ok")
print("This line isn't ok")
Здесь две ошибки: описанная выше и то, что отступ не соответствует ни одному уровню
отступа. Однако показана только одна:

Версия Python 2.х � 2.0 :,:; 2. 7:
print 'This line is ok"
print 'This line isn't ok"

Версия Python 3.х � 3.0:
print('This line is ok")
print("This line isn't ok")

392

Глава 91 . urllib

IndentationError/SyntaxError: unindent does not match any outer indentation level (от­
мена отступа не соответствует ни одному внешнему уровню отступа)
В этом случае чаще всего недостаточно уменьшен уровень отступа. Пример:

Версия Python 2.х ;:,: 2.0 � 2. 7:
def foo():
print 'This should Ье part of foo()"
print "ERROR!"
print "This is not а part of foo()"

Версия Python З.х ;:,: 3.0:
print("This line is ok")
print('This line isn't ok")

IndentationError: expected an indented Ыосk (ожидается блок с отступами)

После двоеточия (и затем новой строки) уровень отступа должен увеличиваться. Эта
ошибка возникает, если этого не произошло. Пример:
if ok:
doStuff()

Примечание: используйте ключевое слово pass (которое ничего не делает), чтобы про­
сто поставить if, else, except, класс, метод или определение, но не говорить, что произойдет,
если вызов/условие будет истинным (а сделать это позже, или в случае except: просто ниче­
го не делать):
def foo():
pass

IndentationError: inconsistent use of tabs and spaces in indentation (непоследователь­
ное использование табуляции и пробелов в отступах)
def foo():
if ok:
return "Two != Four != ТаЬ"
return "Мне все равно, я делаю все, что хочу"
Чтобы избежать этой ошибки, не используйте табуляции. Это не рекомендуется РЕР8, руководством по стилю для Python.
1. Установите в редакторе 4 пробела для отступа.
2. Выполните поиск и замену, чтобы заменить все табуляции на 4 пробела.
3. Убедитесь, что ваш редактор настроен на отображение табуляции в виде 8 пробелов,
чтобы вы могли легко понять эту ошибку и исправить ее.

Глава 9 1 . urllib
9 1 . 1 . НТТР GET

Версия Python 2.х � 2. 7:
Python 2

import urllib
response = urllib.urlopen('http://stackoverflow.com/documentation/')
Использование urllib.urlopen() вернет объект ответа, с которым можно работать как с файлом.

393

91 .2. НТТР POST
print response.code
# В ы водит: 200
response.code

NotFound и т. д.

представляет собой возвращаемое значение http, где 200 - ОК, 404 -

print response. read()
'< !DOCTYPE html>\r\n\r\n\r\n\r\nDocumentation - Stack. etc'

Методы response. read() и response.readlines() могут быть использованы для чтения соб­
ственно html-фaйлa, возвращаемого из запроса. Эти методы работают аналогично f1le. read*.
Версия Python 3.х � 3.0:
Python З
import urllib.request
print(urllib.request.urlopen("http://stackoverflow.com/documentation/"))
# В ы водит:
response = urllib.request.urlopen("http://stackoverflow.com/documentation/")
print(response.code)
# В ы водит: 200
print(response.read())
# В ы водит: Ь'\r\n\r\n\r\n\r\nDocumentation - Stack Overflow

Модуль бьm обновлен для Python З.х, но варианты использования остались в основном
теми же. urllib.request.urlopen вернет аналогичный, подобный файлу объект.

9 1 .2. НТТР POST
Для РОSТ-данных передайте закодированные аргументы запроса в качестве данных
в функцию urlopen().

Версия Python 2.х � 2. 7:
Python 2
import urllib
query_parms = {'username':'stackoverflow', 'password':'me.me'}
encoded_parms = urllib.urlencode(query_parms)
response = urllib.urlopen("https://stackoverflow.com/users/login", encoded_parms)
response.code
# В ы вод: 200
response.read()
# В ы вод: '< !DOCTYPE html>\r\n\r\n\r\n\r\nLog ln - Stack Overflow'

Версия Python 3.х � 3.0:
Python З
import urllib
query_parms = {'username':'stackoverflow', 'password':'me.me'}
encoded_parms = urllib. parse.urlencode(query_parms).encode('utf-8')
response = urllib.request.urlopen("https://stackoverflow.com/users/login", encoded_parms)
response.code
# В ы вод: 200
response. read()
# В ы вод: b'\r\n . . . . etc'

394

Глава 92. Веб-скрапинг с помощью Python

91 .3. Декодирование полученных байтов в соответствии
с кодировкой типа содержимого

Полученные байты должны быть декодированы с помощью правильной кодировки сим­
волов, чтобы быть интерпретированными как текст:
Версия Python З.х :::: 3.0:
import urllib.request
response = urllib.request.urlopen("http://stackoverflow.com/")
data = response.read()
encoding = response.info().get_content_charset()
html = data.decode(encoding)

Версия Python 2.х :,; 2. 7:
import urllib2
response = urllib2.urlopen("http://stackoverflow.com/")
data = response.read()
encoding = response.info().getencoding()
html = data.decode(encoding)

Глава 92. Веб-скрапинг с помощью
Python
Веб-скрапинг (также известен как веб-скрейпинг или скрепинг) - это автоматизиро­
ванный программный процесс, с помощью которого можно постоянно извлекать ("соскре­
бать", scrape) данные с веб-страниц. Также известный как screen scraping или web harvesting,
веб-скрапинг позволяет мгновенно получать данные с любой общедоступной веб-страницы.
На некоторых сайтах веб-скрапинг может быть незаконным.

92. 1 . Скрапинг с использованием фреймворка Scrapy

Сначала необходимо создать новый проект Scrapy. Укажите каталог, в котором будет хра­
ниться ваш код, и запустите его:
scrapy startproject projectName

Для того чтобы выполнить скрапинг, нам нужен "паук" (spider). Пауки определяют, ка­
ким образом будет осуществляться скрапинг с определенного сайта. Вот код паука, кото­
рый переходит по ссьшкам на вопросы, получившие наибольшее количество голосов на
StackOverflow, и считывает некоторые данные с каждой страницы:
import scrapy
class StackOverflowSpider(scrapy.Spider):
name = 'stackoverflow' # кажды й паук имеет уникальное имя
start_urls = ['http://stackoverflow.com/questions?sort=votes']
# парсинг начинается с определенного набора url
def parse(self, response):
# для каждого запроса, выдаваемого этим генератором, его ответ отправляется в parse_question
for href in response.css('.question-summary hЗ a::attr(href)'):
# выполняем скрапинг с использованием селекторов css для поиска url вопросов

92.2. Скрапинг с использованием Selenium WebDriver

395

ful l_url = response.urljoin(href.extract())
yield scrapy. Request(ful l_url, callback=self.parse_question)
def parse_question(self, response):
yield {
'title': response.css('h1 a::text').extract_f1rst(),
'votes': response.css('.question .vote-count-post::text').extract_f1 rst(),
'body': response.css('.question .post-text').extract_f1rst(),
'tags': response.css('.question .post-tag::text').extract(),
'link': response.url,

Сохраните классы-пауки в каталоге projectName\spiders. В данном случае - projectName\

spiders\stackoverflow_spider.py.

Теперь вы можете использовать своего паука. Например, попробуйте выполнить (вката­
логе проекта):
scrapy crawl stackoverflow

92.2. Скрапинг с использованием Selenium WebDriver

Некоторые веб-сайты "не любят" скрапинга. В таких случаях может потребоваться ими­
тация работы реального пользователя с браузером. Selenium запускает и управляет веб-бра­
узером.
from selenium import webdriver
browser = webdriver. Firefox() # запуск браузера Firefox
browser.get('http://stackoverflow.com/questions?sort=votes') # загрузка url
title = browser.f1nd_element_by_css_selector('h1 ').text # заголовок страницы (первый элемент h 1 )
questions = browser.f1nd_elements_by_css_selector('.question-summary') # список вопросов
for question in questions: # итерация по вопросам
question_title = question.find_element_by_css_selector('.summary hЗ a').text
question_excerpt = question.f1nd_element_by_css_selector('.summary .excerpt').text
question_vote = question.find_element_by_css_selector('.stats .vote .votes .vote-count-post').text
print "%s\n%s\n%s votes\n----------\n" % (question_title, question_excerpt, question_vote)

Но Selenium способен на гораздо большее. Он может изменять cookies браузера, запол­
нять формы, имитировать щелчки мыши, делать скриншоты веб-страниц и запускать поль­
зовательский JavaScript.

92.З. Базовый пример использования запросов и lxml
для скрапинга некоторых данных
# Для совместимости с Python 2.
from _future_ import print_function
import lxml.html
import requests
def main():
г = requests.get("https://httpЬin.org")
html_source = r.text
root_element = lxml.html.fromstring(html_source)

396

Глава 92. Веб-скрапинг с помощью Python

# Обратите внимание, что root_element.xpath() дает *список* результатов.
# XPath задает путь к нужному нам элементу.
page_title = rooLelement.xpath('/html/head/title/text()')[0]
print(page_title)
if _name_ == '_main_':
main()

92.4. Поддержание сессии веб-скрапинга с помощью запросов
Для сохранения cookies и других параметров целесообразно поддерживать сессию
веб-скрапинга. Кроме того, это может привести к повышению производительности, посколь­
ку requests.Session повторно использует базовое ТСР-соединение с хостом:
import requests
with requests.Session() as session:
# для всех запросов сессии теперь устанавливается заголовок User-Agent
session.headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; lntel Мае OS Х 1 О_ 1 1 -4) AppleWebКit/537.36 (KHTM L, like Gecko) Chrome/51 .0.2704.1 03 Safari/537.36'}
# установить файлы cookie
session.get('http://httpbin.org/cookies/set?key=value')
# получить файлы cookie
response = session.get('http://httpbln.org/cookies')
print(response.text)

92.5. Скрапинг с использованием Beautifu1Soup4
from bs4 import BeautifulSoup
import requests
# Использование модуля requests для получения страницы
res = requests.get('https://www.codechef.com/proЫems/easy')
# Создать объект BeautifulSoup
page = BeautifulSoup(res.text, 'lxml') # текстовое поле содержит исходник страницы
# Теперь используйте СSS-селектор, чтобы получить таблицу, содержащую список проблем
datataЬle_tags = page.select('taЫe.dataTaЬle') # П роблемы в тэге ,
# с классом dataTaЬle
# Извлекаем из списка первый тег, поскол ьку именно он нам нужен
datataЫe = datataЬle_tags[0]
# Теперь, поскольку нам нужны имена проблем, они содержатся в тегах , которые
# непосредствен но вложены в теги
prob_tags = datataЫe.select('a > Ь')
prob_names = [tag.getТext().strip() for tag in prob_tags]
print prob_names

92.б. Простое скачивание веб-контента с помощью urllib.request
Для скачивания веб-контента можно использовать модуль стандартной библиотеки
urllib.request:
from urllib.request import urlopen
response = urlopen('http://stackoverflow.com/questions?sort=votes')
data = response.read()

92.7. Модификация пользовательского агента Scrapy

397

# Полученные байты обычно должны быть декодированы в соответствии
# с набором символов response
encoding = response.info().get_content_charset()
htm l = data.decode(encoding)

Аналогичный модуль имеется и в Python 2.

92. 7. Модификация пользовательского агента Scrapy

Иногда пользовательский агент по умолчаншо Scrapy ("Scrapy/VERSION (+http://scrapy.org)")
блокируется хостом. Чтобы изменить пользовательский агент по умолчанию, откройте файл
settings.py, откомментируйте и измените следующую строку на нужную вам.
#USER_AG ENT = 'projectName (+http://www.yourdomain.com)'

Например:

USER_AG ENT = 'Mozil l a/5.0 (Maeintosh; lntel Мае OS Х 1 О_ 1 1 _4) AppleWebКit/537.36 (KHTM L, like
Geeko) Chrome/51 .0.2704.1 03 Safari/537.36'

92.8. Скрапинг с использованием инструмента
командной строки cURL
Импортируйте:

from subproeess import Рореп, PIPE
from lxml import etree
from io import String lO

Скачивание:
user_agent = 'Mozi lla/5.0 (Maeintosh; lntel Мае OS Х 1 О_ 1 1 _6) AppleWebКit/537.36 (KHTM L, like
Geeko) Chrome/55.0.2883.95 Safari/537.36'
url = 'http://staekoverflow.eom'
get = Popen(['eurl', '-s', '-/( user_agent, url] , stdout=PI PE)
result = get.stdout.read().deeode('utf8')

-s: тихая загрузка
-А: флаг пользовательского агента

Парсинr:
tгее = etree.parse(String lO(result), etree.HTM LParser())
divs = tree.xpath('//div')

Глава 93. Парсинг HTM L
93. 1 . Использование СSS-селекторов в библиотеке BeautifulSoup

Библиотека BeautifulSoup имеет ограниченную поддержку СSS-селекторов, но охватыва­
ет наиболее часто используемые из них. Используйте метод SELECT() для выбора нескольких
элементов и seleet_one() для выбора одного элемента.
Пример:
from bs4 import Beautifu lSoup
data = """

item1

398

Глава 93. Парси нг HTM L

item2
< li class="item">itemЗ

soup = BeautifulSoup(data, "html. parser")
for item in soup.select("l i. item"):
print(item.get_textO)
Выводит на экран:

iteml
item2
itemЗ

93.2. Библиотека PyQuery
PyQuery - это jquеrу-подобная библиотека для Python. В ней очень хорошо реализована
поддержка селекторов CSS.
from pyquery import PyQuery
html = "'"'
Sales


Lorem
46


lpsum
1 2


Dolor
27


Sit
90


doc = PyQuery(html)
title = doc('h 1 ').text()
print title
taЬle_data =



rows = doc('#taЬle > tr')
for row in rows:
name = PyQuery(row).f1nd('td').eq(0).text()
value = PyQuery(row).find('td').eq(1 ).text()
print "%s\t %s" % (name, value)

93.3. Нахождение текста после элемента в библиотеке BeautifulSoup

399

93.З. Нахождение текста после элемента в библиотеке
BeautifulSoup
Представьте, что у вас есть следующий HTML:

Name:
John Smith

Необходимо расположить текст "John Smith" после элемента label. В этом случае можно
найти элемент label по тексту, а затем использовать свойство .next_siЫing:
from bs4 import Beautifu lSoup

data

= '""'


Name:
John Smith

soup = BeautifulSoup(data, "html . parser")
label = soup.f1nd("label", text="Name:")
print(label.next_sibl ing.strip())
В результате на экран будет выведено: John Smith.

Глава 9 4 . Работа с XML
94. 1 . Открытие и чтение с помощью библиотеки ElementTree
Импортируйте объект ElementТree, откройте соответствующий файл .xml и получите корневой тег:
import xml.etree.ElementТree as ЕТ
tree = ET.parse("yourXM Lf1le.xml")
root = tree.getroot()
Существует несколько способов поиска по дереву. Первый - итерационный:
for child in root:
print(child.tag, child.attrib)
или можно ссылаться на конкретные места, как на список:
print(root[0][1].text)
Для поиска конкретных тегов по имени используйте команду .find или .findall:
print(root.findall("myTag"))
print(root[0].f1nd("myOtherTag"))

94.2. Создание и построение ХМL-документов
Импортируйте модуль Element Tree:
import xml.etree.ElementТree as ЕТ
Функция Element() используется для создания элементов XML:
р=ЕТ. Element('parent')

400

Глава 94. Работа с XML

Функция SuЬElement() используется для создания подэлементов заданного элемента:
с

=

EТ.SubElement(p, 'child1 ')

Функция dump() используется для сброса элементов xml.

ET.dump(p)
# Выходные данные будут в ы глядеть следующим образом:
#

Если необходимо сохранить данные в файл, создайте хml-дерево с помощью функции
а для сохранения в файл используйте метод write().

ElementТree(),

tree = ET. ElementТree(p)
tгее. write("output.xm 1"}

Функция Comment() используется для вставки комментариев в хml-файл.
comment = ET.Comment('user comment')
p.append(comment) # этот ком ментарий будет добавлен к родительскому элементу

94.3. Изменение ХМL-файла

Импортируйте модуль Element Tree и откройте файл xml, получите элемент xml:

import xml.etree. ElementТree as ЕТ
tгее = ET.parse('sample.xml')
root =tree.getroot()
element = root[O] # получение первого дочернего элемента корневого элемента

Объектом элемента можно манипулировать, изменяя его поля, добавляя и изменяя атри­
буты, добавляя и удаляя дочерние элементы:
element.set('attribute_name', 'attribute_value') # установка атрибута для хml-элемента
element.text = "string_text"

Если необходимо удалить элемент, используйте метод Element. remove():
root. remove(element)

Метод ElementТree.write() используется для вывода хml-объекта в файл xml.

tгее. write('output.xml'}

94.4. Поиск в XML с помощью XPath

Начиная с версии 2.7 в ElementТree улучшена поддержка ХРаth-запросов. XPath - это син­
таксис, позволяющий осуществлять навигацию по xml подобно тому, как язык SQL использу­
ется для поиска в базе данных. Функции fi nd и fi ndall поддерживают XPath. В данном примере
будет использован приведенный ниже xml:

< Books>
< Book id = "1 " price ="7. 95">
Do Androids Dream of Electric Sheep?
Philip К. Dick

< Book id = "S" price ="S.95">
The Colour of Magic
Теггу Pratchett

< Book id = "7" ргiсе ="б.95">
The Еуе of The World
Robert Jordan


94.5. Открытие и чтение бол ьших ХМ L-файлов с помощью инкрементного парсинга

401




Поиск по всем книгам:
import xml.etree.cElementТree as ЕТ
tree = ET.parse('sample.xml')
tree.f1ndal l('Books/Book')

Поиск книги с названием = 'Тhе Colour of Magic':
tree.f1nd("Books/Book[Тitle = 'Тhe Colour of Magic']")
# всегда используйте ' ' в правой части сравнения

Поиск книги с id = 5:
tree.f1nd ("Books/Book[@id = ' 5']")
# поиск с хml-атрибутами должен иметь '@' перед именем

Поиск второй книги:
tree.f1nd ("Books/Book[2]")
# индексы начинаются с 1 , а не с О

Поиск последней книги:
tree.f1nd ("Books/Book[I ast()]")
# 'last' - единственная храth-функция, допустимая в ElementТree

Поиск всех авторов:
tree.f1ndall(".//Author")
# поиск с // должен использовать относительный путь

94.5. Открытие и чтение больших ХМL-файлов с помощью
инкрементного парсинга

Иногда мы не хотим загружать весь файл XML, чтобы получить необходимую информа­
цию. В таких случаях полезно иметь возможность постепенно загружать нужные разделы, а
затем удалять их по завершении работы. С помощью инкрементного парсинга можно редак­
тировать дерево элементов, которое сохраняется при разборе XML.
Импортируйте объект ElementТree:
import xml.etree.ElementТree as ЕТ

Откройте файл .xml и выполните итерацию по всем элементам:

for event, elem in ET.iterparse("yourXMLf1le.xml"):
... do something ...

В качестве альтернативы можно искать только специфические события, такие как на­
чальные/конечные теги или пространства имен. Если эта опция опущена (как указано
выше), то возвращаются только "конечные" события:
events= ("start", "епd", "start-ns", "end-ns")
for event, elem in EТ.iterparse("yourXMLf1le.xml", events = events):
... do something ...

Вот полный пример, показывающий, как очистить элементы из дерева памяти, когда мы
с ними закончили работу:
for event, elem in EТ.iterparse("yourXM Lf1 le.xml", events = ("start","end")):
if elem.tag = = "record_tag" and event = = "епd":
print elem.text
elem.clear()
... do something else ...

402

Глава 95. Python Requests Post

Гла ва 9 5 . Python Requests Post

Рассмотрим использование модуля Python Requests в контексте метода НТТР POST и соот­
ветствующей ему функции Requests.

95. 1 . Простая операция Post
from requests import post

foo = post('http://httpbln.org/post', data = {'key':'value'})

Выполняет простую операцию НТТР POST. Передаваемые данные могут быть самых раз­
ных форматов, однако наиболее распространены пары "ключ-значение".
Заголовки
Можно просматривать заголовки:
print(foo.headers)

Пример ответа:

{'Content-Length': '439', 'X-Processed-Тime': 'О.000802993774414', 'X-Powered-By': 'Flask',
'Server': 'meinheld/0.6.1', 'Connection': 'keep-alive', 'Via': '1.1 vegu r', 'Access-Control-Allow­
Credentials': 'true', 'Date': 'Sun, 21 Мау 2017 20:56:05 G MT, 'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json'}

Заголовки также могут быть подготовлены перед отправкой:
headers = {'Cache-Control':'max-age=0',
'Upgrade-l nsecure-Requests':'1',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; х64) AppleWebКit/537.36 (KHTML, like
Gecko) Chrome/54.0.2840.99 Safari/537.36',
'Content-Type':'application/x-www-form-urlencoded',
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Referer':'https://www.groupon.com/signup',
'Accept-Encoding':'gzip, deflate, Ьг',
'Accept-Language':'es-ES,es;q=0.8'
foo = post('http://httpbln.org/post', headers=headers, data = {'key':'value'})

Кодирование
Аналогичным образом можно установить и просмотреть кодировку:
pri nt(foo.encod ing)
'utf-8'
foo.encoding = 'ISO-8859-1'

В ерификация SSL
По умолчанию запросы проверяют SSL-сертификаты доменов. Эта возможность может
быть переопределена:
foo = post('http://httpbln.org/post', data = {'key':'value'}, verify=False)

Перенаправление
Будет выполняться любое перенаправление (например, с http на https), что также может
быть изменено:
foo = post('http://httpbln.org/post', data = {'key':'value'}, allow_redirects=False)

Если операция post бьmа перенаправлена, то к этому значению можно получить доступ:

print(foo.url)

Можно просмотреть полную историю перенаправлений:

print(foo.history)

9 5.2. Данные, з акодированные в фо р ме

403

95.2. Данные, закодированные в форме
from requests import post
payload = {'key1 ' : 'value1 ',
'key2' : 'value2'
foo

=

post('http://httpbln.org/post', data = payload)

Для передачи закодированных в форме данных с помощью операции post эти данные
должны быть структурированы в виде словаря и предоставлены в качестве параметра.
Если данные не кодируются в форме, просто передайте строку или целое число в пара­
метр data. Для того чтобы Python Requests автоматически отформатировал данные, в пара­
метр json следует передать словарь:
from requests import post
payload = {'key1 ' : 'value1 ', 'key2' : 'value2'}
foo = post('http://httpbln.org/post', json= payload)

95.З. Загрузка файлов
В модуле Requests необходимо предоставить только дескриптор файла, в отличие от содержимого, получаемого с помощью функции . геаd():
from requests import post
fi les = {'f1 le' : open('data.txt', 'гЬ')}
foo = post('http://http.org/post', f1les =files)
Также могут быть заданы имя файла, тип содержимого и заголовки:
fi les = {'f1le': ('report.xls', open('report.xls', 'гЬ'), 'appl ication/vnd. ms-excel', {'Expires': 'О'})}
foo = requests.post('http://httpbln.org/post', f1 les = fi les)
Строки также могут быть отправлены в виде файла, если они указаны в качестве пара­
метра f1 les.

Несколько файлов

Несколько файлов могут поставляться почти так же, как и один:

multiple_f1 les = [
('images', ('foo. png', open('foo.png', 'гЬ'), 'image/png')),
('images', ('Ьаг.рпg', open('bar. png', 'гЬ'), 'image/png'))]
foo

=

post('http://httpbln.org/post', f1les = multiple_f1les)

95.4. Ответы
Коды ответов могут быть просмотрены из роst-операции:
from requests import post
foo = post('http://httpbln.org/post', data = {'data' : 'value'})
print(foo.status_code)

Возвращенные данные

Доступ к возвращаемым данным:
foo = post('http://httpbln.org/post', data = {'data' : 'value'})
print(foo.text)

404

Глава 95. Python Requests Post

Сырые ответы

В тех случаях, когда необходимо получить доступ
HTTPResponse, это можно сделать следующим образом:

к базовому объекту urllibЗ response.

foo = post('http://httpbln.org/post', data = {'data' : 'value'})
res = foo.raw
print(res.read{))

95.5. Аутентификация

Простая НТТР- аутентификация

Может быть реализована следующим образом:

from requests import post
foo = post('http://natasO.natas.labs.overthewire.org', auth = ('natasO', 'natasO'))

Технически это означает следующее:
from requests import post
from requests.auth import HTTPBasicAuth
foo = post('http://natasO.natas.labs.overthewire.org', auth = HTTPBasicAuth('natasO', 'natasO'))

Аутентификация по протоколу НТТР Digest

Такая аутентификация осуществляется очень похожим образом, но в запросах для этого
предусмотрен другой объект:
from requests import post
from requests.auth import HTTPDigestAuth
foo = post('http://natasO.natas.labs.overthewire.org', auth = HTTPDigestAuth('natasO', 'natasO'))

Пользовательская аутентификация

В некоторых случаях встроенных механизмов аутентификации может быть недоста­
точно. К примеру, сервер соглашается принять аутентификацию, если отправитель имеет
правильную строку user-agent, определенное значение заголовка и предоставляет правиль­
ные учетные данные через НТТР Basic Authentication. Для этого необходимо подготовить соб­
ственный класс аутентификации, подклассифицирующий AuthBase, который является базо­
вым для реализаций аутентификации запросов:
Это можно использовать с помощью следующего кода:
from requests.auth import AuthBase
from requests.auth import _basic_auth_str
from requests._internal_utils import to_native_string
class CustomAuth(AuthBase):
def _init_(self, secreLheader, user_agent , username, password):
# устанавливаем здесь любые данные, связанные с авторизацией
self.secret_header = secret_header
self.user_agent = user_agent
self.username = username
self.password = password
def _cal l_(self, r):
# модифицировать и вернуть запрос
r. headers['X-Secret'] = self.secret_header
r. headers['User-Agent'] = self.user_agent
r. headers['Authorization'] = _basic_auth_str(self.username, self.password)
return r

Это можно использовать с помощью следующего кода:
foo = get('http://test.com/admin', auth = CustomAuth('SecretHeader', 'CustomUserAgent', 'user',
'password' ))

95.6. Прокси-серверы

405

95.6. Прокси-серверы
Каждая операция РОSТ-запроса может быть настроена на использование прокси-серверов.

Прокси-серверы HTTP/S
from requests import post
proxies = {
'http': 'http:/11 92.1 68.0.1 28:31 28',
'https': 'http://1 92.1 68.0.1 27: 1 080',
}
foo = requests.post('http://httpbln.org/post', proxies=proxies)
Таким образом может быть обеспечена базовая аутентификация НТТР (НТТР Basic
Authentication):
proxies = {'http': 'http://user:pass@1 92.1 68.0.1 28:31 2'}
foo = requests.post('http://httpbln.org/post', proxies=proxies)

Прокси-серверы SOCKS

Для использования прокси-серверов SOCKS требуются сторонние зависимости
requests[socks], после установки которых такие прокси-серверы используются аналогично
HTTPBasicAuth:
proxies = {
'http': 'socks5://user:pass@host:port',
'https': 'socks5://user:pass@host:port'
foo = requests.post('http://httpbln.org/post', proxies=proxies)

Глава 96. Распространение кода
96. 1 . ру2арр
Для использования фреймворка ру2арр необходимо сначала установить его. Для этого от­
кройте терминал и введите следующую команду:
sudo easy_install -U ру2арр
Вы также можете установить пакеты с помощью pip:
pip install ру2арр
Затем создайте установочный файл для вашего Руthоn-скрипта:
py2applet -make-setup MyApplication.py
Отредактируйте параметры установочного файла по своему усмотрению, по умолчанию
используется этот вариант:
This is а setup.py script generated Ьу py2applet
Usage:
python setup.py ру2арр
from setuptools import setup

406

Глава 96. Распространение кода

АРР = ['test.py']
DATA_FI LES =
OPTIONS = {'argv_emulation': True}



setup(
арр=АРР,
data_fi les= DATA_FILES,
options={'py2app': OPTI ONS},
setup_requires=['py2app'],
Чтобы добавить пиктограмму (иконку), которая должна иметь расширение .icns, или
включить изображения в приложение в качестве ссылок, измените параметры, как показа­
но на рисунке:
DATA_FI LES = ['mylnserted lmage.jpg']
OPTIONS = {'argv_emulation': True, 'iconf1 le': 'myCoollcon.icns'}
Наконец, введите в терминал:
python setup.py ру2арр
Скрипт должен быть запущен, и вы обнаружите готовое приложение в папке dist. Для до­
полнительной настройки можно использовать следующие опции:
optimize (-0)

уровень отимизации: -01 для "python -О", -02 для
"python -00", и -00 для отключения [по умолчанию: -00]

includes (-i)

список модулей для включения, разделенны й запятыми

packages (-р)

список пакетов для включения, разделенны й запятыми

extension

Расширение пакета [по умолчанию:.арр для приложения, .plugin для плагина]

extra-scripts

разделенны й запятыми список дополнительных скриптов для включения
в приложение или плагин.

96.2. cx_Freeze
Установите cx_Freeze, распакуйте папку и запустите из нее эти команды:
python setup.py build
sudo python setup.py instal l
Создайте новый каталог для вашего Руthоn-скрипта и создайте в этом ж е каталоге файл
setup.py со следующим содержанием:
application_title = "Му Application" # Используйте собственное имя приложения
main_python_f1 le = "my_script.py" # Ваш руthоn-скрипт
import sys
from cx_Freeze import setup, ExecutaЫe
base = None
if sys.platform == "win32":
base = "Win32GUI"
includes = ["atexit";'re"]
setup(
name = application_title,
version = "0.1 ",

97. 1 . Испол ьзование декоратора @property для свойств чтения и записи

407

description = 'Your Description",
options = {"build_exe" : {"includes" : includes }},
executaЫes = [ExecutaЫe(main_python_fi le, base = base)])
Теперь запустите файл setup.py из терминала:
python setup.py bdist_mac
Примечание: на ОС El Capitan эту операцию необходимо выполнить от имени root с от­
ключенным режимом SIP.

Глава 97. О бъекты свойств
97 . 1 . Использование декоратора @property для свойств
чтения и записи
Если вы хотите использовать @property для реализации пользовательского поведения
при установке и получении, используйте этот паттерн:
class Cash(object):
def _iniL(self, value):
self.value = value
@property
def formatted(self):
return '${:.2f}'.format(self.value)
@formatted.setter
def formatted(self, new):
self.value = float(new[1 :])
Пример:
wallet = Cash(2.50)
print(wallet.formatted)
$2.50
»> print(wallet.value)
2.5
»> wallet.formatted = '$1 23.45'
»> print(wallet.formatted)
$1 23.45
»> print(wallet.value)
1 23.45
»>
»>

97 .2. Еще об использовании декоратора @property
Декоратор @property может быть использован для определения методов в классе, кото­
рые действуют как атрибуты. Например, это может быть полезно при раскрытии информа­
ции, которая может потребовать первоначального (ресурсоемкоrо) поиска и простого извле­
чения впоследствии. Дан некоторый модуль foobar.py:
class Foo(object):
def _iniL(self):
self._bar = None
@property
def bar(self):
if self._bar is None:
self._bar = some_expensive_lookup_operation()
return self._bar

408

Глава 97. Объекты свойств

Затем
»>
»>

42
»>
42

from foobar import Foo
foo = Foo()
print(foo.bar) # Это зай мет некоторое время, так как после инициализации Ьаг равен None
print(foo.bar) # Это гораздо быстрее, так как Ьаг теперь имеет значение

97 .3. Переопределение только функций getter, setter или deleter
объекта свойства

При наследовании от класса со свойством можно предоставить новую реализацию одной
или нескольких функций получения, установки или удаления (getter, setter, deleter) свой­
ства, обратившись к объекту свойства в родительском классе:
class BaseClass(object):
@property
def foo(self):
return some_calculated_value()
@foo.setter
def foo(self, value):
do_something_with_value(value)
class DerivedClass(BaseClass):
@BaseClass. foo.setter
def foo(self, value):
do_something_different_with_value(value)

Также можно добавить функции setter или deleter туда, где их раньше не было.

97 .4. Использование свойств без декораторов

Использование синтаксиса декораторов (с символом @) удобно, но в то же время несколь­
ко скрытно. Свойства можно использовать напрямую, без декораторов. Это показано в следу­
ющем примере Python З.х:
class А:
р = 1 234
def getX (self):
return self._x
def setX (self, value):
self._x = value
def getY (self):
return self._y
def setY (self, value):
self._y = 1 ООО + value # Странно, но возможно
def getY2 (self):
return self._y
def setY2 (self, value):
self._y = value
def getТ (self):
return self._t

97.4. Испол ьзование свойств без декораторов

def setТ (self, value):
self._t = value
def getU (self):
return self._u + 10000
def setU (self, value):
self._u = val ue - 5000
х, у, у2 = property (getX, setX), property (getY, setY), property (getY2, setY2)
t = property (getТ, setТ)
u = property (getU, setU)
A.q = 5678
class В:
def getZ (self):
return self.z_
def setZ (self, value):
self.z_ = value
z = property (getZ, setZ)
class С:
def _iniL (self):
self.offset = 1234
def getW (self):
return self.w_ + self.offset
def setW (self, value):
self.w_ = value - self.offset
w = property (getW, setW)
а1 = А ()
а2 = А ()
а1.у2 = 1000
а2.у2 = 2000
а1.х = 5
а1.у = 6
а2.х = 7
а2.у = 8
a1.t = 77
a1.u = 88
print (а1.х, а1.у, а1.у2)
print (а2.х, а2.у, а2.у2)
print (а1.р, а2.р, а1.q, a2.q)
print (а1.t, а1.u)

ь=во

с

=

со

409

41 0

Глава 98. Перегрузка

b.z = 1 001 00
c.z = 200200
c.w = 300300
print {а1 .х, b.z, c.z, c.w)
c.w = 400400
c.z = 500500
b.z = 600600
print {а1 .х, b.z, c.z, c.w)

Глава 98. Перегрузка
98. 1 . Перегрузка операторов

Ниже перечислены операторы, которые могут быть перегружены в классах, а также не­
обходимые определения методов и пример использования оператора в выражении.
N.B. Использование other в качестве имени переменной не является обязательным, но
считается нормой.
Оператор

+ Сложение
- Вычитание
* Умножение
@ Умножение матриц
/ Деление
/ Деление
// Целочисленное деление
% Остаток от деления
** Возведение в степень
« Побитовый сдвиг влево
» Побитовый сдвиг вправо
& Побитовое И
л Побитовое XOR
1 (побитовое ИЛИ)
- Отрицание (арифметическое)
+ Положительное
~ Побитовое Н Е
< Менее
Больше
>= Больше или равно
[index] оператор индекса
in оператор "in"
{*args, ... ) вызов

Метод

_add_(self, other)
_sub_(self, other)
_mul_(self, other)
_matmul_(self, other)
_div_(self, other)
_truediv_(self, other)
_floordiv_(self, other)
_mod_(self, other)
_pow_(self, other[, modulo])
_lshift_(self, other)
_rshift_(self, other)
_and_(self, other)
_xor_(self, other)
_or_(self, other)
_neg_(self)
_pos_(self)
_invert_(self)
_lt_(self, other)
_le_(self, other)
_eq_(self, other)
_ne_(self, other)
_gt_(self, other)
_ge_(self, other)
_getitem (self, index)
_contains (self, other)
_cal l_(self, *args, **kwargs)

Выражение

а1 + а2
а1 - а2
а1 * а2
а1 @ а2 {Python 3.5)
а1 / а2 (только Python 2)
а1 / а2 {Python 3)
а1 // а2
а1 % а2
а1 ** а2
а1 « а2
а1 » а2
а1 & а2
а1 л а2
а1 1 а2
-а1
+а1
~а1
а1 < а2
а1 а2
а1 >= а2
а1 [index]
а2 in а1
а1 {*args, **kwargs)

41 1

98. 1 . Перегрузка операторов

Необязательный параметр modu lo для _pow_ используется только встроенной функци­
ей pow.
Каждый из методов, соответствующих бинарному оператору, имеет соответствующий
"правый", "right" метод, начинающийся с _r, например _radd_:
class A:
def _iniL(self, а):
self.a = а
def _add_(self, other):
return self.a + other
def _radd_(self, other):
print("radd")
return other + self.a
А(1 ) + 2 # Вывод: 3
2 + А(1 ) # выводит radd. Вывод: 3

а также соответствующая inрlасе-версия, начинающаяся с i:
class В:
def _iniL(self, Ь):
self.b = Ь
def _iadd_(self, other):
self.b += other
print("iadd")
return self

Ь = 8(2)

Ь.Ь
# Вывод: 2
Ь += 1 # выводит iadd
Ь.Ь
# Вывод: 3

Поскольку в этих методах нет ничего особенного, многие другие части языка, части
стандартной библиотеки и даже сторонние модули сами добавляют "магические" методы,
например, методы приведения объекта к типу или проверки свойств объекта. Например,
встроенная функция str() вызывает метод _str_ объекта, если он существует. Некоторые из
этих вариантов использования перечислены ниже.
Функци я
П риведение к int

Метод
_int_(self)

int(a1 )

Абсолютная фун кция

_abs_(self)

abs(a1 )

П риведение к str

_str_(self)

str(a1 )

П риведение к Unicode

_unicode_(self)

unicode(a1 ) (только для
Python 2)

Строковое представление

_repr_(self)

герг(а1 )

Приведение к bool

_nonzero_(self)

bool(a1 )

Вы ражен и е

Форматирование строк

_formaL(self, formatstr)

"Hi {:abc}".format(a1 )

Хеширование

_hash_(self)

hash(a1 )

Длина

_len_(self)

len(a1 )

Переворачивание

_reversed_(self)

reversed(a1 )

Округление в меньшую сторону

_floor_(self)

math.floor(a 1 )

Округление в большую сторону

_ceil_(self)

math.ceil(a1 )

Существуют также специальные методы _enter_ и _exit_ для менеджеров контекста
и многие другие.

Глава 98. Перегрузка

41 2

98.2. "Магические" методы, или Duпdеr- методы
"Магические" методы (также называемые Dunder-мeтoды, Dunder - аббревиатура от
douЫe-underscore, "двойное подчеркивание") в Python служат целям, аналогичным пере­
грузке операторов в других языках. Они позволяют классу определить свое поведение при
использовании его в качестве операнда в унарных или бинарных операторных выраже­
ниях. Они также служат реализациями, вызываемыми некоторыми встроенными функ­
циями.
Рассмотрим данную реализацию двумерных векторов.
import math
class Vector(object):
# и нстанцирование
def _init_(self, х, у):
self.x = х
self.y = у
# унарное отрицание (-v)
def _neg_(self):
return Vector(-self.x, -self.y)
# сложение (v + u)
def _add_(self, other):
return Vector(self.x + other.x, self.y + other.y)
# вычитание (v - u)
def _sub_(self, other):
return self + (-other)
# равенство (v == u)
def _eq_(self, other):
return self.x == other.x and self.y = = other.y
# abs(v)
def _abs_(self):
return math. hypot(self.x, self.y)
# str(v)
def _str_(self):
return ''.format(self)
# repr(v)
def _repr_(self):
return 'Vector({O.x}, {O.y})'.format(self)
Теперь можно естественным образом использовать экземпляры класса Vector в различ­
ных выражениях.
v = Vector(1 , 4)
u = Vector(2, О)
u+v
print(u + v)
u-v
U

== V

+ V == V + U
abs(u + v)
U

# Vector(З, 4)
# "" (нея вное преобразование строки)
# Vector(1 , -4)
# False
# True
# 5.0

98.3. Типы контейнеров и последовательностей

98.З. Типы контейнеров и последовательностей

41 3

Возможна эмуляция контейнерных типов, поддерживающих доступ к значениям по
ключу или индексу.
Рассмотрим эту наивную реализацию разреженного списка, в котором для экономии па­
мяти хранятся только ненулевые элементы.
class sparselist(object):
def _iniL(self, size):
self.size = size
self.data = {}
# l [index]
def _getitem_(self, index):
if index < О:
index += self.size
if index >= self.size:
raise lndexError(index)
try:
return self.data[index]
except KeyError:
return О.О
# l [index] = value
def _setitem_(self, index, value):
self.data[index] = value
# del l [index]
def _delitem_(self, index):
if index in self.data:
del self.data[index]
# value in 1
def _contains_(self, value):
return value == О.О ог value in self.data.values()
# len(I)
def _len_(self):
return self.size
# for value in 1: . . .
def _iter_(self):
return (self[i] for i in range(self.size)) # используйте xrange для python2

Тогда мы можем использовать sparselist, подобно обычному списку.
1 = sparselist(1 О ** б)
О in 1
1 О in 1

# список с 1 млн элементов
# True
# False

1[1 2345] = 1 О
1 О in 1
1[1 2345]

# True
# 10

for v in 1:
pass # О, О, О, . . . 1 О, О, О . . . О

98.4. Вызываемые типы
class adder(object):
def _iniL(self, first):
self.f1 rst = fi rst

Глава 98. Перегрузка

41 4

# а( ... )

def _cal l_(self, second):
return self.fi rst + second

add2 = adder(2)
add2(1 ) # 3
add2(2) # 4

98.5. Обработка нереализованного поведения

Если в вашем классе не реализован специальный перегруженный оператор для указан­
ных типов аргументов, то он должен возвращать Notlmplemented (обратите внимание, что
это специальная константа, а не то же самое, что Notl mplemented Error). Это позволит Python
вернуться к попыткам использовать другие методы для выполнения операции:
Если возвращается Notl mplemented, то интерпретатор пытается выполнить отраженную
операцию для другого типа, либо другой запасной вариант, в зависимости от оператора.
Если все попытки возвращают Notl mplemented, то интерпретатор выдаст соответствующее
исключение.
Например, при заданных х + у, если x._add_(y) возвращается нереализованным, вместо
него делается попытка вычисления y._radd_(x).
class NotAddaЫe(object):
def _init_(self, value):
self.value = value
def _add_(self, other):
return Notlmplemented
class AddaЫe(NotAddaЫe):
def _add_(self, other):
return AddaЫe(self.value + other.value)
_radd_ = _add_
Поскольку речь идет об отраженном методе, следует реализовать _add_ и _radd_, что­
бы получить ожидаемое поведение во всех случаях; к счастью, поскольку в этом простом
примере они оба выполняют одно и то же действие, мы можем воспользоваться коротким
путем.
Использование:
»> х = NotAddaЫe(1 )
»> у = AddaЬle(2)
>>> х + х
Traceback (most recent call last):
File "", line 1 , in
ТуреЕггог: unsupported operand type(s) for +: 'NotAddaЫe' and 'NotAddaЫe'
»> у + у

>>> z = х + у
>>> z

>» z.value
3

99. 1 . "Утиная типизация"

41 5

Глава 99. Поли мор ф изм
99. 1 . "Утиная типизация"

Полиморфизм без наследования в виде т. н. "утиной типизации" доступен в Python бла­
годаря системе динамической типизации. Это означает, что до тех пор, пока классы содер­
жат одинаковые методы, интерпретатор Python не делает различий между ними, поскольку
единственная проверка вызовов происходит во время выполнения.
class Duck:
def quack(self):
ргiпt("Кря !")
def feathers(self):
print("Y утки белые и серые перья.")
class Person:
def quack(self):
ргint("Человек имитирует утку.")
def feathers(self):
ргint("Человек берет с земли перо и показывает его.")
def name(self):
print("John Smith")
def in_the_forest(obj):
obj.quack()
obj.feathers()
donald = Duck()
john = Регsоп()
in_the_forest(donald)
in_the_forestOohn)

На выходе получаем:
Кря!
Утка имеет белые и серые перья.
Человек имитирует утку.
Человек берет с земли перо и показывает его.

99.2. Базовый полиморфизм

Полиморфизм - это возможность выполнять действие над объектом независимо от его
типа. Обычно это реализуется путем создания базового класса и двух или более подклассов,
реализующих методы с одинаковой сигнатурой. Любая другая функция или метод, манипу­
лирующая этими объектами, может вызывать те же методы независимо от типа объекта,
с которым она работает, без необходимости предварительной проверки типа. В объектно­
ориентированной терминологии, когда класс Х расширяет класс У, то У называется суперк­
лассом или базовым классом, а Х - подклассом или производным классом.
class Shape:
Это родительский класс, которы й предназначен для наследования другими классами
def calculate_area(self):
Этот метод должен переопределяться в подклассах.
Если подкласс не реализует его, но он вызывается, то будет выдано
сообщение Notlmplemented.

41 6

Глава 99. Полиморфизм
raise Notlmplemented

class Square(Shape):
Этот класс является подклассом класса Shape и представляет собой квадрат
side_length = 2

# в данном примере длина сторон равна 2 единицам

def calculate_area(self):
Этот метод переопределяет Shape.calculate_area(). Когда у объекта типа Square
вызывается метод calculate_area(), то будет вызван именно этот метод, а не версия
родительского класса.
Он выполняет вычисления, необходимые для данной формы - квадрата,
и возвращает результат.
return self.side_length * 2
class Triangle(Shape):
Это также подкласс класса Shape, и он представляет собой треугольник
base_length = 4
height = 3
def calculate_area(self):
Этот метод также переопределяет Shape.calculate_area() и выполняет вычисление
площади треугольника, возвращая результат.
return 0.5 * self.base_length * self.height
def get_area(input_obj):
Эта функция принимает входной объект и вызывает метод calculate_area() этого объекта.
Обратите внимание, что тип объекта не указывается. Это может быть объект
Square, Triangle или Shape. """
print(input_obj.calculate_area())
# Создать по одному объекту каждого класса
shape_obj = Shape()
square_obj = Square()
triangle_obj = Triangle()
# Теперь передадим каждый объект по очереди в функцию geLarea() и посмотрим результат
get_area(shape_obj)
get_area(square_obj)
get_area(triangle_obj)

В результате получ аем:
None
4
6.0

99.2. Базовый полиморфизм

41 7

Что происходит без полиморфизма?

Без полиморфизма перед выполнением действия над объектом может потребоваться
проверка типа, чтобы определить правильный метод для вызова. Следующий "контрпри­
мер" выполняет ту же задачу, что и предыдущий код, но без использования полиморфизма,
и функции get_area() приходится выполнять больше работы.
class Square:
side_length = 2
def calculate_square_area(self):
return self.side_length ** 2
class Triangle:
base_length = 4
height = 3
def calculate_triangle_area(self):
return (0.5 * self.base_length) * self.height
def get_area(inpuLobj):
# Обратите внимание на проверки типов, которые теперь необходимы. Такие проверки типов
# могут стать очень сложны ми, что приведет
# к дублированию и сложностя м в сопровождении кода.
if type(input_obj)._name_ == "Square":
area = input_obj.calculate_square_area()
elif type(input_obj)._name_ == 'Triangle":
агеа = input_obj.calculate_triangle_area()
print(area)
# Создать по одному объекту каждого класса
square_obj = Square()
triangle_obj = Triangle()
# Теперь передадим каждый объект по очереди в функцию get_area() и посмотрим результат
geLarea(square_obj)
geLarea(triangle_obj)

В результате получаем:
4

6.0

Важное замечание

Обратите внимание, что классы, используемые в примере без полиморфизма, являются
классами "нового стиля" и неявно наследуются от класса object, если используется Python 3.
Полиморфизм будет работать как в Python 2.х, так и в З.х, но код "контрпримера" вызовет
исключение при запуске в интерпретаторе Python 2.х, поскольку type(input_obj).name вернет
"экземпляр" вместо имени класса, если они явно не наследуются от объекта, в результате
чего area никогда не будет присвоена.

41 8

Глава 1 00. Переопределение методов

Глава 1 00. Переопределение методов
1 00.1 . Переопределение базовых методов

Приведем пример базового переопределения в Python (для наглядности и совместимости
с Python 2 и 3 используется класс нового стиля и print с ()):
class Parent(object):
def introduce(self):
print("Hello!")
def prinLname(self):
print("Parent")
class Child(Parent):
def prinLname(self):
print("Child")
р = ParentQ
с = Child()
p.introduce()
p.print_name()
с. i ntrod uce()
c. print_name()
$ python basic_override.py
Hello!
Parent
Hello!
Child

Когда создается класс Child, он наследует методы класса Parent. Это означает, что все ме­
тоды, которые есть у родительского класса, будут и у дочернего класса. В примере introduce
определен для класса Child, поскольку он определен для Parent, несмотря на то, что он не был
определен явно в определении класса Child.
В данном примере переопределение происходит, когда Child определяет свой собствен­
ный метод print_name. Если бы этот метод не был объявлен, то c.print_name() вывел бы
"Parent". Однако Child переопределил родительское определение метода print_name, и теперь
при вызове c.print_name() выводится слово "Child".

Глава 1 0 1 . Пол ьзовател ьские методы
1 01 . 1 . Создание пользовательских объектов метода

Объекты пользовательских методов могут быть созданы при получении атрибута класса
(возможно, через экземпляр этого класса), если этот атрибут является объектом пользователь­
ской функции, несвязанным объектом пользовательского метода или объектом метода класса.
class A(object):
# func: Объект функции, определяем ы й пользователем
#

1 01 . 1 . Создание пользовательских объектов метода

41 9

# Обратите внимание, что func - это объект функции, когда он определен,
# и несвязанны й (unbound) объект метода, когда он извлекается.
def func(self):
pass
# classMethod: Метод класса
@classmethod
def classMethod(self):
pass
class B(object):
# unboundMeth: Объект несвязанного пол ьзовател ьского метода
#
# Parent.func здесь является несвязанны м объектом пользовательского метода,
# потому что он извлекается.
unbound Meth = A.func
а = А()
Ь = В()
print A.func
# результат: < unbound method A.func>
print a.func
# результат:
print A.classMethod
# результат: return а/Ь
(Pdb)
Часто эта команда используется в одной строке, поэтому она может быть закомментиро­
вана одним символом #.
import pdf; pdb.set_trace()
В приглашении (РdЬ) можно вводить команды. Эти команды могут быть командами от­
ладчика или Python. Для печати переменных можно использовать команду р из отладчика
или print из Python.
(Pdb) р а
1
(Pdb) print а
1
Для просмотра списка всех локальных переменных используйте
locals
Полезные команды отладчика, которые необходимо знать:
Ь 1 : установить точку останова на строке *n* или функции с именем *f*.
#ЬЗ
# Ь divide
Ь: показать все точки останова.
с: продолжить работу до следующей точки останова.
s: перешагнуть через эту строку (перейдет в функцию).
n: перешагнуть через эту строку (перепрыгнет через функцию).
r: продолжить работу до возврата текущей функции.
1: перечислить окно кода вокруг этой строки.
р : печать переменной с именем *var*.
#рх
q: выход из отладчика.
Ьt: печать трассировки стека вызовов текущего выполнения
up: переместить область видимости вверх по стеку вызовов функций к вызывающей функции
down: Переместить область видимости на один уровень вниз по стеку вызовов функций
step: Выпол нить программу до следующей строки выпол нения в программе, затем вернуть
управление обратно отладчику
next: выпол нение програм мы до следующей строки в текущей функции, затем возврат
управления обратно в отладчик

426

Глава 1 04. Чтение и запись в формате CSV

return: выполнение программы до возврата текущей функции, затем возврат управления
обратно в отладчик
continue: продолжить выполнение програм м ы до следующей точки останова (или повторного
вызова set_trace si)

Отладчик также может оценивать Python в интерактивном режиме:
-> return а/Ь
(Pdb) р а+Ь
3

(Pdb) [ str(m) for m in [а,Ь]]
['1 ', '2']
(Pdb) [ d for d in xrange(S)]
[О, 1 , 2, 3, 4]

Примечание. Если имена переменных совпадают с командами отладчика, используйте
восклицательный знак '!' перед переменной, чтобы явно сослаться на переменную, а не на
команду отладчика. Например, часто бывает так, что вы используете имя переменной 'с' для
счетчика и хотите вывести его в отладчике. Простая команда 'с' продолжит выполнение до
следующей точки останова. Вместо этого используйте команду '!с', чтобы вывести значение
переменной следующим образом:
(Pdb) !с
4

1 03.3. Удаленный отладчик

Иногда требуется отладить Python-кoд, выполняемый другим процессом, в таких случаях
на помощь приходит rpdb. Это обертка вокруг pdb, которая перенаправляет stdin и stdout в об­
работчик сокетов. По умолчанию он открывает отладчик на порту 4444.
Использование:
# В файле Python, которы й вы хотите отлаживать
import rpdb
rpdb.seLtrace()

А затем для подключения к этому процессу необходимо выполнить в терминале следу­
ющее.
# Вызов терминала для просмотра вы вода
$ пс 1 27.0.0.1 4444

и вы получите рdЬ подсказку:
> /home/usr/ook. py(3)()
-> print("Hello world!")
(Pdb)

Глава 1 04. Чтение и запись в формате CSV
1 04. 1 . Использование pandas
Запись CSV из dict или DataFrame:

import pandas as pd
d = {'а': (1 , 1 01 ), 'Ь': (2, 202), 'с': (3, 303)}
pd.DataFrame.from_dict(d, orient="index")
df.to_csv("data.csv")

427

1 04.2. Запись файла TSV
Чтение CSV как DataFrame и преобразование его в dict:
df = pd. read_csv{"data.csv")
d = df.to_dict()

1 04.2. Запись файла TSV
Python:

import csv
with open('/tmp/output.tsv', 'wt') as ouLf1le:
tsv_writer = csv.writer(out_fi le, delimiter='\t')
tsv_writer. writerow{['name', 'f1eld'])
tsv_writer.writerow{['Dijkstra', 'Computer Science'])
tsv_writer. writerow(['Shelah', 'Math'])
tsv_writer.writerow(['Aumann', 'Economic Sciences'])

Файл вывода:
$ cat /tmp/output.tsv
name
Dijkstra
Shelah
Aumann

f1eld
Computer Science
Math
Economic Sciences

Глава 1 05. Запись в формат CSV
из строки или списка
П араметр

П одробно сти

open ("/path/", "mode")

Укажите путь к вашему CSV файлу

open (path, "mode")

Укажите режим открытия файла (чтение, запись и т. д.)

csv.writer{file, delimiter)

Передайте сюда открытый CSV файл

csv.writer{f1 le, delimiter=' ')

Укажите символ или шаблон разделителя

Запись в файл .csv в большинстве случаев не отличается от записи в обычный файл и яв­
ляется достаточно простой задачей. Далее будет рассказано о наиболее простом и эффектив­
ном подходе к решению проблемы.

1 05. 1 . Пример базовой записи
import csv

#----- В этой функции мы будем писать в CSV ---------­
def csv_writer{data, path):
# Открыть СSV-файл, путь к которому мы передали.
with open(path, "wb") as csv_f1 le:
writer = csv.writer(csv_f1 le, delimiter=';)
for line in data:
writer. writerow(line)

428

Глава 1 06. Динамическое вы полнение кода с помощью функций ехес и eval

#---- О пределим здесь наш список и вызовем функцию ---------­
if _name_ == "_main_":
data = наш список, который мы хотим записать.
Разделим его так, чтобы получился список списков.
data = ["firsLname,last_name,age".split(","),
"John,Doe,22".spl it(","),
"Jane,Doe,31 ".split(","),
"Jack,Reacher,27".split(",")
]
# Путь к СSV-файлу, в который мы хотим записать данные.
path = "output.csv"
csv_writer(data, path)

1 05.2. Добавление строки в качестве новой строчки в СSV-файл
def append_to_csv(input_string):
with open("fi leName.csv", "а") as csv_f1 le:
csv_fi le.write(input_row + ''\п")

Глава 1 Об. Динамическое выполнение
кода с помощью функций ехес и eval
Аргум ент
expression

Подроб ности

object

Код высказывания в виде строки или объекта code

globals

Словарь, используемый для глобальных переменных. Если не определено
в locals, то этот словарь также используется для locals. Если опущено, то ис­
пользуются globals() вызывающей области видимости.

locals

Объект отображения (mapping), используемый для локальных переменных.
Если это значение опущено, то используется объект, переданный для гло­
бальных переменных. Если оба параметра опущены, то используются glo­
bals() и locals() вызывающей области видимости для globals и locals соответ­
ственно.

Код выражения в виде строки или объекта code

1 06. 1 . Выполнение кода, предоставленного недоверенным
пользователем, с использованием ехес, eval или ast.literal_eval

Невозможно использовать eval или ехес для безопасного вьmшrnения кода от недове­
ренного пользователя. Даже ast.literal_eval подвержен сбоям в парсере. Иногда можно защи­

титься от выполнения вредоносного кода, но это не исключает возможности сбоев в парсере
или токенизаторе.
Для оценки кода недоверенного пользователя необходимо обратиться к какому-либо сто­
роннему модулю или, возможно, написать собственный парсер и собственную виртуальную
машину на языке Python.

1 06.2. Оценка строки, содержащей литерал Python, с помощью ast.literal eval

429

1 06.2. Оценка строки, содержащей литерал Python, с помощью
ast.literal_eval

Если у вас есть строка, содержащая литералы Python, такие как strings, floats и т. д., вы мо­
жете использовать ast.literal_eval для оценки ее значения вместо eval. При этом добавляется
возможность использования только определенного синтаксиса.
»> import ast
>>> code = """ (1 , 2, {'foo': 'Ьаг'})"""
»> object = ast.literal_eval(code)
»> object
(1 , 2, {'foo': 'Ьаг'})
»> type(object)


Однако это небезопасно для выполнения кода, предоставленного недоверенным пользо­
вателем, и можно легко вывести интерпретатор из строя с тщательно обработанными вход­
ными данными.
»> import ast
»> ast.literal_eval('()' * 1 000000)
[5] 21 358 segmentation fau lt (саге dumped) pythonЗ

Здесь на вход подается строка из (), повторяющаяся миллион раз, что приводит к аварий­
ному завершению работы парсера CPython. Разработчики CPython не рассматривают ошиб­
ки в парсере как проблемы безопасности.

1 06.З. Оценка утверждений с помощью функции ехес
»> code ="""for i in range(5):\n print('Hello world !')"""
»> exec(code)
Hello world !
Hello world !
Hello world !
Hello world !
Hello world !

1 06.4. Оценка выражения с помощью функции eval
>>> expression = '5 + 3 * а'
>>> а = 5
»> result = eval (expression)
>>> result
20

1 06.5. Предварительная компиляция выражения для его
многократной оценки

Встроенная функция compile может быть использована для предварительной компиля­
ции выражения в объект кода, который затем может быть передан в eval. Это позволит уско­
рить повторное выполнение оцениваемого кода. Третьим параметром для compile должна
быть строка 'eval'.
»> code = compile('a * Ь + с', '', 'eval')
>>> code

>>> а, Ь, с = 1 , 2, 3
»> eval(code)
5

430

Глава 1 07. Pylnstaller - распространение кода Python

1 06.б. Оценка выражения с помощью eval с использованием
пользовательских глобальных переменных
»> variaЫes = {'а': 6, 'Ь': 7}
»> eval('a * Ь', globals=variaЫes)
42
В этом коде невозможно случайно сослаться на имена, заданные "снаружи":
»> eval('variaЫes')
{'а': 6, 'Ь': 7}
»> eval('variaЫes', globals=variaЫes)
Traceback (most recent call last):
File "", l ine 1 , in
File "", line 1, in
NameError: name 'variaЫes' is not def1ned
Использование defaultdict позволяет, например, установить нулевое значение
пределенных переменных:

для нео-

>» from collections import defau ltdict
»> variaЫes = defaultdict(int, {'а': 42})
»> eval('a * с', globals=variaЫes) # обратите внимание, что 'с' не определено явно

о

Глава 1 07. Pyl nstal ler - распространение
кода Python
1 07 . 1 . Установка и настройка
Pyinstaller - это обычный пакет Python. Он может быть установлен с помощью pip:
pip install pyinstaller

Установка в Windows

Для Windows необходимым условием является наличие pywin32 или pypiwin32. Последний
устанавливается автоматически при установке pyinstaller с помощью pip.

Установка в Мае OS Х

Pylnstaller работает с Python 2.7, поставляемым по умолчанию с текущей версией
Мае OS Х. Если предполагается использовать более поздние версии Python или основные па­
кеты, такие как PyQT, Numpy, Matplotlib и т.п., то рекомендуется устанавливать их с помощью
MacPorts или Homebrew.

Установка из архива

Если pip недоступен, загрузите сжатый архив с PyPI. Чтобы протестировать версию разра­
ботки, загрузите сжатый архив из ветки develop на странице загрузки Pylnstaller. Раскройте
архив и найдите скрипт setup. py. Выполните python setup.py install с правами администратора
для установки или обновления Pylnstaller.

Проверка установки

После успешной установки команда pyinstaller должна быть в системном пути для всех
платформ. Убедитесь в этом, набрав в командной строке pyinstaller --version. В результате бу­
дет выведена текущая версия pyinstaller.

1 07.2. Использование Pyinstaller

431

1 07.2. Использование Pyinstaller

В простейшем случае достаточно перейти в каталог, в котором находится ваш файл, и
ввести:
pyinstaller myf1le.py

Pyinstaller анализирует file и создает:
• файл myf1le.spec в том же каталоге, что и myf1le.py
• Папка build в том же каталоге, что и myfile.py
• Папка dist в том же каталоге, что и myfile.py
• Файлы логов (журнал) в папке build.
Поставляемое в комплекте приложение (bundled арр) находится в папке dist.
ОIЩИИ

Существует несколько опций, которые могут быть использованы в pyinstaller. Приложе­
ние можно запустить, открыв 'dist\myf1le\myfi le.exe'.

1 07.З. Объединение в одну папку

Если Pyl nstaller используется без каких-либо опций для компоновки myscript.py, то по
умолчанию на выходе получается одна папка (с именем myscript), содержащая исполняемый
файл с именем myscript (myscript.exe в Windows) вместе со всеми необходимыми зависимы­
ми файлами.
Приложение может быть распространено путем сжатия папки в ziр-архив. Режим One
Folder может быть явно задан с помощью опции -D или --onedir.
pyinstaller myscript.py -D

Преимущества
Одним из основных преимуществ компоновки в одну папку является удобство отлад­
ки проблем. Если какие-либо модули не импортируются, это можно проверить, просмотрев
папку.
Еще одно преимущество ощущается при обновлениях. Если в коде произошло несколько
изменений, но используемые зависимые файлы точно такие же, дистрибьюторы могут про­
сто поставлять исполняемый файл (который меньше, чем вся папка).
Недостатки
Единственным недостатком этого метода является то, что пользователю приходится ис­
кать исполняемый файл среди большого количества других файлов.
Также пользователи могут удалять или изменять другие файлы, что может привести
к некорректной работе приложения.

1 07 .4. Объединение в один файл
pyinstaller myscript.py -F

Для создания одного файла можно использовать опции -F или --onef1le. В этом случае про­
грамма объединяется в один файл myscript.exe. Этот вариант работает медленнее, чем компо­
новка в одну папку. Кроме того, единственный файл труднее отлаживать.

432

Глава 1 08. Визуализация данных с помощью Python

Глава 1 08. Визуализация данных
с помощью Python
1 08.1 . Seaborn

Seaborn - это "обертка" вокруг Matplotlib, позволяющая легко создавать распространен­
ные статистические графики. Список поддерживаемых графиков включает графики одно­
мерных и двумерных распределений, графики регрессии, а также ряд методов построения
графиков категориальных переменных. Полный список графиков, которые предоставляет
Seaborn, приведен в справке по API.
Создание графиков в Seaborn сводится к вызову соответствующей графической функции.
Ниже приведен пример создания гистограммы, оценки плотности ядра и графика ковра для
случайно сгенерированных данных.
import numpy as пр # numpy испол ьзуется при построении графиков
import seaborn as sns # распространенная форма импорта seaborn
# Генерировать нормал ьно распределенные данные
data = np.random.randn(1ООО)
# Построить гистограм му с наложением графиков типов rugplot и kde
sns.distplot(data, kde = True, rug = True)
0.45 �--�--��--�--�---�--�---�--�
0.40
0.35

J_l----+-----+-----+------1

t---------t-------t---------i----=-v(Т\1

О 30 f-----+--------,f-----+--ff
0.25 f---------+---------+--------

L

0.20 t-----+---t----11/ ....

: : : =====,,.=/__
0.05 f--0.00

-4

--+ - ---�

,/
---itl I

1lnt.11
-3

-2

-1

о-------+\----+-----+------+-----<

f
·

·

f

J��

\

\
1

о

\

\
2

3

4

Стиль графика также может управляться с помощью декларативного синтаксиса.
# Испол ьзование ранее проведенного импорта и данных
# Испол ьзовать тем ный фон без сетки.
sns.set_style('dark')
# Создать график заново
sns.distplot(data, kde = True, rug = True)

433

1 08.1 . Seaborn
0.45
0.40
0.35
0.30
0.25
0.20
0. 1 5
0. 1 0
0.05
0.00

-2

-3

-1

2

о

4

3

Кроме того, к графикам Seaborn можно применять обычные команды библиотеки
matplotlib. Вот пример добавления заголовков осей к ранее созданной гистограмме.
# Использовуем те же дан н ы е и стиль
# Доступ к командам matplotlib
import matplotlib.pyplot as plt
# Ранее созда н н ы й график
sns.distplot(data, kde = True, rug = True)
# Установка меток осей
plt.xlabel('Тhis is my x-axis')
plt.ylabel('Тhis is my y-axis')
0.45
0.40
0.35
0.30


·х

1 0.25

- � 0.20


Е
1--

0. 1 5
0. 1 0
0.05

О ОО

-3

-2

-1

о

This is mv x-axis

2

3

4

434

Глава 1 08. Визуализация данных с помощью Python

1 08.2. Matplotlib
Matplotlib - это библиотека математического построения графиков для Python, предостав­
ляющая множество различных функций.
Matplotlib предоставляет два различных метода построения графиков, хотя по большей
части они взаимозаменяемы:
• Интерфейс pyplot - прямой и простой в использовании, позволяет строить
сложные графики в стиле, близком к стилю пакета MATLAB.


matplotlib позволяет пользователю управлять различными аспектами (осями,
линиями, тиками и т. д.) непосредственно с помощью объектно-ориентированной
системы. Это сложнее, но позволяет полностью контролировать построение
графика.
Ниже приведен пример использования интерфейса pyplot для построения графика некоторых сгенерированных данных:
import matplotlib.pyplot as plt
# Сгенерировать некоторые данные для построения графика.
х = [О, 1 , 2, 3, 4, 5, б]
у = [i """2 for i in х]
# Построить график данных х, у с некоторы м и ключевыми аргументами, управля ющими
# стилем построения.
# Используйте две разные ком анды plot для построения как точек (scatter), так и линии (plot).
plt.scatter(x, у, с = 'Ыuе', marker = 'x', s = 1 00) # Создание синих маркеров форм ы "х" и размера 1 00
plt.plot(x, у, color = 'red', linewidth = 2) # Создаем красную линию с шириной линии 2.
# Добавьте текст на оси и заголовок.
plt.xlabel('x data')
plt.ylabel('y data') plt.title('An example plot')
# Сформировать график и показать его пользователю.
plt.show()

An exa m p l e p l ot

40
35
30
25
rn
rn
>,

20
15
10
5

о
-5
-1

о

1

2

3
х data

4

5

6

7

1 08.3. Plotly

435

Заметим, что plt.show() может вызывать проблемы в некоторых средах из-за запуска
в интерактивном режиме, и если это так, то блокировку можно явно
отменить, передав необязательный аргумент plt.show(Ыock = True), чтобы снять эту
проблему.
matplot l i b . pyplot

1 08.З. Plotly

Plotly - это современная платформа для построения графиков и визуализации данных.
Полезная для построения разнообразных графиков, особенно в области наук о данных, Plotly
доступна в виде библиотеки для Python, R, JavaScript, Julia и МАТLАВ. Ее можно использовать
как веб-приложение. Пользователи могут установить библиотеку plotly и использовать ее в
оффлайн-режиме после аутентификации. Кроме того, графики можно строить и с использо­
ванием Jupyter Notebooks.
Для использования этой библиотеки требуется учетная запись с именем пользователя
и паролем. Она предоставляет рабочую область (workspace) для сохранения графиков и дан­
ных в облаке.
Бесплатная версия библиотеки имеет несколько ограниченные возможности и рас­
считана на создание 250 графиков в день. Платная версия имеет все возможности, не­
ограниченное количество скачек графиков и более приватное хранилище данных. Более
подробную информацию можно получить на главной странице сайта. Пример графика из
документации:
import plotly.graph_objs as go
import plotly as ply
# Создание случайных данных с помощью numpy
import numpy as пр
N = 1 00
random_x = np. linspace(O, 1 , N)
random_yO = np. random.randn(N)+5
random_y1 = np. random.randn(N)
random_y2 = np. random.randn(N)-5
# Создаем графики
tгасеО = go.Scatter(
х = random_x,
у = random_yO,
mode = 'lines',
name = 'lines'

)

tгасе1 = go.Scatter(
х = random_x,
у = random_y1 ,
mode = 'lines+markers',
name = 'l ines+markers'

)

tгасе2 = go.Scatter(
х = random_x,
у = random_y2,
mode = 'markers',
name = 'markers'

)

data

=

[tгасеО, tгасе1 , tгасе2]

ply.offline.plot(data, f1 lename = 'line-mode')

436

Глава 1 08. Визуализация данных с помощью Python
-- lines
- lines+markers


5

markers


• • • .• • •
.
.
. . ,.. ... . .,,•..•--. , •. .- . .---• .



•• •

-5




о

0.2


0.4

0.6

1 08.4. MayaVI

о.в

MayaVI - это инструмент трехмерной визуализации научных данных. В нем исполь­
зуется набор инструментов визуализации или VTK (Visualization Tool Юt). Используя воз­
можности VTK, MayaVI способен создавать разнообразные трехмерные графики и ди­
аграммы. Он доступен как в виде отдельного программного приложения, так и в виде
библиотеки. Подобно Matplotlib, эта библиотека предоставляет объектно-ориентирован­
ный интерфейс языка программирования для создания графиков без необходимости зна­
ния VТК.
MayaVI доступен только в cepm1 версий Python 2.7х! Надеемся, что в скором времени он
будет доступен и для Python серии Зх. (Хотя замечены некоторые успехи при использовании его
зависимостей в Python 3.)
Здесь приведен пример графика, созданного с помощью MayaVI, из официальной документации:
# Автор: Gael Varoq uaux
# Copyright (с) 2007, Enthought, lnc.
# Лицензия: BSD Style.
from numpy import sin, cos, mgrid, pi, sqrt
from mayavi import mlab
mlab.figure(fgcolor = (0, О, О), bgcolor = (1 , 1 , 1 ))
u, v = mgrid[- 0.035:pi:0.01 , - 0.035:pi:0.0 1 ]
Х = 2 / 3. * (cos(u) * cos(2 * v)
+ sqrt(2) * sin(u) * cos(v)) * cos(u) / (sqrt(2) sin(2 * u) * sin(3 * v))
У = 2 / 3. * (cos(u) * sin(2 * v) sqrt(2) * sin(u) * sin(v)) * cos(u) / (sqrt(2)
- sin(2 * u) * sin(3 * v))
Z = -sqrt(2) * cos(u) * cos(u) / (sqrt(2) - sin(2 * u) * sin(3 * v))
S = sin(u)
mlab.mesh(X, У, Z, scalars = S, colormap = 'YIGnBu', )
# Хороший вид спереди
mlab.view(.0, - 5.0, 4)
mlab.show()

1 09.1 . Получение общей помощи

437

Глава 1 09. Интерпретатор
(консоль командно й строки)

1 09.1 . Получение общей помощи

Если функция help вызывается в консоли без аргументов, то Python представляет интер­
активную справочную консоль, в которой можно узнать о модулях Python, символах, ключе­
вых словах и многом другом.
»> help(}
Добро пожаловать в справочную утилиту Python 3.4!
Если вы впервые испол ьзуете Python, то вам обязательно следует ознакомиться с учебны м
пособием, размещенны м в Интернете по адресу http://docs.python.org/3.4/tutorial/.
Введите имя л юбого модуля, кл ючевого слова или тем ы, чтобы получить справку по написанию
програм м на Python и использованию модулей Python. Чтобы вы йти из этой справочной
утилиты и вернуться в интерпретатор, достаточно набрать "qu it".
Чтобы получить список доступных модулей, кл ючевых слов, символов или тем, введите
"modules", "keywords", "symbols" или "topics". Кажды й модул ь также имеет однострочное краткое
описание своей работы; чтобы получить список модулей, название или краткое описание
которых содержит задан ную строку, например "spam", введите "modules spam".

1 09.2. Ссылка на последнее выражение

Чтобы получить в консоли значение последнего результата из последнего выражения,
используйте знак подчеркивания _.
>>> 2 + 2

4

>>>

4

>>> _ + б
10

Это значение магического подчеркивания обновляется только при использовании выра­
жения Python, результатом которого является значение. Выполнение функций или циклов
for не приводит к изменению значения. Если выражение вызывает исключение, то никаких
изменений для "_" не произойдет.
»> "Hello, {0}".format("World")
'Hello, World'
>>>
'Hello, World'
»> def wontchangethings():
... pass
>>>
'Hello, World'
>» 27 / О
Traceback (most recent call last):
File "", line 1, in
ZeroDivisionError: division Ьу zero
>>>
'Hello, World'

Помните, что эта магическая переменная доступна только в интерактивном интерпрета­
торе Python. Запущенные скрипты этого не сделают.

438

Глава 1 09. Интерпретатор (консол ь командной строки)

1 09.3. Открытие консоли Python

Консоль для основной версии Python обычно можно открыть, набрав ру в консоли
Windows или набрав python на других платформах.
$ ру
Python 3.4.3 (v3.4.3:9Ы3f1 с3е601 , Feb 24 201 5, 22:44:40) [MSC v.1 600 64 Ьit (AMD64)] оп win32
Туре "help", "copyright", "credits" ог "license" for more information.
>>>

Если у вас несколько версий, то по умолчанию их исполняемые файлы будут сопостав­
лены с python2 или python3 соответственно. Это, конечно, зависит от наличия исполняемых
файлов Python в РАТН.

1 09.4. Переменная PYTHONSTARTUP

Для консоли Python можно установить переменную окружения PYTHONSTARTUP. При ка­
ждом входе в консоль Python будет выполняться эта переменная, что позволяет добавить
в консоль дополнительные функции, например, автоматический импорт часто используе­
мых модулей.
Если переменная PYТHONSTARTUP была установлена в местоположение файла, содержа­
щего это:
print("Welcome!")

тогда открытие консоли Python приведет к появлению такого дополнительного вывода:

$ ру
Python 3.4.3 (v3.4.3:9Ы3f1 с3е601 , Feb 24 201 5, 22:44:40) [MSC v. 1 600 64 blt (AMD64)] оп win32
Туре "help", "copyright", "credits" ог "license" for more information.
Welcome!
>>>

1 09.5. Аргументы командной строки

В Python существует множество переключателей командной строки, которые можно пе­
редавать в ру. Их можно найти, выполнив команду ру -- help, которая на Python 3.4 выдает
следующее сообщение:
Python Launcher
usage: ру [ launcher-arguments ] [ python-arguments ] script [ script-arguments ]
Аргументы програм мы запуска:
-2 : Запуск последней версии Python 2.х
-3 : Запуск последней версии Python 3.х
-Х.У : Запуск указанной версии Python
-Х.У-32: Запуск указанной 32-разрядной версии Python
Ниже приведен текст справки из языка Python:
испол ьзование: G:\\\Python34\\python.exe [option] ... [-с cmd 1 -m mod I f1le 1 -] [arg] ... Опции
и аргументы (и соответствующие переменные окружения):
-Ь : выдать п редупреждения о str(bytes_instance), str(bytearray_instance) и сравнении байтов/
байтовых массивов с str. (-ЬЬ: выдать ошибки).
-В : не записы вать файлы .ру[со] при импорте; также PYTHONDONTWRITEBYTECODE=x
-с cmd : программа передается в виде строки (завершает список опций)
-d : вы вод отладки парсера (синтаксического анализатора); также PYTHONDEBUG=x
-Е : игнорировать переменные окружения PYTHON* (например, PYTHONPATH)
-h : вы вести это справочное сообщение и вы йти (также --help)
-i : проверка в интерактивном режиме после выполнения скрипта; вызы вает приглашение,
даже если stdin не я вляется терминалом; также PYTH O N I NSPECT=x
-1 : изолировать Python от окружения пользователя (подразумевает -Е и -s)
-m mod : запуск библиотечного модуля как скрипта (завершает список опций)
-0 : немного оптимизировать генерируемый байткод; также PYTHONOPTI MIZE=x

1 09.6. Получение справки об объекте

439

-00 : удаление dос-строк в дополнение к оптимизации -0
-q : не печатать сообщения о версии и авторских правах при интерактивном запуске
-s : не добавлять каталог сайта пользователя в sys.path; также PYTHONNOUSERSITE
-S : не подразумевать 'import site' п ри инициализации
-u : небуферизованные двоичные stdout и stderr, stdin всегда буферизован;
также PYTHONUNBUFFERED=x
Подробности о внутренней буферизации, связанной с '-u', см. на основной странице.
-v : verbose (трассировка операторов импорта); также можно многократно указать
PYTHONVERBOSE =x для увеличения объема информации
-V : вывести номер версии Python и выйти (также --version)
-W arg : управление п редупреждениями; arg - это action:message:category:module:lineno
также PYTHONWARN INGS =arg
-х : п ропускает первую строку исходного текста, позволяя использовать не-Uпiх-формы #!cmd
-Х opt : установить специфическую опцию реализации файла : програм ма, п рочитанная из
файла сценария
- : п рограм ма, п рочитанная из stdin (по умолчанию; интерактивный режим, если tty) arg ... :
аргументы, переданные программе в sys.argv[1 :]
Другие переменные окружения:
PYTHONSTARTUP: файл, выполняемый при интерактивном запуске (по умолчанию отсутствует)
PYTHON PATH : ';'-разделенный список каталогов с префиксом к пути поиска модулей по
умолчанию. В результате получается sys.path.
PYTHONHO M E : альтернативный каталог (или ;). По умолчанию для поиска модулей
используется путь \\lib.
PYTHONCASEOK : игнорирование регистра в операторах 'import' (Windows).
PYTHON I OENCODI NG: Кодировка[:еггогs], используемая для stdin/stdout/stderr.
PYTHON FAULTHANDLER: дамп трассировки Python при фатальных ошибках.
PYTHONHASHSEED: если эта переменная имеет значение 'random', то для засева хешей
объектов str, bytes и datetime используется случайная величина. Она также может быть
задана целым числом в диапазоне [О,4294967295] для получения хеш-значений
с п редсказуемым seed.

1 09.6. Получение справки об объекте
В консоли Python добавлена новая функция help, с помощью которой можно получить
информацию о функции или объекте. Для функции help выводит ее сигнатуру (аргументы)
и docstring, если она есть.
»> help(print)

Помощь по встроенной функции print в модуле builtins:
print(...)
print(value, ..., sep = ' ', end = '\n', f1le = sys.stdout, flush = False)
Выводит значения в поток или по умолчанию в sys.stdout.
Необязательные аргументы в виде ключевых слов:
f1le: файлоподобный объект (поток); по умолчанию - текущий sys.stdout.
sep: строка, вставляемая между значениями, по умолчанию пробел.
end: строка, добавляемая после последнего значения, по умолчанию - новая строка.
flush: следует ли принудительно очищать поток.

Для объекта в справке выводятся erodocstring и различные функции-члены, которыми обладает объект.
>>> х = 2
»> help(x)

Справка по объекту int:

class int(object)
1 int(x = 0) -> integer
1 int(x, base = 1 О) -> integerl
1
1 Преобразовать число или строку в целое число или вернуть О, если аргументы не заданы.

440

Глава 1 1 О. *args и **kwargs

Если х - число, то возвращается х. int (). Для чисел с плавающей точкой происходит
усечение до нуля.
Если х не является числом или задано основание, то х должен быть строкой, байтом или
экземпляром байтового м ассива, представляющим целочисленный литерал в заданном
основании. Перед литералом может стоять знак '+' или '-', и он может быть
окружен пробелами. По умолчанию основание равно 1 О.
Допустимыми основаниями являются О и 2-36.
Base О означает интерпретацию основания из строки как целочисленного литерала.
»> int('0bl 00', base = 0}
4
Методы определены здесь:
_abs_ (self, /)
abs(self}
_add_ (self, value, /)
Возвращает self+value...

Глава 1 1 О. *args и **kwargs
1 1 0. 1 . Использование **kwargs при написании функций
Определить функцию, принимающую произвольное количество ключевых (именован­
ных) аргументов, можно с помощью двойной звездочки ** перед именем параметра:
def print_kwargs(**kwargs):
print(kwargs)
При вызове метода Python построит словарь всех аргументов ключевого слова и сделает
его доступным в теле функции:
prinLkwargs(a = "two", Ь = 3}
# выведет: "{а: "two", Ь = 3}"
Обратите внимание, что параметр **kwargs в определении функции всегда должен быть
последним, и он будет соответствовать только тем аргументам, которые были переданы по­
сле предыдущих.
def example(a, **kw}:
print kw
example(a = 2, Ь = 3, с = 4} # = > {'Ь': 3, 'с': 4}
Внутри тела функции работа с kwargs осуществляется так же, как и со словарем; для до­
ступа к отдельным элементам kwargs достаточно пройтись по ним циклом, как это делается
с обычным словарем:
def print_kwargs(**kwargs):
for key in kwargs:
print("key = {О}, value = {1 }".format(key, kwargs[key]}}
Теперь вызов prinLkwargs(a = "two", Ь = 1 ) приводит к следующему результату:
prinLkwargs(a = "two", Ь
key = а, value = "two"
key = Ь, value = 1

=

1)

1 1 0.2. Использование *args при написании функций

441

1 1 0.2. Использование *args при написании функций
При написании функции можно использовать звездочку *, чтобы собрать все позицион­
ные (т.е. неименованные) аргументы в кортеж:
def print_args(farg, *args):
print("formal arg: %s" % farg)
for arg in args:
print("another positional arg: %s" % arg)
Метод вызова:
print_args(1 , "two", 3)
В этом вызове farg будет присвоен, как всегда, а два других будут переданы в кортеж args
в порядке их поступления.

1 1 0.3. Заполнение значений kwarg с помощью словаря
def foobar(foo = None, bar = None):
return "{}{}".format(foo, Ьаг)

values

=

{"foo": "foo", "Ьаг": "Ьаг"}

foobar(**values) # "fооЬаг"

1 1 0.4. Аргументы только для ключевых слов
(keyword-only arguments) и аргументы, требующие
ключевых слов (keyword-required arguments)
Python 3 позволяет определить аргументы функции, которые могут быть назначены
только по ключевому слову, даже без значений по умолчанию. Это делается с помощью сим­
вола * для потребления дополнительных позиционных параметров без задания параметров
ключевого слова. Все аргументы после * являются аргументами только для ключевого слова
(т.е. непозиционными, (keyword-only, non-positional)). Обратите внимание, что если аргумен­
там, относящимся только к ключевым словам, не задано значение по умолчанию, то они все
равно требуются при вызове функции.
def print_args(arg1 , *args, keyword_required, keyword_only = True):
ргiпt("первый позиционны й аргумент: {}".format(arg1 ))
for arg in args:
ргiпt("другой позицион н ы й аргумент: {}".format(arg))
print("keyword_req u i red va I ue: {}".format(keyword_req ui red))
print("keyword_only value: {}".format(keyword_only))
print(1 , 2, 3, 4) # ТуреЕггог: print_args() missing 1 required keyword-only argument: 'keyword_required'
print(1 , 2, 3, keyword_required = 4)
# перв ы й позицион н ы й аргумент: 1
# другой ПОЗИЦИОН Н Ы Й аргумент: 2
# другой ПОЗИЦИОН Н Ы Й аргумент: 3
# keyword_required value: 4
# keyword_only value: True

1 1 0.5. Использование �kwargs при вызове функций
Для присвоения значений параметрам функции можно использовать словарь, в котором
в качестве ключей используются имена параметров, а к каждому ключу привязано значе­
ние этих аргументов:
def test_func(arg1 , arg2, arg3): # Обычная функция с тремя аргументами
print("arg1 : %s" % arg1 )
print("arg2: %s" % arg2)
print("arg3: %s" % arg3)

442

Глава 1 1 1 . Сборка мусора

# Обратите внимание, что словари неупорядочены, поэтому м ы можем
# поменять местами arg2 и arg3. И меют значение только имена.
kwargs = {"arg3": 3, "агg2": "2"}
# Привязать первый аргумент (т.е. arg1 ) к 1 , а для привязки остальных
# использовать словарь kwargs
test_var_args_cal l (1 , **kwargs)

1 1 0.6. �kwargs и значения по умолчанию
Чтобы использовать значения по умолчанию с **kwargs:
def fun(**kwargs):
print kwargs.get('value', О)
fun()
# print О
fun(value = 1 )
# print 1

1 1 О. 7. Использование *args при вызове функций

Эффект использования оператора * на аргументе при вызове функции заключается в рас­
паковке списочного или кортежного аргумента:
def print_args(arg 1 , arg2):
print(str(arg1 ) + str(arg2))
а = [1 ,2]
Ь = tuple([3,4])
prinLargs(*a) # 1 2
prinLargs(*b) # 34
Обратите внимание, что длина аргумента со звездочкой должна быть равна количеству
аргументов функции. Распространенной идиомой в Python является использование опера­
тора распаковки * с функцией zip, чтобы обратить ее действие:
а = [1 ,3,5,7,9]
Ь = [2,4,6,8, 1 О]
zipped = zip(a,b)
# [(1 ,2), (3,4), (5,6), (7,8), (9, 1 О)]
zip(*zipped)
# (1 ,3,5,7,9), (2,4,6,8,1 О)

Глава 1 1 1 . Сборка мусора
1 1 1 . 1 . Повторное использование примитивов
Интересный момент, который может помочь оптимизировать ваши приложения, заклю­
чается в том, что примитивы на самом деле также пересчитываются. Для всех целых чисел
от -5 до 256 Python всегда повторно использует один и тот же объект:
»> import sys
»> sys.getrefcount(1 )
797
>>> а = 1
>>> Ь = 1

1 1 1 .2. Последствия команды del

443

»> sys.getrefcount(1 )

799

Обратите внимание, что счетчик ссылок (refcount) увеличивается, что означает, что а и Ь
ссылаются на один и тот же базовый объект, когда они ссьшаются на примитив 1 . Однако для
больших чисел Python фактически не использует базовый объект повторно:
»> а = 999999999
»> sys.getrefcount(999999999)

3

»> Ь = 999999999
»> sys.getrefcount(999999999)

3

Поскольку счетчик ссылок для 999999999 не меняется при присвоении его а и Ь, можно
сделать вывод, что они ссылаются на два разных базовых объекта, хотя им присвоен один и
тот же примитив.

1 1 1 .2. Последствия команды del

Удаление имени переменной из области видимости с помощью del v, удаление объекта
из коллекции с помощью del v[item] или del[i:j], удаление атрибута с помощью del v.name или
любой другой способ удаления ссьшок на объект сами по себе не приводят к вызову деструк­
тора или освобождению памяти. Объекты уничтожаются только тогда, когда количество их
ссылок достигает нуля.
»> import gc
»> gc.disaЫe() # откл ючить сборщик мусора
»> class Track:

def init (self):
print("lnitial ized")
def del (self):
print("Destructed")
»> def Ьаг():
return Track()
»> t = bar()
lnitialized
»> another_t = t # присвоить другую ссылку
»> print(" ...")

»> del t
# еще не уничтожен - another_t все еще ссылается на него
»> del another_t # конечная ссылка исчезла, объект уничтожен

Destructed

1 1 1 .З. Подсчет ссылок

В подавляющем большинстве случаев управление памятью в Python осуществляется
с помощью подсчета ссылок.
Каждый раз, когда на объект ссылаются (например, присваивают переменной), количе­
ство ссылок на него автоматически увеличивается. При исключении ссылки (например, при
выходе переменной из области видимости) количество ссылок автоматически уменьшается.
Когда счетчик ссьшок достигает нуля, объект немедлеIШо уничтожается и память сразу
же освобождается. Таким образом, для большинства случаев сборщик мусора даже не нужен.
»> import gc; gc.disaЫe() # отключить сборщик мусора
»> class Track:

def _init_(self):
print("lnitial ized")
def _del_(self):
print("Destructed")

444

Глава 1 1 1 . Сборка мусора

>» def foo():
Track()
# уничтожается немедленно, так как больше не имеет ссылок
print("-")
t = Track()
# на переменную есть ссылка, поэтому она еще не уничтожена
print("--")
# переменная уничтожается при выходе из функции
»> foo()
lnitial ized
Destructed
lnitial ized
Destructed
Для дальнейшей демонстрации концепции ссьmок:
»> def Ьаг():
return Track()
»> t = Ьаг()
lnitial ized
>» another_t = t
»> print(" . . .")
>>> t = None
>» another_t = None
Destructed

# присвоить другую ссылку
# еще не уничтожен - на него по-прежнему ссылается another_t
# конечная ссылка исчезла, объект уничтожен

1 1 1 .4. Сборщик мусора для ссылочных циклов

Единственный случай, когда сборщик мусора необходим, - это цикл ссылок. Простейшим
примером цикла ссылок является цикл, в котором А ссылается на В, В ссьmается на А, и боль­
ше ничто не ссылается ни на А, ни на В. Ни А, ни В недоступны из любой точки программы,
поэтому они могут быть безопасно уничтожены, но их счетчики ссылок равны 1 , и поэтому
они не могут быть освобождены только алгоритмом подсчета ссылок.
»> import gc; gc.disaЫe() # отключить сборщик мусора
»> class Track:
def _init_(self}:
print("lnitialized"}
def _del_(self}:
print("Destructed"}
»> А = Track()
lnitial ized
»> В = Track()
lnitial ized
>» A.other = В
»> B.other = А
»> del А; del В # объекты не уничтожаются из-за ссылочного цикла
>» gc.collect() # запустить сбор
Destructed
Destructed
4
Цикл ссылок может быть произвольной длины. Если А указывает на В указывает на С
указывает на ... указывает на Z, который указывает на А, то ни А, ни Z не будут собраны,
вплоть до фазы сборки мусора:
»> objs = [Track() for _ in range(1 О}]
lnitial ized
lnitial ized

445

1 1 1 . 5. Принудител ьное удаление объектов
lnitialized
lnitialized
lnitialized
lnitialized
lnitialized
lnitialized
lnitialized
lnitialized
»> for i in range(len(objs)-1 ):
... objs[i] .other = objs[i + 1]

»> objs[-1 ] .other = objs[O] # завершить цикл
# теперь никто не может ссылаться на objs - они все еще не уничтожены
»> del objs
»> gc.collect()

Destructed
Destructed
Destructed
Destructed
Destructed
Destructed
Destructed
Destructed
Destructed
Destructed

20

1 1 1 .5. Принудительное удаление объектов

Как в Python 2, так и в Python 3 можно принудительно освободить (деаллоцировать) объ­
екты, даже если их счетчик ссылок refcount не равен О. В обеих версиях для этого использу­
ется модуль ctypes.
ПРЕДУПРЕЖДЕНИЕ: в результате этого ваша среда Python станет нестабильной и склон­
ной к аварийному завершению работы! Использование этого метода также может привести
к проблемам безопасности (что весьма маловероятно). Деаллокация объектов производится
только в том случае, если вы уверены, что никогда больше не обратитесь к ним.
Версия Python З.х � 3.0:
import ctypes
deallocated = 1 2345
ctypes. pythonapi ._Ру_Dealloc(ctypes. ру_object( deal located))

Версия Python 2.х � 2.3:
import ctypes, sys
deallocated = 1 2345
(ctypes.c_char * sys.getsizeof(dea llocated)).from_address(id(deallocated))[:4]

=

'\хОО' * 4

После выполнения любая ссылка на деаллоцированный объект приведет к тому, что
Python либо выдаст неопределенное поведение, либо аварийно завершится - без трасси­
ровки (traceback). Вероятно, все-таки была причина, по которой сборщик мусора не удалил
этот объект... При деаллокации None выдается специальное сообщение Fatal Python error:
deallocating None и происходит аварийное завершение.

1 1 1 .6. Просмотр счетчика ссылок (refcount) объекта
»> import sys
»> а = object()
»> sys.getrefcount(a)

2

>>> Ь = а

446

Глава 1 1 1 . Сборка мусора

>» sys.getrefcount(a)
3
»> del Ь
>» sys.getrefcount(a)
2

1 1 1 . 7. Не ждите, пока сборщик мусора наведет порядок

Тот факт, что сборка мусора будет очищаться, не означает, что следует ждать, пока цикл
сборки мусора очистится. В частности, не следует ждать, пока сборка мусора закроет де­
скрипторы файлов, соединения с базами данных и открытые сетевые соединения.
Например, в следующем коде предполагается, что файл будет закрыт на следующем ци­
кле сборки мусора, если f была последней ссьmкой на файл.
»> f = open("test.txt")
»> del f

Более явным способом очистки является вызов f.close(). Можно сделать это еще более
"элегантно", с помощью оператора with (с помощью менеджера контекста):
»> with open("test.txt") as f:
... pass
... # сделать что-нибудь с f
»> # теперь объект f все еще существует, но он закрыт

Оператор with позволяет узнать, как долго файл остается открытым. Кроме того, оператор
всегда закрывает файл, даже если в блоке while возникло исключение.

with

1 1 1 .8. Управление сборкой мусора

Существует два подхода влияния на выполнение очистки памяти. Один из них - повли­
ять на частоту выполнения автоматического процесса, а другой - ручной запуск очистки.
Сборщиком мусора можно управлять, настраивая пороговые значения сбора, которые
влияют на частоту запуска сборщика. В Python используется система управления памятью
на основе поколений (generations). Новые объекты сохраняются в самом новом поколении generationO, и с каждой новой сборкой объекты переходят в более старшие поколения. После
достижения последнего поколения - generation2 - они больше не продвигаются.
Изменить пороговые значения можно с помощью следующего фрагмента кода:
import gc
gc.set_threshold(1 ООО, 1 00, 1 О) # Значения приведены только для демонстрации

Первый аргумент представляет собой порог для сбора generationO. Каждый раз, когда ко­
личество аллокаций превышает количество деаллокаций на 1000, будет вызван сборщик
мусора.
Для оптимизации процесса старые поколения не очищаются при каждом запуске. Вто­
рой и третий аргументы являются необязателып.IМИ и определяют частоту очистки стар­
ших поколений. Если generationO бьmо обработано 100 раз без очистки generation1, то будет
обработано generation1. Аналогично объекты в generation2 будут обрабатываться только
в том случае, если объекты в generation1 были очищены 10 раз, не затрагивая generation2.
Один из случаев, когда ручная установка пороговых значений полезна, - это когда про­
грамма аллоцирует много мелких объектов, не деаллоцируя их, что приводит к слишком
частому запуску сборщика мусора. Несмотря на то, что сборщик довольно быстр, при работе
с огромным количеством объектов он создает проблемы с производительностью. В любом
случае не существует универсальной стратегии выбора пороговых значений, и она зависит
от конкретного случая.
Ручное срабатывание коллекции может быть выполнено, как в следующем фрагменте
кода:
import gc
gc.col lect()

1 1 2. 1 . Использование модуля Pickle для сериализации и десериализации объекта

447

Сборка мусора автоматически запускается на основе количества аллокаций и деалло­
каций, а не на основе потребляемой или доступной памяти. Следовательно, при работе с
большими объектами память может быть исчерпана до того, как сработает автоматическая
очистка. В этом случае целесообразно вызывать сборщик мусора вручную.
Хотя это и возможно, но не приветствуется. Оптимальным вариантом является избега­
ние утечек памяти. В любом случае в больших проектах обнаружение утечки памяти может
оказаться сложной задачей, и ручной запуск сборки мусора может быть использован в каче­
стве быстрого решения до дальнейшей отладки.
Для длительно работающих программ сборка мусора может запускаться по времени или
по событию. В качестве примера первого варианта можно привести веб-сервер, который за­
пускает сборку мусора после определенного количества запросов. В качестве второго приме­
ра можно привести веб-сервер, который запускает сборку мусора при получении определен­
ного типа запроса.

Глава 1 1 2. Сериализация данных
при помощи модуля Pickle
Параметр

П одр об н о ст и

object

Объект, подлежащий хранению
Открытый файл, который будет содержать объект
Протокол, используемый для Pickle (необязательный параметр)
Байтовый объект, содержащий сериализованный объект

fi le
protocol
buffer

1 1 2. 1 . Использование модуля Pickle для сериализации
и десериализации объекта

Модуль pickle реализует алгоритм превращения произвольного объекта Python в после­
довательность байтов. Этот процесс также называется сериализацией объекта. Поток бай­
тов, представляющий объект, может быть передан или сохранен, а затем реконструирован
для создания нового объекта с теми же характеристиками. Для простейшего кода мы ис­
пользуем функции dump() и load(}.
Для сериаJШзации объекта
import pickle
# П роизвол ьная коллекция объектов, поддерживаем ых pickle.
data = {
'а': [1 , 2.0, 3, 4+бj],
'Ь': ("character string", b"byte string"),
'с': {None, True, False}
with open('data.pickle', 'wb') as f:
# Применим Pickle к словарю 'data' с использованием самого высокого из доступных протоколов.
pickle.dump(data, f, pickle. н l GHEST_PROTOCOL)
Для десериаJШзации объекта
import pickle

448

Глава 1 1 2. Сериализация данных при помощи модуля Pickle

with open('data.pickle', 'гЬ') as f:
# Версия используемого протокола определяется автоматически, поэтому нам не
# нужно ее указывать.
data = pickle.load(f)

Pickle и байтовые объекты

Также возможна сериализация в байтовые объекты и десериализация из них с помощью
функций dumps и loads, которые эквивалентны функциям dump и load.
serialized_data = pickle.dumps(data, pickle.H I G H EST_PROTOCOL)
# type(serialized_data) - это байты
deserialized_data = pickle.loads(serialized_data)
# deserialized_data == дан н ые

1 1 2.2. Данные для Pickle

Не все данные подходят для модуля Pickle, а некоторые данные не следует обрабатывать
этим модулем по другим причинам.
То, что будет направляться в модуль, может быть определено в методе _getstate_. Этот
метод должен возвращать то, что можно направлять в Pickle. Метод _setstate_ получит то,
что создал _getstate_ и должен инициализировать объект.
class A(object):
def _init_(self, importanLdata):
self.important_data = importanLdata
# Добавить данн ые, которые нельзя направлять в Pickle:
self.func = lambda: 7
# Добавить данные, которые никогда не направлять в Pickle,
# так как они быстро заканчиваются::
self.is_up_to_date = False
def _getstate_(self):
return [self.important_data] # тол ько это необходимо
def _setstate_(self, state):
self.important_data = state[0]
self.func = lambda: 7 # просто некая жестко закодированная функция, которую
# не надо направлять в Pickle
self.is_up_to_date = False # даже если это было до направления в Pickle

Теперь можно сделать следующее:
»> а1 = A('very important')
>>>
»> s = pickle.dumps(a1 ) # calls а1 ._getstate_()
>>>
»> а2 = pickle.loads(s) # cal ls а1 ._setstate_(['very important'])
>>> а2

»> a2.important_data
'very important'
>» a2.func()
7

Реализация здесь направляет в Pickle список с одним значением: [self.importanLdata]. Это
был просто пример, _getstate_ мог бы возвращать все, что можно направлять в Pickle, если

1 1 3.1 . Форматирование списка значений в байтовый объект

449

бы _setstate_ знал, как сделать обратное. Хорошей альтернативой является словарь всех
значений: {'important_data': self.important_data}.
Конструктор не вызывается! Обратите внимание, что в предыдущем примере экземпляр
а2 был создан в pickle.loads без вызова A._init_, поэтому A._setstate_ должен был инициали­
зировать все то, что инициализировал бы _iniL, если бы он бьm вызван.

Глава 1 1 3. Двоичные данные
1 1 3. 1 . Форматирование списка значений в байтовый объект
from struct import pack
print(pack('l3c', 1 23, Ь'а', Ь'Ь', Ь'с')) # Ь'{\хОО\хОО\хООаЬс'

1 1 3.2. Распаковка байтового объекта в соответствии
со строкой формата
from struct import unpack
print(unpack('l3c', Ь'{\хОО\хОО\хООаЬс')) # (1 23, Ь'а', Ь'Ь', Ь'с')

1 1 3.3. Упаковка структуры
Модуль struct предоставляет возможность упаковывать объекты Python в виде непрерыв­
ного "куска байтов" (chunk of bytes) или разъединять кусок байтов на структуры Python.
Функция pack принимает строку формата и один или несколько аргументов и возвраща­
ет двоичную строку. Это очень похоже на форматирование строки, за исключением того, что
на выходе получается не строка, а кусок байтов.
import struct
import sys
print "Native byteorder: ", sys. byteorder
# Если порядок байтов не указан, то используется
# собственны й порядок байтов (Native Ьуtеогdег)
buffer = struct.pack("ihb", 3, 4, 5)
print "Byte chunk: ", repr(buffer)
print "Byte chunk unpacked: ", struct. unpack("ihb", buffer)
# Последний элемент в виде unsigned short вместо unsigned сhаг (2 байта)
buffer = struct.pack("ihh", 3, 4, 5)
print "Byte chunk: ", repr(buffer)

Выход:
Native byteorder: little Byte chunk: '\х03\х00\х00\х00\х04\х00\х05' Byte chunk unpacked: (3, 4, 5) Byte
chunk: '\х03\х00\х00\х00\х04\х00\х05\х00'

Можно использовать сетевой порядок байтов с данными, полученными из сети, или упаковать данные для отправки их в сеть.
import struct
# Если порядок байтов не указан, то используется
# собственны й порядок байтов (Native Ьуtеогdег)
buffer = struct. pack("hhh", 3, 4, 5)
print "Byte chunk native byte огdег: ", repr(buffer)

450

Глава 1 1 4. Идиомы

buffer = struct.pack("!hhh", 3, 4, 5)
print "Byte chunk network byte order: ", repr(buffer)

Выход:
Byte chunk native byte order: '\х03\х00\х04\х00\х05\х00'
Byte chunk network byte order: '\хОО\хОЗ\хОО\х04\хОО\х05'

Вы можете оптимизировать работу, избежав накладных расходов на выделение нового
буфера, предоставив ранее созданный буфер.
import struct
from ctypes import create_string_buffer
bufferVar = create_string_buffer(B)
bufferVar2 = create_string_buffer(B)
# Мы используем уже созданный буфер
# предоставляем формат, буфер, смещение и данные
struct.pack...into("hhh", bufferVar, О, 3, 4, 5)
print "Byte chunk: ", repr(bufferVar.raw)
struct.pack...into("hhh", bufferVar2, 2, 3, 4, 5)
print "Byte chunk: ", repr(bufferVar2. raw)

Выход:
Byte chunk: '\х03\х00\х04\х00\х05\х00\х00\х00'
Byte chunk: '\х00\х00\х03\х00\х04\х00\х05\х00'

Глава 1 1 4. Идиом ы
1 1 4. 1 . Инициализация ключей словаря

Предпочтителен метод dict.get, если вы не уверены в наличии ключа. Он позволяет вер­
нуть значение по умолчанию, если ключ не наЙДен. Традиционный метод с использованием
dict[key] вызовет исключение KeyError.
Вместо использования:
def add_student():
try:
students['count'] += 1
except KeyError:
students['count'] = 1

осуществите:
def add_student():
students['count'] = students.get('count', О) + 1

1 1 4.2. Переключение переменных

Для переключения значений двух переменных можно использовать распаковку кортежей.

х = True
у = False
х, у = у, х

х

# False
у
# True

1 1 4.3. Использование проверки значения истинности

451

1 1 4.З. Использование проверки значения истинности
Python неявно преобразует любой объект в булево значение для тестирования, поэтому
используйте его везде, где это возможно.
# Хорошие п римеры, испол ьзующие нея вное тестирование истинности
if attr:
# сделать что-нибудь
if not attr:
# сделать что-нибудь
# Неудачные п римеры, испол ьзующие конкретные тип ы
если attr == 1 :
# сделать что-нибудь
if attr == True:
# сделать что-нибудь
if attr != ":
# сделать что-нибудь
# Если вы хотите проверить наличие Nопе, используйте 'is' или 'is not'.
if attr is None:
# сделать что-нибудь
Как правило, в этом случае код получается более читабельным, а при работе с неожидан­
ными типами - более безопасным.

1 1 4.4. Тест на функцию _main_, чтобы избежать
неожиданного выполнения кода
Хорошей практикой является проверка переменной _name_, вызывающей программы
перед выполнением своего кода.
import sys
def main():
# Ваш код начинается здесь
# Не забудьте указать код возврата
return О
if _name_ == "_main_":
sys.exit(main())
Использование этого паттерна гарантирует, что ваш код будет выполняться только тогда,
когда вы этого ожидаете; например, при явном запуске вашего файла:
python my_program.py
Однако польза от этого будет в том случае, если вы решите импортировать свой файл
в другую программу (например, если вы пишете его как часть библиотеки). Тогда вы може­
те импортировать файл, а "ловушка" _main_ будет гарантировать, что никакой код не будет
выполнен неожиданно:
# Нов ы й файл п рограм м ы
import my_program # main() н е в ы полняется
# Но вы можете запустить main() я вно, если действительно хотите, чтобы она вы полнялась:
my_program.main()

452

Глава 1 1 5. Сериализация данных

Глава 1 1 5. Сериализация данных
Параметр

Подробности

protocol

При использовании pickle или cPickle - это метод, которым объекты сериали­
зуются/несериализуются (Serialized/Unserialized). Вероятно, вы захотите ис­
пользовать pickle . H I G H EST_PROTOCOL, что означает самый новый метод.

1 1 5.1 . Сериализация с использованием формата JSON
JSON - это формат, который может использоваться практически с любым языком про­
rраммирования; он широко используется для сериализации данных. Поддерживаемые
в нем типы данных: int, float, boolean, string, list и dict. Далее будет приведен пример, демон­
стрирующий базовое использоваIШе JSON:
import json
families = (['John'], ['Mark', 'David', {'name': 'Avraham'}])
# Вы грузка в строку
json_families = json.dumps(families)
# [["John"], ["Магk", "David", {"name": "Avraham"}]]
# Вы грузка в файл
withopen('families.json', 'w') as json_file:
json.dump(families, json_f1le)
# Загрузка из строки
json_families = json.loadsQson_families)
# Загрузка из файла
with open('families.json', 'г') as json_f1le:
json_families = json.loadQson_file)
Подробную информацию о JSON см. в соответствующей посвященной ему rлаве.

1 1 5.2. Сериализация с использованием модуля Pickle

Приведем пример, демонстрирующий базовое использоваIШе модуля Pickle:

# Импортирование Pickle
tгу:
import cPickle as pickle # Python 2
except l mportError:
import pickle # Python 3
# Соэдание Руthоп-объекта:
class Family(object):
def _init_(self, names):
self.sons = names
def _str_(self):
return ' '.join(self.sons)
my_family = Family(['John', 'David'])
# Вы грузка в строку
pickle_data = pickle.dumps(my_family, pickle . H I G H EST_PROTOCOL)

1 1 6. 1 . Запуск двух простых процессов

453

# В ы грузка в файл
with open('family.p', 'w') as pickle_f1 le:
pickle.dump(families, pickle_fi le, pickle. н l G H EST_PROTOCOL)
# Загрузка из строки
my_family = pickle. loads(pickle_data)
# Загрузка из файла
with open('family.p', 'r') as pickle_fi le:
my_family = pickle. load(pickle_f1 le)

Подробную информацию о модуле Pickle см. в соответствующей главе.
ПРЕДУПРЕЖДЕНИЕ: В официальной документации по Pickle указано, что никаких гарантий безопасности нет. Не загружайте данные, в происхождении которых вы не уверены.

Глава 1 1 6. Многопроцессорная обработка
1 1 6. 1 . Запуск двух простых процессов

Простым примером использования нескольких процессов могут служить два процесса
(workers), которые выполняются раздельно. В следующем примере запускаются два процесса:
• countUp() отсчитывает 1 единицу вверх, каждую секунду.
• countDown() отсчитывает 1 единицу вниз, каждую секунду.
import multiprocessing
import time
from random import randint
def countUp():
i=

о

while i = О:
print('Down:\t{}'. format(i))
time.sleep(randint(1 , 3)) # сон 1 , 2 или 3 секунды
i -= 1
if _name_ == '_main_':
# Инициировать процессы (workers).
workerUp = multiprocessing.Process(target=countUp)
workerDown = multiprocessing.Process(target=countDown)
# Запуск процессов (workers).
workerUp.start()
workerDown.start()
# Присоединиться к workers. Это приведет к блокировке главного (родительского) процесса
# до завершения работы workers.
workerUp.join()
workerDown.join()

454

Глава 1 1 7. Многопоточность

Выходные данные выглядят следующим образом:
Up: O
Down: З
Up: 1
Up: 2
Down: 2
Uр: З
Down: 1
Down: O

1 1 6.2. Использование класса Pool and функции pool.map
from multiprocessing import Pool
def cube(x):
геtuгп х ** З
if _name_ == "_main_":
pool = Pool(S)
result = pool.map(cube, [О, 1, 2, З])
Pool - это класс, который управляет несколькими workers (процессами) "за кулисами"
и позволяет вам, программисту, использовать их.
Pool(S) создает новый Pool с 5 процессами, а pool.map работает так же, как и map, но ис­
пользует несколько процессов (количество определяется при создании пула). Аналогичных
результатов можно добиться, используя map_async, apply и apply_async.

Глава 1 1 7 . М ногопото ч ность
Потоки позволяют программам Python выполнять несколько функций одновременно,
в отличие от выполнения последовательности команд по отдельности. В этой главе объяс­
няются принципы работы потоков и демонстрируется их использование.

1 1 7 . 1 . Основы многопоточности

С помощью модуля threading можно запустить новый поток выполнения, создав новый
поток threading.Thread и назначив ему функцию для выполнения:
import threading
def foo():
print "Hel lo threading !"
my_thread

=

threading.Thread(target = foo)

Параметр target ссьmается на запускаемую функцию (или вызываемый объект). Поток не
начнет выполнение до тех пор, пока не будет вызван start на объекте Thread.
Запуск потока
my_thread.staгt() # в ы водит 'Hel lo threading!'

Теперь, когда поток my_thread запущен и завершен, повторный вызов staгt приведет
к ошибке RuntimeError. Если вы хотите запустить свой поток в качестве демона (daemon), то,
передав kwarg daemon= True или установив значение my_thread.daemon равным Тгuе перед вы­
зовом staгt(), вы заставите ваш поток работать в фоновом режиме в качестве демона.

1 1 7.2. Общение между потоками

455

Присоединение к потоку

В тех случаях, когда вы разделили одно большое задание на несколько маленьких и хоти­
те выполнять их одновременно, но при этом необходимо дождаться завершения всех зада­
ний, прежде чем продолжить выполнение, вам подойдет метод Thread.join().
Допустим, вы хотите загрузить несколько страниц сайта и скомпилировать их в одну
страницу. Это можно сделать следующим образом:
import requests
from threading import Thread
from queue import Queue
q = Queue(maxsize = 20)
def put_page_to_q(page_num):
q. put(requests.get('http://some-website.com/page_%s.html' % page_num)
def compile(q):
# магическая функция, которой для выполнения необходим ы все страницы
if not q.fu l l ():
raise ValueError
else:
print("Done compiling!")



threads =
for page_num in range(20):
t = Thread(target = requests.get, args = (page_num,))
t.start()
threads.append(t)
# Далее объединяем все потоки, чтобы убедиться, что все потоки завершили работу, прежде чем
# продолжить. join() - блокирующий вызов (если не указано иное с помощью
# kwarg Ыocking = False при вызове join).for t in threads:
t.join()
# Вызовем compile(), поскольку все потоки завершены
compile(q)

Создание класса пользовательских потоков

Используя класс threading.Thread, мы можем подклассифицировать новый пользовательский класс Thread. При этом мы должны переопределить метод run в подклассе.
from threading import Thread
import time
class Sleepy(Thread):
def run(self):
time.sleep(S)
print("Hello form Thread")
if _name_ = = "_main_":
t = Sleepy()
t.start() # запуск автоматически вызывает метод Thread class run
# print 'Основная программа продолжает выполняться в фоновом режиме.'
t.join()
ргiпt("Основная программа продолжает выполняться в фоновом режиме.")

1 1 7.2. Общение между потоками

В коде имеется несколько потоков, и необходимо обеспечить безопасное взаимодействие
между ними. Для этого можно использовать Queue из библиотеки queue.
from queue import Queue
from threading import Thread

456

Глава 1 1 7. Многопоточность

# создать производителя данных
def producer(outpuLqueue):
while True:
data = data_computation()
outpuLqueue.put(data)
# создать потребителя
def consumer(input_queue):
while True:
# получение данных
data = inpuLqueue.get()
# сделать что-нибудь с данны ми
# указать на то, что данные были израсходованы
input_queue.task_done()

Создание потоков производителя и потребителя с общей очередью:
q Queue()
t1 = Thread(target =consumer, args = (q,))
t2 = Thread(target = producer, args = (q,))
t1 .start()
t2.start()
=

1 1 7 .3. Создание пула процессов (workers)
Использование threading и queue:

from socket import socket, AF_I N EТ, SOCl sock.recvfrom(81 92)
(b'Wed Aug 1 5 20:35:08 201 2', ('1 27.0.0.1 ', 5000))

1 24.4. Запуск простого HttpServer в потоке
и открытие браузера

Полезно, если ваша программа выводит н еб-страницы по ходу работы:

from http.server import HTTPServer, CG IHTTPRequestHandler
import webbrowser
import threading
def staгt_server(path, рогt = 8000):
"'Запуск простого веб-сервера, обслуживающего путь на порту"'
os.chdir(path)
httpd = НТТРSегvег((", рогt), CG IHПPRequestHand ler)
httpd.serve_forever()
# Запуск сервера в новом потоке
рогt = 8000
daemon = threading.Thread(name = 'daemon_server',
target =staгt_server,
args = ('.', рогt)
daemon.setDaemon(True) # Устанавливается в качестве демона, поэтому он будет завершен,
# как только завершится основной поток.
daemon.staгt()
# Открыть браузер
webbrowser.open('http://localhost:{}'.format(port))

1 24.5. Простейший пример клиент-сервер сокетов на Python
Сторона сервера:

import socket
serversocket = socket.socket(socket.AF_IN ET, socket.SOCl О:
print(buf)
Ьгеаk

Сторона клиента:
import socket
clientsocket = socket.socket(socket.AF_I N EТ, socket.SOCl value
descr._set_(self, obj, val ue) --> None
descr._delete_(self, obj} -> None
Реализованный пример:
class DescPrinter(object):
"""Дескриптор данных, регистрирующий активность."""
_val = 7

490

Глава 1 28. Дескриптор

def _get_(self, obj, objtype=None):
print('Getting ...')
return self._val
def _seL(self, obj, val):
print('Setting', val)
self._val = val
def _delete_(self, obj):
print('Deleting ...')
del self._val
class Foo():
х = DescPrinter()
i = Foo()

i.x

# Получение ...
#7

i.x = 1 00
# Установ ка 1 00
i.x

# Получение ...
# 1 00
del i.x
# Удаление ...

i.x

# Получение ...
#7

1 28.2. Двусторонние преобразования

Объекты-дескрипторы могут позволить связанным с ними атрибутам объекта автомати­
чески реагировать на изменения.
Предположим, что мы хотим смоделировать осциллятор с заданной частотой (в герцах) и
периодом (в секундах). При обновлении частоты мы хотим, чтобы обновлялся период, а при
обновлении периода - частота:
>» oscillator = Oscillator(freq=1 00.0)
»> oscillator.period
0.01
»> oscillator.period = 0.02
>» oscillator.freq
50.0
»> oscillator.freq = 200.0
>» oscillator.period
0.005

# Установить частоту 1 00,0 Гц
# Период равен 1 / частота, т.е. 0,01 секунды
# Установить период 0,02 секунды
# Частота перестраивается автоматически
# Установить частоту 200,0 Гц
# Период регулируется автоматически

Мы выбираем одно из значений (частота, в герцах) в качестве "якоря", т.е. того, которое
может быть задано без преобразования, и пишем для него класс-дескриптор:
class Hertz(object):
def _get_(self, instance, owner):
return self.value
def _seL(self, instance, value):
self.value = float(value)

1 29.1 . Создание известного постоянного временного файла и запись в него

491

Период, в секундах ("другое" значение), определяется в терминах "якоря". Мы пишем
класс дескриптора, который выполняет наши преобразования:
class Second(object):
def _geL(self, instance, owner):
# При чтении периода пересч итать из частоты
return 1 / instance. freq
def _set_(self, instance, value):
# При установке периода обновить частоту
instance.freq = 1 / float(value)
Теперь мы можем написать класс Oscillator:
class Oscillator(object):
period = SecondO # Установить другое значение в качестве атрибута класса
def _init_(self, freq):
self.freq = Hertz() # Установить значение я коря как атрибут э кземпляра
self.freq = freq # Присвоить передан ное значение - self.period будет скорректирован

Глава 1 29. Времен н ы й файл
NamedTemporaryFile
Параметр

mode

delete
suff1x
pref1x
dir
buffsize

Опи сание

режим открытия файла, по умолчанию =w+Ь
удалить файл при закрытии, по умолчанию = Тruе
суффикс имени файла, по умолчанию = "
префикс имени файла, по умолчанию 'tmp'
каталог размещения временного файла, по умолчанию = None
по умолчанию =-1, (используется значение ОС по умолчанию)

1 29. 1 . Создание известного постоянного временного файла
и запись в него

Вы можете создавать временные файлы, которые имеют видимое имя в файловой систе­
ме, доступ к которому можно получить через свойство name. В Uniх-системах такой файл мо­
жет быть настроен на удаление при закрытии (задается параметром delete, по умолчанию True) или может быть открыт позже.
В следующем примере создается и открывается именованный временный файл и в него
записывается 'Hello World! . Доступ к пути временного file можно получить через name, в дан­
ном примере он сохраняется в переменной path и выводится для пользователя. После закры­
тия файл снова открывается, содержимое временного файла считывается и выводится для
пользователя.
import tempf1le
with tempf1le.NamedTemporaryFile(delete = False) as t:
t.write('Hello World !')

492

Глава 1 30. Ввод, подмножество и вы вод внешних файлов данных с помощью библиотеки Pandas

path = t.name
print path
with open(path) as t:
print t.read()

Вывод:
/tmp/tmpбpireJ
Hello World!

Глава 1 3 0. Ввод, подмножество
и вы вод внешних файлов данных
с помощью библиотеки Pandas
В этом разделе демонстируется базовый код для чтения, подстановки и записи внешних
данных с помощью библиотеки Pandas.

1 30.1 . Базовый код для импорта, подмножества и записи
внешних файлов данных с помощью библиотеки Pandas
# Вы вести рабочий каталог
import os
print os.getcwd()
# C:\Python27\Scripts
# Установить рабочий каталог
os.chdir('C:/Users/general 1 /Documents/simple Python files')
print os.getcwd()
# C:\Users\general 1 \Documents\simple Python files
# загрузить pandas
import pandas as pd
# прочитать файл данных csv с именем 'small_dataset.csv', содержащий 4 строки и 3 переменные
my_data = pd. read_csv("smal l_dataset.csv")
my_data
# х у z
#О 1 2 3
#1 4 5 б
#2 7 8 9
#3 10 11 12
my_data.shape
# (4, 3)

# количество строк и столбцов в наборе данных

my_data.shape[O]
#4

# количество строк в наборе данных

my_data.shape[1 ]
#3

# количество столбцов в наборе данных

1 30.1 . Базовы й код для импорта, подмножества и записи внешних файлов данных

493

# В Python испол ьзуется О-ориентированная индексация. Первая строка или столбец
# в наборе данных располагается # в позиции О. В R первая строка или столбец в наборе
# данных располагается # в позиции 1 .
# Выбрать первые две строки
my_data[0:2]
# х у z
#О 1 2 3
#1 4 5 6
# Выбрать вторую и третью строки
my_data[1 :З]
# х у z
#1 4 5 6
#2 7 8 9
# Выберите третью строку
my_data[2:3]
# х у z
#2 7 8 9
# В ыбрать первые два элемента первого столбца
my_data.iloc[0:2, 0:1 ]
# х
#О 1
#1 4
# Выбор первого элемента переменных у и z
my_data. loc[0, ['у', 'z'] I
#у 2
#z 3
# Выделить первые три элемента переменных у и z
my_data. loc[0:2, ['у', 'z']]
# у z
#О 2 3
#1 5 6
#2 8 9
# Записать первые три элемента переменных у и z
# во внешний файл . Здесь index = О означает, что имена строк не записы ваются .
my_data2 = my_data.loc[0:2, ['у', 'z']]
my_data2.to_csv('my.output.csv', index = О)

Глава 1 31 . Распаковка файлов
Для извлечения или распаковки архивов tarball, Z I P или gzip в Python предоставляются
модули tarf1le, zipf1le и gzip соответственно. Модуль tarfile в Python предоставляет функцию
TarFile.extractall(path=".", members=None) для извлечения из tаr-файла. Модуль Python zipfile пре­
доставляет функцию ZipFile.extractall([pathL members[, pwdll) для разархивирования ZIР-фай­
лов. Наконец, модуль gzip в Python предоставляет для распаковки соответствующего форма­
та класс GzipFile.

494

Глава 1 32. Работа с ZI Р-архивами

1 31 . 1 . Использование Python ZipFile.extractall() для распаковки
ZIР-файла
f1le_unzip = 'fi lename.zip'
unzip = zipfi le.ZipFile(f1 le_unzip, 'г')
unzip.extractal l()
unzip.closeO

1 31 .2. Использование Python TarFile.extractall() для распаковки
tarball-apxивa
file_untar = 'f1 lename.tar.gz'
untar = tarf1le.TarFile(file_untar)
untar.extractall ()
untar.close()

Глава 1 32. Работа с ZIР-архивами
1 32.1 . Изучение содержимого ZIР-файла

Существует несколько способов проверки содержимого ziр-файла. Можно использовать
отправляемую в stdout:

printdir, чтобы просто получить разнообразную информацию,
with zipfi le.ZipFile(f1lename) as zip:
zip.printdir()
# Вывод:
Modified
# File Name
201 6-06-25 22:1 3:34
# pyexpat. pyd
201 6-06-25 22:1 3:34
# python.exe
201 6-06-25 22:1 3:34
# pythonЗ.dll
# python35.dll
201 6-06-25 22:1 3:34

Size
1 57336
39576
51 864
3 1 27960

Мы также можем получить список имен с помощью метода namelist. Здесь мы просто вы­
водим список:
with zipfi le.ZipFile(f1lename) as zip:
print(zip.namel istO)
# Вы вод: ['pyexpat.pyd', 'python.exe', 'python3.dl l', 'python35.dl l', . . . etc . . . . ]

Вместо namelist можно вызвать метод infolist, который возвращает список объектов Ziplnfo, со­
держащих дополнительную информацию о каждом файле, например метку времени и размер:
with zipfi le.ZipFile(f1lename) as zip:
info = zip.infol istO
print(zip[0].f1 lename)
print(zip[0].date_time)
ргi nt(i nfo[0]. fi le_size)
# Вы вод: pyexpat.pyd
# Вы вод: (201 6, 6, 25, 22, 1 3, 34)
# Вы вод: 1 57336

1 32.2. Открытие ZIР-файлов

Для начала импортируем модуль zipf1le и задаем имя файла.

import zipf1le
fi lename = 'zipf1le.zip'

1 32.3. Извлечение содержимого ZI Р-файла в каталог

495

Работа с ziр-архивами очень похожа на работу с file, вы создаете объект, открывая ziр-архив, который позволяет вам работать с ним, прежде чем снова закрыть его.
zip = zipf1le.ZipFile(filename)
print(zip)
#
zip.close()

В Python 2.7 и в Python 3 версий выше 3.2 можно использовать менеджер контекста with.
Открываем файл в режиме чтения, а затем выводим список имен файлов:
with zipf1 le.ZipFile(f1lename, 'r') as z:
print(zip)
#

1 32.З. Извлечение содержимого ZI Р-файла в каталог
Извлечение всего содержимого ziр-файла:

import zipfile
with zipf1 le.ZipFile('zipfi le.zip';r') as zfile:
zfile.extractall('path')

Если необходимо извлечь отдельные элементы, используйте метод extract, который принимает в качестве входного параметра список имен и путь:
import zipfile
f=open('zipf1 le.zip';гb')
zf1 le=zipfi le.ZipFi le(f)
for cont in zf1 le.namelist():
zfi le.extract(cont,path)

1 32.4. Создание новых архивов

Для создания нового архива откройте ziр-файл в режиме записи.

import zipfile
new_arch=zipf1le.ZipFile("f1lename.zip",mode="w")

Для добавления элементов в этот архив используйте метод write().

new_arch.write('filename.txt';fi lename_in_archive.txt') # первый параметр - имя файла,
# второй - имя файла в архи ве, по умолчанию будет взято имя файла, есл и оно не указано
new_arch.close()

Если необходимо записать в архив строку байтов, то можно воспользоваться методом
writestr().
str_bytes="string buffer"
new_arch.writestr('fi lename_string_in_archive.txt',str_bytes)
new_arch.close()

Глава 1 33 . Работа с GZip
Этот модуль предоставляет простой интерфейс для сжатия и распаковки файлов подобно
тому, как это делают GNU-программы gzip и gunzip. Сжатие данных обеспечивается модулем
zlib. Модуль gzip предоставляет класс GzipFile, который создан по образцу объекта файла в
Python. Класс GzipFile читает и записывает файлы в формате gzip, автоматически сжимая или
распаковывая данные так, чтобы они выглядели как обычный объект файла.

Глава 1 34. Стек

496

1 33.1 . Чтение и запись файлов GNU zip
import gzip
import os

outfilename = 'example.txt.gz'
output = gzip.open(outfilename, 'wb')
try:
output.write('Contents of the example fi le go here.\n')
final ly:
output.close()
print outf1lename, 'contains', os.stat(outfi lename).st_size, 'bytes of compressed data'
os.system('f1le -Ь --mime %s' % outf1lename)
Сохраните его под именем 1 gzip_write. py1 и запустите через терминал:

$ python gzip_write. py
application/x-gzip; charset=Ьinary
example.txt.gz contains 68 bytes of compressed data

Глава 1 3 4. Стек
Стек - это контейнер с объектами, которые чаще всего вставляются и вынимаются по
принципу "последним пришел - первым ушел" (LIFO, last in first out). В pushdown-cтeкax раз­
решены только две операции: заталкивание, или проталкивание (push) элемента в стек
и выталкивание, или удаление (рор) элемента из стека. Стек является структурой данных
с ограниченным доступом - добавлять и удалять элеме1ПЪ1 из стека можно только сверху.
Вот структурное определение стека: стек либо пуст, либо состоит из вершины и остальной
части, которая является стеком.

1 34. 1 . Создание класса Stack с объектом List

Используя объект list, можно создать полнофункциональный общий стек с такими вспо­
могательными методами, как чтение головного элемента (peeking) и проверка пустоты стека.
#определение класса стека
class Stack:
def _init_(self):
self.items =



#метод для проверки того, пуст стек или нет
def isEmpty(self):
return self. items = =



#метод для протал кивания элемента
def push(self, item):
self.items.append(item)
#метод для вытал кивания элемента
def pop(self):
return self. items. pop()
#проверить, какой элемент находится на вершине стека, не удаляя его
def peek(self):
return self.items[-1 ]

1 34.2. Разбор (парсинг) круглых скобок

497

#метод для получения размера
def size(self):
return len(self.items)
#для п росмотра всего стека
def fullStack{self):
return self.items
Пример:
stack = Stack()
ргiпt('Текущий стек:', stack.fullStack{})
ргiпt{'Стек пуст?:', stack.isEmpty())
ргiпt{'Проталкиваем integer 1 ')
stack.push{1 )
ргiпt{'Проталкиваем строку "Я обычный стек! "')
stack.push{'Я обычны й стек!!')
ргiпt{'Проталкиваем integer З')
stack.push{З)
ргiпt('Текущий стек:', stack.fullStack{})
ргiпt{'Вытолкнуты й элемент:', stack.pop())
ргiпt('Текущий стек:', stack.fullStack(})
ргiпt{'Стек пуст?:', stack.isEmpty())
Выход:

Текущий стек: []
Стек пуст?: True
Проталкиваем integer 1
Проталкиваем string "Я обычный стек!"
Проталкиваем integer 3
Текущий стек: [1, 'Я обычный стек!', З]
Вытолкнутый элемент : 3
Текущий стек: [1, 'Я обычный стек !']
Стек пуст?: False

1 34.2. Разбор (парсинг) круглых скобок
Стеки часто используются для синтаксического анализа (парсинга). Простой задачей
синтаксического анализа является проверка соответствия строки крутлых скобок.
Например, строка (□) является совпадающей, так как внешние и внутренние скобки обра­
зуют пары. {}) не совпадает, так как последняя крутлая скобка не имеет партнера. {[)] также
не совпадает, так как пары должны быть либо полностью внутри, либо снаружи друтих пар.
def checkParenth{str):
stack = Stack{}
pushChars, popChars = ")}]"
for с in str:
if с in pushChars:
stack.push{c)
elif с in popChars:
if stack.isEmpty():
return False
else:
stackTop = stack.pop()
# Проверяет, совпадает ли открывающая скобка с закрывающей.
balancing Bracket = pushChars[popChars.index(c)]
if stackTop != balancing Bracket:
return False
else:
return False
return not stack.isEmpty()

498

Глава 1 35. Работа в обход глобал ьной блокировки и нтерп ретатора (G I L)

Глава 1 35. Работа в обход глобальной
блокировки интерпретатора {G I L)
1 35.1 . Multiprocessing.Pool

Когда возникает вопрос, как использовать потоки в Python, чаще всего звучит совет: "Не
надо с ними связываться. Вместо этого используйте процессы". Модуль мультипроцессинга
позволяет создавать процессы с помощью синтаксиса, аналогичного созданию потоков, но
лучше использовать их удобный объект Pool.
Используя код, который впервые применил Дэвид Бизли, чтобы показать опасность ис­
пользования потоков в обход GIL, перепишем его с использованием multiprocessing.Pool:
Вот код Дэвида Бизли, демонстрирующий проблемы с потоками и GIL:
from threading import Thread
import time
def countdown(n):
while n > О:
п -= 1

COUNT = 1 0000000
t1 = Thread(target = countdown,args = (COUNT/2,))
t2 = Thread(target = countdown,args = (COUNT/2,))
start = time.timeO
t1 .start();t2.start()
t1 .join();t2.join()
end = time.time()
print end-start

Перепишем его с использованием multiprocessing.Pool:
import multiprocessing
import time
def countdown(n):
while n > О:
п -= 1

COUNT = 1 0000000
start = time.time()
with multiprocessing. Pool as pool:
pool.map(countdown, [COUNT/2, COUNT/2])
pool.close()
pool.join()
end = time.time()
print(end-start)

Вместо создания потоков здесь создаются новые процессы. Поскольку каждый процесс яв­
ляется собственным интерпретатором, проблем с GIL не возникает. multiprocessing.Pool откроет
столько процессов, сколько ядер имеется на машине, хотя в приведенном примере ему потре­
буется только два. В реальных условиях необходимо, чтобы список имел длину не меньше, чем
количество процессоров на машине. Pool будет запускать функцию, которую вы укажете ему
выполнить с каждым аргументом, вплоть до количества создаваемых процессов. После завер­
шения работы функции все оставшиеся в списке функции будут запущены на этом процессе.
Даже при использовании оператора with, если не закрьггь и не присоединить пул, процессы про­
должают существовать. Для очистки ресурсов следует всегда закрывать и присоединять пулы.

1 35.2. Использование Cython nogil

1 35.2. Использование Cython nogil

499

Cython - это альтернативный интерпретатор Python. Он использует GIL, но позволяет от­
ключить его. В качестве примера, используя код, который впервые использовал Дэвид Биз­
ли, чтобы показать опасность использования потоков в обход GIL (см. предыдущую главу),
перепишем его, используя ключевое слово nogil в Cython:
from threading import Thread
import time
def countdown(n):
while n > О:
n -= 1
COUNT = 1 0000000
with nogil:
t1 = Thread(target = countdown,args= (COUNT/2,))
t2 = Thread(target = countdown,args= (COUNT/2,))
start = time.time()
t1 .start();t2.start()
t1 .join();t2.join()
end = time.time()
print end-start

Все очень просто, если использовать Cython. Обратите внимание: в документации сказано, что необходимо следить за тем, чтобы не изменять никакие объекты Python:
Код в теле утверждения не должен манипулировать объектами Python каким-либо образом и не
должен вызывать ничего, что манипулирует объектами Python без предварительного повторно­
го обращения к GIL. Cython не проверяет это.

Глава 1 3 6. Разверты вание
1 36. 1 . Загрузка пакета Conda

Перед началом работы необходимо, чтобы в вашей системе был установлен кроссплат­
форменный дистрибутив Anaconda на системном аккаунте на Binstar. Если вы не используе­
те Anaconda 1.6+, установите клиент командной строки Ьinstar:
$ conda instal l Ьinstar
$ conda update Ьinstar

Если вы не используете Anaconda, то Binstar также доступен на pypi:
$ pip install Ьinstar

Теперь мы можем войти в систему:

$ Ьinstar login

Протестируйте свой вход в систему с помощью команды whoami:

$ Ьinstar whoami

Будем загружать пакет с простой функцией 'hello world'. Чтобы проследить за этим, нач­
нем с получения демонстрационного пакета с Github:
$ git clone https://github.com/< NAM E>/< Package>

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

package/
setup.py

500

Глава 1 37. Модуль logging
tesLpackage/
_init_.py
hello.py
Ыd.bat
build.sh
meta.yaml

Setup.py - это стандартная сборка Python, а hello.py содержит нашу единственную функ­
цию hello_world().
Ыd.bat, build.sh и meta.yaml - это скрипты и метаданные для пакета Conda. Более подроб­
ную информацию об этих трех файлах и их назначении можно найти на странице сборки
Conda.
Теперь создадим пакет, выполнив команду:
$ conda build tesLpackage/

Это все, что требуется для создания пакета Conda. Последний шаг - загрузка в Ьinstar пу­
тем копирования и вставки последней строки вывода после выполнения команды build test_
package/ в Conda. Это может выглядеть следующим образом:
$ Ьinstar upload /home/xavier/anaconda/conda-Ыd/linux-64/test_package-0.1 .0-py27_0.tar.bz2

Поскольку вы создаете пакет и релиз впервые, вам будет предложено заполнить несколь­
ко текстовых полей, что можно сделать и через н еб-приложение. На экране появится сооб­
щение об успешной загрузке вашего пакета Conda в Binstar.

Глава 1 37. Модуль logging
1 37 . 1 . Введение в использование модуля logging

Функции и классы этого модуля реализуют гибкую систему регистрации событий для
приложений и библиотек. Все модули Python могут участвовать в протоколировании, поэто­
му журнал вашего приложения (application log) может включать ваши собственные сообще­
ния, интегрированные с сообщениями сторонних модулей. Итак, начнем:
Пример конфигурации непосредственно в коде:
import logging
logger = logging.getLogger()
handler = logging.StreamHandler()
formatter = logging. Formatter(
'%(asctime)s %(name}-1 2s %(1evelname}-8s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.DEBUG}
logger.debug('this is а %s test', 'debug')

Пример вывода:
2016-07-26 18:53:55,332 root

DEBUG this is а debug test

Пример конфигурации через файл INI:

Предположим, что файл имеет имя logging_config.ini.
[loggers]
keys = root
[handlers]
keys =stream_handler

1 37.2. Протоколирование исключений

501

[formatters]
keys= formatter
[logger_root]
level =DEBUG
hand lers =stream_hand ler
[hand ler_stream_hand ler]
class = StreamHand ler
level =DEBUG
formatter = formatter
args = (sys.stderr,)
[formatter_formatter]
format = %(asctime)s %(name)-1 2s %(1evelname)-8s %(message)s

Затем используйте logging.conf1g.f1leConf1g() в коде:
import logging
from logging.conf1g import f1 leConfig
f1 leConfig('logging_conf1g. ini')
logger = logging.getlogger()
logger.debug('often makes а very good meal of %s', 'visiting tourists')

Пример конфигурирации с использованием словаря

Начиная с версии Python 2. 7 можно использовать словарь с деталями конфигурации. РЕР 391
содержит список обязательных и необязательных элементов конфигурационного словаря.
import logging
from logging.conf1g import dictConfig
logging_config = dict(
version = 1 ,
formatters = {
'f': {'format':
'%(asctime)s %(name)-1 2s %(Ievelname)-8s %(message)s'}
},
handlers = {
'h': {'class': 'logging.StreamHand ler',
'formatter': 'f',
'level': logging.DEBUG}
},
root = {
'hand lers': ['h'],
'level': logging.DEBUG,
},
dictConf1g(logging_config)
logger = logging.getlogger()
logger.debug('often makes а very good meal of %s', 'visiting tourists')

1 37.2. Протоколирование исключений

Если вы хотите регистрировать исключения, то можно и нужно использовать метод

logging.exception(msg):

»> import logging
»> logging.basicConf1g()

502

Глава 1 37. Модуль logging

>» try:
raise Exception('foo')
... except:
logging.exception('bar')
ERROR:root:bar
Traceback (most recent call last):
File "", l ine 2, in
Exception: foo

Не передавайте исключение в качестве аргумента:

Так как logging.exception(msg) ожидает аргумент msg, распространенной ошибкой являет­
ся передача исключения в вызов протоколирования в таком виде:
>» try:
raise Exception('foo')
... except Exception as е:
logging.exception(e)
ERROR:root:foo
Traceback (most recent call last):
File "", l ine 2, in
Exception: foo

Хотя на первый взгляд может показаться, что это правильно, на самом деле это пробле­
матично из-за того, как исключения и различные кодировки работают вместе в модуле про­
токолирования:
>» try:
raise Exception(u'fёiёi')
... except Exception as е:
logging.exception(e)
Traceback (most recent call last):
File "/.../python2.7/logging/_init_.py", line 861 , in emit
msg = self.format(record)
File "/.../python2.7/logging/_init_.py", line 734, in format
return fmt.format(record)
File "/.../python2.7/logging/_init_.py", line 469, in format
s = self._fmt % record._dict_
UnicodeEncodeError: 'ascii' codec can't encode characters in position 1 -2: ordinal not in range(1 28)
Logged from f1le , line 4

Попытка зарегистрировать исключение, содержащее символы Unicode, таким способом
будет безуспешной. Он скроет стек-трассировку исходного исключения, переопределив его
на новое, которое будет вызвано при форматировании вашего вызова logging.exception(e).
Очевидно, что в собственном коде вы можете знать о кодировке в исключениях. Однако
в библиотеках сторонних разработчиков этот вопрос может решаться по-другому.
Правильное использование:

Если вместо исключения передать сообщение и позволить Python самому "сделать свое
волшебство", то все будет работать:
>» try:
raise Exception(u'foo')
... except Exception as е:
logging.exception('bar')
ERROR:root:bar
Traceback (most recent call last):

1 38.1 . Объект (метод) сервера

503

File "", line 2, in
Exception: f\xfб\xfб

Как видно, в этом случае мы не используем е, а вызов logging.exception( ... ) магическим об­
разом форматирует последнее исключение.
Регистрация исключений с уровнями журнала, отличными от ERROR

Если вы хотите регистрировать исключение не на уровне ERROR, а на другом уровне,
можно использовать аргумент exc_info регистраторов (логгеров) по умолчанию:
logging.debug('exception occurred', exc_info=1 )
logging.info('exception occurred', exc_info=1 )
logging.warning('exception occurred', exc_info=1 )

Доступ к сообщению об исключении

Следует помнить, что библиотеки могут выдавать исключения с сообщениями в виде лю­
бой из форм представления Unicode или байт-строки (utf-8, если повезет). Если вам действи­
тельно нужно получить доступ к тексту исключения, то единственный надежный способ, ко­
торый всегда будет работать, - это использовать repr(e) или форматирование строки %r:
>>> try:
raise Exception(u'foo')
... except Exception as е:
logging.exception('received this exception: %r' % е)
ERROR:root:received this exception: Exception(u'f\xfб\xfб',)
Traceback (most recent call last):
File "", line 2, in
Exception: f\xfб\xfб

Глава 1 3 8. Стандарт Web Server Gateway
l nterface (WSG I)
Параметр

Опи сание

start_response

Функция, используемая для обработки запуска

1 38. 1 . Объект (метод) сервера

Нашему серверному объекту передается параметр 'application', который может быть лю­
бым вызываемым объектом приложения. Он записывает сначала заголовки, а затем тело
данных, возвращаемых нашим приложением, в стандартный вывод системы.
import os, sys
def run(application):
environ['wsgi. input']
environ['wsgi.errors']



headers_set =
headers_sent =

= sys.stdin
= sys.stderr



def write (data):
Записы вает данные заголовка из 'start_response()', а также данные тела из 'response'
в стандартны й вы вод систем ы.

504

Глава 1 39. События, посылаем ые сервером (SSE)
if not headers_set:
raise AssertionError("writeO before start_response()")
elif not headers_sent:
status, response_headers = headers_sent[:] = headers_set
sys.stdout.write('Status: %s\r\n' % status)
for header in response_headers:
sys.stdout.write('%s: %s\r\n' % header)
sys.stdout. write('\r\n')
sys.stdout. write(data)
sys.stdout. flush()

def starLresponse(status, response_headers):
""" Устанавливает заголовки для ответа, возвращаемого этим сервером."""
if headers_set:
raise AssertionError("Зaroлoвки уже установлены!")
headers_set[:] = [status, response_headers]
return write
# Это самая важная часть нашего 'server object'
# Наш результат будет сгенерирован приложением 'application', переданным этому методу
# в качестве параметра
result = application(environ, start_response)
try:
for data in resu lt:
if data:
# Тело не пусто, отправьте его данные в 'write()'
write(data)
if not headers_sent:
write(")
# Тело пустое, передаем пустую строку в 'write()'

Глава 1 39. События, посылаем ые
сервером (SSE)
Server Sent Events (SSE) - это однонаправленное соединение между сервером и клиен­
том (обычно веб-браузером), которое позволяет серверу передавать ("проталкивать", "push")
информацию клиенту. Технология похожа на технологии веб-сокетов и "длинного опроса"
(Long Polling). Основное различие заключается в том, что SSE является однонаправленным,
только сервер может посылать информацию клиенту, а при использовании веб-сокетов оба
могут посылать информацию друг другу. Считается, что SSE гораздо проще в использовании
и реализации, чем веб-сокеты.

1 39 . 1 . Flask SSE
@route("/stream")
def stream():
def event_streamO:
while True:
if message_to_send:
yield "data:
{}\n\n". format( message_to_send)"
return Response(event_stream(), mimetype="text/event-stream")

1 39.2. Asyncio SSE

505

1 39.2. Asyncio SSE
В данном примере используется SSЕ-библиотека asyncio:
import asyncio
import sse
class Hand ler(sse.Handler):
@asyncio.coroutine
def hand le_request(self):
yield from asyncio.sleep(2)
self.send('foo')
yield from asyncio.sleep(2)
self.send('bar', event='wakeup')
start_server = sse.serve(Hand ler, 'localhost', 8888)
asyncio.get_event_loop(). run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

Глава 1 40. Ал ьтернативы оператору
switch из других язы ков
1 40. 1 . Используйте то, что предлагает Python: конструкция if/else
Если вам нужна конструкция switch/case, то наиболее простым способом будет использо­
вание старых добрых if/else:
def switch(value):
if value == 1 :
return "опе"
if value == 2:
return "two"
if value == 42:
return "ответ на вопрос о жизни, Вселенной и всем остальном"
raise Exception("Дeлo не найдено!")
Это может выглядеть излишним и не всегда красивым, но это, безусловно, самый надежный способ, и он работает:
»>

switch(1 )

»>

switch(2)

»>

switch(З)

one
two

Exception: Дело не найдено!
switch(42)
ответ на вопрос о жизни, Вселенной и всем остальном
»>

1 40.2. Использование словаря функций
Другой прямой путь - это создание словаря функций:
switch = {
1 : lambda: 'опе',
2: lambda: 'two',
42: lambda: 'ответ о жизни, Вселенной и всем остальном',

506

Глава 1 40. Альтернативы оператору switch из других язы ков

затем добавляется функция по умолчанию:
def default_case():
raise Ехсерtiоп('Дело не найдено!'}
и с помощью метода get словаря получаем функцию, заданную значением для проверки,
и можем запустить ее. Если значение не существует в словаре, то выполняется defau lt_case.
»> switch.get(1 , defaulLcase)()

one

»> switch.get(2, defaulLcase)()

two

>» switch.get(З, defaulLcase)()

Exception: Дело не найдено!

>» switch.get(42, defau lLcase)()

ответ о жизни, Вселенной и всем остальном
Также можно сделать "синтаксический сахар", чтобы переключатель выглядел красивее:
def run_switch(val ue):
return switch.get(value, defaulLcase)()
»> run_switch(1 )

one

1 40.3. Использование интроспекции классов
Для имитации структуры switch/case можно использовать класс. Ниже показано исполь­
зование интроспекции класса (с помощью функции getattr(), разрешающей строку в связан­
ный метод на экземпляре) для разрешения части "case". Затем этот интроспективный метод
алиасится на метод _cal l_ для перегрузки оператора ().
class SwitchBase:
def switch(self, case):
m = getattr(self, 'case_{}'.format(case), None)
if not m:
return self.default
return m
_call_ = switch
Затем, чтобы все выглядело красивее, мы подклассифицируем класс SwitchBase (но это
можно сделать и в одном классе), и там все case определяем как методы:
class CustomSwitcher:
def case_1 (self}:
return 'one'
def case_2(self}:
return 'two'
def case-42(self}:
return 'ответ на вопрос о жизни, Вселенной и всем остальном!'
def default(self}:
raise Exception('Not а case!'}
и тогда мы сможем его использовать:
»> switch = CustomSwitcher()
»> print(switch(1 ))

1 40.4. Использование менеджера контекста
опе
»>

two
»>

507

print(switch(2}}
print(switch(З}}

Exception: Not а case!
print(switch(42}}
ответ на вопрос о жизни, Вселенной и всем остальном!
»>

1 40.4. Использование менеджера контекста

Другой способ, удобный для чтения и элегантный, но гораздо менее эффективный, чем
структура if/else, заключается в создании класса, который будет считывать и хранить значе­
ние, с которым нужно сравнить, и представлять себя в контексте как вызываемый класс, ко­
торый будет возвращать true, если он соответствует сохраненному значению:
class Switch:
def _iniL(self, value):
self._val = value
def _enter_(self}:
return self
def _exit_(self, type, value, traceback):
return False # Позволяет в ы полнить обратную трассировку
def _cal l_(self, cond, *mconds):
return self._val in (cond,}+mconds

определение случаев практически совпадает с реальной конструкцией switch/case (пока­
занной внутри нижеприведенной функции):
def run_switch(value):
with Switch(value) as case:
if case(1 ):
return 'one'
if case(2}:
return 'two'
if саsе(З}:
return 'ответ на вопрос о жизни, Вселенной и всем остальном!'
# default
raise Exception('Not а case!'}

Таким образом, исполнение будет следующим:
»>

опе
»>

two
»>

run_switch(1 )
run_switch(2}
run_switch(З}

Exception: Not а case!
run_switch(42}
ответ на вопрос о жизни, Вселенной и всем остальном !
»>

Nota Bene:
Это решение предлагается в качестве модуля switch, доступного на pypi.

508

Глава 1 41 . Деструктуризация списка, упаковка и распаковка

Глава 1 4 1 . Деструктуризация списка,
упаковка и распаковка
1 41 . 1 . Присваивание деструктуризации

В присваиваниях можно разбить итерируемый объект на значения, используя синтак­
сис распаковки:
Раскладывание на значения
а, Ь = (1 , 2}

print(a)
# Выводит: 1
print(b}
# Выводит: 2

Если попытаться распаковать больше, чем длина итерируемого объекта, то будет выдана ошибка:
а, Ь, с = [1 ]
# Raises: ValueError: not enough values to unpack (expected 3, got 1 } (недостаточно значений для
распаковки (ожидалось 3, получено 1 }}

Версия Python З.х > 3.0
Деструктуризация в виде списка
Распаковать список неизвестной длины можно с помощью следующего синтаксиса:
head, "'1ail = [1 , 2, 3, 4, 5]

Здесь мы извлекаем первое значение в виде скаляра, а остальные значения - в виде списка:

print(head}
# Выводит: 1
print(tail}
# Выводит: [2, 3, 4, 5]

Что эквивалентно:
1 = [1 , 2, 3, 4, 5]
head = 1[0]
tail = 1 [1 :]

Он также работает с несколькими элементами или элементами, находящимися в конце
списка:
а, Ь, *other, z = [1 , 2, 3, 4, 5]
print(a, Ь, z, other)
# Выводит: 1 2 5 [3, 4]

Игнорирование значений в деструктурирующих присваиваниях

Если вас интересует только данное значение, вы можете использовать _, чтобы указать
то, что вас не интересует. Примечание: при этом _ все равно будет установлено, просто боль­
шинство людей не используют его в качестве переменной.
а, _ = [1 , 2]
print(a)
# Выводит: 1
а, � с = (1 , 2, 3)
print(a)
# Выводит: 1
print(c)
# Выводит: 3

1 41 .2. Аргументы функции упаковки

509

Версия Python З.х > 3.0

ИгнорироваШiе списков в деструктурирующих присваиваниях

Наконец, с помощью синтаксиса *_ в присваивании можно игнорировать множество значений:
а, *_ = [1 , 2, 3, 4, 5]
print(a)
# В ыводит: 1
что не очень интересно, так как вместо этого можно использовать индексацию списка.
Это удобно для работы с первым и последним значениями в одном присваивании:
а, *� Ь = [1 , 2, 3, 4, 5]
print(a, Ь)
# Prints: 1 5
или извлечении нескольких значений сразу:
а, � Ь, � с, *_ = [1 , 2, 3, 4, 5, б]
print(a, Ь, с)
# Prints: 1 3 5

1 41 .2. Ар гументы функции упаковки

Можно определить ряд обязательных аргументов функции:

def fun1 (arg 1 , arg2, arg3):
return (arg1 ,arg2,arg3)
что сделает ее вызываемой только при наличии трех аргументов:
fun 1 (1 , 2, 3)
и можно определить аргументы как необязательные, используя значения по умолчанию:
def fun2(arg 1 ='а', arg2='b', arg3='c'):
return (arg1 ,arg2,arg3)
поэтому функцию можно вызывать разными способами, например:
fun2(1 )
fun2(1 , 2)
fun2(arg2=2, arg3=3)

_, (1 ,Ь,с)
-> (1 ,2,с)
-> (а,2,3)

Но вы также можете использовать синтаксис деструктуризации для упаковки аргумен­
тов, так что вы можете присваивать переменные, используя список или словарь.

Упаковка списка аргументов

Предположим, что у вас есть список значений:

1 = [1 ,2,3]
Вызвать функцию со списком значений в качестве аргумента можно с помощью синтаксиса *:
fun 1 (*1)
# Возвращает: (1 ,2,3)
fun 1 (*['w', 't', 'f'])
# Возвращает: ('w','t';t')
Если только вы не предоставите список, длина которого соответствует количеству аргументов:
fun 1 (*['oops'])
# Raises: ТуреЕггог: fun1 () missing 2 req uired positional arguments: 'агg2' and 'arg3' (в fun 1 ()
отсутствует 2 необходимых позиционных аргумента: 'arg2' и 'arg3')

Глава 1 41 . Деструктуризация списка, упаковка и распаковка

51 0

Упаковка именованных аргументов

Можно также упаковывать аргументы с помощью словаря. Вы можете использовать оператор **, чтобы указать Python распаковать словарь в качестве значений параметров:
d={

'arg 1 ': 1 ,
'arg2': 2,
'arg3': 3
}
fun1 (**d)
# Возвращает: (1 , 2, 3)

если функция имеет только позиционные аргументы (без значений по умолчанию), то
необходимо, чтобы словарь содержал все ожидаемые параметры и не имел лишних, иначе
будет выдана ошибка:
fun1 (**{'arg 1 ': 1 , 'arg2':2})
# Вызы вает: TypeError: fun 1 () missing 1 required positional argument: 'arg3' (у fun1 () отстуствует
1 требуемы й позиционны й аргумент: 'arg3')
fun1 (**{'arg 1 ': 1 , 'arg2':2, 'arg3':3, 'arg4':4})
# Вызы вает: TypeError: fun 1 () got ап unexpected keyword argument 'arg4' (fun1 () получила
неожиданны й аргумент в виде ключевого слова 'arg4')

Для функций, имеющих необязательные аргументы, аргументы можно упаковать в сло­
варь аналогичным образом:
fun2(**d)
# Возвращает: (1 , 2, 3)

Но там можно не указывать значения, так как они будут заменены значениями по умол­
чанию:
fun2(**{'arg2': 2})
# Возвращает: ('а', 2, 'с')

При этом, как и ранее, нельзя задавать дополнительные значения, не являющиеся существующими параметрами:
fun2{**{'arg 1 ': 1 , 'arg2':2, 'arg3':3, 'arg4':4})
# Вызы вает: TypeError: fun2() got an unexpected keyword argument 'arg4' (fun2() получила
неожиданны й аргумент в виде ключевого слова 'arg4')

В реальных условиях функции могут иметь как позиционные, так и необязательные ар­
гументы, и это работает одинаково:
def fun3(arg1 , arg2='b', arg3='c')
return (arg 1 , arg2, arg3)

можно вызвать функцию, используя только итерируемый объект:
fun3(*[1 ])
# Возвращает: (1 , 'Ь', 'с')
fun3(*[1 ,2,3])
# Возвращает: (1 , 2, 3)

или словарь:
fun3(**{'arg 1 ':1 })
# Возвращает: (1 , 'Ь', 'с')
fun3(**{'arg 1 ': 1 , 'arg2':2, 'arg3':3})
# Возвращает: (1 , 2, 3)

или вы можете использовать оба варианта в одном вызове:
fun3(*[1 ,2], **{'arg3':3})
# Возвращает: (1 ,2,3)

1 41 .3. Распаковка аргументов функции

51 1

Однако следует помнить, что для одного и того же аргумента нельзя указывать несколько значений:
fun3(*[1 ,2], **{'arg2':42, 'argЗ':3})
# Вызы вает: TypeError: fun3() got multiple values for argument 'arg2' (fun3() получ ил несколько
значений для аргумента 'arg2')

1 41 .З. Распаковка ар гу ментов функции

Если в ы хотите создать функцию, которая может принимать любое количество аргумен­
тов и при этом не задавать позицию или имя аргумента во время "компиляции", то это воз­
можно, и вот как следует это сделать:
def fun1 (*args, **kwargs):
print(args, kwargs)
Параметры *args и **kwargs - это специальные параметры, которые устанавливаются в
виде кортежа и словаря соответственно:
fun 1 (1 ,2,3)
# Вы водит: (1 , 2, 3) {}
fun 1 (а = 1 , Ь = 2, с = 3)
# Вы водит: () {'а': 1, 'Ь': 2, 'с': 3}
fun 1 ('х', 'у', 'z', а = 1 , Ь = 2, с = 3)
# Вы водит: ('х', 'у', 'z') {'а': 1 , 'Ь': 2, 'с': 3}
Если вы просмотрите достаточно много примеров кода на языке Python, то быстро обна­
ружите, что подобное широко используется при передаче аргументов в другую функцию.
Например, если вы хотите расширить строковый класс:
class MyString(str):
def _iniL(self, *args, **kwarg):
print('Constructing MyString')
super(MyString, self) ._init_(*args, **kwarg)

Глава 1 42. Доступ к исходному коду
Python и байт- коду
1 42. 1 . Отображение байт-кода функции

Интерпретатор Python компилирует код в байт-код перед его выполнением на виртуаль­
ной машине Python (см. также главу "Что такое байткод Python?").
Вот как просмотреть байт-код функции на Python:
import dis
def fi b(n):
if п < = 2: return 1
return fi b(n-1 ) + fib(n-2)
# Вы вести дизассембл ированный байт-код фун кци и.
dis.dis(f1b)
Функция dis.dis в модуле dis вернет декомпилированный байт-код переданной ей
функции.

51 2

Глава 1 42. Доступ к исходному коду Python и байт-коду

1 42.2. Отображение исходного кода объекта
Объекты, не являющиеся встроенными

Для вывода исходного кода объекта Python используйте команду inspect. Обратите внима­
ние, что это не работает ни для встроенных объектов, ни для объектов, создаваемых в интер­
активном режиме. Для них потребуются другие методы, о которых будет рассказано далее.
Вот как вывести исходный код метода randint из модуля random:
import random
import inspect
print(inspect.getsource(random. randint))
# Выходные дан н ые:
# def randint(self, а, Ь):
""" Возвращает случайное целое число в диапазоне [а, Ь], включая обе конеч н ые точки.
#
#
#
#
return self.randrange(a, Ь+1 )

Чтобы просто вывести строку документации:
print(inspect.getdoc(random.randint))
# Вы вод:
# Возвращает случайное целое число в диапазоне [а, Ь], вкл ючая обе конеч ные точки.

Вывести полный путь к файлу, в котором определен метод random.randint:
print(inspect.getfile(random.randint))
# c:\Python35\lib\random.py
print(random.randint._code_.co_f1lename) # эквивалентно приведенному вы ше
# c:\Python35\lib\random.py

Объекты, создаваемые в интерактивном режиме

Если объект создается в интерактивном режиме, то команда inspect не может предоста­
вить исходный код, но вы можете использовать вместо нее dill.source.getsource:
# определить новую функциюв интерактивной оболоч ке
def add(a, Ь):
return а + Ь
print(add._code_.co_f1lename) # Output:
import dill
print dill.source.getsource(add)
# def add(a, Ь):
return а + Ь

Встроенные объекты

Исходный код встроенных функций Python написан на языке С и может быть получен
только путем просмотра исходного кода Python (размещенного на Mercurial или загружае­
мого с сайта https://www.python.org/downloads/source/).
print(inspect.getsource(sorted)) # выдает ошибку типа ТуреЕггог
type(sorted) #

1 42.3. Исследование кодового объекта функции

CPython позволяет получить доступ к объекту кода для объекта функции. Объект _code_
содержит исходный "сырой" байт-код (co_code) функции, а также другую информацию, на­
пример, имена констант и переменных.
def f1b(n):
if п book.author = "" #Какой-то парень не писал эту книгу!
»> book.author
Unknown

Глава 1 45. П акет ArcPy
1 45.1 . Использование createDissolvedGDB для создания
gdЬ-файла в рабочей области
def createDissolvedGDB(workspace, gdbName):
gdb_name = workspace + "/" + gdbName + ".gdb"

if(arcpy. Exists(gdb_name):
arcpy. Delete_management(gdb_name)
arcpy.CreateFileGDB_management(workspace, gdbName, "")
else:
arcpy.CreateFileGDB_management(workspace, gdbName, "")
return gdb_name

1 45.2. Печать значения одного поля для всех строк класса
признаков в файле geodatabase с помощью функции SearchCursor

Для печати тестового поля (TestField) из тестового класса характеристик (TestFC) в тесто­
вой базе геоданных (Test.gdb), расположенной во временной папке (C:\Temp):
with arcpy.da.SearchCursor(r"C:\Temp\Test.gdb\TestFC",["TestField"]) as cursor:
for row in cursor:
print row[O]

1 46.1 . Установка метакласса ABCMeta

51 7

Глава 1 46. Абстрактные
базовые классы (аЬс)
1 46. 1 . Установка метакласса ABCMeta

Абстрактные классы - это классы, которые предназначены для наследования, но не ре­
ализуют конкретные методы, оставляя только сигнатуры методов, которые должны реа­
лизовывать подклассы. Абстрактные классы полезны для определения и реализации аб­
стракций классов на высоком уровне, подобно концепции интерфейсов в типизированных
языках, без необходимости реализации методов.
Один из концептуальных подходов к определению абстрактного класса заключается в
том, чтобы заглушить методы класса, а при обращении к ним выдавать ошибку Notlmple­
mented Error. Это не позволяет дочерним классам обращаться к родительским методам, не пе­
реопределив их сначала. Например:
class Fruit:

def check....ripeness(self):
raise Notlmplemented Error("мeтoд check....ripeness не реализован !")
class Apple(Fruit):
pass
а = AppleO
a.check....ripeness() # вызы вает ошибку Notl mplemented Error
Создание абстрактного класса предотвращает неправильное использование методов, ко­
торые не переопределены, и, конечно, поощряет определение методов в дочерних классах,
но не принуждает к их определению. С помощью модуля аЬс мы можем запретить инстан­
цирование дочерних классов, если они не переопределяют методы абстрактных классов сво­
их родителей и предков:
from аЬс import ABCMeta

class AbstractClass(object):
# атрибут метакласса всегда должен быть задан как переменная класса
_metaclass_ = ABCMeta
# декоратор abstractmethod регистри рует этот метод как неоп ределен н ы й
@abstractmethod
def virtual_method_subclasses_must_def1ne(self):
# Можно оставить пол ностью пустым или предоставить базовую реал изацию
# Обратите вниман ие, что обычно пустая интерпретация неявно возвращает 'None',
# но при регистрации это поведение бол ьше не при меняется
Теперь можно просто подклассифицировать и переопределять:
class Subclass(AbstractClass):
def virtual_method_subclasses_must_deflne(self):
return

1 46.2. Зачем и как использовать ABCMeta и @abstractmethod

Абстрактные базовые классы (АБС) устанавливают, какие производные классы реализу­
ют те или иные методы базового класса.
Чтобы понять, как это работает и почему мы должны это использовать, давайте рас­
смотрим пример, который понравился бы Гвидо ван Россуму. Допустим, у нас есть базовый
класс MontyPython с двумя методами Uoke и punch line), которые должны быть реализованы
всеми производными классами.

51 8

Глава 1 46. Абстрактные базовые классы (аЬс)

class MontyPython:
def joke(self):
raise Notlmplemented Error()
def punchline(self):
raise Notlmplemented Error()
class ArgumentClinic(MontyPython):
def joke(self):
return "Hahahahahah"
При инстанцировании объекта и вызове двух его методов мы получим ошибку (как и
следовало ожидать) с методом punchline().
>» sketch = ArgumentClinic()
»> sketch.punchline()
Notlmplemented Error
Однако это все равно позволяет нам инстанцировать объект класса ArgumentClinic без воз­
никновения ошибки. Фактически мы не получаем ошибки до тех пор, пока не найдем функ­
цию punchline().
Избежать этого можно, используя модуль AЬstract Base Class (АБС). Посмотрим, как это ра­
ботает, на том же примере:
from аЬс import ABCMeta, abstractmethod
class MontyPython(metaclass =ABCMeta):
@abstractmethod
def joke(self):
pass
@abstractmethod
def punchline(self):
pass
class ArgumentClinic(MontyPython):
def joke(self):
return "Hahahahahah"
На этот раз при попытке инстанцировать объект из неполного класса мы сразу же получаем ошибку TypeError!
»> с = ArgumentClinic()
TypeError:
"Can't instantiate abstract class ArgumentClinic with abstract methods punchline"
В этом случае достаточно просто завершить класс, чтобы избежать ошибок типа
TypeError:
class ArgumentClinic(MontyPython):
def joke(self):
return "Hahahahahah"
def punchline(self):
return "Send in the constaЫe!"
На этот раз при инстанцировании объекта все работает.

1 47. 1 . Миксины

51 9

Глава 1 4 7. Плагины и расширения

1 47. 1 . Миксины

В объектно-ориентированном языке программирования миксин (mixin) - это класс, кото­
рый содержит методы для использования другими классами без необходимости быть роди­
тельским классом этих классов. То, как дРугие классы получают доступ к методам миксина,
зависит от языка.
Миксины обеспечивают механизм множественного наследования, позволяя нескольким
классам использовать общую функциональность, но без сложной семантики множествен­
ного наследования. Они полезны, когда программист хочет разделить функциональность
между разными классами. Вместо того чтобы повторять один и тот же код снова и снова, об­
щая функциональность может быть просто сгруппирована в миксин и затем унаследована в
каждом классе, которому она требуется.
Когда мы используем более одного миксина, важен порядок следования:
class M ixin1 (object):
def test(self):
print "Mixin 1 "
class M ixin2(object):
def test(self):
print "M ixin2"
class MyClass(Mixin 1 , M ixin2):
pass

В данном примере мы вызываем класс MyClass и метод test:
»> obj = MyClass()
»> obj.test()

Mixin1

В результате должен получиться M ixin 1 , так как порядок следования - слева направо. Это
может привести к неожиданным результатам при добавлении суперклассов. Поэтому луч­
ше использовать обратный порядок:
class MyClass(Mixin2, M ixin1 ):
pass

Результатом будет:
»> obj = MyClass()
»> obj.test()

Mixin2

Миксины могут быть использованы для создания пользовательских плагинов.
Версия Python З.х � 3.0:
class Base(object):
def test(self):
print("Base.")
class PluginA(object):
def test(self):
super().test()
print("Plugin А.")
class PluginB(object):
def test(self):

520

Глава 1 47. Плаги н ы и расш ирения

super().test()
print("Plugin В.")
plugins = PluginA, PluginB
class PluginSystemA(PluginA, Base):
pass
class PluginSystemB(PluginB, Base):
pass
PluginSystemA().test()
# Base.
# Plugin А.
PluginSystemB().test()
# Base.
# Plugin В.

1 47.2. Плагины с пользовательскими классами
В Python 3 . 6 в РЕР 487 был добавлен специальный метод _iniLsubclass_, который упро­
щает и расширяет настройку классов без использования метаклассов. Следовательно, эта
возможность позволяет создавать простые плагины. Продемонстрируем эту возможность,
модифицируя предыдущий пример:

Версия Python З.х � 3.6:
class Base:
plugins =



def _init_subclass_(cls, **kwargs):
super()._init_subclass_(**kwargs)
cls. plugins.append(cls)
def test(self):
print("Base.")
class PluginA(Base):
def test(self):
super().test()
print("Plugin А.")
class PluginB(Base):
def test(self):
super().test()
print("Plugin В.")
Результаты:
PluginA().test()
# Base.
# Plugin А.
PluginB().test()
# Base.
# Plugin В.
Base.plugins
# Lmain_. PluginA, _main_. PluginB]

1 48.1 . О тдельные символы стро к не подлежат присвоению

521

Глава 1 4 8. Неизменяем ые типы данных
(int, float, str, tuple и frozenset)

1 48. 1 . Отдельные символы строк не подлежат присвоению
foo = "Ьаг"
foo[0] = "с" # Еггог

Значение неизменяемой переменной не может быть изменено после ее создания.

1 48.2. Индивидуальные члены кортежа не могут быть
присвоены
foo = ("Ьаг", 1 , "Hello!",)
foo[1 ] = 2 # ERROR!!

Вторая строка вернет ошибку, так как члены кортежа после создания не могут быть на­
значены. Это связано с неизменяемостью кортежа.

1 48.З. Frozenset является неизменяемым и не подлежит
присваиванию
foo = frozenset(["bar", 1 , "Hello!"])
foo[2] = 7 # ERROR
foo.add(3) # ERRO R

Вторая строка вернет ошибку, так как созданные члены frozenset не могут быть присвое­
ны. Третья строка вернет ошибку, так как frozenset не поддерживает функции, которые мо­
гут манипулировать элементами.

Глава 1 4 9. Несовместимости
при переходе с Python 2 на Python 3
В отличие от большинства языков программирования, Python поддерживает две основ­
ные версии. С 2008 года, когда был выпущен Python 3, многие успели перейти на него, но
многие по-прежнему используют Python 2. Поэтому в данной главе мы рассмотрим важные
различия между Python 2 и Python 3.

1 49. 1 . Целочисленное деление

Стандартный символ делеШIЯ (/) действует по-разному в Python 3 и Python 2 при приме­
нении к целым числам. При делении целого числа на дРугое целое число в Python 3 опера­
ция деления х / у представляет собой истинное деление (используется метод truediv) и выдает
результат в виде числа с плавающей точкой. Та же операция в Python 2 представляет собой
классическое деление, при котором результат округляется в меньшую сторону по направле­
нию к отрицательной бесконечности.
Например:
Код

3/2
2/3
-3 / 2

результат в Python 2
1

о

2

результат в Python 3
1 .5
О.бббббббббббббббб
-1 . 5

522

Глава 1 49 . Несовместимости при переходе с Python 2 на Python 3

Такое округлеIШя в сторону нуля было признано устаревшим в Python 2.2, но осталось
в Python 2. 7 для обеспечения обратной совместимости и было окончательно убрано в Python 3.
Примечание: для получения результата в виде числа с плавающей точкой (без округле­
ния в меньшую сторону) в Python 2 можно указать один из операндов с десятичной точкой.
Приведенный выше пример с 2/3, который дает О в Python 2, можно записать как 2/3.0, или
2.0/3, или 2.0/3.0, чтобы получить 0,бббббббббббббббб.
Код

результат в Python 2

3.0 / 2.0

1 .5

1 .5

2 / 3.0

О. бббббббббббббббб

О. бббббббббббббббб

-3 .0 / 2

1 .5

-1 . 5

результат в Python 3

Существует также оператор деления //, который работает одинаково в обеих версиях: он
округляет до ближайшего меньшего целого числа (хотя при использовании с числами типа
float (числами с плавающей точкой) и возвращается float). В обеих версиях используется ме­
тод _floordiv_.
Код

3 // 2

результат в Python 2

результат в Python 3

1

1

2 // 3

о

о

-3 // 2

-2

-2

3.0 // 2.0

1 .0

1 .0

2.0 // 3

о.о

о.о

-3 // 2.0

-2.0

-2.0

Можно явно обеспечить истинное деление или деление с округлением в меньшую сторону с помощью собственных функций в модуле operator:
from operator i mport trued iv, floord iv
assert truediv(1 О, 8) = = 1 .25
# эквивалентно 'Г в Python 3
assert floordiv(1 О, 8) == 1
# эквивалентно '/Г

Использование операторной функции для каждого деления, хотя и является понятным
и явным, может быть утомительным. Изменение поведения оператора / часто оказывается
предпочтительнее. Обычной практикой является устранение типичного поведения при де­
лении путем добавления оператора from _future_ import division в качестве первого утвержде­
ния в каждом модуле:
# должно быть пер в ы м утверждением в м одуле
from _future_ import division

Код

результат в Python 2

3/2

1 .5

1 .5

2/3

О. бббббббббббббббб

О. бббббббббббббббб

-3 / 2

-1 . 5

-1 . 5

результат в Python 3

Использование from _future_ import division гарантирует, что оператор / обеспечивает ис­
тинное деление и обеспечивает его только в пределах модулей, содержащих _future_ import,
поэтому нет веских причин не включать его во всех новых модулях.
Примечание: в некоторых других языках программирования используется округление
в сторону нуля (усечение), а не округление в сторону отрицательной бесконечности, как в
Python (т.е. в этих языках -3 / 2 == -1 ). Такое поведение может привести к путанице при пере­
носе или сравнении кода.

1 49.2. Распаковка итерируемых объектов

523

Примечание по поводу операндов ТIШа float: В качестве альтернативы from _future_ im­
port division можно использовать обычный символ деления / и убедиться, что хотя бы один из
операндов имеет тип float: 3 / 2.0 == 1 .5. Однако это можно считать плохой практикой. Слиш­
ком легко написать average = sum(items) / len(items) и забыть привести один из аргументов
к типу float. Кроме того, такие случаи часто могут остаться незамеченными при тестирова­
нии, например, если тестирование проводится на массиве, содержащем числа с плавающей
точкой, а в процессе работы вы получаете массив целых чисел. Кроме того, если этот же код
использовать в Python 3, то программы, ожидающие, что 3 / 2 == 1 будет равно True, будут ра­
ботать некорректно.
Более подробное обоснование того, почему оператор деления бьm изменен в Python 3
и почему следует избегать деления в старом стиле, см. в РЕР 238.

1 49.2. Распаковка итерируемых объектов

Версия Python 3.х � 3.0
В Python 3 можно распаковать итерируемый объект, не зная точного количества элемен­
тов в нем, и даже привязывать переменную к конечному элементу итерируемого объекта.
Для этого необходимо указать переменную, которая может собирать список значений. Для
этого перед ее именем ставится звездочка. Например, распакуем список:

first, second, -ktail, last = [1 , 2, 3, 4, 5]
# Результат: 1
print(first)
print(second} # Результат: 2
# Результат: [3, 4]
print(tail}
# Результат: 5
print(last)
Примечание: при использовании синтаксиса -кvагiаЫе переменная всегда будет списком,
даже если исходный тип не был списком. Она может содержать ноль или более элементов в
зависимости от количества элементов в исходном списке.
first, second, *tail, last = [1 , 2, 3, 4]
# Результат: [3]
print(tail}

first, second, -ktail, last = [1 , 2, 3]
# Результат: D
print(tail}
# Результат: 3
print(last)
Аналогично, распаковка строки str:
begin, -ktail = "Hello"
# Результат: 'Н'
print(begin)
print(tail}
# Результат: ['е', '1', '1', 'о']
Пример распаковки типа date; _ используется в данном примере как отбрасываемая переменная (нас интересует только значение года уеаг):
person = ('John', 'Doe', (1 О, 1 6, 201 б}}
*� (*� year_of_Ьirth} = person
print(year_of_Ьirth}
# Результат: 201 6
Следует отметить, что поскольку звездочка * потребляет переменное количество элемен­
тов, нельзя использовать две * для одного итерируемого объекта в присваивании - не будет
понятно, сколько элементов входит в первую распаковку, а сколько во вторую:
*head, -ktail = [1 , 2]
# Out: SyntaxError: two starred expressions in assignment
(в присваивании два выражения со звездочками)
Версия Python 3.х � 3.5
До сих пор мы обсуждали распаковку в присваиваниях. В Python 3.5 использование
* и ** было расширено. Теперь в одном выражении можно выполнять несколько операций
распаковки:

524

Глава 1 49. Несовместимости при переходе с Python 2 на Python 3

{*range(4), 4, *(5, 6, 7)}
# Результат: {О, 1 , 2, 3, 4, 5, 6, 7}

Версия Python 2.х ;:,: 2.0:

Также возможна распаковка итерируемого объекта в аргументы функции:

iteraЫe = [1 , 2, 3, 4, 5]
# Резул ьтат: [1 , 2, 3, 4, 5]
print(iteraЫe)
print(*iteraЫe) # Резул ьтат: 1 2 3 4 5

Версия Python 3.х ;:,: 3.5

При распаковке словаря используются две звездочки ** (РЕР 448):

tail = {'у': 2, 'z': 3}
{'х': 1, **tail}
# Резул ьтат: {'х': 1, 'у': 2, 'z': 3}
Это позволяет как переопределять старые значения, так и объединять словари.
dict1 = {'х': 1 , 'у': 1 }
dict2 = {'у': 2, 'z': 3}
{**dict1 , **dict2}
# Результат: {'х': 1 , 'у': 2, 'z': 3}

Версия Python 3.х ;:,: 3.0:

В Python 3 удалена распаковка кортежей в функциях (подробное обоснование
см. в РЕР 3113.). Таким образом, в Python 3 не работает следующее:
# Работает в Python 2, но выдает си нтаксическую ошибку в Python 3:
map(lambda (х, у): х + у, zip(range(5), range(5)))
# То же самое справедл и во и для не-лямбд:
def example((x, у)):
pass
# Работает как в Python 2, так и в Python 3:
map(lambda х: х[О] + х[1 ], zip(range(5), range(5)))
# И не-лямбда тоже:
def working_example(x__y):
Х, у = Х_у

pass

1 49.3. Строки: байты и Unicode

Версия Python 2.х :,; 2. 7:

В Python 2 существует два вида строк: состоящие из байтов с типом str и состоящие из тек­
ста с типом Unicode. В Python 2 объект типа str всегда является последовательностью байтов,
но обычно используется как для текстовых, так и для двоичных данных. Строковый литерал
интерпретируется как байтовая строка.
# type(s) == str
s = 'Cafe'
Существуют два исключения: вы можете явно выделить текстовый литерал Unicode,
предварительно обозначив его символом u:
s = u'Cafe'
# type(s) == unicode
Ь = 'Lorem ipsum'# type(b) == str
В качестве альтернативы можно указать, что строковые литералы всего модуля должны
создавать текстовые литералы Unicode:
from _future_ import unicode_literals
# type(s) == unicode
s = 'Cafe'
Ь = 'Lorem ipsum'# type(b) == unicode

1 49.3. Строки: байты и Unicode

525

Для проверки того, является ли переменная строкой (Unicode или байтовой строкой),
можно использовать:
isinstance(s, basestring)

Версия Python 3.х � 3.0:
В Python 3 тип str является текстовым типом Unicode.
s = 'Cafe' # type(s) == str
s = 'Cafe' # type(s) == str (обратите внимание на акцентирован ное завершающее е)

Кроме того, в Python 3 добавлен объект bytes, который подходит для двоичных объектов
или записи в независимые от кодировки файлы. Чтобы создать объект bytes, можно предва­
рительно записать Ь в строковый литерал или вызвать метод encode строки:
# Или, если вам действительно нужна байтовая строка:
s = b'Cafe'
# type(s) == bytes
s = 'Cafe'.encode()
# type(s) == bytes

Для проверки того, является ли значение строкой, используйте:
isinstance(s, str)

Версия Python 3.х � 3.3:
Для облегчения совместимости междУ кодовыми базами Python 2 и Python З можно пред­
варять строковые литералы символом u. Поскольку в Python 3 все строки по умолчанию яв­
ляются Unicode, добавление к строковому литералу символа u не имеет никакого эффекта:
u'Cafe' == 'Cafe'

Однако необработанная строка Unicode из Python 2 с префиксом ur не поддерживается:

»> ur'Cafe'
File "", line 1
ur'Cafe'
л

SyntaxError: invalid syntax

Обратите внимание, что для преобразования текстового объекта Python 3 (str) в байтовое
представление этого текста (bytes) необходимо его закодировать. По умолчанию в этом мето­
де используется кодировка UTF-8. С помощью decode можно запросить у объекта bytes, какой
текст Unicode он представляет:
»> b.decode()
'Cafe'

Версия Python 2.х � 2.6
В то время как тип bytes существует как в Python 2, так и в Python 3, тип unicode есть толь­
ко в Python 2. Чтобы использовать неявные Unicode-cтpoки Python 3 в Python 2, добавьте в
верхнюю часть кода следующую фразу:
from _future_ import unicode_l iterals
print(repr("hi"))
# u'hi'

Версия Python 3.х � 3.0:
Еще одно важное отличие заключается в том, что индексирование байтов в Python З при­
водит к выводу int следующим образом:
Ь"аЬс"[О] == 97

В то время как нарезка размером один приводит к получению объекта длиной 1 байт:

Ь"аЬс"[О: 1 ] == Ь"а"

Кроме того, в Python 3 устранено необычное поведение с Unicode, т.е. реверсирование
байтовых строк в Python 2.

526

Глава 1 49. Несовместимости при переходе с Python 2 на Python 3

1 49.4. Оператор Print и функция Print
В Python 2 print - это

Версия Python 2.х :,; 2. 7:

print "Hello World"
print
print "No newline",
print »sys.stderr, "Error"
print("hello")
print()
print 1 , 2, 3
print(1 , 2, 3)

оператор:

# вы вести новую строку
# добавить запятую для удаления новой строки
# вывод в stderr
# вы вести "hello", так как ("hel lo") = = "hel lo"
# вывести пустой кортеж "()".
# вывести аргументы, разделенные пробелами: "1 2 3"
# вы вести кортеж "(1 , 2, 3)"

В Python 3 функция print() является функцией, имеющей ключевые слова-аргументы для
общего использования:

Версия Python З.х ;?: 3.0

# SyntaxError
print "Hello World"
print("Hello World")
# вывести новую строку (необходимо использовать круглые скобки)
print()
print("No newline", end="") # end указывает, что добавлять (по умолчанию - новая строка)
print("Error", f1 le = sys.stderr)# fi le указы вает выходной буфер
print("Comma", "separated", "output", sep =",") # sep задает разделитель
print("A", "В", "С", sep = "") # нулевая строка для sep: вы водится как АВС
print("Flush this", fl ush = True) # очистка выходного буфера, добавлена в Python 3.3
# печать аргументов, разделенных пробелами: "1 2 3"
print(1 , 2, 3)
# вы вести кортеж "(1 , 2, 3)".
print((1 , 2, 3))

Функция печати имеет следующие параметры:
print(*objects, sep = ' ', end = '\n', f1le = sys.stdout, fl ush = False)

sep - это то, что разделяет объекты, передаваемые на печать. Например:

print('foo', 'bar', sep = '~') # вывод: foo~bar
print('foo', 'bar', sep = '.') # вывод: foo.bar
end - это то, чем заканчивается оператор печати. Например:
print('foo', 'Ьаг', end = '!') # вывод: foo bar!

Повторная печать после оператора печати, не завершающего новую строку, будет выведена на ту же строку:
print('foo', end = ' ~')
print('bar')
# вывод: foo~bar
ПримечаJШе: для обеспечения совместимости функция print доступна и в Python 2.6 и да­
лее; однако ее нельзя использовать, если только разбор оператора print не отключен с помо­
щью функции
from _future_ import print_function

Эта функция имеет точно такой же формат, как и функция Python 3, за исключением того,
что в ней отсутствует параметр flush. Обоснование см. в РЕР 3105.

1 49.5. Отличия между функциями range и xrange

В Python 2 функция range возвращает список, а xrange создает специальный объект xrange,
представляющий собой неизменяемую последовательность, которая, в отличие от других
встроенных типов последовательностей, не поддерживает нарезку и не имеет методов index
и count:

1 49.5. Отличия между функциями range и xrange

527

Версия Python 2.х � 2.3:
print(range(1 , 1 О))
print(isinstance(range(1 , 1 О), list))
print(xrange(1 , 1 О))
print(isinstance(xrange(1 , 1 О), xrange))

# Вывод: [1 , 2, 3, 4, 5, 6, 7, 8, 9]
# Вывод: True
# Вывод: xrange(1 , 1 О)
# Вывод: True

В Python 3 xrange был расширен до последовательности range, которая, таким образом, те­
перь создает объект range. В настоящее время xrange не существует:

Версия Python 3.х � 3.0:
print(range(1 , 1 О))
# Вывод: range(1 , 1 О)
print(isinstance(range(1 , 1 О), range))
# Вывод: True
# print(xrange(1 , 1 О)) # Выходные данные будут иметь вид:
#Traceback (last recent call last):
# File "", l ine 1 , in
#NameError: имя 'xrange' не определено
Кроме того, начиная с Python 3.2 range также поддерживает нарезку, index и count:
print(range(1 , 1 0) [3:7])
# Вывод: range(3, 7)
print(range(1 , 1 0).count(5)) # Вывод: 1
print(range(1 , 1 0).index(7)) # Вывод: б
Преимущество использования специального типа последовательности вместо списка за­
ключается в том, что интерпретатору не нужно выделять память под список и заполнять его:

Версия Python 2.х � 2.3:
# range(1 0000000000000000)
# Результатом будет:
# Traceback (most recent call last):
# File "", line 1 , in
# MemoryError
print(xrange(1 00000000000000000))
# Вывод: xrange(1 00000000000000000)
Поскольку последнее поведение в целом желательно, раннее бьmо убрано в Python 3.
Если вы все же хотите иметь список в Python 3, вы можете просто использовать конструктор
listQ на объекте range:

Версия Python 3.х � 3.0:
print(list(range(1 , 1 О)))
# Вывод: [1 , 2, 3, 4, 5, 6, 7, 8, 9]

Совместимость

Для поддержания совместимости между версиями Python 2.х и Python 3.х можно исполь­
зовать модуль builtins из внешнего пакета future для достижения прямой и обратной совме­

стимости:

Версия Python 2.х � 2.0
#прямая совместимость
from builtins import range
for i in range(1 о-н-8):
pass

528

Глава 1 49. Несовместимости при переходе с Python 2 на Python 3

Версия Python 3.х ;:,: 3.0:
#обратная совместимость
from past.builtins import xrange
for i in xrange(1 Q-k-k8):
pass
Функция range в библиотеке future померживает нарезку, index и count во всех версиях
Python, как встроенный метод в Python 3.2+.

1 49.б. Возникновение и обработка исключений
Вот пример синтаксиса Python 2, обратите внимание на запятые в строках raise и except:

Версия Python 2.х ;:,: 2.3:
try:
raise IOError, "input/output error"
except IOError, ехс:
print ехс
В Python 3 запятые исключены и заменены круглыми скобками и ключевым словом as:
try:
raise IOError("input/output error'')
except IOError as ехс:
print(exc)
Для обеспечения обратной совместимости синтаксис Python 3 доступен и в Python 2.6 и
далее, поэтому его следует использовать для всего нового кода, который не должен быть со­
вместим с предыдущими версиями.

Версия Python 3.х ;:,: 3.0:

В Python 3 также добавлена цепочка исключений, с помощью которой можно сигнализи­
ровать о том, что причиной данного исключения стало другое исключение. Например:
try:
fi le = open('database.db')
except FileNotFound Error as е:
raise DatabaseError('Cannot open {}') from е
Исключение, вызванное в операторе except, имеет тип DatabaseError, но исходное исклю­
чение помечено как атрибут причины _cause_ этого исключения. При выводе трассировки
исходное исключение также будет отображено:
Traceback (most recent call last):
File '"', l ine 2, in
Fi leNotFou nd Error
The above exception was the direct cause of the fol lowing exception:
Traceback (most recent call last):
File '"', l ine 4, in
DatabaseError('Cannot open database.db')
Если вы добавите блок исключений без явной цепочки:
try:
fi le = open('database.db')
except FileNotFound Error as е:
raise DatabaseError('Cannot open {}')
Трассировка:
Traceback (most recent call last):
File "", line 2, in

1 49.7. Утечка переменных в генераторе списка

529

FileNotFound Error
During hand l ing of the above exception, another exception occurred:
Traceback (most recent call last):
File "", l ine 4, in
DatabaseError('Cannot ореп database.db')

Версия Python 2.х � 2.0

Ни тот, ни другой вариант не поддерживается в Python 2.х; исходное исключение и его
трассировка будут потеряны, если в блоке except будет вызвано другое исключение. Для со­
вместимости можно использовать следующий код:
import sys
import traceback
tгу:
funcWithError()
except:
sys_vers = getattr(sys, 'version_info', (О,))
if sys_vers < (3, О):
traceback. prinLexc()
raise Exception("new exception")

Версия Python 3.х � 3.3:

Чтобы "забыть" о ранее созданном исключении, используйте raise from None:

tгу:
f1le = open('database.db')
except FileNotFound Error as е:
raise DatabaseError('Cannot ореп {}') from None
Теперь трассировка будет выглядеть следующим образом:
Traceback (most recent call last):
File "", l ine 4, in
DatabaseError('Cannot ореп database.db')
Или, для совместимости с Python 2 и 3, можно использовать пакет six следующим образом:
import six
tгу:
f1le = open('database.db')
except FileNotFound Error as е:
six.raise_from(DatabaseError('Cannot ореп {}'), None)

1 49.7. Утечка переменных в генераторе списка

Версия Python 2.х � 2.3:

х = 'hello world!'
vowels = [х for х in 'AEIOU']
print (vowels)
print(x)

# Результат: ['А', 'Е', '1', 'О', 'U']
# Результат: 'U'

Python 3.х Version � 3.0
х = 'hello world!'
vowels = [х for х in 'AEIOU']
# Результат: ['А', 'Е', '1', 'О', 'U']
print (vowels)
print(x)
# Результат: 'hello world!'

Как видно из примера, в Python 2 произошла утечка значения х: оно вывело u, так как
это было последнее значение х на момент завершения цикла. Однако в Python З.х выводится

530

Глава 1 49. Несовместимости при переходе с Python 2 на Python 3

первоначально заданное hello world!, поскольку локальная переменная из генератора спи­
ска не маскирует переменные из окружающей области видимости.
Кроме того, ни генераторные выражения (доступные в Python начиная с версии 2.5), ни
генераторы наборов и словарей (которые были перенесены в Python 2.7 из Python 3) не вызы­
вают утечки переменных в Python 2.
Обратите внимание, что как в Python 2, так и в Python 3 при использовании цикла for возникает утечка переменных в окружающую область видимости:
х = 'hel lo world!'
vowels =
for х in 'AEIOU':
vowels.append(x)
print(x)
# Вы вод: 'U'



1 49.8. True, False и None
В Python 2 True, False

можно переназначать.

и None являются встроенными константами. Это означает, что их

Версия Python 2.х ;с: 2.0:
True, False = False, True
True
# False
False # True

Начиная с версии Python 2.4 вы не можете переназначать None.
Версия Python 2.х ;с: 2.4:
None = None # SyntaxError: cannot assign to None

В Python 3 True, False и None стали ключевыми словами.

Версия Python 3.х ;с: 3.0:
True, False = False, True # SyntaxError: can't assign to keyword (невозможно присвоить ключевое слово)
None = None # SyntaxError: can't assign to keyword (невозможно присвоить ключевое слово)

1 49.9. Ввод данных пользователем
В Python 2 пользовательский ввод принимается с помощью

функции raw_input.

Версия Python 2.х ;с: 2.3:
user_input

=

raw_inputO

в то время как в Python 3 пользовательский ввод принимается с помощью функции input.

Версия Python 3.х ;с: 3.0:
user_input

=

input()

В Python 2 функция input принимает входные данные и интерпретирует их. Хотя это мо­
жет быть полезно, но вызывает сомнения в безопасности и бьто убрано в Python 3. Для досту­
па к той же функциональности можно использовать eval(input()). Чтобы сохранить переноси­
мость кода в двух версиях Python, можно поместить приведенный ниже код в начало скрипта:
try:
input = raw_input
except NameError:
pass

1 49.1 О. Сравнение разных типов

531

1 49. 1 О. Сравнение разных типов

Версия Python 2.х � 2.3:
Можно сравнивать объекты разных типов. Результаты получаются произвольными, но
непротиворечивыми. Они упорядочены таким образом, что None меньше, чем все осталь­
ное, числовые типы меньше, чем нечисловые, а все остальное упорядочено лексикоrрафиче­
ски по типу. Таким образом, int меньше, чем str, а кортеж больше, чем список:
[1, 2] > 'foo'
{1, 2) > 'foo'
[1, 2] > {1, 2)
100 < [1, 'х'] < 'xyz' < {1, 'х')

# Результат: False
# Результат: True
# Результат: False
# Результат: True

Изначально это было сделано для того, чтобы можно было отсортировать список смешанных типов и сгруппировать объекты по типам:
1 = [7, 'х', {1, 2), [5, б], 5, В.О, 'у', 1.2, [7, 8], 'z']
sorted{I)
# Результат: [1.2, 5, 7, В.О, [5, б], [7, 8], 'х', 'у', 'z', {1, 2)]

Версия Python 3.х � 3.0:
При сравнении различных (нечисловых) типов возникает исключение:

1 < 1.5 # Результат: True
[1, 2] > 'foo'
# ТуреЕггог: uпогdегаЫе types: list() > str() (неупорядоченные типы: list() > str())
(1, 2) > 'foo'
# ТуреЕггог: uпогdегаЫе types: tuple{) > str() (неупорядоченные типы: tuple{) > str())
[1, 2] > {1, 2)
# ТуреЕггог: uпогdегаЫе types: list() > tuple{) (неупорядоченные типы: list() > tuple{))

Для сортировки смешанных списков в Python 3 по типам и для достижения совместимости между версиями необходимо предоставить ключ функции sorted :
»> list = [1, 'hello', [3, 4], {'python': 2}, 'stackoverflow', 8, {'python': 3}, [5, 61]
»> sorted{list, key=str)
# Результат: [1, 8, [3, 4], [5, б], 'hello', 'stackoverflow', {'python': 2}, {'python': 3}]

Использование функции str в качестве ключевой (key) временно преобразует каждый
элемент в строку только для целей сравнения. Затем она видит строковое представление,
начинающееся с L ', { или 0-9, и может отсортировать их (и все последующие символы).

1 49. 1 1 . Переименование метода .next() для итераторов

В Python 2 для обхода итератора можно использовать метод next на самом итераторе:
Версия Python 2.х � 2.3:
g = (i for i in range{O, 3))
g .next() # Выдает О
g .next() # Выдает 1
g .next() # Выдает 2
В Python 3 метод .next бьш переименован в ._next_, что свидетельствует о его "магиче­
ской" роли, поэтому при вызове .next будет возникать ошибка AttributeError. Правильным спо­
собом доступа к этой функциональности как в Python 2, так и в Python 3 является вызов функ­
ции next с итератором в качестве аргумента.
Версия Python 3.х � 3.0:
g = (i for i in range{O, 3))
next(g) # Выдает О
next(g) # Выдает 1
next(g) # Выдает 2

Этот код переносится на все версии, начиная с 2.6 и заканчивая текущими релизами.

532

Глава 1 49. Несовместимости при переходе с Python 2 на Python 3

1 49.1 2. Функции filter(), map() и zip() возвращают итераторы
вместо последовательностей

Версия Python 2.х :.,; 2. 7:
В Python 2 встроенные функции filter, map и zip возвращают последовательность: map и zip
всегда возвращают список, в то время как у f1 lter тип возвращаемого параметра зависит от
типа заданного параметра:
>» s = f1 lter(lambda х: x.isalpha(), 'а1 Ь2с3')
>>> s
'аЬс'
»> s = map(lambda х: х * х, [О, 1 , 2])
>>> s
[О, 1 , 4]
»> s = zip([0, 1 , 2], [З, 4, 5])
>>> s
[(О, 3), (1 , 4), (2, 5)]

Версия Python 3.х � 3.0:
В Python 3 функции f1 lter, map и zip вместо этого возвращают итератор:
»> it = f1 lter(lambda х: x.isalpha(), 'а1 Ь2с3')
>» it

>» ".join(it)
'аЬс'
»> it = map(lambda х: х * х, [О, 1 , 2])
>» it

>» l ist(it)
[О, 1 , 4]
»> it = zip([0, 1 , 2], [З, 4, 5])
>» it

»> l ist(it)
[(О, 3), (1 , 4), (2, 5)]

Поскольку itertools.izip из Python 2 эквивалентен zip в Python 3, izip был убран из Python 3.

1 49.1 3. Переименованные модули

Несколько модулей в стандартной библиотеке были переименованы:

Старое имя

_winreg
Conf1gParser
copy_reg
Queue
SocketServer
_markupbase
reprlib
герг
test.test_support
Tkinter
tkFileDialog
url l i b / urllib2

Новое имя

winreg
conf1gparser
copyreg
queue
socketserver
markupbase
test.support
tkinter
tkinter.f1 ledialog
urllib, urllib.parse, urllib.error, urllib.response, urllib.request, urllib.robotparser

Некоторые модули даже были преобразованы в библиотеки. В качестве примера можно
привести tkinter и urllib.

1 49.1 4. Удалены операторы и ", синонимичные операторам != и герг()

533

Совместимость
При поддержании совместимости между версиями Python 2.х и З.х можно использовать
внешний пакет future, позволяющий импортировать пакеты стандартной библиотеки верх­
него уровня с именами Python З.х в версии Python 2.х.

1 49. 1 4. Удалены операторы и ",
синонимичные операторам != и repr()

В Python 2 является синонимом для !=; аналогично, 'foo' является синонимом для repr(foo).

Версия Python 2.х :,; 2. 7:
>>> 1 < > 2
True
>>> 1 1
False
»> foo = 'hello world'
»> repr(foo)
'"hello world"'
>>> 'foo'
'"hello world"'

Версия Python 3.х � 3.0:
>>> 1 2
File "", line 1
1 2
л

SyntaxError: invalid syntax
>>> 'foo'
File "", line 1
'foo'
л

SyntaxError: invalid syntax

1 49. 1 5. Типы данных long и int

В Python 2 любое целое число, большее, чем ssize_t в языке С, преобразуется в тип данных
обозначается суффиксом L на литерале. Например, в 32-битной сборке Python:

long, что

Версия Python 2.х :,; 2. 7:
»> 2�31
2147483648L
»> tуре(2�з1)

»> 2�30
1073741824
»> tуре(2�зо)

»> 2�31 - 1 # 2**31 is long and long - int is long
2147483647L
Однако в Python 3 тип данных long бьш удален; независимо
будет иметь тип int.

Версия Python 3.х � 3.0:
2**1024

от размера целого числа, оно

534

Глава 1 49. Несовместимости при переходе с Python 2 на Python 3

# Вы вод: 1 7976931 3486231 59077293051 9078902473361 797697894230657273430081 1 577326758
055009631 32708477322407536021 1 201 1 3879871 39335765878976881 441 66224928474306394741
2437776789342486548527630221 9601 2460941 1 94530829520850057688381 50682342462881 4739
1 3 1 1 05408272371 6335051 068458629823994724593847971 63048353563296242241 3721 6
type(2**1 024)
# Вы вод:

1 49.1 6. Все классы в Python З являются "классами нового стиля"

В Python З.х все классы являются классами нового стиля; при создании нового класса
Python неявно заставляет его наследоваться от object. Таким образом, указание объекта в
определении класса является совершенно необязательным:
Версия Python 3.х ::с: 3.0:
class Х: pass
class V(object): pass

Оба эти класса теперь содержат object в своем mго (method resolution order, порядок раз­
решения метода):
Python 3.х Версия ::с: 3.0:
>>> Х._mго_
(_main_.X, object)
>>> V._mro_
(_main_.v; object)
В Python 2.х классы по умолчанию являются классами старого типа, они не наследуют­
ся неявно от object. Это приводит к тому, что семантика классов меняется в зависимости от
того, добавляем ли мы явно object в качестве базового класса:
Версия Python 2.х ::с: 2.3:
class Х: pass
class Y(object): pass

В этом случае, если попытаться вывести mго из У, то появится вывод, аналогичный тому,
что бьш в случае Python З.х:
Версия Python 2.х ::с: 2.3:

>>> V._mro_
(, )
Это происходит потому, что при создании класса У мы явно указали, что он наследует­
ся от объекта: class V(object): pass. Для класса Х, который не наследуется от объекта, атрибут
_mго_ не существует, попытка доступа к нему приводит к ошибке AttributeError.
Для обеспечения совместимости между обеими версиями Python классы могут быть
определены с использованием object в качестве базового класса:
class mycls(object):
"""Я пол ностью совмести м с Python 2/3"""
В качестве альтернативы, если переменная _metaclass_ установлена в значение type в
глобальной области видимости, то все последующие определенные классы в данном модуле
неявно становятся классами нового стиля без необходимости явного наследования от object:
_metaclass_ = type
class mycls:
"""Я также пол ностью совмести м с Python 2/3"""

1 49.1 7. Функция reduce бол ьше не я вляется встроенной

535

1 49. 1 7. Функция reduce больше не является встроенной
В Python 2 функция reduce доступна либо как встроенная функция, либо из пакета
functools (начиная с версии 2.6), в то время как в Python 3 reduce доступна только из functools.
Однако синтаксис reduce в Python 2 и Python 3 одинаков и представляет собой reduce(function_
to_reduce, list_to_reduce).
В качестве примера рассмотрим сокращение списка до одного значения путем деления
каждого из соседних чисел. Здесь мы используем функцию truediv из библиотеки орегаtог.
В Python 2.х это делается просто:

Версия Python 2.х � 2.3:
my_list = [1 , 2, 3, 4, 5]
import operator
»> reduce(operator.truediv, my_list)
»>
»>

О.008333333333333333

В Python З.х пример несколько усложняется:

Версия Python З.х � 3.0:
my_list = [1 , 2, 3, 4, 5]
import operator, functools
»> functools.reduce(operator.truediv, my_list)
»>
»>

О.008333333333333333

Мы также можем использовать from functools import reduce, чтобы избежать вызова reduce
с именем пространства имен.

1 49. 1 8. Абсолютный и относительный импорт
В Python 3 РЕР 404 изменяет принцип работы импорта по сравнению с Python 2. Неявный
относительный импорт больше не допускается в пакетах, а импорт from ... import * разрешен
только в коде уровня модуля.
Чтобы добиться поведения Python 3 в Python 2:
• Функция абсолютного импорта может быть включена при помощи from _future_
import absolute_import


явный относительный импорт поощряется вместо неявного относительного

импорта
Например, в Python 2 модуль может импортировать содержимое другого модуля,
расположенного в том же каталоге, следующим образом:
import foo
Обратите внимание, что местоположение foo только из выражения импорта неясно.
Поэтому такой тип неявного относительного импорта не рекомендуется использовать в
пользу явного относительного импорта, который выглядит следующим образом:
from .moduleV import spam
from .moduleY import spam as ham
from . import moduleY
from ..subpackage1 import moduleY
from ..subpackage2.moduleZ import eggs
from ..moduleA import foo
from ...package import Ьаг
from ...sys import path
Точка . позволяет явно объявить местоположение модуля в дереве каталогов.

Глава 1 49. Несовместимости при переходе с Python 2 на Python 3

536

Подробнее об относительном импорте

Рассмотрим некоторый пользовательский пакет под названием shapes. Структура
каталогов выглядит следующим образом:
shapes
� _i n it_.py
1
� c i rcle.py
1
� square.py
1
L- t r i a n gle.py
c i rcle. py, square.py и t r i a n gle.py импортируют u ti l.py в качестве модуля. Как они будут
ссьmаться на модуль на одном уровне?
from . i mport ut i l # испол ьзовать u ti l . PI, uti l.sq(x) и т. д.

или

from .uti l i mport * #испол ьзовать PI, sq(x) и т. д. для вызова фун кций
Символ точки используется для относительного импорта того же уровня.
Теперь рассмотрим альтернативный вариант расположения модуля форм:

lt
ltС

shapes
� -i n it_.py
1
c i rcle
c -i n it_.py
c i rcle.py

1
1

square

i n it .ру
�ua;_py

t r i a n gle
� _i n it_.py
� t r i a n gle.py

1
L- u ti l.py

Как эти 3 класса будут ссылаться на u ti l.py?
from . i mport ut i l # испол ьзовать u ti l . PI, ut i l.sq(x), и т. д.

или

from .uti l i mport * #испол ьзовать PI, sq(x) и т. д. для вызова фун кций
Символ . . (двойные точки) используется для относительного импорта родительского
уровня. Добавьте дополнительные точки с увеличением количества уровней между
родительским и дочерним.

1 49.1 9. Функция map()

map() - это встроенная функция, полезная для применения функции к элементам
итерируемого множества. В Python 2 функция map возвращает список. В Python 3 map
возвращает объект map, который является генератором.
# Pytho n 2.Х
»> map(st r, [1 , 2, 3, 4, 5])
1· 1 •• ·2·.

·з·. '4', ' 5'1

»> type(_)


1 49.20. Функция round() - "тай-брейкинг" и возвращаем ый тип

537

# Python 3.Х
»> map(str, [1 , 2, 3, 4, 5])

»> type(_)

# Нам нужно снова применить map, так как мы "поглотили" предыдущую map ....
»> map(str, [1 , 2, 3, 4, 5])
»> list(_)
[' 1 ', '2', 'З', '4', ' 5 ']

В Python 2 можно передавать None в качестве функции идентификации. В Python 3 это
больше не работает.
Версия Python 2.х � 2.3:
»> map(None, [О, 1 , 2, 3, О, 4])
[О, 1 , 2, 3, О, 4]

Версия Python 3.х � 3.0:
»> list(map(None, [О, 1 , 2, 3, О, 5]))
Traceback (most recent call last):
File "", line 1 , in
ТуреЕггог: 'NoneType' object is not callaЫe

Более того, в Python 2 при передаче в качестве аргумента более одного итерируемого мно­
жества map заполняет более короткие итерируемые множества при помощи None (аналогич­
но itertools.izip_longest). В Python 3 итерация останавливается после самого короткого итери­
руемого множества.
В Python 2:
Версия Python 2.х � 2.3:
»> map(None, [1 , 2, 3], [1 , 2], [1 , 2, 3, 4, 5])
[(1 , 1 , 1 ), (2, 2, 2), (3, None, 3), (None, None, 4), (None, None, 5)]

В Python 3:
Версия Python 3.х � 3.0:
»> list(map(lambda х, у, z: (х, у, z), [1 , 2, 3], [1 , 2], [1 , 2, 3, 4, 5]))
[(1 , 1 , 1 ), (2, 2, 2}]
# для получения такого же дозаполнения (padding), как в Python 2, используйте zip_longest из itertools
»> import itertools
»> list(itertools.zip_longest([1 , 2, 3], [1 , 2], [1 , 2, 3, 4, 5]))
[(1 , 1 , 1 ), (2, 2, 2), (3, None, 3), (None, None, 4), (None, None, 5)]

Примечание: вместо map можно использовать генераторы списков, которые совмести­
мы с Python 2/3. Замена map(str, [1 , 2, 3, 4, 5]) :
»> [str(i) for i in [1 , 2, 3, 4, 51]
[' 1 ', '2', '3 ', '4', ' 5 ']

1 49.20. Функция round{) - "тай-брейкинг" и возвращаемый тип
"Тай-брейКIПlг" ("прекращение ничьей") в фушщии round()

В Python 2 использование функции round() для числа, одинаково близкого к двум целым
числам, вернет наиболее удаленное от О. Например:
Версия Python 2.х � 2. 7:
round(1 .5)
round(0.5)

# Результат: 2.0
# Результат: 1 .0

538
round(-0.5)
round(-1 . 5)

Глава 1 49. Несовместимости при переходе с Python 2 на Python 3
# Резул ьтат: -1 .0
# Резул ьтат: -2.0

Однако в Python 3 функция roundO будет возвращать четное целое число (так называемое
банковское округление). Например:
Версия Python 3.х � 3.0:
round(1 .5)
round(0. 5)
round(-0.5)
round(-1 . 5)

# Резул ьтат: 2
# Резул ьтат: О
# Резул ьтат: О
# Резул ьтат: -2

Функция roundO следует стратегии округления от половины к четным числам, в результате
чего половинные числа округляются до ближайшего четного целого (например, round(2.5) те­
перь возвращает 2, а не 3.0). Согласно Википедии, эта стратегия также известна как несмещен­
ное округление, сходящееся округление, статистическое округление, голландское округление,
гауссово округление или четное округление. Округление от половины до четного является ча­
стью стандарта IEEE 754, а также режимом округления по умолчанию в Microsoft .NET. Такая
стратегия округления, как правило, уменьшает суммарную ошибку округления. Поскольку в
среднем количество чисел, округленных в большую сторону, равно количеству чисел, окру­
гленных в меньшую сторону, ошибки округления нивелируются. Другие методы округления,
наоборот, имеют тенденцию к смещению средней ошибки в большую или меньшую сторону.
round() return type

Функция round() возвращает тип float в Python 2.7.

Версия Python 2.х : 2. 7:
round(4.8) # 5.0

Начиная с версии Python 3.0, если второй аргумент (количество цифр) опущен, то возвра­
щается int.
Версия Python 3.х � 3.0:
round(4.8) # 5

1 49.21 . Файловый ввод/вы вод
В версиях З.х fi le больше не является встроенным именем (но open по-прежнему работа­
ет). Внутренние детали ввода/вывода файлов были перенесены в модуль io стандартной би­
блиотеки, в которой теперь также размещен модуль String lO:
import io
assert io.open is ореп # встроенны й модул ь является псевдонимом (alias)
buffer = io.String lO()
buffer.write('hello, ') # возвращает количество записанных символов
buffer.write('world !\n')
buffer.getvalue() # 'hello, world!\n'

Режим файла (текстовый или двоичный) теперь определяет тип данных, получаемых
при чтении файла (и тип, требуемый для записи):
with open('data.txt') as f:
firsLline = next(f)
assert type(f1 rst_line) is str
with open('data.Ьin', 'гЬ') as f:
firsLkb = f.read(1 024)
assert type(f1 rst_kb) is bytes

Кодировка, возвращаемая locale.getpreferredencoding(False), используется по умолчанию для
текстовых файлов. Для явного указания кодировки используйте ключевой параметр encoding:

1 49.22. Функция cmp отсутствует в Python 3

539

1 49.22. Функция cmp отсутствует в Python З
В Python 3 встроенная функция cmp была убрана вместе со специальным методом _cmp_.
Из документации: Функцшо cmp() следует считать исчезнувшей, а специальный метод _cmp_()
больше не поддерживается. Используйте _lt_() для сортировки, _eq_() с _hash_() идругие
"богатые сравнения" (rich comparisons) при необходимости. (Если вам действительно нужна функ­
циональность cmp(), вы можете использовать выражение (а > Ь) - (а < Ь) в качестве эквивалента
cmp(a, Ь)).
Кроме того, все встроенные функции, принимавшие параметр cmp, теперь принимают
только параметр для ключевых слов key. В модуле functools таюке имеется полезная функция
cmp_to_key(func), которая позволяет преобразовать функцию в стиле cmp в функцию в стиле key:
Преобразовывайте функцию сравнения старого типа в ключевую функцию. Она используется с ин­
струментами, принимающими ключевые функции (такими как sorted(), min(), max(), heapq.nlarg­
est(), heapq.nsmal lest(), itertools.groupby()). Эта функция используется в основном как переход­
ное средство для программ, конвертируемых из Python 2, который поддерживал использование
функций сравнения.

1 49.23. Восьмеричные константы
В Python 2 восьмеричный литерал может быть определен как:
»> 0755

# только Python 2

Для обеспечения кросс-совместимости используйте:
00755 # как в Python 2, так и в Python 3

1 49.24. Возвращаемое значение при записи в файловый объект
В Python 2 запись непосредственно в дескриптор возвращает None:

Версия Python 2.х :::: 2.3:
hi = sys.stdout.write('hello world\\n')
# Результат: hello world
type(hi)
# Результат:
В Python 3 запись в дескриптор возвращает количество записанных символов при записи
текста и количество записанных байтов при записи байтов:

Версия Python З.х :::: 3.0:
import sys
char_count = sys.stdout.write('hello world ?\\п')
# Результат: hello world ?
char_count
# Результат: 1 4
byte_count = sys.stdout.buffer.write(b'hello world \\xf0\\x9f\\x90\\x8d\\n')
# Результат: hello world ?
byte_count
# Результат: 1 7

1 49.25. В Python З ехес является функцией
В Python 2 ехес - это оператор, имеющий специальный синтаксис: ехес code [in globalsL lo­
cals]]. В Python 3 ехес теперь представляет собой функцию: exec(code, L globalsL locals]]), а син­
таксис Python 2 вызовет ошибку SyntaxError.

540

Глава 1 49. Несовместимости при переходе с Python 2 на Python 3

Однако никакого импорта from _future_ import exec_function не добавлено, поскольку
в нем нет необходимости: оператор ехес в Python 2 можно использовать и с синтаксисом,
который выглядит точно так же, как вызов функции ехес в Python 3. Таким образом, можно
изменить утверждения:

Версия Python 2.х ;:,: 2.3:
ехес 'code'
ехес 'code' in global_vars
ехес 'code' in global_vars, local_vars
к формам

Версия Python 3.х ;:,: 3.0:
exec('code')
exec('code', global_vars)
exec('code', global_vars, local_vars)
причем последнее гарантированно работает одинаково как в Python 2, так и в Python 3.

1 49.26. Кодирование в шестнадцатеричный формат
и декодирование из него больше не доступно

Версия Python 2.х :,; 2. 7:

"1 deadbeefЗ".decode('hex')
# Результат: '\х1 d\xea\xdb\xee\xfЗ'
'\х1 d\xea\xdb\xee\xfЗ'.encode('hex')
# Результат: 1 deadbeefЗ

Версия Python 3.х ;:,: 3.0:
"1 deadbeefЗ".decode('hex')
# Traceback (most recent call last):
# File "", line 1 , in
# AttributeError: 'str' object has по attribute 'decode' (у объекта 'str' нет атрибута 'decode')
Ь"1 deadbeefЗ".decode('hex')
# Traceback (most recent call last):
# File "", line 1 , in
# LookupError: 'hex' is not а text encoding; use codecs.decodeO to handle arbltrary codecs
# ('hex' не является текстовой кодировкой; используйте codecs.decode() для работы
# с произвольн ы ми кодеками)
'\х1 d\xea\xdb\xee\xfЗ'.encode('hex')
# Traceback (most recent call last):
# File "", line 1 , in
# LookupError: 'hex' is not а text encoding; use codecs.encode() to handle arbltrary codecs
# ('hex' не является текстовой кодировкой; используйте codecs.decode() для работы
# с произвольными кодеками)
Ь'\х1 d\xea\xdb\xee\xfЗ'.encode('hex')
# Traceback (most recent call last):
# File "", line 1 , in
# AttributeError: 'bytes' object has по attribute 'епсоdе' (у объекта 'bytes' нет атрибута 'епсоdе')
Однако, как следует из сообщения об ошибке, для достижения того же результата можно
использовать модуль codecs:
import codecs
codecs.decode('1 deadbeef4', 'hex')
# Результат: Ь'\х1 d\xea\xdb\xee\xf4'
codecs.encode(b'\x1 d\xea\xdb\xee\xf4', 'hex') # Результат: Ь'1 deadbeef4'

1 49.27. Изменения в методах словарей

541

Обратите внимание, что codecs.encode возвращает объект с типом bytes. Для получения
объекта str достаточно выполнить декодирование в ASCII:
codecs.encode(b'\x1 d\xea\xdb\xee\xff', 'hex').decode('ascii') # Out: '1 deadbeeff'

1 49.27. Изменения в методах словарей

В Python 3 многие методы словарей значительно отличаются по поведению от Python 2,
а многие и вовсе бьши убраны: исчезли has_key, iter* и view*. Вместо давно устаревшей функ­
ции d.has_key(key) теперь нужно использовать key in d. В Python 2 методы словаря keys, values
и items возвращают списки. В Python 3 они возвращают объекты представления; эти объек­
ты не являются итераторами и отличаются от них двумя способами, а именно:
• они имеют размер (для них можно использовать функцию len)
• их можно многократно итерировать.
Кроме того, как и в случае с итераторами, изменения в словаре отражаются на объектах
представления.
В Python 2.7 эти методы бьши перенесены из Python 3; они доступны в виде viewkeys,
viewvalues и viewitems. Для преобразования кода Python 2 в код Python 3 используются следу­
ющие подходы:
• d.keys(), d.values() и d.items() из Python 2 должны быть заменены на list(d.keys()),
list(d.values()) и list(d.items())
• d.iterkeys(), d.itervalues() и d.iteritems() следует заменить на iter(d.keys()), а еще лучше
на iter(d); iter(d.values()) и iter(d.items()) соответственно
• вызовы методов Python 2.7 d.viewkeys(), d.viewvalues() и d.viewitems() могут быть
заменены на d.keys(), d.values() и d.items().
Перенос на Python 2 кода, выполняющего итерации по ключам, значениям или элемен­
там словаря с одновременным его изменением, иногда оказывается непростой задачей. Рас­
смотрим:
d = {'а' : О, 'Ь': 1 , 'с' : 2, '!': З}
for key in d.keys():
if key.isalpha():
del d[key]
Код выглядит так, как будто он будет аналогично работать и в Python 3, но там метод keys
возвращает объект представления, а не список, и если в процессе итерации словарь изме­
нит размер, то код Python 3 завершится с ошибкой RuntimeError: dictionary changed size during
iteration. Решением, конечно, является правильная запись for key in list(d). Аналогично объек­
ты представления ведут себя иначе, чем итераторы: для них нельзя использовать функцию
next(), нельзя возобновить итерацию - она будет перезапущена; если в коде Python 2 переда­
ется возвращаемое значение d.iterkeys(), d.itervalues() или d.iteritems() в метод, который ожи­
дает итератор, а не итерируемый объект, то в Python 3 это должно быть iter(d), iter(d.values())
или iter(d.items()).

1 49.28. Булево значение класса

Версия Python 2.х � 2. 7:
В Python 2, если вы хотите самостоятельно определить булево значение класса, вам необ­
ходимо реализовать метод _nonzero_ в вашем классе. По умолчанию его значение равно True.
class MyClass:
def _nonzero_(self):
return False

my_instance = MyClass()
print bool(MyClass) # True
print bool(my_instance) # False
Версия Python З.х � 3.0:
В Python 3 вместо _nonzero_ используется _bool_:

542

Глава 1 50. Инструмент 2to3

1 49.29. Ошибка функции hasattr в Python 2
В Python 2, если свойство вызывает ошибку, hasattr будет игнорировать это свойство, воз­
вращая False.
class A(object):
@property
def get(self):
raise IOError
class B(object):
@property
def get(self):
return 'get in Ь'
а = А()
Ь = В()
print 'а hasattr get: ', hasattr(a, 'get')
# выводит False в Python 2 (исправлено, True в Python 3)
print 'Ь hasattr get', hasattr(b, 'get')
# выводит True в Python 2 и Python З
Эта ошибка исправлена в PythonЗ. Поэтому, если вы работаете в Python 2, используйте:
try:
a.get
except AttributeError:
print("нeт свойства get!")
или используйте вместо этого getattr:
р getattr(a, "get", None)
if р is not None:
print(p)
else:
print("нeт свойства get!")
=

Глава 1 50. Инструмент 2to 3
П араметр

Описание

filename / directory_name

В качестве аргумента 2to3 принимает список файлов
или каталогов, которые необходимо преобразовать. Каталоги
рекурсивно просматриваются в поисках источников Python.

Опция

Описание опции

-f FIX,

-fix = FIX

-j PROCESSES,

Укажите, какие преобразования должны быть применены; по
умолчанию: all. Список доступных преобразований доступен
с помощью команды --list-f1xes
Одновременный запуск 2to3

--processes = PROCESSES
-х NOFIX, --nof1x = NOFIX

Исключить преобразование

-1, --list-flxes

Список доступных преобразований

-р, --print-function

Изменение грамматики таким образом, чтобы print()
считалась функцией

1 50 . 1 . Базовое использование

-v, --verbose

Более подробный вывод

--no-diffs

Не выводить различия рефакторинга

543

-w

Записать обратно измененные файлы

-п, --nobackups

Н е создавать резервные копии модифицированных файлов

-о OUTPUT_DI R,
--output- dir = OUTPUT_DI R

Поместить выходные файлы в этот каталог вместо перезаписи
входных файлов. Требуется флаг -п flag, так как резервные файлы
не нужны, если входные файлы не модифицируются.

W, -write-unchanged-fi les

Записать выходные файлы, даже если никаких изменений
не требовалось. Используется вместе с -о, чтобы перевести
и скопировать полное дерево источников. Подразумевает -w.

--add-suff1x = ADD_SUFFIX

Укажите строку, которая будет добавляться ко всем выводимым
именам файлов. Требуется -п, если она непустая. Пример:
-add-suff1x = 'З' будет генерировать файлы .руЗ.

1 50. 1 . Базовое использование
Рассмотрим следующий код Python 2.х. Сохраните файл под именем example.py.

Версия Python 2.х � 2.0:
def greet(name):
print "Hello, {0}!".format(name)
print "What's your name?"
name = raw_input()
greet(name)
В приведенном выше примере есть несколько несовместимых строк. В Python 3.х метод
raw_input() был заменен на input(), а print теперь не оператор, а функция. Этот код может быть
преобразован в код Python З.х с помощью инструмента 2to3.

Unix
$ 2to3 example.py

Windows
> path/to/2to3. py example.py
В результате выполнения приведенного выше кода будут выведены различия по сравнению с исходным текстом, как показано ниже.
RefactoringTool: Skipping implicit f1xer: buffer
RefactoringTool: Skipping implicit f1xer: idioms
RefactoringTool: Skipping implicit f1xer: set_literal
RefactoringTool: Skipping implicit f1xer: ws_comma
RefactoringTool: Refactored example.py
-- example.py (original)
+++ example.py (refactored)
@@ -1 ,5 + 1 ,5 @@

def greet(name):
- print "Hello, {0}!".format(name)
-print "What's your name?"
-name = raw_input()
+ print("Hello, {0}!".format(name))
+print("What's your name?")
+name = input()
greet(name)
RefactoringTool: Files that need to Ье modif1ed:
RefactoringTool: example.py

544

Глава 1 51 . Неофициал ьные реал изации

Python

Модификации могут быть записаны обратно в исходный файл с помощью флага -w. Ре­
зервная копия исходного файла под названием example.py.bak будет создана, если флаг -n не
указан.
Unix

$ 2to3 -w example.py
Windows

> path/to/2to3.py -w example.py
Теперь файл example.py преобразован из кода Python 2.х в код Python З.х. После заверше­
ния преобразования example.py будет содержать следующий корректный код PythonЗ.x:
Версия Python З.х ::с: 3.0:

Python З.х Version 2: 3.0
def greet(name):
print("Hello, {O}!".format(name))
print("What's your name?")
name = input()
greet(name)

Глава 1 51 . Неофициальные реализации
Python
1 51 . 1 . lronPython

Это реализация с открытым исходным кодом для .NET и Mono, написанная на языке С#
и лицензируемая по лицензии Apache License 2.0. Она опирается на DLR (Dynamic Language
Runtime). Поддерживается только версия 2. 7, версия 3 находится в стадии разработки.
Отличия от CPython:
• Тесная интеграция с .NET Framework.
• Строки по умолчанию имеют формат Unicode.
• Не поддерживает расширения для CPython, написанные на языке С.
• Не защищает от глобальной блокировки интерпретатора.
• Производительность обычно ниже, хотя это зависит от используемых тестов.
Hello World

print "Hello World!"
Можно также использовать функции .NET:
import clr
from System import Console
Console.Writeline("Hello World!")
Внепmие ссылки
• Официальный сайт: http://ironpython.net/
• Репозиторий GitНub: https://github.com/lronlanguages/main

1 51 .2. Jython

545

1 51 .2. Jython

Это реализация с открытым исходным кодом для JVM (Java Virtual Machine) , написанная
на языке Java и лицензированная по лицензии Python Software Foundation License. Поддер­
живается только версия 2.7, версия 3 находится в стадии разработки.
Отличия от CPython:
• Тесная интеrрация с JVМ.
• Строки имеют формат Unicode.
• Не поддерживает расширения для CPython, написанные на языке С.
• Не защищает от глобальной блокировки интерпретатора.
• Производительность обычно ниже, хотя это зависит от тестов.
Hello World
print "Hello World!"

В Jython можно использовать функции Java:

from java.lang import System
System.out.println("Hello World!")

Внешние ссылки
• Официальный сайт: http://www.jython.org/
• Репозиторий Mercurial: https://hg.python.org/jython

1 51 .З. Transcrypt

Transcrypt - это инструмент для предварительной компиляции достаточно обширного
подмножества языка Python в компактный, легко читаемый Javascript. Он обладает следую­
щими характеристиками:
• Позволяет осуществлять классическое объектно-ориентированное
проrраммирование с множественным наследованием с использованием чистого
синтаксиса Python, разбираемого собственным синтаксическим анализатором
CPython
• Интеграция с множеством высококачественных веб-ориентированных JаvаSсriрt­
библиотек, а не десктоп-ориентированных Руthоn-библиотек
• Иерархическая система модулей на основе URL, позволяющая распространять
модули через PyPi
• Простая связь между исходным кодом Python и сгенерированным JavaScript-кoдoм
для удобства отладки
• Многоуровневые карты исходного кода и дополнительная аннотация целевого
кода со ссылками на исходный текст
• Компактные загрузки (килобайты, а не мегабайты)
• Оптимизированный JavaScript-кoд, использующий мемоизацию (кэширование
вызовов) для опционального обхода цепочки поиска прототипов
• Перегрузка операторов может быть включена и выключена локально для
облегчения чтения числовых вычислений
Размер и скорость кода
Опыт показывает, что 650 кБ исходного кода Python примерно переводится в такой же
объем исходного кода JavaScript. Скорость соответствует скорости рукописного JavaScript и
может превосходить ее, если включена мемоизация вызовов.
Интеграция с HTML

Hello demo

546

Глава 1 51 . Неофициальные реализации Python


...
Нажмите на меня еще paз!

...
И меня тоже нажмите несколько paз!

Интеграция с JavaScript и DOM
from itertools import chain
class SolarSystem:
planets = [list (chain (planet, (index + 1 ,))) for index, planet in enumerate ((
('Мегсuгу', 'hot', 2240),
('Venus', 'sulphurous', 6052),
('Earth', 'fertile', 6378),
('Mars', 'reddish', 3397),
('Jupiter', 'stormy', 71 492),
('Saturn', 'ringed', 60268),
('Uranus', 'cold', 25559),
('Neptune', 'vегу cold', 24766)

))]

lines = (
'{} is а {} planet',
The radius of {} is {} km',
'{} is planet nr. {} counting from the sun'
def _init_ (self):
self.linel ndex = О
def greet (self):
self.planet = self.planets [int (Math.random () * len (self.planets))I
document.getElementByld ('greet') .innerHTM L = 'Hello {}'.format (self.planet [О])
self.explain ()
def explain (self):
document.getElementByld ('explain').innerHTM L = (
self.lines [self.linelndex] .format (self.planet [О], self.planet [self.linelndex + 1 ])

)

self.linel ndex = (self.linel ndex + 1 ) % 3
solarSystem = SolarSystem ()

Интеграция с другими библиотеками JavaScгipt

Transcrypt может быть использован в сочетании с любой библиотекой JavaScript без
специальных мер и синтаксиса. В документации приведены примеры для react.js, riot.js,

fabric.js и node.js.

Взаимосвязь между Python и JavaScript кодом
Python
class А:
def _init_ (self, х):
self.x = х
def show (self, label):
print ('A.show', label, self.x)

1 51 .3. Transcrypt
class В:
def _iniL (self, у):
alert {'1 n В constructor')
self.y = у
def show (self, label):
print ('B.show', label, self.y)
class С (А, В):
def _iniL (self, х, у):
alert ('1 n С constructor')
A._init_ (self, х)
B._init_ (self, у)
self.show ('constructor')
def show (self, label):
B.show (self, label)
print ('C.show', label, self.x, self.y)
а = А {1 001 )
a.show ('america')
Ь = В {2002)
b.show ('russia')
с = С {3003, 4004)
c.show ('netherlands')
show2 = c.show
show2 ('сору')

JavaScript

var А = _class_ ('!>:, [object], {
get _init_ () {return _get_ (this, function (self, х) {
self.x = х;
});},
get show () {return _get_ (this, function (self, label) {
print ('A.show', label, self.x);
});}
});
var В = _class_ ('В', [object], {
get _init_ () {return _get_ (this, function (self, у) {
alert {'1 n В constructor');
self.y = у;
});},
get show () {return _get_ (this, function (self, label) {
print ('B.show', label, self.y);
});}
});
var С = _class_ ('С', [А, В], {
get _init_ () {return _get_ (this, function (self, х, у) {
alert ('1 n С constructor');
A._init_ (self, х);
B._init_ (self, у);
self.show ('constructor');
});},
get show () {return _get_ (this, function (self, label) {
B.show (self, label);
print ('C.show', label, self.x, self.y);
});}
});

547

548

Глава 1 52. Абстрактное синтаксическое дерево

var а = А (1 001 );
a.show ('america');
var Ь = В (2002);
b.show ('russia');
var с = С (3003, 4004);
c.show ('netherlands');
var show2 = c.show;
show2 ('сору');

Внеnmие ссьшки
• Официальный сайт: http://www.transcrypt.org/
• Репозиторий GitНub: https://github.com/JdeH/Тranscrypt

Глава 1 52. Абстрактное синтакси ч еское
дерево
1 52.1 . Анализ функций в скрипте Python

Приведенный ниже код анализирует скрипт Python и для каждой удаленной функции со­
общает номер строки, где начинается функция, где заканчивается сигнатура, где заканчива­
ется docstring и где заканчивается описание функции.
#!/usr/local/bin/python3
import ast
import sys
"'"' Данные, которые мы собираем. Каждый ключ - это имя функции; каждое значение - это
словарь с ключами: f1 rstline, sigend, docend и lastline и значениями номеров строк, где это
происходит. "'"'
functions = {}
def process(functions):
"'"' Работа с данными функций, хранящимися в функциях. "'"'
for funcname,data in functions.items():
print("function:",funcname)
print("\tstarts at line:",data['f1 rstl ine'])
print("\tsignature ends at line:",data['sigend'])
if ( data['sigend'] < data['docend'] ):
print("\tdocstring ends at line:",data['docend'])
else:
print("\tno docstring")
print("\tfunction ends at line:",data['lastl ine'])
print()
class Funclister(ast.NodeVisitor):
def visit_FunctionDef(self, node):
"'"' Рекурсивно посетить все функции, определяя, где начинается каждая функция, где
заканчивается ее сигнатура, где заканчивается docstring и где заканчивается
сама функция. ""''
functions[node.name] = {'f1rstline':node.lineno}
sigend = max(node.lineno,lastline(node.args))
functions[node.name]['sigend'] = sigend
docstring = ast.geLdocstring(node)

1 53.1 . Обработка ошибок кодирования и декодирования

549

docstringlength = len(docstring.split('\n')) if docstring else -1
functions[node.name] ['docend'] = sigend+docstringlength
functions[node.name] ['lastline'] = lastline(node)
self.generic_visit(node)
def lastline(node):
""'' Рекурсивно найти последнюю строку узла """
return max( [ node.lineno if hasattr(node,'lineno') else -1 , ]
+[lastline(child) for child in ast.iter_child_nodes(node)] )
def readin(pythonfi lename):
""" Считать имя файла и сохранить данные функции в функции "'"'
with open(pythonfi lename) as f:
code = f.read()
Funclister().visit(ast.parse(code))
def analyze(f1 le,process):
""" Чтение файла и обработка данных функции """
readin(f1 le)
process(functions)
if _name_ = = '_main_':
if len(sys.argv)>1 :
for f1le in sys.argv[1 :]:
analyze(fi le,process)
else:
analyze(sys.argv[0],process)

Глава 1 53. Unicode и bytes
П ара м етр
encoding

Подроб ности

errors

Режим ошибок, например, 'replace' для замены "плохих" символов на
знаки вопроса, 'ignore' для игнорирования "плохих" символов и т. д.

Используемая кодировка, например, 'ascii', 'utf8' и т. д.

1 53.1 . Обработка ошибок кодирования и декодирования
И .encode и .decode имеют режимы ошибок.
По умолчанию используется режим 'strict', который вызывает исключения при ошибках.
Другие режимы более щадящие.

Кодирование

»> "f1 3. 55".encode('ascii', errors = 'replace')
Ь'?1 3. 55'
»> "f1 3. 55".encode('ascii', errors = 'ignore')
Ь'1 3. 55'
»> "f1 3. 55".encode('ascii', errors = 'namereplace')
b'\\N{POUND SIGN}1 3.55'
»> "f1 3. 55".encode('ascii', errors = 'xmlcharrefreplace')
Ь'&#1 63;1 3.55'
»> "f1 3. 55".encode('ascii', errors = 'backslashreplace')
Ь'\\ха31 3. 55'

550

Глава 1 53. Unicode и bytes

Декодиров юше
»> Ь = "f:1 3.55".encode('utf8'}
»> b.decode('ascii', errors = 'replace')
'??1 3. 55'
»> b.decode('ascii', errors = 'ignore')
'1 3.55'
»> b.decode('ascii', errors = 'backslashreplace')
'\\хс2\\ха31 3. 55'

Вывод

Из вышесказанного ясно, что при работе с Unicode и Ьytes крайне важно соблюдать пра­
вильность кодировок.

1 53.2. Файловый ввод/вы вод
Файлы, открытые в недвоичном режиме (например, 'г' или 'w'), работают со строками.
По умолчанию используется кодировка 'utf8'.
open{fn, mode = 'r')
# откры в ает файл для чтения в формате utf8
open{fn, mode = 'r', encoding='utf1 б'} # открывает файл для чтения в формате utf1 б
# ERROR: невозможно записать байты, когда ожидается строка
open("foo.txt", "w").write{b"foo")
Файлы, открытые в двоичном режиме (например, 'гЬ' или 'wb'), работают с байтами. Аргумент кодировки не может быть задан, так как кодировки не существует.
# открыть файл для записи байтов
open(fn, mode = 'wb'}
# ERROR: невозможно записать строку, когда ожидаются байты
open(fn, mode = 'wb'}.write{"hi")

1 53.3. Основы
В Python 3 str - это тип для строк с поддержкой Unicode, а bytes - тип для последователь­
ностей "сырых", необработанных байтов (raw bytes).
type("f"} = = type(u"f"} # True,
type{b"f"} #
В Python 2 обычная строка по умолчанию представляла собой последовательность не­
обработанных байтов, а строкой Unicode бьша любая строка с префиксом "u".
type("f") == type{b"f") # True,
type(u"f"} #

Перевод Unicode в байты

Строки Unicode могут бьrгь преобразованы в байты с помощью функции .encode{encoding).

Python З
»> "f:1 3.55".encode('utf8'}
Ь'\хс2\ха3 1 3.55'
»> "f:1 3.55".encode('utf1 б'}
b'\xff\xfe\xaЗ\x00 1 \хООЗ\хОО.\х005\х005\х00'

Python 2

Кодировка консоли по умолчанию равна sys.getdefaultencoding() == 'ascii', а не utf-8, как в
Python 3, поэтому ее вывод напрямую, как в предыдущем примере, невозможен.
»> print type(u"f:1 3. 55".encode('utf8'})

»> print u"f:1 3.55".encode('utf8'}
SyntaxError: Non-ASCII character '\хс2' in. . .

1 54.1 . Инициализация последовательного устройства

551

# с кодировкой, установленной внутри файла
# -*- coding: utf-8 -*»> print u"f:1 3. 55".encode('utfB')

?u1 з.ss

Если кодировка не может обработать строку, то выдается ошибка 'UnicodeEncodeError':

Перевод байтов в Unicode

Байты могут быть преобразованы в строки Unicode с помощью функции .decode(encoding).

Последовательность байтов может быть преобразована в строку юникода только с помо­
щью соответствующей кодировки!
»> b'\xc2\xa31 3.55'.decode('utf8')
'f:1 3.55'
Если кодировка не может обработать строку, то выдается ошибка UnicodeDecodeError:
»> b'\xc2\xa31 3.55'.decode('utf1 6')
Traceback (most recent call last):
File "", line 1, in
File "/Users/csaftoiu/csaftoiu-githuЬ/yahoo-groupsbackup/.
virtualenv/Ьin/ . ./liЬ/pythonЗ. 5/encodings/utf_ 1 6.ру", line 1 6, in decode
return codecs. utf_ 1 6_decode(input, errors, True)
UnicodeDecodeError: 'utf-1 6-le' codec can't decode byte Ох35 in position 6: truncated data (кодек
'utf-1 6-le' не может декодировать байт Ох35 в позиции 6: усеченные данные)

Глава 1 5 4 . Последовательное
соединение в Python (pyserial)
П ара м етр
port

Подроб ности

baudrate

Тип скорости передачи: int по умолчанию: 9600; стандартные значения:
50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400,
57600, 115200

Имя устройства, например, /dev/ttyUSB0 в GNU/Linux или СОМ3 в Windows

1 54. 1 . Инициализация последовательного устройства
import serial
#Serial принимает эти два параметра: последовательное устройство и скорость передачи данных
ser = serial .Serial('/dev/ttyUSB0', 9600)

1 54.2. Чтение из последовательного порта
Инициализация последовательного устройства:
import serial
#Serial принимает два параметра: последовательное устройство и скорость передачи данных
ser = serial .Serial('ldev/ttyUSB0', 9600)
считывание одного байта с последовательного устройства
data = ser.read()
считывание заданного количества байт с последовательного устройства
data = ser.read(size = S)

552

Глава 1 55. Работа с Neo4j и Cypher с использованием библиотеки Py2Neo

для чтения одной строки из последовательного устройства.
data = ser. readline()

считывать данные с последовательного устройства в то время, когда на него что-то
записывается.
data = ser. read(ser.inWaiting())
ser.read(ser.inWaiting)

#для Python 2. 7
#для Python 3

1 54.3. Проверка доступности последовательных портов
Для получения списка доступных последовательных портов используйте

python -m serial.tools.list_ports

в командной строке или

from serial.tools import l ist_ports
list_ports.comports() # Выводит список доступных последовательных портов

из оболочки Python.

Глава 1 55. Работа с Neo4j и Cypher
с использованием библиотеки Py2Neo
1 55.1 . Добавление узлов в граф в графовой СУБД Neo4j
results = News.objects.todays_news()
for г in results:
article = graph.merge_one("NewsArticle", "news_id", г)
article.properties["title"] = results[r] ['news_title']
article.properties["timestamp"] = results[r] ['news_timestamp']
article.push()
[ ... ]

Добавление узлов в граф является довольно простой задачей, важен graph.merge_one, так
как он предотвращает дублирование. (Если запустить скрипт дважды, то во второй раз он об­
новит заголовок и не будет создавать новые узлы для одних и тех же артикулов (articles)). time­
stamp должен быть целым числом, а не строкой даты, так как в Neo4j нет типа данных date. Это
приводит к проблемам сортировки при хранении даты в виде '05-06-1 989'. article.push() - это
вызов, который фактически фиксирует операцшо в Neo4j. Не забывайте об этом шаге.

1 55.2. Импорт и аутентификация

from py2neo import authenticate, Graph, Node, Relationship
authenticate("localhost:7474", "neo4j", "")
graph = Graph()
Необходимо убедиться, что база данных Neo4j существует

по адресу localhost:7474 с со­
ответствующими учетными данными. Объект графа является вашим интерфейсом к экзем­
пляру Neo4j в остальном коде на языке Python. Вместо того чтобы делать его глобальной пе­
ременной, лучше хранить его в методе _init_ класса.

1 55.3. Добавление отношений в граф Neo4j
results = News.objects.todays_news()
for г in results:
article = graph.merge_one("NewsArticle", "news_id", r)
if 'LOCATION' in results[r] .keys():
for loc in results[r]['LOCATION']:

1 55.4. Запрос 1 : автозаполнение заголовков новостей

553

loc = graph.merge_one("Location", "паmе", loc)
try:
rel = graph.create_uniq ue(Relationship(article, "abouLplace", loc))
except Exception, е:
print е
Для исключения дубликатов важно использовать create_unique; в целом операция доволь­
но проста. Имя отношения также важно, поскольку его можно использовать в расширенных
случаях.

1 55.4. Запрос 1 : автозаполнение заголовков новостей
def get_autocomplete(text):
q uery = '""'
start n = node(*) where n.name = ~ '(?i)%s.*' return n.name,labels(n) limit 1 О;
,,,,,,

q uery = query % (text)
obj = D
for res in graph.cypher.execute(query):
# print res[O],res[1 ]
obj.append({'name':res[O],'entity_type':res[1 ]})
return res
Это пример Сурhеr-запроса для получения всех узлов со свойством name, которое начи­
нается с аргумента text.

1 55.5. Запрос 2: получение новостных статей
по местоположению на определенную дату
def search_news_by_entity(location,timestamp):
query = '''"'

МАТСН (п)-0->(I)
where l.name = '%s' and n.timestamp = '%s'
RETURN n.news_id limit 1 О
q uery = query % (location,timestamp)
news_ids = D
for res in graph.cypher.execute(query):
news_ids.append(str(res[O]))
return news_ids
С помощью этого запроса можно найти все новостные статьи (п), связанные отношения­
ми с местом (1).

1 55.6. Образцы запросов языка Cypher
Подсчет статей, связанных с конкретным человеком, с течением времени
МАТСН (n)-0->(I)
where l.name = 'Donald Trump'
RETURN n.date,count(*) order Ьу n.date
Поиск других людей / местностей, связанных с теми же новостными статьями, что и
Трамп, с не менее чем 5 общими узлами взаимосвязи.
МАТСН (n: NewsArticle)-0->(I)
where l.name = 'Donald Trump'
МАТСН (n: NewsArticle)-0->(m)
with m,count(n) as num where num>S
return labels(m)[O],(m.name), num order Ьу num desc limit 1 О

554

Глава 1 56. Основы работы с библиотекой Curses в Python

Глава 1 56. О сновы работы
с библиотекой Curses в Python
1 56. 1 . Вспомогательная функция wrapper()

Библиотека Curses предоставляет вспомогательную функцию wrapper(func, ... ). В приве­
денном примере wrapper инициализирует curses, создает stdscr, WindowObject и передает в
func как stdscr, так и все дополнительные аргументы. Когда func вернется, wrapper восстано­
вит терминал перед выходом из программы.
main(scr, *args):
# -- Выполнить действие с экраном scr.border(0)
scr.addstr(5, 5, 'Привет от Curses!', curses.д_BOLD)
scr.addstr(б, 5, 'Нажмите q, чтобы закрыть этот экран', curses.д_NORMAL)
while True:
# оставаться в этом цикле до тех пор, пока пользователь не нажмет 'q'
ch = scr.getch()
if ch == ord('q'):
curses.wrapper(main)

1 56.2. Пример базового обращения
import curses
import traceback
try:
# -- Инициализация ... -stdscr = curses. initscr() # инициализировать экран curses
# отключить вывод на экран эхо-сигнала о нажатии клавиш
curses.noecho()
curses.cbreak()
# вход в режим преры в ания при нажатии клавиши Enter
# после нажатия клавиши не требуется для регистрации
# включить специальные значения клавиш, как curses. KEY_LEFT и т. д.
stdscr. keypad(1 )
# -- Выполнить действие с экраном stdscr. border(0)
stdscr.addstr(5, 5, 'Привет от Curses!', curses.д_BOLD)
stdscr.addstr(б, 5, 'Нажмите q, чтобы закрыть этот экран', curses.д_NORMAL)
while True:
# оставаться в этом цикле до тех пор, пока пользователь не нажмет 'q'
ch = stdscr.getch()
if ch == ord('q'):
break
# -- End of user code except:
traceback. print_exc()

# вывести журнал трассировки ошибки

final ly:
# -- Очистка при выходе -­
stdscr. keypad(0)
curses.echo()
curses.nocbreak()
curses.endwin()

1 57.1 . Простая программа вы вода данных с использованием шаблона

555

Глава 1 57. Шаблоны в Python

1 57. 1 . Простая программа вывода данных с использованием
шаблона
from string import Template

data = dict(item = "конфеты", price = 8, qty = 2)
# определение шаблона (template)
t = Теmрlаtе("Саймон купил $qty $item за $price долларов")
pri nt(t.substitute(data))
Результат:
Саймон купил 2 конфеты за 8 долларов
Шаблоны поддерживают подстановки на основе $ вместо подстановок на основе %. Sub­
stitute (mapping, keywords) выполняет подстановку шаблона, возвращая новую строку.
Отображение (mapping) - это любой словареподобный объект с ключами, совпадающими
с заполнителями шаблонов. В данном примере в качестве заполнителей (местодержатели,
placeholders) выступают price и qty. Аргументы ключевых слов также моrут использоваться
в качестве заполнителей. Если оба аргумента присутствуют, то приоритет имеют заполни­
тели из ключевых слов.

1 57.2. Изменение разделителя
Разделитель "$" можно заменить на любой другой. Например:
from string import Template
class MyOtherTemplate(Template):
del imiter = "#"
data = dict(id = 1 , name = "Ricardo")
t = MyOtherTemplate("My name is #name and I have the id: #id")
print(t.substitute( data))

Глава 1 58. Библиотека Pillow
1 58.1 . Чтение файла изображения
from P I L import lmage
im = lmage.open("lmage.bmp")

1 58.2. Преобразование файла в формат JPEG
from _future_ import print_function
import os, sys
from P I L import lmage

for inf1 le in sys.argv[1 :]:
f, е = os.path.spl itext(inf1le)
outf1 le = f + ".jpg"
if infi le != outf1 le:
try:
lmage.open(infi le).save(outfile)
except IOError:
print("cannot convert", infile)

556

Глава 1 59. Оператор pass

Глава 1 59. О ператор pass
1 59.1 . Игнорирование исключения
try:
metadata = metadata['properties']
except KeyError:
pass

1 59.2. Создание нового исключения, которое может быть
перехвачено
class CompileError(Exception):
pass

Глава 1 60. Подкоманды CLI (Command
Line l nterface) для аккуратного вы вода
справочной ин ф ормации
Тема разбора аргументов командной строки относится к более широкой теме разбора ар­
гументов.

1 60.1 . Способ без использования библиотек
usage: sub
commands:
status - show status
list - print list
import sys
def check():
print("status")
return О
if sys.argv[1:] == ['status']:
sys.exit(check())
elif sys.argv[1 :] == ['l ist']:
print("list")
else:
print(_doc_.strip())

Вывод без аргументов:
usage: sub
commands:

1 60.2. Использование argparse (средство форматирования справки по умолчанию)
status - show status
l ist - print list
Плюсы:

без библиотек


каждый может прочитать



полный контроль над форматированием справки

1 60.2. Использование argparse (средство форматирования
справки по умолчанию)
import argparse
import sys
def check():
print("status")
return О
parser = argparse.ArgumentParser(prog="sub", add_help=False)
subparser = parser.add_subparsers(dest="cmd"}
subparser.add_parser('status', help='show status')
subparser.add_parser('list', help='print list')
# хак для отображения справки п ри отсутствии аргументов
if len(sys.argv) == 1 :
parser.print_help()
sys.exit(O}
args = parser.parse_args()
if args.cmd == 'list':
print('l ist')
elif args.cmd == 'status':
sys.exit(check()}
Вывод без аргументов:
usage: sub {status,l ist} ...
positional arguments:
{status,list}
status
show status
list
print l ist
Плюсы:
• поставляется с Python


включен парсинг опций

1 60.З. Использование argparse (создание пользовательского
средства форматирования справки)
import argparse
import sys
class CustomHelpFormatter(argparse.HelpFormatter):
def _formaLaction(self, action):
if type(action) == argparse._SubParsersAction:

557

558

Глава 1 60. Подкоманды CLI (Command Line lnterface) для аккуратного вы вода
# ввести новую переменную класса для форматирования подкоманд
subactions = action._get_subactionsO
invocations = [self._formaLaction_invocation(a) for а in subactions]
self._subcommand_max_length = max(len(i) for i in invocations)
if type(action) == argparse._SubParsersAction._ChoicesPseudoAction:
# форматирование строки справки по подкомандам
subcommand = self._format_action_invocation(action) # type: str
width = self._subcommand_max_length
hel p_text = "''
if action.help:
help_text = self._expand_hel p(action)
return " {:{width}} - {}\n".format(subcommand, help_text, width = width)
elif type(action) = = argparse._SubParsersAction:
# раздел справки по подкомандам процесса
msg = '\n'
for subaction in action._get_subactions():
msg + = self._format_action(subaction)
return msg
else:
return super(CustomHelpFormatter, self)._format_action(action)

def check():
print("status")
return О
parser = argparse.ArgumentParser(usage ="sub ", add_help = False,
formatter_class = Custom HelpFormatter)
subparser = parser.add_subparsers(dest ="cmd")
subparser.add_parser('status', help = 'show status')
subparser.add_parser('list', help = 'print list')
# пол ьзовател ьское сообщение о помощи
parser._positionals.title = "commands"
# хак для отображения справки при отсутствии аргументов
if len(sys.argv) = = 1 :
parser.print_helpO
sys.exit(0)
args

=

parser.parse_args()

if args.cmd == 'list':
print('Iist')
elif args.cmd == 'status':
sys.exit(check())

Вывод без аргументов:
usage: sub
commands:
status - show status
list
- print l ist

1 61 . 1 . SQLite

559

Глава 1 61 . Доступ к базам данных

1 61 . 1 . SQLite

SQLite - это легкая дисковая база данных. Поскольку она не требует отдельного сервера
баз данных, ее часто используют для создания прототипов или для небольших приложений,
которые часто используются одним пользователем или одним пользователем в определен­
ный момент времени.
import sqlite3
conn = sqlite3.connect("users.db")
с = conn.cursor()
c.execute("CREATE TABLE user (name text, age integer)")
c.execute("I NSERT I NTO user VALUES ('User А', 42)")
c.execute("I NSERT I NTO user VALUES ('User В', 43)")
conn.commit()
c.execute("SELECT * FROM user")
print(с.fetchall())
conn.close()

Приведенный выше код подключается к базе данных, хранящейся в файле с именем
базой данных мож­
но с помощью операторов SQL. Результат этого примера:
users.db, создавая файл, если он еще не существует. Взаимодействовать с
[(u'User А', 42), (u'User В', 43)]

Синтаксис SQLite: Углубленный анализ

Начало работы
1. Импортируйте модуль sqlite с помощью
»> import sqlite3

2. Для использования модуля необходимо сначала создать объект Connection, представля­
ющий базу данных. Здесь данные будут храниться в файле example.db:
»> conn = sqlite3.connect('users.db')

Кроме того, для создания временной базы данных в оперативной памяти можно задать
специальное имя :memory:, как показано ниже:
»> conn = sqlite3.connect(':memory:')

3. Теперь можно создать объект Cursor и вызвать его метод execute() для выполнения
SQL-команд:
с = conn.cursor()
# Создать таблицу
c.execute("' CREATE TABLE stocks
(date text, trans text, symbol text, qty real, price real) "')
# Вставить строку данных
c.execute("I NSERT I NTO stocks VALUES ('2006-01 -0S';вuv·;RHAT, 1 00,35.1 4)")
# Сохранить (зафиксировать) изменения
conn.commit()

Глава 1 61 . Доступ к базам данных

560

# Мы также можем закрыть соединен ие, есл и закончили с н и м работать.

# Убедитесь, что все изменения были зафиксированы, иначе они будут потеря н ы conn.closeO

Важные атрибуты и функции соединения (Connection)

1. isolation_level - это атрибут, используемый для получения или установки текущего уровня изоляции. Нет для режима автокоммита или один из DEFERRED, I M M EDIATE или EXCLUSIVE.
2. cursor - этот объект используется для выполнения команд и запросов SQL.
3. commit() - выполняет фиксацию текущей транзакции.
4. rоllЬасk() - откатьmает все изменения, сделанные с момента предыдущего вызова commit().
5. closeO - закрывает соединение с базой данных. При этом не происходит автоматическо­
го вызова commitQ. Если вызвать close() без предварительного вызова commitO (при условии,
что вы не находитесь в режиме autocommit), то все сделанные изменения будут потеряны.
6. total_changes - атрибут, регистрирующий общее количество строк, модифицирован­
ных, удаленных или вставленных с момента открытия базы данных.
7. execute, executemany и executescript - эти функции выполняются так же, как и функции
объекта cursor. Это сокращение, поскольку вызов этих функций через объект соединения
приводит к созданию промежуточного объекта курсора и вызову соответствующего метода
объекта курсора.
8. row_factory - этoт атрибут можно заменить на вызываемый элемент, который будет при­
нимать курсор и исходную строку в виде кортежа и возвращать реальную строку результата.

Важные функции курсора

1. execute(sql[, parameters]) - выполняет один SQL-oпepaтop. SQL-oпepaтop может быть па­
раметризован (т.е. вместо SQL-литералов могут использоваться заполнители). Модуль sql ite3
поддерживает два вида заполнителей: вопросительные знаки ? ("стиль qmark") и именован­
ные заполнители :name ("именованный стиль").
import sql ite3
conn = sqlite3.connect(":memory:")
cur = conn.cursor()
cur.execute("create tаЫе people (name, age)")

who = "Sophia"
age = 37
# стиль qmark:
cur.execute("insert into people values (?, ?)",
(who, age))
# именова н н ы й стиль:
cur.execute("select * from people where name = :who and age = :age",
{"who": who, "age": age}) # the KEYS correspond ТО the placeholders IN SQL

pri nt(cur. fetchone())
Осторожно: не используйте % для вставки строк в SQL-команды, так как это может сделать вашу
программу уязвимой для кибератаки внедрения SQL-кoдa (SQL injection).
2. executemany(sql, seq_of_parameters) - выполняет команду SQL по всем последовательно­
стям параметров или сопоставлениям, найденным в последовательности sql. Модуль sqlite3
также позволяет использовать вместо последовательности итератор, выдающий параметры.
L = [(1 , 'abcd', 'dfj', 300), # Список кортежей, которые необходимо вставить в базу данных
(2, 'cfgd', 'dyfj', 400),
(3, 'sdd', 'dfjh', 300. 50)]

conn = sqlite3.connect("test1 .db")
conn.execute("create tаЫе if not exists book (id int, name text, author text, price real)")
conn.executemany("insert into book values (?, ?, ?, ?)", L)
for row in conn.execute("select * from book"):
print(row)

1 61 . 1 . SQLite

561

В качестве параметра функции executemany можно передавать объекты-итераторы, и
функция будет выполнять итерацию по каждому кортежу значений, который возвращает
итератор. Итератор должен возвращать кортеж значений.
import sq liteЗ
class lterChars:
def _iniL(self):
self.count = ord('a')
def _iter_(self):
return self
def _next_(self):
# (use next(self) for Python 2)
if self.count > ord('z'):
raise Stoplteration
self.count += 1
return (chr(self.count - 1 ),)
сопп = sq liteЗ.connect("abc.db")
cur = conn.cursor()
cur.execute("create tаЫе characters(c)")
thelter = lterChars()
cur.executemany("insert into characters(c) values (?)", thelter)
rows = cur.execute("select с from characters")
for row in rows:
print(row[O]),
3. executescript(sq l_script) - это нестандартный удобный метод для одновременного выпол­
нения нескольких SQL-операторов. Сначала он выдает оператор COMMIT, а затем выполняет
сценарий SQL, который получает в качестве параметра. sql_script может быть экземпляром
типов str или bytes.
import sq liteЗ
сопп = sq liteЗ.connect(":memory:")
cur = conn.cursor()
cur.executescript("""
create tаЫе person(
firstname,
lastname,
age

);

create tаЫе book(
title,
author,
puЬl ished

);

insert into book(title, author, puЬlished)
values (
'Dirk Gently"s Holistic Detective Agency',
'Douglas Adams',

);

1 987

"'"')

Следующий набор функций используется совместно с операторами SELECT в SQL. Для
получения данных после выполнения оператора SELECT можно либо рассматривать кур-

562

Глава 1 61 . Доступ к базам данных

сор как итератор, либо вызвать метод fetchoneO курсора для получения одной совпадающей
строки, либо вызвать fetchallO для получения списка совпадающих строк.
Пример с итератором:
import sq l ite3
stocks = [('2006-01 -05', 'BUY', 'RНАТ', 1 00, 35.1 4},
('2006-03-28', 'BUY', 'IBM', 1 ООО, 45.0},
('2006-04-06', 'SELL, 'I BM', 500, 53.0},
('2006-04-05', 'BUY', 'MSFT, 1 ООО, 72.0}]
сопп = sq l ite3.connect(":memory:")
conn.execute("create tаЫе stocks (date text, buysell text, symb text, amount int, price real}")
conn.executemany("insert into stocks values (?, ?, ?, ?, ?}", stocks}
cur = conn.cursor()
for row in cur.execute('SELECT * FROM stocks ORDER ВУ price'):
print(row)
# В ыходные данные:
# ('2006-01 -05', 'BUY', 'RHAT', 1 00, 35. 1 4}
# ('2006-03-28', 'BUY', 'IBM', 1 ООО, 45.0}
# ('2006-04-06', 'SELL, 'IBM', 500, 53.0}
# ('2006-04-05', 'BUY', 'MSFT, 1 ООО, 72.0}

4. fetchone() - получает следующую строку из набора результатов запроса, возвращая
единственную последовательность, или None, если больше нет данных.
cur.execute('SELECT * FROM stocks ORDER ВУ price')
i = cur.fetchone()
while(i):
print(i)
i = cur.fetchone()
# В ыходные данные:
# ('2006-01 -05', 'BUY', 'RHAT', 1 00, 35. 1 4}
# ('2006-03-28', 'BUY', 'IBM', 1 ООО, 45.0}
# ('2006-04-06', 'SELL, 'IBM', 500, 53.0}
# ('2006-04-05', 'BUY', 'MSFT, 1 ООО, 72.0}
5. fetchmany(size = cursor.arraysize) - отбирает очередной набор строк результата запроса (за­
данный размером), возвращая список. Если размер не указан, то fetchmany возвращает один
ряд. Пустой список возвращается, если больше нет доступных строк.
cur.execute('SELECT * FROM stocks ORDER ВУ price')
print(cur.fetchmany(2}}
# В ыходные данн ые:
# [('2006-01 -05', 'BUY', 'RНАТ', 1 00, 35.1 4}, ('2006-03-28', 'BUY', 'IBM', 1 ООО, 45.0}]
6. fetchall(} - выбирает все

(оставшиеся) строки результата запроса, возвращая список.

cur.execute('SELECT * FROM stocks ORDER ВУ price')
print(cur. fetchall(}}

# В ыходные данн ые:
# [('2006-01 -05', 'BUY', 'RНАТ', 1 00, 35.1 4}, ('2006-03-28', 'BUY', 'IBM', 1 ООО, 45.0}, ('2006-04-06', 'SELL,
'IBM', 500, 53.0}, ('2006-04-05', 'BUY', 'MSFT', 1 ООО, 72.0}]

Типы данных SQLite и Python

SQLite нативно поддерживает следующие типы: NULL, INTEGER, REAL, ТЕХТ, BLOB.
Преобразование типов данных при переходе от SQL к Python или наоборот производится
следующим образом:

1 61 .2. Доступ к базе данных MySQL с помощью MySQLdb
None
int
float
str
bytes







563

NULL
I NTEGER/ INT
REAL/FLOAT
TEXT/VARCHAR(n)
BLO B

1 61 .2. Доступ к базе данных MySQL с помощью MySQLdb

Прежде всего необходимо создать соединение с базой данных с помощью метода connect.
После этого необходимо создать курсор, который будет работать с этим соединением. Для
взаимодействия с базой данных используйте метод execute курсора, время от времени фик­
сируйте изменения с помощью метода comm it объекта соединения.
Когда все будет сделано, не забудьте закрыть курсор и соединение. Ниже приведен класс
Dbconnect, содержащий все необходимое.
import MySQ Ldb
class Dbconnect(object):
def _iniL(self):
self.dbconection = MySQ Ldb.connect(host='hosLexample',
port=int('port_example'),
user='user_example',
passwd='pass_example',
db='schema_example')
self.dbcursor = self.dbconection.cursor()
def commit_db(self):
self.dbconection.commit()
def close_db(self):
self.dbcursor.close()
self.dbconection.close()

Взаимодействие с базой данных является очень простым. После создания объекта доста­
точно воспользоваться методом execute.
db = Dbconnect()
db.dbcursor.execute('SELECT * FROM %s' % 'taЫe_example')

Если вы хотите вызвать хранимую процедуру, используйте следующий синтаксис. Обра­
тите внимание, что список параметров является необязательным.
db = Dbconnect()
db.callproc('stored_procedure_name', [parameters] )

После выполнения запроса можно получить доступ к результатам различными способа­
ми. Объект курсора представляет собой генератор, который может получить все результаты
или быть зациклен.
results = db.dbcursor.fetchall ()
for individual_row in results:
f1rst_f1eld = individual_row[0]

Если вы хотите получить цикл, использующий непосредственно генератор:
for individual_row in db.dbcursor:
f1rst_f1eld = individual_row[0]

Если вы хотите зафиксировать изменения в базе данных:
db.commiLdb()

Если необходимо закрыть курсор и соединение:

db.close_db()

564

Глава 1 61 . Доступ к базам данных

1 61 .3. Соединение

Создание соединения

Согласно РЕР 249, соединение с базой данных должно устанавливаться с помощью конструк­
тора connectQ, который возвращает объект Connection. Аргументь1 этого конструктора зависят от
базы данных. Соответствующие аргументы следует искать в спецификациях баз данных.
import MyDBAPI
соп = MyDBAPl.connect{*database_dependent_args)

Объект соединения имеет четыре метода:
1: close
con.close()

Мгновенно закрывает соединение. Обратите внимание, что соединение автоматически
закрывается, если вызывается метод Connection._del_. Произойдет неявный откат всех неза­
вершенных транзакций.
2: commit
con.commit()

Выполняет фиксацию всех незавершенных транзакций в базе данных.

3: rollback
con.rollback{)

Откат к началу любой ожидающей транзакции. Другими словами, отменяется любая не­
зафиксированная транзакция в базе данных.
4: cursor

Возвращает объект Cursor. Используется для выполнения транзакций над базой данных.

1 61 .4. Доступ к базе данных PostgreSQL с помощью psycopg2

psycopg2 - самый популярный адаптер баз данных PostgreSQL, который одновременно яв­
ляется легким и удобным. Это текущая реализация адаптера PostgreSQL.
Его основными особенностями являются полная реализация спецификации Python DB API 2.0
и потокобезопасность (несколько потоков могут использовать одно и то же соединение).

Установление соединения с базой данных и создание таблицы
import psycopg2
# Установить соединение с базой данных.
# Замените значения параметров на учетные данные базы данных.
сопп = psycopg2.connect{database = "testpython",
user = "postgres",
host = "localhost",
password = "abc1 23",
port = "5432")
# Создать курсор. Курсор позволяет выполнять запросы к базе данных.
cur = conn.cursor()
# Создать таблицу. Инициализируйте имя таблицы, имена столбцов и тип данных.
cur.execute('"'"CREATE TABLE FRU ITS {
id
I NT ,
fruit_name ТЕХТ,

1 61 .5. Работа с базами данных Oracle

) JIJJIJ)

color
price

565

ТЕХТ,
REAL

conn.commit()
conn.close()

Вставка данных в таблицу:
# После создания таблицы, как показано выше, заполните ее значениями.
cur.execute("""INSERT I NTO FRU ITS (id, fruiLname, color, price)
VALUES (1 , 'Apples', 'green', 1 .00)""")
cur.execute("""INSERT I NTO FRU ITS (id, fruiLname, color, price)
VALUES (1 , 'Bananas', 'yellow', 0.80)""")

Получе1П1е данныхтаблицы:
# Создать запрос и выполнить его
cur.execute("'"'SELECT id, fruit_name, color, price
FROM fruits""")
# Получить данные
rows = cur.fetchall()
# Выполнить действия с данны ми
for row in rows:
print " ID = {} ".format(row[0])
print "FRUIT NAME = {}".format(row[1 ])
print("COLOR = {}".format(row[2]))
print("PRICE = {}".format(row[З]))
Выходными данными будут:

1D = 1

NAM E = Apples
COLOR = green
PRICE = 1 .0

1D = 2

NAM E = Bananas
COLOR = yel low
PRICE = 0.8

1 61 .5. Работа с базами данных Oracle
Предварительные требования:



Пакет cx_Oracle - смотрите все версии по адРесу https://pypi.python.org/pypi/
cx_Oracle/5.2.1
Мгновенный клиент Oracle instant client - для Windows х:64 (http://www.oracle.com/
technetwork/topics/winx64soft-089540.html), Linux х:64 (http://www.oracle.com/technet­
work/topics/l in uxx86-64soft-092277. html)

Установка:


Установите пакет cx_Oracle при помощи:

sudo rpm -i


Извлеките клиент Oracle instant client и установите переменные окружения:

ORACLE_HOM E=< PATH_TO_I NSTANTCLI ENT>
PATH=$ORACLE_HOME:$PATH
LD_LI BRARY_РАТН=< РАтн_ TO_I NSTANTCLI ENT>:$LD_LI BRARY_РАТН

566

Глава 1 61 . Доступ к базам данных

Создание соединения:
import cx_Oracle
class OraExec(object):
_db_connection = None
_db_cur = Nопе
def _init_(self):
self._db_connection =
cx__Oracle.connect{'/< PASSWORD>@< HOSTNAM E>:< PORT>/')
self._db_cur = self._db_connection.cursor()

Получение версии базы данных:
ver = con.version.split(".")
print ver

Пример вывода: ['1 2', '1 ', ·о·, '2', 'О']

Выполнить запрос: SELECT
_db_cur.execute("select * from employees огdег Ьу emp_id")
for result in _db_cur:
print result
Выходные данные будут представлены в виде кортежей Python:
(1 О, 'SVSADM I N', 'IT-IN FRA', 7)
(23, 'HR ASSOCIATE', 'HUMAN RESOU RCES', б)

Выполнить запрос: INSERT
_db_cur.execute("insert into employees(emp_id, title, dept, grade)
values (31 , 'MTS', 'EN G I N EERI NG', 7)
_db_connection.commit()
Когда вы выполняете операции вставки, обновления или удаления в Oracle Database, из­
менения доступны только в пределах вашей сессии до тех пор, пока не будет выполнена
фиксация (commit). Когда обновленные данные фиксируются в базе данных, они становятся
доступными для других пользователей и сеансов.

Выполнить запрос : INSERT с использованием связанных переменных Bind variaЬles

Привязка переменных позволяет повторно выполнять операторы с новыми значениями,
не прибегая к повторному разбору оператора. Это повышает удобство повторного использо­
вания кода и снижает риск атак типа SQL Injection.
rows = [ (1 , "First" ),
(2, "Second" ),
(3, "Third" ) ]
_db_cur.Ьindarraysize = 3
_db_cur.setinputsizes(int, 1 О)
_db_cur.executemany("insert into mytab(id, data) values (:1 , :2)", rows)
_db_connection.commit()

Закрытие соед1П1ения
_db_connection.close()
Метод close() закрывает соединение. Все соединения, не закрытые явным образом, будут
автоматически освобождены при завершении работы скрипта.

1 61 .6. Использование бибилиотеки sqlalchemy

1 61 .6. Использование бибилиотеки sqlalchemy

567

Использование sqlalchemy для базы данных:
from sqlalchemy import create_engine
from sqlalchemy.engine.url import URL
url

=

URL{drivername = 'mysq l',
username = 'user',
password = 'passwd',
host = 'host',
database = 'db'}

engine = create_engine(url} # движок sqlalchemy
Теперь этот движок можно использовать: например, вместе с Pandas для получения датафреймов непосредственно из mysql.
import pandas as pd
con = engine.connect()
dataframe = pd.read_sq l (sql = query, соп = соп)

Глава 1 62. Подключение Python
к SQL Server
1 62. 1 . Подключение к серверу, создание таблицы, запрос данных
Установите пакет:
import pymssql
SERVER = "servername"
USER = "username"
PASSWORD = "password"
DATABASE = "dbname"
connection = pymssq l.connect(server = SERVER, user = USER,
password = PASSWORD, database = DATABASE}
cursor = connection.cursor() # для доступа к полю как к словарю используйте cursor(as_dict =True)
cursor.execute("SELECT ТОР 1 * FROM TaЬleName")
row = cursor.fetchone()
######## создать таблицу ########
cursor.execute("""
CREATE TABLE posts (
post_id I NT PRIMARY КЕУ NOT N ULL,
message ТЕХТ,
puЬlish_date DATETIM E

######## вставить данные в таблицу ########
cu rsor. execute("""

568

Глава 1 63. Реляционная база данных PostgreSQL

INSERT I NTO posts VALU ES(1 , "Неу There", "1 1 .23.201 6"}

'""')

# commit your work to database
connection.commit()
######## итерация результатов ########
cursor.execute("SELECT ТОР 1 О * FROM posts ORDER ВУ puЬlish_date DESC"}
for row in cursor:
print("Message: " + row[1 ] + " 1 " + "Date: " + row[2]}
# если передать курсору значение as_dict=True
# print(row["message"])
connection.close()

Если ваша работа связана с SQL-выражениями, вы можете делать что угодно, просто пе­
редавайте эти выражения в метод execute (СRUD-операции). Для получения информации об
операторе with, вызове хранимой процедуры, обработке ошибок и т. д. обратитесь к ресурсу
http://pymssql.org/.

Глава 1 6 3 . Реляционная база данных
PostgreSQL
1 63. 1 . Начало работы

PostgreSQL - активно развивающаяся и зрелая база данных с открытым исходным кодом.
Используя модуль psycopg2, можно выполнять запросы к этой базе данных.
Установка с помощью pip
pip install psycopg2

Базовое использование
Предположим, что у нас есть таблица my_taЫe в базе данных my_database, заданная сле­
дующим образом.
last_name
id
first_name
Doe
John
1
Мы можем использовать модуль psycopg2 для выполнения запросов к базе данных следующим образом.
import psycopg2
# Установить соединение с существующей базой данных 'my_database', используя
# пользователя 'my_user' с паролем 'my_password'
con = psycopg2.connect("host=localhost dbname=my_database user=my_user password=my_pass­
word"}
# Создать курсор
cur = con.cursor()
# Вставка записи в 'my_taЫe'
cur.execute("INSERT I NTO my_taЫe(id, fi rsLname, lasLname) VALUES (2, 'Jane', 'Doe');")

1 64.1 . Чтение данных Excel с помощью модуля xlrd

569

# В ы полнить текущую транзакцию
con.commitO
# Получение всех записей из 'my_taЫe' cur.execute("SELECT * FROM my_taЫe;") results = cur.fetchallQ
# Закрыть соединение с базой дан ных
con.close()
# Вывести результаты
print(results)
# Результат: [(1 , 'John', 'Doe'), (2, 'Jane', 'Doe')]

Глава 1 64. Python и Excel
1 64. 1 . Чтение данных Excel с помощью модуля xlrd
Библиотека Python xlrd предназначена для извлечения данных из электронных таблиц
Microsoft Excel.

Установка
pip install xlrd
Или можно использовать setup.py из pypi:
https://pypi.python.org/pypi/xl rd.

Чтение табтщы Excel

Импортируйте модуль xlrd и откройте таблицу Excel с помощью метода open_workbook().

import xlrd
book =xlrd.open_workbook('sample.xlsx')
Проверка количества листов в Excel:
print book.nsheets
Печать названий листов:
print book.sheet_names()
Получение листа по индексу:
sheet = book.sheet_by_index(1 )
Чтение содержимого ячейки:
cel l = sheet.cell(row,col) #где гоw = номер строки и соl = номер столбца
print cell.value #для вывода содержимого ячейки
Получение количества строк и количества столбцов в листе Excel:
num_rows =sheet.nrows
num_col =sheet.ncols
Получение листа Excel по имени:
sheets = book.sheet_namesO
cur_sheet = book.sheet_by_name(sheets[0])

570

Глава 1 64. Python и Excel

1 64.2. Форматирование файлов Excel с помощью модуля
xlsxwriter
import xlsxwriter

# создать новы й файл
workbook = xlsxwriter.Workbook('your_fi le.xlsx')
# добавить несколько новых форматов, которые будут использоваться рабочей книгой
percent_format = workbook.add_format({'num_format': '0%'})
percent_with_decimal = workbook.add_format({'num_format': 'О.О%'})
bold = workbook.add_format({'bold': True})
red_font = workbook.add_format({'fonLcolor': 'геd'})
remove_format = workbook.add_format()
# добавить Н О В Ы Й Л И СТ
worksheet = workbook.add_worksheet()
# установить ширину столбца А
worksheet.seLcolumn('A:A', 30, )
# установить в столбце В значение 20 и включить формат процентов, который мы создали ранее
worksheet.seLcol umn('B: B', 20, percent_format)
# удалить форматирование из первой строки (изменить на height=None)
worksheet.seLrow('0:0', None, remove_format)
workbook.close()

1 64.З. Помещение списковых данных в файл Excel
import os, sys
from openpyxl import Workbook
from datetime import datetime
dt = datetime.now()
list_values = [["01 /01 /201 б", "05:00:00", 3], \
["01 /02/201 б", "06:00:00", 4], \
["01 /03/201 б", "07:00:00", 5], \
["01 /04/201 б", "08:00:00", б], \
["01 /05/201 б", "09:00:00", 71]
# Создать рабочую книгу в Excel :
w b = Workbook()
sheet = wb.active
sheet.title = 'data'
# Вывести заголовки в рабочую книгу Excel:
row = 1
sheet['A'+str(row)] = 'Date'
sheet['B'+str(row)] = 'Hour'
sheet['C'+str(row)] = 'Value'
# Заполнить дан н ы ми:
for item in list_values:
row += 1
sheet['A'+str(row)] = item[0]
sheet['B'+str(row)] = item[1 ]
sheet['C'+str(row)] = item[2]

1 64.4. Модуль OpenPyXL

571

# Сохранить файл по дате:
f1 lename = 'data_' + dt.strftime("%Y%m%d_%1%M%S") + '.xlsx'
wb.save(fi lename)
# Открыть файл для пользователя:
os.chdir(sys. path[O])
os.system('start excel.exe "%s\\%s"' % (sys. path[O], fi lename, ))

1 64.4. Модуль O p enPyXL

OpenPyXL - это модуль для создания рабочих книг (workЬooks) форматов xlsx/xlsm/xltx/xltm
и работы с ними в памяти.
Работа с суще ствующим файлом
import openpyxl as арх
#Для изменения существующей workbook мы находим ее, ссылаясь на ее путь
workbook = opx. load_workbook(workbook_path)
load_workbookO содержит параметр read_only, установка которого в значение True приводит
к загрузке рабочей книги в режиме read_only, что удобно при чтении больших хlsх-файлов:
workbook = opx. load_workbook(workbook_path, read_only=True)

После загрузки рабочей книги в память можно получить доступ к отдельным листам с
помощью workbook.sheets:
fi rst_sheet = workbook.worksheets[O]

Если необходимо указать имя доступного листа, можно воспользоваться функцией

workbook.get_sheet_names().
sheet = workbook.get_sheet_by_name('Sheet Name')

Наконец, доступ к строкам листа можно получить с помощью sheet. rows. Для итерации
по строкам листа используйте:
for row in sheet. rows:
print row[O].value

Поскольку каждая строка представляет собой список ячеек (Cel ls), для получения содер­
жимого ячейки используйте Cell.value.
С оздание нового ф айла в памяти
#Вызов функции Workbook() создает в памяти новую книгу
wb = opx.Workbook()
#М ы можем создать нов ы й лист в книге
ws = wb.create_sheet('Sheet Name', О) #О обозначает индекс порядка листа в рабочей книге

Некоторые свойства могут быть изменены с помощью openpyxl, например, tabColor:
ws.sheet_properties.tabColor = 'FFCOCB'

Чтобы сохранить созданную нами рабочую книгу, выполним следующее:

wb.save('fi lename.xlsx')

1 64.5. Создание диаграмм Excel с помощью xlsxwriter
import xlsxwriter

# образец данных
chart_data = [
{'name': 'Lorem', 'value': 23},
{'name': 'l psum', 'value': 48},

572
{'name': 'Dolor', 'value': 1 5},
{'name': 'Sit', 'value': В},
{'name': 'Amet', 'value': 32}
# путь к файлу excel
xls_f1 le = 'chart.xlsx'
# рабочая книга
workbook = xlsxwriter.Workbook(xls_fi le)
# добавление листа в рабочую книгу
worksheet = workbook.add_worksheet()
row_ = О
col_ = О
# запись заголовков
worksheet.write(row� col� 'NAM E')
col_ += 1
worksheet.write(row_, col_, 'VALUE')
row_ += 1
# запись данных
for item in chart_data:
col_ = О
worksheet.write(row� col_, item['name'])
col_ += 1
worksheet.write(row� col� item['value'])
row_ += 1
# создание круговой диаграммы
pie_chart = workbook.add_chart({'type': 'pie'})
# добаление series на диаграмму
pie_chart.add_series({
'name': 'Series Name',
'categories': '=Sheet1 !$A$3:$A$%s' % row_,
'values': '=Sheet1 !$8$3:$8$%s' % row�
'marker': {'type': 'circle'}
})
# вставка круговой диаграм мы
worksheet. inserLchart('D2', pie_chart)
# создание столбцовой диаграммы
column_chart = workbook.add_chart({'type': 'column'})
# добавление series в столбцовую диаграмму
column_chart.add_series({
'name': 'Series Name',
'categories': '=Sheet1 !$A$3:$A$%s' % row�
'values': '=Sheet1 !$8$3:$8$%s' % row�
'marker': {'type': 'circle'}
})
# вставка столбцовой диаграмм ы
worksheet. inserLchart('D20', column_chart)
workbook.close()

Глава 1 64. Python и Excel

573

1 65.1 . Создание N inja Twist при помощи Turtle Graphics

Результат:
в

8

A m et

L

G

J_

к

Н
--1--

Se ries N a m e

4 D o lo r
5 Sit
6

D

с

32

-+------;

9

■ l ps u m
■ Dol o r
■ S it
■ Amet

10

11

12
13

14
15

16

17
18

19

20
21

Se ries N a m e

22
23
24
25

26

t

27

28

40

■ Ser l es Na me

+ 30

_2_
9 --+---+- ----+----+ 20
30
31

32
33
34
35

f

±

Dol or

l psum

l

S it

Amet

L1

Глава 1 65. Turtle Graphics
("Черепашья графика")
1 65. 1 . Создание Ninja Twist при помощи Turtle Graphics
import turtle
ninja = turtle.TurtleO
ninja.speed(1 О)
for i in range(1 80):
ninja.forward(1 00)
ninja.right(З0)
ninja.forward(20)

t

574

Глава 1 66. Продолжающиеся действия (Persistence) в Python

ninja.left(60)
ninja.forward(50)
ninja.right(ЗO)
ninja.penup()
ninja.setposition(O, О)
ninja.pendown()
ninja.right(2)
turtle.done()

Глава 1 66. П родолжающиеся действия
(Persistence) в Python
П араметр

Описание

obj

Рiсklе-репрезентация объекта открытому файлу объекта файла
целое число, указывающее на использование заданного протокола, где
о - ASCII, 1 - старый двоичный формат
Аргумент fi le должен иметь write() метод wb для метода dump и для загруз­
ки read() метод гЬ

protocol
file

1 66. 1 . Продолжающиеся действия в Python

Такие объекты, как числа, списки, словари, вложенные структуры и объекты экземпля­
ров классов, "живут" в памяти компьютера и теряются при завершении работы скрипта. Мо­
дуль pickle хранит данные продолжающимся образом в отдельном файле. Его представление
объекта всегда и во всех случаях является байтовым объектом, поэтому для хранения дан­
ных необходимо открывать файлы, указывая wb, а для загрузки данных из pickle - rb.
Данные могут быть любого вида, например:
data={'a':'some_value',
'Ь':[9,4,7],
'c':['some_str','another_str','spam','ham'],
'd':{'key':'nested_dictionary'},
}

1 66.2. Функциональная утилита для сохранения и загрузки

575

Хранение данных:
import pickle
file = open('filename','wb')
pickle.dump(data,f1le)
f1le.close()

#файловый объект в режиме двоичной записи
#вы грузка данных в файловый объект
#закрыть файл для записи в него данных

Загрузка данных:

import pickle
file = open('filename','rb')
data = pickle.load(f1le)
f1le.close()

#объект файла в режиме бинарного чтения
#загрузить данные обратно

»>data
{'Ь': [9, 4, 7], 'а': 'some_value', 'd': {'key': 'nested_dictionary'},
'с': ['some_str', 'another_str', 'spam', 'ham']}

Модуль pickle принимает:
1. None, True и False.

2. Целые числа, числа с плавающей точкой, комплексные числа.
3. Строки, байты, байтовые массивы.
4. Кортежи, списки, наборы и словари, содержащие только рiсklаЫе-объекты.
5. Функции, определяемые на верхнем уровне модуля (с использованием def, а не lambda).
6. Встроенные функции, определяемые на верхнем уровне модуля.
7. Классы, определяемые на верхнем уровне модуля.
8. Экземпляры классов, чей словарь или результат вызова getstate() может быть обработан.

1 66.2. Функциональная утилита для сохранения и загрузки
Сохранение данных в файл и из него:

import pickle
def save(filename,object):
f1le = open(filename,'wb')
pickle.dump(object,f1le)
f1le.close()
def load(filename):
f1le = open(filename,'rb')
object = pickle.load(file)
f1le.close()
return object
>>>list_object = [1,1,2,3,5,8,'a','e','i','o',' u']
»>save(list_f1le,list_object)
»>new_list =load(lisLf1le)
>»new_list
[ 1 J 1 , 2, 3, 5, В, 'а', 'е', ' i\ 'о', ' u '

Глава 1 67. Шаблоны проектирования
Шаблон (паттерн) проектирования - это общее решение часто встречающейся проблемы
при разработке программного обеспечения. В данной главе представлены примеры распро­
страненных паттернов проектирования в Python.

576

Глава 1 67. Шаблоны проектирования

1 б 7 . 1 . Введение в паттерны проектирования и паттерн синглтон

Шаблоны проектирования обеспечивают решение часто встречающихся проблем при
проектировании программного обеспечения. Впервые они были представлены группой GoF
(Gang of Four), которые описали общие шаблоны как проблемы, которые возникают снова и
снова, и пути решения этих проблем.
Шаблоны проектирования состоят из четырех основных элементов:

1. Имя (name) - это дескриптор, с помощью которого мы можем кратко описать проблему
проектирования, ее решения и последствия.
2. Задача (ргоЫеm) описывает, когда следует применять шаблон.
3. Решение (solution) описывает элементы, составляющие проект, их взаимосвязи, ответ­
ственность и взаимодействие.
4. Последствия (consequences) - это результаты применения паттерна и компромиссы
при его использовании.
Преимущества шаблонов проектирования:

1.
2.
3.
4.

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

Шаблоны проектирования можно разделить на три категории:

1. Creational Pattern (творческий шаблон, творческая модель).
2. Structural Pattern (структурный шаблон, структурная схема).
3. Behavioral Pattern (поведенческий шаблон).
Creational Pattern - относятся к тому, как может быть создан объект, изолируя детали соз­
дания объекта.
Structural Pattern - разрабатывают структуру классов и объектов таким образом, чтобы их
можно было компоновать для достижения больших результатов.
Behavioral Pattern - концентрируются на взаимодействии между объектами и ответствен­
ности объектов.
Паттерн синглтона:

Синглтон (Singleton, "одиночка") - это разновидность творческого шаблона, которая пре­
доставляет механизм, позволяющий иметь единственный объект заданного типа и обе­
спечивающий глобальную точку доступа. Например, синглтон может использоваться в
операциях с базами данных, когда мы хотим, чтобы объект базы данных поддерживал со­
гласованность данных.
Реализация

Мы можем реализовать паттерн синглтона в Python, создав только один экземпляр клас­
са Singleton и обслуживая тот же объект повторно.
class Singleton(object):
def _new_(cls):
# Метод hasattr п роверяет, есть ли у объекта класса свойство экзем пляра или нет
if not hasattr(cls, 'instance'):
cls.instance = super(Singleton, cls)._new_(cls)
return cls.instance
s = Singleton()
print ("Object created", s)
s1 = Singleton()
print ("Object2 created", s1 )

1 67.2. Стратегически й шаблон

577

Результат:

('Object created', )
('Object2 created', )
Заметим, что в таких языках, как С++ или Java, этот паттерн реализуется за счет того, что
конструктор делается приватным и создается статический метод, выполняющий инициали­
зацию объекта. Таким образом, при первом вызове создается один объект, и в дальнейшем
класс возвращает один и тот же объект. Но в Python мы не имеем возможности создавать та­
кие конструкторы.

Фабричный шаблон (Factory Pattern)

Это тоже творческий шаблон. Термин "фабричный" означает, что класс отвечает за созда­
ние объектов других типов. В качестве фабрики выступает класс, который имеет объекты и
методы, связанные с ним. Клиент создает объект, вызывая методы с определенными параме­
трами, а фабрика создает объект нужного типа и возвращает его клиенту.
from аЬс import ABCMeta, abstractmethod

class Music():
_metaclass_ = ABCMeta
@abstractmethod
def do_play(self):
pass
class MpЗ(Music):
def do_play(self):
print ("Воспроизведение музыки в формате .mрЗ!")
class Ogg(Music):
def do_play(self):
print ("Воспроизведение музыки в формате .ogg !")
class MusicFactory(object):
def play_sound(self, object_type):
return eval(object_type)().do_play()
if _name_ == "_main_":
mf = MusicFactory()
music = input("Kaкyю музыку вы хотите восп роизвести - М рЗ или Ogg?")
mf.play_sound(music)
Выход:

Какую муз ы ку вы хотите воспроизвести - МрЗ или Ogg?
Воспроизведение муз ы ки в формате .ogg!
MusicFactory - это фабричный класс, который создает объект типа М рЗ или Ogg в зависи­
мости от выбора пользователя.

1 67.2. Стратегический шаблон

Этот шаблон позволяет определить семейство алгоритмов, инкапсулировать каждый из
них и сделать их взаимозаменяемыми. Стратегический шаблон позволяет алгоритму изме­
няться независимо от клиентов, которые его используют.
Например, животные могут "ходить" разными способами. Ходьба может рассматривать­
ся как стратегия, реализуемая разными видами животных:
from types import MethodType
class Animal(object):
def _iniL(self, *args, **kwargs):

578

Глава 1 67. Шаблоны проектирования
self.name = kwargs.pop('name', None) or 'Animal'
if kwargs.get('walk', None):
self.walk = MethodType{kwargs.pop('walk'), self)

def walk{self):
,,,,,,

Функционал ьность ходьбы я вляется стратегией и п редназначена
для раздельной реализации разными видами животных.
message = '{} should implement а walk method'.format(
self._class_._name_)
raise Notlmplemented Error(message)
# Вот нескол ько различных ал горитмов ходьбы, которые могут быть испол ьзованы с Animal
def snake_wal k{self):
print('Я ползаю, потому что я {}.'.format(self.name))
def four_legged_animal_wal k{self):
print{'Я использую все четыре ноги, чтобы ходить, потому что я {}.'.format(
self.name))
def two_legged_animal_wal k{self):
print{'Я встаю на две ноги, чтобы ходить, потому что я {}.'.format(
self.name))

В результате выполнения этого примера будет получен следующий результат:
generic_animal = Animal{)
king_cobra = Animal{name = 'кoбpa', walk=snake_walk)
elephant = Animal{name = 'cлoн', walk = four_legged_animal_walk)
kangaroo = Animal{name = 'кeнrypy', walk=two_legged_animal_walk)
kangaroo.walk{)
elephant.walk()
king_cobra.wal k{)
# ошибка Notlmplemented Error, чтобы дать понять п рограммисту
# что метод walk предназначен для использования в качестве стратегии
generic_animal.walk{)
# Резул ьтат:
# Я встаю на две ноги, чтобы ходить, потому что я кенгуру.
# Я испол ьзую все четыре ноги, чтобы ходить, потому что я слон.
# Я ползаю, потому что я кобра.
# Traceback (most recent cal l last):
# File "./strategy.py", line 56, in
# generic_animal .walk()
# File "./strategy.py", line 30, in walk
# raise Notlmplemented Error(message)
# Notlmplemented Error: Animal should implement а walk method (животное должно реализовать
метод walk)

Заметим, что в таких языках, как С++ или Java, этот паттерн реализуется с помощью аб­
страктного класса или интерфейса для определения стратегии. В Python более целесообраз­
но просто определить некоторые функции извне, которые можно динамически добавлять в
класс с помощью types. MethodType.

1 67.З. Прокси-объект

Прокси-объект часто используется для обеспечения защищенного доступа к другому объ­
екту, внутреннюю логику которого мы не хотим "загрязнять" требованиями безопасности.
Предположим, мы хотим гарантировать, что доступ к ресурсу может получить только
пользователь с определенными правами.

1 67.3. Прокси-объект

579

Определим прокси: (будет гарантировать, что только те пользователи, которые могут видеть резервирование, смогут воспользоваться услугой reservation_service):
from datetime import date
from operator import attrgetter
class Ргоху:
def _iniL(self, current_user, reservation_service):
self.currenLuser = currenLuser
self. reservation_service = reservation_service
def highesLtotal_price_reservations(self, date_from, date_to, reservations_count):
if self.current_user.can_see_reservations:
return self. reservation_service. highesLtotal_price_reservations(
date_from,
date_to,
reservations_count
)
else:
return



#Models and ReservationService:
class Reservation:
def _iniL(self, date, total_price):
self.date = date
self.total_price = total_price
class ReservationService:
def highesLtotal_price_reservations(self, date_from, date_to, reservations_count):
# обычно считы вается из базы данных/внешнего сервиса
reservations = [
Reservation(date(201 4, 5, 1 5), 1 00),
Reservation(date(201 7, 5, 1 5), 1 О),
Reservation(date(201 7, 1 , 1 5), 50)
f1 ltered_reservations = [г for г in reservations if (date_from < = r.date < = date_to)]
sorted_reservations = sorted(f1 ltered_reservations, key = attrgetter('total_price'), reverse = True)
return sorted_reservations[0:reservations_count]
class User:
def _init_(self, can_see_reservations, name):
self.can_see_reservations = can_see_reservations
self.name = name
#Потребительский сервис:
class StatsService:
def _init_(self, reservation_service):
self. reservation_service = reservation_service
def year_top_ 1 00_reservations_average_total_price(self, уеаг):
reservations = self. reservation_service. highest_total_price_reservations(
date(year, 1 , 1 ),
date(year, 1 2, 31 ),
1
if len(reservations) > О:
total = sum(r.total_price for г in reservations)

580

Глава 1 68. Модуль hashlib
return total / len(reservations)
else:
return О

#Тест:
def test(user, уеаг):
reservations_service = Proxy(user, ReservationService())
stats_service = StatsService(reservations_service)
average_price = stats_service.year_top_ 1 00_reservations_average_total_price(year)
print("{0} wil l see: {1 }".format(user.name, average_price))
test(User(True, "John the Admin"), 201 7)
201 7)
test(User(False, "Guest"),

Выгоды
• Мы избегаем любых изменений в ReservationService при изменении ограничений
доступа.
• Мы не смешиваем данные, связанные с бизнесом (date_from, date_to,
reservations_count), с понятиями, не связанными с предметной областью
(разрешениями для пользователей).
• Потребитель (StatsService) также свободен от логики, связанной с разрешениями.
Предостережения
• Интерфейс прокси всегда точно такой же, как и скрываемый им объект, поэтому
пользователь, использующий сервис, "обернутый" прокси, даже не подозревает о
его наличии.

Глава 1 68. Модуль hashlib
hashlib реализует общий интерфейс для многих различных алгоритмов безопасного хе­
ширования и дайджеста сообщений. В него входят защищенные по стандарту FIPS хеш-алго­
ритмы SHA1, SHA224, SHA256, SHA384 и SНА512.

1 68.1 . МD5-хеш строки

Данный модуль реализует общий интерфейс для многих различных безопасных алгорит­
мов хеширования и дайджеста сообщений. В модуль включены защищенные алгоритмы хе­
ширования SHA1, SHA224, SHA256, SНАЗ84 и SНА512 (определенные в стандарте FIPS 180-2), а
также алгоритм MDS компании RSA (описанный в документе Internet RFC 1321).
Для каждого типа хеша существует свой метод-конструктор. Все они возвращают
хеш-объект с одним и тем же простым интерфейсом. Например: используйте sha1 () для соз­
дания хеш-объекта SHA1.
hash.sha1 ()

Конструкторы для хеш-алгоритмов, которые всегда присутствуют в этом модуле, - это
и sha51 2(). Теперь можно передавать этому объек­
ту произвольные строки с помощью метода update(). В любой момент можно запросить у
него дайджест конкатенации строк, переданных ему на данный момент, используя методы
digest() или hexdigest().
md5(), sha1 (), sha224(), sha256(), sha384()

hash.update(arg)
ОбновлеlПlе хеш-объекта с помощью строкового аргумента. Повторные вызовы эквивалент­
ны одному вызову с конкатенацией всех аргументов: m.update(a); m.update(b) эквивалентно
m.update(a+b).

1 68.2. Алгоритм, предоставляемый OpenSSL

581

hash.digest()
Возвращает дайджест строк, переданных на данный момент в метод update(). Это строка разме­
ром digest_size байт, которая может содержать символы, отличные от ASCII, включая нулевые
байты.
hash.hexdigest()
Аналогично digest(), только дайджест возвращается в виде строки двойной длины, содержащей
только шестнадцатеричные цифры. Это может быть использовано для безопасного обмена зна­
чениями в электронной почте или других недвоичных средах.
Приведем пример:
import hashlib
m = hashl ib.md5{}
»> m.update("Hиктo не проверяет")
»> m.uрdаtе("спам-повторение")
»> m.digest()
'\xbbd\x9c\x83\xdd\x1 e\xa5\xc9\xd9\xde\xc9\xa1 \x8d\xf0\xff\xe9'
»> m.hexdigest()
'bb649c83dd1 ea5c9d9dec9a1 8df0ffe9'
»> m.digest_size
16
» > m.Ыock_size
64
»>
»>

или:
hashlib.mdS("Hиктo не проверяет cnaм-noвтopeниe").hexdigest()
'bb649c83dd1 ea5c9d9dec9a1 8df0ffe9'

1 68.2. Алгоритм, предоставляемый OpenSSL
Для доступа к перечисленным выше хешам, а также к любым другим алгоритмам, ко­
торые может использовать ваша библиотека OpenSSL, также существует общий конструк­
тор new(), принимающий в качестве первого параметра строковое имя требуемого алгорит­
ма. Именованные конструкторы работают гораздо быстрее, чем new(), и им следует отдавать
предпочтение. Использование new() с алгоритмом, предоставляемым OpenSSL:
h = hashl ib.new('ripemd1 60')
h.update("Hиктo не проверяет спам-повторение")
»> h.hexdigest()
'сс4а5се1 b3df48aec5d22d 1 f1 бЬ894а0Ь894ессс'
»>
»>

Глава 1 69. Создание службы Windows
с помощью Python
Процессы без пользовательского интерфейса (Headless processes) в Windows называются
службами. Ими можно управлять (запускать, останавливать и т. д.) с помощью стандартных
средств управления Windows, таких как командная консоль, PowerShell или вкладка Services
в Task Manager. Хорошим примером может служить приложение, предоставляющее сетевые
услуги, например, веб-приложение, или приложение для резервного копирования, выпол­
няющее различные фоновые архивные задачи. Существует несколько способов создания и
установки приложения Python в качестве службы в Windows.

582

Глава 1 69. Создание службы Windows с помощью Python

1 69 . 1 . Сценарий на языке Python, который может быть запущен
как служба
import win32serviceuti l
import win32service
import win32event
import servicemanager
import socket
class AppServerSvc (win32serviceutil.ServiceFramework}:
_svc_name_ = "TestService"
_svc_display_name_ = 'Test Service"
def _init_(self,args):
win32serviceutil.ServiceFramework._init_(self,args)
self.hWaitStop = win32event.CreateEvent(None,0,0,None)
socket.setdefaulttimeout(б0}
def SvcStop(self}:
self. ReportServiceStatus(win32service.SERVICE_STOP_PENDI NG}
win32event.SetEvent(self. hWaitStop)
def SvcDoRun(self}:
servicemanager. LogMsg(servicemanager. EVENTLOG_I N FO RMATI О N_ТУРЕ,
servicemanager. PYS_SERVI CE_STARTED,
(self._svc_name�"))
self. main()
def main(self}:
pass
if _name_ == '_main_':
win32serviceutil.Hand leCommand line(AppServerSvc)

Это просто шаблон. Ваш прикладной код, возможно вызывающий отдельный скрипт, бу­
дет находиться в функции main(). Кроме того, необходимо установить его в качестве службы.
Лучшим решением для этого на данный момент является использование Nonsucking Service
Manager. Он позволяет установить службу и предоставляет графический интерфейс для на­
стройки командной строки, которую выполняет служба. Для Python можно создать службу
за один раз:
nssm instal l MyServiceName c:\python27\python.exe c:\temp\myscript.py

где my_script. py - это приведенный выше шаблонный скрипт, модифицированный для
вызова сценария или кода вашего приложения в функции main(). Обратите внимание, что
сервис не запускает Руthоn-скрипт напрямую, а запускает интерпретатор Python и передает
ему основной скрипт в командной строке.
Кроме того, для создания службы можно использовать инструменты, входящие в ком­
плект Windows Server Resource Юt для вашей версии операционной системы.

1 69.2. Запуск веб-приложения Flask в качестве сервиса
Это вариация на тему общего примера. Достаточно импортировать сценарий приложе­
ния и вызвать его метод run() в функции main() сервиса. В данном случае мы также использу­
ем модуль многопроцессорной обработки из-за проблемы с доступом к WSG I RequestHandler.
import win32serviceuti l
import win32service
import win32event

1 70.1 . Изменяемые и неизменяемые типы

583

import servicemanager
from multiprocessing i mport Process
from арр import арр
class Service(win32serviceutil.ServiceFramework):
_svc_name_ = "TestService"
_svc_display_name_ = 'Test Service"
_svc_description_ = "Тестирует сервисный фрей мворк Python, п ринимая и передавая эхо­
сообщения через именованны й pipe"
def _iniL(self, *args):
super()._init_(*args)
def SvcStop(self):
self. ReportServiceStatus(win32service.SERVICE_STOP_PEN D I N G)
self.process.terminate()
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
def SvcDoRun(self):
self.process = Process(target =self.main)
self.process.start()
self.process.run()
def main(self):
app.run()
if _name_ == '_main
win32serviceutil.Hand leCommand Line(Service)

Адаптированный пример взят с ресурса http://stackoverflow.com/a/251 30524/3 1 8488.

Глава 1 70. Изменяем ые, неизменяем ые
и хешируемые в Python
1 70 . 1 . Изменяемые и неизменяемые типы

В Python существует два вида типов: неизменяемые и изменяемые.
Неизменяемые

Объект неизменяемого типа не может быть изменен. Любая попытка модифицировать
объект приведет к созданию его копии. В эту категорию входят: целые числа, комплексные,
строки, байты, кортежи, диапазоны и фрозенсеты.
Чтобы выделить это свойство, давайте используем встроенную функцию id. Эта функция
возвращает уникальный идентификатор объекта, переданного в качестве параметра. Если
id не изменился, то это тот же самый объект. Если он изменился, то это другой объект. (Неко­
торые утверждают, что на самом деле это адрес памяти объекта, но остерегайтесь их, они с
темной стороны силы ... )
>>> а = 1
»> id(a)
1 401 281 42243264

584

Глава 1 70. Изменяемые, неизменяемые и хешируемые в Python

>>> а += 2
>>> а
3
>» id(a)
1 401 281 42243328

Так, 1 - это не 3 ... Интересные новости ... Однако об этом поведении часто забывают, когда
речь заходит о более сложных типах, особенно о строках.
>>> stack = "Overflow"
»> stack
'Overflow'
»> id(stack)
1 401 281 23955504
>>> stack += " rocks!"
»> stack
'Overflow rocks!'

Видите? Мы можем это изменять!
»> id(stack)
1 401 281 2391 1 472

Нет. Хотя кажется, что мы можем изменить строку, названную переменной стека, на са­
мом деле мы создаем новый объект, содержащий результат конкатенации. Нас обманывают,
потому что в процессе старый объект уничтожается. В другой ситуации это бьmо бы более
очевидно:
»> stack = "Stack"
>» stackoverflow = stack + "Overflow"
»> id(stack)
1 401 280693481 84
>» id(stackoverflow)
1 401 281 2391 1 480

В этом случае понятно, что если мы хотим сохранить первую строку, то нам нужна ее ко­
пия. Но так ли это очевидно для других типов?

Упражнение

Теперь, зная, как работают неизменяемые типы, что бы вы сказали о приведенном ниже
фрагменте кода? Является ли он разумным?
s = ,т

for i in range(1 , 1 ООО):
s += str(i)

s + = ","

Изменяемые
Объект изменяемого типа может быть изменен, причем изменение происходит на ме­
сте. Никаких неявных копий не производится. К этой категории относятся: списки, словари,
байтовые массивы и наборы. Вернемся к нашей функции id.
»> Ь = bytearray(b'Stack')

bytearray(b'Stack')
»> Ь = bytearray(b'Stack')
»> id(b)
1 401 28030688288
»> Ь += b'Overflow'

bytearray(b'StackOverflow')
»> id(b)
1 401 28030688288

ь

ь

1 70.2. Изменяемые и неизменяемые типы как аргументы

585

(Для наглядности используются байты, содержащие данные в формате ASCII, но помните,
что байты не предназначены для хранения текстовых данных). Что мы имеем? Мы создаем
байтовый массив, модифицируем его и, используя id, можем убедиться, что это тот же самый
объект, модифицированный. А не его копия.
Конечно, если объект будет часто модифицироваться, то изменяемый тип справится с
этой задачей гораздо лучше, чем неизменяемый. К сожалению, о реальности этого свойства
часто забывают, когда это наиболее болезненно.
>>> с = ь
>>> с + = Ь' rocks!'
>>> с
bytearray(b'StackOverflow rocks!')
Хорошо ...
>>> ь
bytearray(b'StackOverflow rocks!')
Подождите секунду...
»> id(c) = = id(b)
Тгuе
Действительно, с не является копией Ь. с - это Ь.

Упражнение

Теперь, когда вы лучше понимаете, какой побочный эффект вызывается изменяемым типом, можете ли вы объяснить, что в этом примере происходит не так?
»> 11 = [ D ]*4 # Создание списка из 4 списков, содержащего наши результаты
»> 11

Ш, □, □, DI

»> ll[0].append(23) # Добавить результат 23 в первый список
»> 11
[[23], [23], [23], [231 ]
»> # Oops ...

1 70.2. Изменяемые и неизменяемые типы как ар гументы

Одним из основных случаев, когда разработчику необходимо учитывать изменяемость,
является передача аргументов в функцию. Это очень важно, поскольку от этого зависит воз­
можность функции изменять объекты, не входящие в ее область видимости, или, другими
словами, наличие у функции побочных эффектов. Это также важно для понимания того, где
должен быть доступен результат работы функции.
»> def lisLadd3(1in):
lin + = [3]
геtuгп lin
»> а = [1 , 2, 3]
»> Ь = lisLadd3(a)
>>> ь
[1 , 2, 3, 3]

>>> а

[1 , 2, 3, 3]

Здесь ошибка заключается в том, что lin, как параметр функции, может быть изменен ло­
кально. Вместо этого lin и а ссьmаются на один и тот же объект. Поскольку этот объект изме­
няемый, модификация выполняется на месте, что означает, что объект, на который ссьmа­
ются и lin, и а, модифицируется. На самом деле возвращать lin не нужно, поскольку у нас уже
есть ссылка на этот объект в виде а. а и Ь в итоге ссылаются на один и тот же объект.

586

Глава 1 71 . Модуль conf1gparser

С кортежами дело обстоит иначе.

»> def tuple_add3(tin):
tin += (3,)
return tin
»> а = (1 , 2, 3)
>» Ь = tuple_add3(a)
>>> ь
(1 , 2, 3, 3)
>>> а
(1 , 2, 3)

В начале функции tin и а ссылаются на один и тот же объект. Но это неизменяемый объ­
ект. Поэтому, когда функция попытается его модифицировать, tin получит новый объект с
модификацией, а а сохранит ссылку на исходный объект. В этом случае возврат tin обязате­
лен, иначе новый объект будет потерян.

Упражнение

>» def yoda(prologue, sentence):
sentence.reverse()
prologue += " ".join(sentence)
return prologue
>» focused = ["You must", "stay focused"]
»> saying = "Voda said: "
»> yoda_sentence = yoda(saying, focused)

Примечание: реверс работает в режиме "на месте".
Что вы думаете об этой функции? Есть ли у нее побочные эффекты? Необходим ли воз­
врат? После вызова какое значение имеет высказывание? В том, чтобы сосредоточиться?
Что произоЙДет, если функцию вызвать еще раз с теми же параметрами?

Глава 1 71 . Модуль configparser
Этот модуль предоставляет класс Conf1gParser, который реализует базовый язык конфигу­
рации в I N l-файлах. С его помощью можно писать программы на языке Python, которые мо­
гут быть легко настроены конечными пользователями.

1 71 .1 . Создание конфиrурационного файла программным способом

Конфигурационный файл содержит секции, каждая из которых содержит ключи и зна­
чения. Модуль conf1gparser может использоваться для чтения и записи соnfig-файлов. Созда­
дим такой файл:
import configparser
config = conf1gparser.Conf1gParser()
config['settings'] ={'resolution':'320x240',
'color':'Ыue'}
with open('example. ini', 'w') as conf1gf1le:
config. write(configf1le)

Выходной файл содержит следующую структуру:

[settings]
resolution = 320х240
color = Ыuе

1 71 .2. Базовое использование

587

Если вы хотите изменить конкретное поле, получите это поле и присвойте ему значение:
settings=conf1g['settings']
settings['color']='red'

1 71 .2. Базовое использование
В файле config.ini:

[DEFAULT]
debug = True
name = Test
password = password
[FILES]
path = /path/to/f1 le

В Python:
from ConfigParser import Conf1g Parser
conf1g = Conf1g ParserO
#Загрузка файла конфигурации
conf1g.read("config. ini")
# Доступ к кл ючу "debug" в секции "DEFAU LT'
conf1g.get("DE FAU LT', "debug")
# Return 'Тгuе'
# Доступ к ключу "path" в разделе "FI LES"
conf1g.get("FI LES", "path")
# Возвращает '/path/to/fi le'

Глава 1 72. Оптическое распознавание
символов (OCR)
Оптическое распознавание символов - это преобразование изображений текста в реаль­
ный текст. В этих примерах рассмотрены способы использования OCR в Python.

1 72. 1 . PyTesseract
PyTesseract -

вольно просто:

это разрабатываемый пакет для OCR на языке Python. Использовать его до­

try:
import lmage
except lmportError:
from PIL import l mage
import pytesseract
#Basic OCR
print(pytesseract.image_to_string(l mage.open('test.png')))
#На французском языке
print(pytesseract.image_to_string(lmage.open('test-european.jpg'), lang='fra'))

588

Глава 1 72. Оптическое распознавание сим волов (OCR)

1 72.2. PyOCR
Другим полезным модулем является PyOCR, исходный код которого находится здесь:
https://github.com/jflesch/pyocr. Он также прост в использовании и имеет больше возможно­
стей, чем PyТesseract.
Для инициализации:
from PIL import l mage
import sys
import руосг
import pyocr.builders
tools = pyocr.get_availaЬle_tools()
# Инструменты возвращаются в рекомендуемом порядке использования
tool = tools[O]
langs = tool.get_availaЬle_languages()
lang = langs[O]
# Обратите внимание, что языки никак не сортируются. Обратитесь к настройкам систем ы для
# определения язы ка для использования по умолчанию.
Некоторые примеры использования:
txt = tool.image_to_string(
l mage.open('test.png'),
lang = lang,
builder = pyocr.builders.TextBuilder()
)
# txt - строка Python
word_boxes = tool.image_to_string(
l mage.open('test.png'),
lang = "eng",
builder = pyocr.builders.Word BoxBuilder()
)
# список блоковых (Ьох) объектов. Для каждого блокового объекта Ьох:
# box.content - слово, находящееся в блоке
# box. position - его положение на странице (в пикселях) #
Ьох
# Следует иметь в виду, что некоторые инструменты OCR (например, Tesseract)
# могут возвращать пустые блоки
line_and_word_boxes = tool.image_to_string(
l mage.open('test.png'), lang = "fra",
builder = pyocr.builders. LineBoxBuilder()
)
# список объектов строчек. Для каждого такого объекта:
# line.word_boxes - список блоков слов (отдельных слов в строчке)
# line.content - весь текст строчки
# line. position - положение всей строчки на странице (в пикселях)
#
# Следует иметь в виду, что некоторые инструменты OCR (например, Tesseract)
# могут возвращать пустые блоки
# Цифры -только Tesseract (пока не 'liЬtesseract'!)
digits = tool.image_to_string(
l mage.open('test-digits. png'),
lang = lang,
builder = pyocr.tesseract.DigitВuilder()
)
# digits - строка в формате python

1 73.1 . Создание и использование виртуальной среды

Глава 1 73. Виртуальные среды

589

Виртуальная среда (виртуальное окружение) - это инструмент для хранения зависимо­
стей, необходимых для разных проектов в разных местах, путем создания для них виртуаль­
ных окружений Python. Это позволяет решить проблему "проект Х зависит от версии 1.х, но
проекту У требуется 4.х", а также изолировать окружения для разных проектов друг от дру­
га и от системных библиотек.

1 73. 1 . Создание и использование виртуальной среды
virtualenv - это инструмент для создания изолированных сред Python. Эта программа соз­
дает папку, содержащую все необходимые исполняемые файлы для использования пакетов,
необходимых проекту Python.

Установка инструмеmа virtualenv

Это требуется только один раз. Программа virtualenv может быть доступна в вашем
дистрибутиве. В DеЬlаn-подобных дистрибутивах пакет называется python-virtualenv или
pythonЗ-virtualenv. В качестве альтернативы можно установить virtualenv с помощью pip:
$ pip install virtualenv

Создание новой виртуальной среды

Это требуется только один раз для каждого проекта. При запуске проекта, для которого
требуется изолировать зависимости, можно настроить новую виртуальную среду для этого
проекта:
$ virtualenv foo
В результате будет создана папка foo, содержащая инструментальные скрипты и копию
самого бинарного файла Python. Имя папки не имеет значения. После создания виртуальной
среды она становится автономной и не требует дальнейших манипуляций с инструментом
virtualenv. Теперь можно приступать к работе с виртуальной средой.

Активация существую щей виртуальной среды
Для активации требуется некоторая магия оболочки, чтобы ваш Python оказался внутри

foo. Для этого используется файл activate, который необходимо расположить в текущей обо­
лочке:
$ source foo/Ьin/activate
Пользователям Windows следует набрать:
$ foo \Scri pts \activate. bat
После активации виртуальной среды внутри foo оказываются исполняемые файлы
python и pip, а также все скрипты, установленные с помощью сторонних модулей. В частно­
сти, все модули, установленные с помощью pip, будут развернуты в виртуальной среде, что
позволяет создать замкнутую среду разработки. Активация виртуальной среды также долж­
на добавить в приглашение команду-префикс, как показано в следующих командах.
# Устанавли вает 'requests' тол ько на foo, а не глобал ьно
(foo)$ pip instal l requests

Сохранение и восстановление зависимостей

Для сохранения модулей, установленных с помощью pip, можно составить список всех
этих модулей (и соответствующих версий) в виде текстового файла с помощью команды
freeze. Это позволит другим пользователям быстро установить необходимые для прило­
жения модули Python с помощью команды install. Традиционное название такого файла requirements.tx1:

590

Глава 1 73. Ви ртуал ьные среды

Обратите внимание, что в файле freeze перечислены все модули, включая транзитивные
зависимости, требуемые модулями верхнего уровня, которые вы установили вручную. Поэ­
тому вы можете предпочесть составить файл requirements.txt вручную, включив в него толь­
ко те модули верхнего уровня, которые вам необходимы.

Выход из виртуальной среды

Если вы закончили работу в виртуальной среде, вы можете отключить ее, чтобы вер­
нуться в обычную оболочку:
(foo)$ deactivate

Использова1П1е виртуальной среды на общем хосте

Иногда нет возможности использовать $ source Ьin/activate, например, если вы использу­
ете mod_wsgi на общем хосте или не имеете доступа к файловой системе, как в Amazon API
Gateway или Google AppEngine. В этих случаях можно развернуть установленные библиоте­
ки в локальной virtualenv и внести исправления в sys.path. К счастью, виртуальная среда по­
ставляется со скриптом, который обновляет как sys. path, так и sys.prefix.
import os

mydir = os. path.dirname(os. path.real path(_f1 le_))
activate_this = mydir + '/Ьin/activate_this.py'
execfi le(activate_this, dict(_fi le_=activate_this))
Эти строки следует добавить в самое начало файла, который будет выполняться вашим
сервером.
Это приведет к тому, что Ьin/activate_this.py, созданный виртуальной средой, окажется
в том же каталоге, где находится выполняемый вами файл, и добавит вашу liЬ/python2.7/
site-packages в sys. path. Если вы хотите использовать скрипт activate_this.py, не забудьте
выполнить развертывание, по крайней мере, каталогов Ьiп и liЬ/python2.7/site-packages и их
содержимого.
Версия Python З.х :::: 3.3

Встроенные виртуальные среды

Начиная с Python 3.3 модуль venv позволяет создавать виртуальные среды. Команда
pyvenv не требует отдельной установки:
$ pyvenv foo
$ source foo/Ьin/activate
или

$ pythonЗ -m venv foo
$ source foo/Ьin/activate

1 73.2. Указание версии Python для использования в скрипте
на Unix/Linux

Для того чтобы указать, какую версию Python должен использовать командный
интерпретатор Linux, первой строкой Руthоn-скриптов может быть шебанг-строка, которая
начинается с #!:

#!/usr/Ьin/python
Если вы находитесь в виртуальной среде, то python myscript.py будет использовать
Python из вашей виртуальной среды, а ./myscript.py будет использовать интерпретатор
Python в #!-строке. Чтобы убедиться, что используется Python из виртуальной среды,
измените первую строку:
#!/usr/Ьin/env python

591

1 73.3. Создание виртуальной среды для различных версий Python

После указания шебанг-строки не забудьте дать скрипту права на выполнение, выполнив
команду:
chmod +х myscript.py

Это позволит выполнить скрипт, запустив ./myscript. py (или указав абсолютный путь к
скрипту) вместо python myscript.py или python3 myscript.py.

1 73.З. Создание виртуальной среды для различных версий
Python

Если предположить, что установлены и Python 2, и Python 3, то можно создатьвиртуальную
среду для Python 3, даже если эта версия не используется по умолчанию:
virtualenv -р python3 foo

или

virtualenv -python = pythonЗ foo

или

python3 -m venv foo

или

pyvenv foo

На самом деле вы можете создать виртуальную среду на основе любой версии рабочего
Python вашей системы. Вы можете проверить разные версии рабочего Python в папке /usr/Ьin/
или /usr/local/Ьin/ (в Linux) или в папке /Library/Frameworks/Python.framework/Versions/X.X/Ьin/
(OSX), затем определить имя и использовать его в флаге --python или -р при создании
виртуальной среды.

1 73.4. Создание виртуальных сред с помощью Anaconda

Мощной альтернативой virtualenv является Anaconda (https://www.continuum.io/downloads) ­
кpoccплaтфopмeнный менеджер пакетов, похожий на pip и снабженный функциями для
быстрого создания и удаления виртуальных окружений. Установив Anaconda, выполните
несколько команд для начала работы:
Создание среды
conda create --name python =
где - произвольное имя вашей виртуальной

версия Python, которую вы хотите установить.

среды, а - конкретная

Активация и дезактивация среды

# Linux, Мае
source activate
source deactivate

или
# Windows
activate
deactivate

Просмотр списка создаШiых сред

conda env list

Удалить среду
conda env remove -n

Более подРобную информацию о командах и возможностях можно найти в официальной
документации менеджера (http://conda.pydata.org/docs/using/envs.html#create-an-environment).

592

Глава 1 73. Ви ртуал ьные среды

1 73.5. Управление несколькими виртуальными средами
с помощью утилиты virtualenvwrapper

Утилита virtualenvwrapper (https://virtualenvwrapper.readthedocs.io/) упрощает работу с вирту­
альными средами и особенно полезна, если вы имеете дело с большим количеством вирту­
альных сред и проектов. Вместо того чтобы самому разбираться с каталогами виртуальных
сред, virtualenvwrapper управляет ими за вас, храня все виртуальные среды в центральном ка­
талоге (по умолчанию ~/.virtualenvs).
Установка

Установите virtualenvwrapper с помощью менеджера пакетов вашей системы.
На базе DeЬian/UЬuntu:

apt-get install virtualenvwrapper

Fedora/CentOS/RНEL:

yum install python-virtualenvrwapper

Arch Linux:

pacman -s python-virtualenvwrapper

или установите его из PyPI с помощью pip:

pip install virtualenvwrapper

Под Windows можно использовать virtualenvwrapper-win или virtualenvwrapper-powershell.

Исполъзова1Ше

Виртуальные среды создаются с помощью команды mkvirtualenv. При этом принимаются
все аргументы исходной команды virtualenv.
mkvirtualenv my-project

или, например

mkvirtualenv --system-site-packages my-project

Новая виртуальная среда активизируется автоматически. В новых оболочках можно
включить виртуальную среду с помощью команды workon:
workon my-project

Преимущество команды workon по сравнению с традиционной . path/to/my-env/Ьin/activate
заключается в том, что команда workon будет работать в любом каталоге; вам не нужно
помнить, в каком каталоге хранится конкретное виртуальное окружение вашего проекта.
Каталоги проектов

Указать каталог проекта можно даже во время создания виртуальной среды с помощью
опции -а или позже с помощью команды setvirtualenvproject.
mkvirtualenv -а /path/to/my-project my-project

или

workon my-project
cd /path/to/my-project
setvirtualenvproject

Установка проекта приведет к тому, что команда workon автоматически переключится на
этот проект и включит команду cdproject, позволяющую поменять каталог проекта.
Для просмотра списка всех виртуальных сред, управляемых virtualenvwrapper, используйте
команду lsvirtualenv. Для удаления виртуальной среды используйте команду rmvirtualenv:
rmvirtualenv my-project

Каждая виртуальная среда, управляемая virtualenvwrapper, включает в себя 4 пустых
Ьаsh-скрипта: preactivate, postactivate, predeactivate и postdeactivate. Они служат в качестве

1 73.6. Установка пакетов в виртуальной среде

593

"крючков" (hooks) для выполнения Ьаsh-команд в определенные моменты жизненного цик­
ла virtualenv; например, любые команды в скрипте postactivate будут выполняться сразу по­
сле активации virtualenv. Это хорошее место для установки специальных переменных окру­
жения, псевдонимов (aliases) или чего-либо еще. Все четыре сценария находятся в папке
.viгtuаlепvs//Ьiп/.
Более подробную информацию можно найти в документации по virtualenvwrapper
(https://Virtualenvwrapper. readthedocs.io/).

1 73.6. Установка пакетов в виртуальной среде
После активации виртуальной среды все устанавливаемые пакеты теперь будут уста­
навливаться в virtualenv, а не глобально. Таким образом, новые пакеты могут не требовать
гооt-привилегий.
Для проверки того, что пакеты устанавливаются в virtualenv, выполните следующую
команду, чтобы проверить путь к используемому исполняемому файлу:
{ requirements.txt

В качестве альтернативы можно не активировать виртуальную среду каждый раз, когда
требуется установить пакет. Для установки пакетов можно напрямую использовать испол­
няемый файл pip в каталоге виртуальной среды.
$ //Ьin/pip instal l requests

Более подробную информацию об использовании pip можно найти в соответствующей
главе.
Поскольку установка производится без гооt-прав в виртуальной среде, это не глобальная
установка на всю систему - установленный пакет будет доступен только в текущей вирту­
альной среде.

Глава 1 73. Виртуал ьные среды

594

1 73. 7. Определение используемой виртуальной среды

Если вы используете стандартное Ьаsh-приглашение в Linux, то в начале приглашения
вы должны увидеть имя виртуальной среды.
(my-project-env) user@hostname:~$ which python
/home/user/my-project-env/Ьi n/python

1 73.8. Проверка на работу в виртуальной среде

Иногда в приглашении командной строки не отображается имя виртуальной среды, и вы
хотите быть уверены, находитесь ли вы в виртуальной среде или нет.
Запустите интерпретатор Python и попробуйте:
import sys
sys.pref1x
sys.real_prefix


Вне виртуальной среды sys.pref1x будет указывать на системную установку Python,
а sys.real_pref1x не определен.
• В виртуальной среде sys.pref1x будет указывать на установку Python в виртуальной
среде, sys.real_pref1x будет указывать на системную установку Python.
Для виртуальных сред, созданных с помощью модуля venv, отсутствует sys.real_pref1x. Вме­
сто этого следует проверить, совпадает ли sys.base_prefix с sys.prefix.

1 73.9. Использование виртуальной среды с оболочкой Fish shell

Fish shell "дружелюбна" к пользователям, но при использовании с virtualenv или virtualen­
vwrapper могут возникнуть проблемы. Тут на помощь приходит менеджер virtualf1sh. Чтобы
начать использовать оболочку Fish shell с virtualenv, выполните следующую последователь­
ность действий.
Установить virtualfish в глобальное пространство:
sudo pip instal l virtualf1sh
Загрузка Руthоn-модуля virtualf1sh при запуске оболочки Fish shell:

$ echo "eval (python -m virtualfish)" > ~/.conf1g/fish/config.fish
Отредактируйте функцию f1sh_prompt с помощью $ funced f1sh_prompt --editor vim и добавьте
следующие строки, после чего закройте редактор vim:
if set -q VI RTUALENV
echo -n -s (seLcolor -Ь Ыuе white) "(" (basename "$VI RTUALENV") '')" (set_color normal) " "
end

Примечание: если вы не используете vim, просто поставьте ваш любимый редактор, на­
пример, так: $ funced f1sh_prompt -- editor nano или $ funced f1sh_prompt -editor gedit.
Сохранение изменений с помощью функции funcsave:
funcsave f1sh_prompt
Для создания новой виртуальной среды используйте команду vf new:

vf new my_new_env # Убедитесь, что $HOME/.virtualenv существует
Если вы хотите создать новое окружение Python 3, укажите его при помощи флага -р:

vf new -р pythonЗ my_new_env
Для переключения между виртуальными средами используйте vf deactivate и vf activate
another_env.
Ссылки:
• https://github.com/adambrenecki/virtualf1sh
• http://virtualf1sh.readthedocs.io/en/latest/

1 74.1 . Установка

Глава 1 7 4 . Виртуальная среда Python virtualenv

595

Виртуальная среда (virtualenv) - это инструмент для создания изолированных окружений
Python. Она позволяет хранить зависимости, необходимые для разных проектов, в разных
местах, создавая для них виртуальные среды Python. Это решает дилемму наподобие "про­
ект А зависит от версии 2.ххх, но проекту В нужна версия 2.ххх" и позволяет сохранить гло­
бальный каталог site-packages чистым и управляемым.
virtualenv создает папку, содержащую все необходимые ресурсы (lib и Ьin) для использова­
ния пакетов, необходимых проекту на Python.

1 74. 1 . Установка

Установите virtualenv с помощью pip / (apt-get):

pip instal l virtualenv
или
apt-get instal l python-virtualenv

Примечание: если у вас возникают проблемы с правами доступа, используйте sudo.

1 74.2. Использование

$ cd tesLproj
Создание виртуальной среды:
$ virtualenv test_proj
Для начала использования виртуальной среды ее необходимо активировать:
$ source test_project/Ьin/activate
Для выхода из virtualenv достаточно набрать "deactivate":
$ deactivate

1 74.З. Установка пакета в виртуальной среде

Если вы посмотрите на каталог Ьin в вашей виртуальной среде, то увидите easy_install, ко­
торый был модифицирован для размещения пакетов в каталоге site-packages виртуальной
среды. Чтобы установить приложение в виртуальную среду, выполните следующие действия:
$ source test_project/Ьin/activate
$ pip install flask
При этом не нужно использовать sudo, поскольку все элементы будут установлены в ло­
кальный каталог виртуальной среды site-packages. Он был создан под вашей собственной
учетной записью пользователя.

1 74.4. Другие полезные команды virtualenv

lsvirtualenv: вывести список всех окружений.
cdvirtualenv: переход в каталог активированной в данный момент виртуальной среды, что
позволяет просматривать, например, ее site-packages.
cdsitepackages: аналогично описанному выше, но переходит непосредственно в каталог
site-packages.
lssitepackages: показывает содержимое каталога site-packages.

596

Глава 1 75. Создание виртуальной среды с помощью надстройки virtualenvwrapper

Глава 1 75. Создание виртуальной среды
с помощью надстройки virtualenvwrapper

Предположим, что вам необходимо работать над тремя разными проектами - проек­
том А, проектом В и проектом С. Для проектов А и В требуются Python 3 и некоторые необхо­
димые библиотеки. Но для проекта С нужны Python 2. 7 и зависимые библиотеки. Поэтому
лучше всего разделить эти проектные среды.
Хотя существует несколько путей создания виртуальной среды, предпочтительнее использовать virtualenvwrapper, потому что он предоставляет больше возможностей.
$ pip instal l virtualenvwrapper
$ export WORKON_HOME=~/Envs
$ mkdir -р $WORKON_HOME
$ source /usr/local/Ьin/virtualenvwrapper.sh
$ printf '\n%s\n%s\n%s' '# virtualenv' 'ехрогt WORKON_HOME=~/virtualenvs' 'source
/home/salayhin/Ьin/virtualenvwrapper.sh' » ~/. bashrc
$ source ~/. bashrc
$ mkvirtualenv python_З. 5
lnstalling
setuptools. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . done.
virtualenvwrapper.user_scripts Creating /Users/salayhin/Envs/python_З.5/bin/predeactivate
virtualenvwrapper.user_scripts Creating /Users/salayhin/Envs/python_З. 5/Ьin/postdeactivate
virtualenvwrapper.user_scripts Creating /Users/salayhin/Envs/python_З.5/bin/preactivate
virtualenvwrapper.user_scripts Creating /Users/salayhin/Envs/python_З. 5/bin/postactivate
New python executaЫe in python_З.5/Ьin/python
(python_З. 5)$ Is $WORKON_H O M E
python_З. 5 hook. log
Теперь мы можем установить в среду некоторое программное обеспечение.
(python_З. 5)$ pip install django
Downloading/unpacking django
Downloading Django-1 . 1 . 1 .tar.gz (5. бМЬ}: 5.бМЬ downloaded
Running setup.py egg_info for package django
lnstalling col lected packages: django
Running setup.py instal l for django
changing mode of build/scripts-2.6/django-admin.py from 644 to 755
changing mode of /Users/salayhin/Envs/env1 /Ьin/django-admin.py to 755
Successfully instal led django
Мы можем увидеть новый пакет с помощью lssitepackages:
(python_З. 5)$ lssitepackages
Django-1 .1 . 1 -py2.6.egg-info easy-instal l . pth
setuptools-0.6.1 0-py2.6.egg pip-0.6.3-py2.6.egg
django setuptools.pth
При желании мы можем создать несколько виртуальных сред.
Переключение между средами осуществляется с помощью команды workon:
(python_З. 6)$ workon python_З. 5
(python_З.5)$ echo $VI RTUALENV
/Users/salayhin/Envs/env1
(python_З. 5)$
Выход из виртуальной среды
$ deactivate

1 74.4. Другие полезные команды virtualenv

597

Глава 1 76. Создание виртуальной среды
с помощью virtualenvwrapper в Windows
Предположим, что вам необходимо работать над тремя разными проектами - проектом
А, проектом В и проектом С. Для проектов А и В требуются Python 3 и некоторые необходи­
мые библиотеки. А для проекта С нужны Python 2. 7 и зависимые библиотеки.
Поэтому наилучшей практикой является разделение этих окружений проекта. Для созда­
ния отдельной виртуальной среды Python необходимо выполнить следующие шаги:
Шаг 1. Установите pip с помощью следующей команды: python -m pip instal l -U pip
Шаг 2. Затем установите пакет virtualenvwrapper-win с помощью нижеприведенной коман­
ды (эта команда может быть исполнена в Windows PowerShell):
pip install virtualenvwrapper-win

Шаг 3. Создайте новое окружение виртуальной среды с помощью команды:

mkvirtualenv python_З.5

Шаг 4: Активируйте среду с помощью команды:

workon < environment name>

Основные команды virtualenvwrapper:

mkvirtualenv
Создает новое окружение virtualenv с именем . Среда будет создана в WORKON_HOME.
lsvirtualenv
Перечисляет все окружения, хранящиеся в WORKON_HOME.
rmvirtualenv
Удаляет окружение . Испол ьзует файл folder_delete.bat.
workon []
Если указано , активизирует окружение с именем (измените рабочий virtualenv на
). Если каталог проекта был определен, то мы перейдем в него. Если аргумент не указан,
то будет выдан список доступных окружений. Можно передать дополнительную опцию -с после
имени virtualenv для перехода в каталог virtualenv, если не задан каталог projectdir.
deactivate
Дезактивирует рабочую virtualenv и переключает обратно на стандартны й системный Python.
add2virtualenv
Если среда virtualenv активна, то добавляется в virtualenv_path_extensions.pth внутри sitе­
пакетов среды, что фактически добавляет в PYTHO N PATH среды. Если среда virtualenv
не активна, то добавляется в virtualenv_path_extensions.pth внутри site-packages Python по
умолчанию. Если не существует, то он будет создан.

Глава 1 77. Модуль sys
Модуль sys предоставляет доступ к функциям и значениям, относящимся к среде выпол­
нения программы, таким как параметры командной строки в sys.argv или функция sys.exit()
для завершения текущего процесса из любой точки программного потока.
Хотя в чистом виде sys выделен в модуль, на самом деле он является встроенным и как
таковой всегда будет доступен в обычных условиях.

598

Глава 1 78. Пакет ChemPy

1 77 . 1 . Аргументы командной строки
if len(sys.argv) ! = 4:
# И мя скрипта также должно быть учтено.
raise RuntimeError("expected 3 command line arguments" (ожидалось 3 аргумента командной строки))
f = open(sys.argv[1], 'гЬ') # Использовать первый аргумент командной строки.
starLline = int(sys.argv[2]) # Все аргументы приходят в виде строк, поэтому должны быть
end_l ine = int(sys.argv[З]) # преобразованы явно, если требуются другие типы

Заметим, что в больших и более совершенных программах можно использовать для об­
работки аргументов командной строки такие модули, как click (http://click. pocoo.org/), а не де­
лать это самостоятельно.

1 77 .2. Имя скрипта
# Имя выполняемого скрипта находится в начале списка argv.
print('usage:', sys.argv[0], ' ')
# Можно использовать для генерации префикса пути выполняемой программы
# (в отличие от текущего модуля) для доступа к файлам относительно него,
# что было бы полезно, например, для ассетов игры
program_file = sys.argv[0]
import pathlib
program_path = pathlib. Path(program_f1le). resolve().parent

1 77.3. Ошибки и стандартный вы вод
# Сообщения об ошибках по возможности не должны вы водиться на стандартный вы вод
print('ERROR: We have по cheese at all.', f1le =sys.stderr)
try:
f = open('nonexistent-file.xyz', 'гЬ')
except OSError as е:
print(e, file =sys.stderr)

1 77 .4. Преждевременное завершение процесса и возврат кода
выхода
def main():
if len(sys.argv) ! = 4 or '--help' in sys.argv[1 :]:
print('usage: my_program ', file =sys.stderr)
sys.exit(1) # использовать код выхода для сигнала о неудачном завершении программы
process_data()

Глава 1 78. Пакет Chem Py
ChemPy - это пакет, предназначенный в основном для решения задач физической, ана­
литической и неорганической химии. Это бесплатный инструмент с открытым исходным
кодом на языке Python для применения в химии, химической инженерии и материалове­
дении.

1 78.1 . Разбор формул

599

1 78. 1 . Разбор формул
from chempy import Substance
ferricyanide = Substance.from_formula('Fe(CN)6-3')
ferricyanide.composition == {О: -3, 26: 1 , 6: 6, 7: 6}
True
print(ferricyanide.unicode_name)

Fe(CN)/-

print(ferricyanide.latex_name + ", " + ferricyanide.html_name)
Fe(CN)_{6}л {3-}, Fe(CN)63-
print('%.3f' % ferricyanide.mass)
21 1 .955

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

1 78.2. Стехиометрия химических реакций
from chempy import balance_stoichiometry # Основная реакция в ракетах-носителях NASA:
геас, prod = balance_stoichiometry({'N H4CI04', 'A I'}, {'АI2O3', 'HCI', 'Н2O', 'N2'})
from pprint import pprint
pprint(reac)
{'AI ' : 1 О, 'NH4CI04': 6}
pprint(prod)
{'АI2O3': 5, 'Н2O': 9, 'HCI': 6, 'N2': З}
from chempy import mass_fractions
for fractions in map(mass_fractions, [геас, prod]):
... pprint({k: '{0:.3g} wt%'.format(v*1 00) for k, v in fractions.items()})
{'AI': '27.7 wt%', 'NH4CI04': 72.3 wt%'}
{'АI2O3': '52.3 wt%', 'Н2O': '1 6.6 wt%', 'HCI': '22.4 wt%', 'N2': '8.62 wt%'}

1 78.З. Уравнение химической реакции
from chempy import Equil ibrium
from sympy import symbols
К1 , К2, Kw = symbols('K1 К2 Kw')
е1 = Equilibrium({'MnO4-': 1 , 'Н+': 8, 'е-': 5}, {'Мп+2': 1 , 'Н2O': 4}, К1 )
е2 = Equilibrium({'O2': 1 , 'Н2O': 2, 'е-': 4}, {'ОН-': 4}, К2)
coeff = Equilibrium.eliminate([e1 , е2], 'е-')
coeff
[4, - 5]
redox = е1 *coeff[0] + e2*coeff[1 ]
print(redox)
20 ОН- + 32 Н+ + 4 М пO4- = 26 Н2O + 4 М п+2 + 5 02; К1 **4/К2**5
autoprot = Equil ibrium({'H2O': 1 }, {'Н+': 1 , 'ОН-': 1 }, Kw)
п = redox.cancel(autoprot)

п

20
redox2 = redox + n*autoprot
print(redox2)
1 2 Н+ + 4 МпO4- = 4 Мп+2 + 5 02 + 6 Н2O; К1 **4*Kw**20/K2**5

1 78.4. Химическое равновесие
from chempy import Equil ibrium
from chempy.chemistry import Species
water_autop = Equilibrium({'H2O'}, {'Н+', 'ОН-'}, 1 О**-1 4) # принята единица измерения "моль"

600

Глава 1 78. Пакет ChemPy

ammonia_prot = Equil ibrium({'N H4+'}, {'N H3', 'Н+'}, 1 О**-9.24} # здесь также
from chempy.equilibria import EqSystem
substances = map(Species.from_formula, 'Н2O ОН- Н+ NH3 NH4+'.split())
eqsys = EqSystem([water_autop, ammonia_prot], substances)
print('\n'.join(map(str, eqsys.rxns))) # "rxns" здесь сокращение от "реакций"
Н2O = Н+ + ОН-; 1 е-1 4
N H4+ = Н+ + N H3; 5.75е-1 0
from col lections import defau ltdict
init_conc = defaultdict(float, {'Н2O': 1 , 'NH3': 0.1 })
х, sol, sane = eqsys.root(iniLconc)
assert sol ['success'] and sane
print(sorted(sol.keys())) # см. пакет "pyneqsys" для получения дополнительной информации
['fun', 'intermediate_info', 'internal_x__vecs', 'nfev', 'njev', 'success', 'х', 'x_vecs']
print(', '.join('%.2g' % v for v in х))
1 , 0.001 3, 7.бе-1 2, 0.099, 0.001 3

1 78.5. Ионная сила
from chempy.electrolytes import ionic_strength
ionic_strength({'Fe+3': 0.050, 'CI04-': 0.1 50}} == .3
True

1 78.6. Химическая кинетика (система обыкновенных
дифференциальных уравнений)
from chempy import ReactionSystem # Константы скорости ниже являются произвольными
rsys = ReactionSystem.from_string("'"' 2 Fe+2 + Н2O2 -> 2 Fe+3 + 2 ОН-; 42
2 Fe+3 + Н2O2 -> 2 Fe+2 + 02 + 2 Н+; 1 7
Н+ + ОН- -> Н2O; 1 е1 О
Н2O -> Н+ + ОН-; 1 е-4
Fe+3 + 2 Н2O -> FeOOH(s) + 3 Н+; 1
FeOOH(s) + 3 Н+ -> Fe+3 + 2 Н2O; 2.5'""' )
from chempy.kinetics.ode import geLodesys
odesys, extra = geLodesys(rsys)
from col lections import defau ltdict
import numpy as пр
tout = sorted(np.concatenate((np.linspace(0, 23}, np.logspace(-8, 1 }}))
со = defaultdict(float, {'Fe+2': 0.05, 'Н2O2': 0.1 , 'Н2O': 1 .0, 'Н+': 1 е-7, 'ОН-': 1 е-7}}
result = odesys.integrate(tout, со, atol = 1 е-1 2, rtol=1 е-1 4}
import matplotlib.pyplot as plt
_ = plt.subplot(1 , 2, 1 )
_ = result.plot(names=[k for k in rsys.substances i f k ! = 'Н20']}
_ = plt.legend(loc='best', prop={'size': 9}}; _ = plt.xlabel('Time'); _ = plt.ylabel('Concentration')
_ = plt.subplot(1 , 2, 2}
_ = result.plot(names=[k for k in rsys.substances if k != 'Н2O'], xscale='log', yscale='log')
_ = plt.legend(loc='best', prop={'size': 9}}; _ = plt.xlabel('Time'); _ = plt.ylabel('Concentration')
_ = plt.tight_layout()
plt.show()

601

1 79.1 . Модуль mixer в Pygame
0. 1 2

г--т-----;:::===:::;----г-,

0. 1 0

он о,



10 · 2

з +

Fe 2 +

-

-

----

4
10 10 · 6

Н"

н,о,

10 - 8

FeOOH(s)

10 · 1 0

0.08

с 10 - 12
о

с

о

·;:;

·;:;

с О.Об
QJ
u
с

о

0.04

10 • 1

4

с
QJ
u 10 · 1 6

о
1

10 · 1 8
10 - 2 0

1

Fe1 +
Fe 2

10 - 22
0.02

10 • 2

4

-

10 - 26
0.00

0

5

10

Time

15

20

25

+

оно,
Н'

Н2О2

F'eOOH(s)

10 - 28 L.....J.........L......L.......!-J..........L..====c.::::.::J
1O - 8!O · 1O ·61O - 5.Lo ·4.J.o - 3.L o ·2.J.o · 11O ° 1O 1 1O 2

Ti m e

Глава 1 7 9. Б иблиотека Pygame
П ара м етр
count

Подроб ности

Положительное целое число, представляющее собой нечто вроде
количества каналов, которые необходимо зарезервировать.
force
Булево значение (False или True), определяющее, должен ли f1nd_channel()
возвращать канал (неактивный или нет) с True или нет
(в случае если неактивных каналов нет) с False
Pygame - это библиотека для создания мультимедийных приложений, особенно игр, на
языке Python. Официальный сайт: http://www. pygame.org/.

1 79. 1 . Модуль mixer в Pygame
Модуль pygame. mixer позволяет управлять музыкой, используемой в программах pygame.
На данный момент существует 15 различных функций для модуля mixer.
Ишщиализация

Подобно тому, как необходимо инициализировать pygame с помощью pygame.init(), необ­
ходимо инициализировать и pygame.mixer.
Используя первую опцию, мы инициализируем модуль, используя значения по умолча­
нию. Однако эти значения можно переопределить. При использовании второй опции мы
можем инициализировать модуль с помощью значений, которые мы сами вводим вручную.
Стандартные значения:
pygame. mixer.init(frequency=22050, size=-1 6, channels=2, buffer=4096)

Чтобы проверить, инициализировали мы его или нет, можно использовать pygame.mixer.
которая возвращает True, если инициализировали, и False, если не инициализиро-

get_init(),

602

Глава 1 80. Модуль Pyglet

вали. Для выхода/отмены инициализации достаточно использовать команду pygame.mixer.
quit(). Если вы хотите продолжить воспроизведение звуков с помощью модуля, то, возможно,

придется инициализировать модуль заново.

Возможные действия
Во время воспроизведения звука его можно временно приостановить с помощью
pygame.mixer.pause() . Чтобы возобновить воспроизведение звука, достаточно использо­
вать pygame.mixer.unpause(). Также можно задать затухание звука по окончании с помощью
pygame.mixer.fadeout(). Она принимает аргумент, который представляет собой количество
миллисекунд, необходимое для затухания (fading out) музыки.
Каналы
Вы можете воспроизводить столько композиций, сколько необходимо, при условии, что
открытых каналов достаточно для их поддержки. По умолчанию имеется 8 каналов. Чтобы
изменить количество каналов, используйте pygame.mixer.set_num_channels(). Аргументом яв­
ляется целое неотрицательное число. При уменьшении числа каналов все звуки, воспроиз­
водимые на удаленных каналах, немедленно прекращаются.
Чтобы узнать, сколько каналов используется в данный момент, вызовите pygame.mixer.
get_channels(count). В результате будет получено количество каналов, которые в данный мо­
мент не открыты. Также с помощью pygame.mixer.set_reserved(count) можно зарезервировать
каналы для звуков, которые должны быть воспроизведены. Аргумент также является не­
отрицательным целым числом. Все звуки, воспроизводимые на зарезервированных каналах,
не будут остановлены. Выяснить, какой канал не используется, можно с помощью pygame.
mixer.find_channel(force). Его аргументом является булево значение: либо True, либо False. Если
не существует незадействованных каналов, а значение force равно False, то возвращается
None. Если значение force равно True, то будет возвращен канал, который играл дольше всего.

1 79.2. Установка Pygame
При помощи pip:
pip instal l pygame
С использованием conda:
conda instal l -с tlatorre pygame=1 .9.2

Прямая загрузка с сайта: http://www.pygame.org/download.shtml
Вы можете найти подходящие инсталляторы для Windows и других операционных си­
стем. На сайте http://www.pygame.org/ также можно найти примеры проектов.

Глава 1 80. Модуль Pyglet
Pyglet - это модуль Python, используемый для работы с визуальными и звуковыми эффек­
тами. Он не имеет зависимостей от других модулей. Подробную информацию см. на сайте
http://pyglet.org.

1 80 . 1 . Установка Pyglet
Установите Python, войдите в командную строку и введите:

B Python 2:

pip instal l pyglet

B Python З:

рiрЗ install pyglet

1 80.2. "Hello World" в Pyglet

1 80.2. "Hello World" в Pyglet
import pyglet
window = pyglet.window.Window()
label = pyg let.text.Label('Hel lo, world',
font_name = 'Тi mes New Roman',
font_size = Зб,
x = window.width//2, y = window.height//2,
anchor_x = 'center', anchor_y = 'center')
@window.event
def on_draw():
window.clear()
label.draw()
pyglet.app.run()

1 80.З. Воспроизведение звука в Pyglet
sound = pyglet.media.load(sound.wav)
sound.play()

1 80.4. Использование Pyglet для OpenGL
import pyglet
from pyglet.gl import *

win

=

pyglet.window.Window()

@win.event()
def on_draw():
#Испол ьзуйте OpenGL как обычно.
pyglet.app.run()

1 80.5. Рисование точек с помощью Pyglet и OpenGL
import pyglet
from pyglet.gl import *

win = pyglet.window.Window()
glClear(GL_COLOR_BUFFER_BIT)
@win.event
def on_draw():
gl Begin(G LPOI NTS)
gIVertex2f(x, у) # х - желаемое расстояние от левой части окна,
# у - желаемое расстояние от нижней части окна
# создайте столько вершин, сколько хотите
glEnd
Чтобы соединить точки, замените GL_PO I NTS на G LLI N E_LOOP.

Глава 1 8 1 . Работа со звуком
1 81 . 1 . Работа с файлами в формате WAV
winsound


Среда Windows

import winsound
winsound.PlaySound("path_to_wav_f1 le.wav", winsound.SND_FI LENAME)

603

604

Глава 1 81 . Работа со звуком

wave


Поддержка моно/стерео



Не поддерживает компрессию/декомпрессию

import wave
with wave.open("path_to_wav_f1le.wav", "гЬ") as wav_f1le: # Открыть WАV-файл в режиме "только чтение".
# Получение базовой информации.
n_channels = wav_fi le.getnchannels()
# Количество каналов (1 =Мопо, 2=Stereo).
# Ширина выборки в байтах.
sample_width = wav_fi le.getsampwidth()
framerate = wav_fi le.getframerate()
# Ч астота.
# Число фреймов.
n_frames = wav_fi le.getnframes()
comp_type = wav_f1 le.getcomptype()
# Тип сжатия (поддерживается только "NO N E").
comp_name = wav_fi le.getcompname()
# Имя сжатия.
# Считывание аудиоданных.
frames = wav_f1le.readframes(n_frames) # Считывание n_frames новых фреймов.
assert len(frames) == sample_width * n_frames
# Дублирование в новый WАV-файл
with wave.open("path_to_new_wav_fi le.wav", "wb") as wav_fi le: # Открыть WАV-файл в режиме
# "только запись".
# Запись аудиодан ных
params = (n_channels, sample_width, framerate, n_frames, comp_type, comp_name)
wav_f1 le.setparams(params)
wav_f1 le. writeframes(frames)

1 8 1 .2. Преобразование любого звукового файла с помощью
Python и ffmpeg
from subprocess import check_call
ok = check_call(['ffmpeg','-i','input.mpЗ','o utput.wav'])
if ok:
with open('output.wav', 'гЬ') as f:
wav_f1 le = f.read()
Рекомендованные ресурсы:
1. http://superuser.com/questions/507386/why-would-i-choose-libav-over-ffmpeg-or-is-there-even-a-dif­
ference
2. Каковы различия и сходства между ffmpeg, libav и avconv? http://stackoverflow.com/ques­
tions/94771 1 5/what-are-the-differences-and-similarities-between-ffmpeg-libav-and-avconv

1 8 1 .З. Воспроизведение звуковых сигналов Windows
Windows предоставляет интерфейс, через который модуль winsound позволяет воспроизводить необработанные звуковые сигналы с заданной частотой и длительностью.
import winsound
# Установить частоту 2500 Гц
freq = 2500
# Установить длительность в 1 ООО мс == 1 секунда
dur = 1 ООО
winsound. Beep(freq, dur)

1 8 1 .4. Воспроизведение звука с помощью Pyglet
import pyglet
audio = pyglet.media. load("audio.wav")
audio.play()
Дополнительную информацию см. по адресу https://pyglet.readthedocs.io/en/pyglet-1 .2-main­
tenance/programming_guide/media.html

1 82.1 . Режим обратного вызова звукового ввода/вы вода

Глава 1 82. Pyaudio

605

Инструмент PyAudio обеспечивает связь Python с PortAudio, кроссплатформенной
библиотекой ввода-вывода аудиоданных. С помощью PyAudio вы можете леrко использовать
Python для воспроизведения и записи звука на различных платформах. В основе PyAudio
находятся:
1. pyPortAud io/fastaudio: связка Python для API PortAudio v18.
2. tkSnack: кроссплатформенный звуковой инструментарий для Tcl{Гk и Python.

1 82. 1 . Режи м обратного вызова звукового ввода/вы вода
"""Пример PyAudio: Воспроизведение .wаv-файла (режим обратного вызова)"""
import pyaudio
import wave
import time
import sys
if len(sys.argv) < 2:
print("Plays а wave f1 le.\n\nUsage: %s fi lename.wav" % sys.argv[0])
sys.exit(-1 )
wf = wave.open(sys.argv[1 ], 'гЬ')
# инстан цировать PyAudio (1 )
р = pyaudio. PyAudioO
# определить обратный вызов (2)
def callback(in_data, frame_count, time_info, status):
data = wf. readframes(frame_count)
return (data, pyaudio.paContinue)
# открыть поток с помощью обратного вызова (3)
stream = p.open(format = p.get_format_from_width(wf.getsampwidth()),
channels= wf.getnchannels(),
rate = wf.getframerate(),
output = True,
stream_callback = callback)
# запустить поток (4)
stream.start_stream()
# ожида ние завершения потока (5)
while stream.is_active():
time.sleep(0.1 )
# остановить поток (6)
stream.stop_stream()
stream.close()
wf.close()
# закрыть PyAudio (7)
p.terminate()
В режиме обратного вызова PyAudio будет вызывать определенную функцию обратно­
го вызова (2) всякий раз, когда ему потребуются новые аудиоданные (для воспроизведения)
и/или когда будут доступны новые (записанные) аудиоданные. Обратите внимание, что
PyAudio вызывает функцию обратного вызова в отдельном потоке. Функция имеет следую-

606

Глава 1 82. Pyaudio

щую сигнатуру callback(, , , ) и должна возвра­
щать кортеж, содержащий frame_count фреймов аудиоданных и флаг, который обозначает,
есть ли еще фреймы для воспроизведения/записи.
Начните обработку аудиопотока с помощью функции pyaudio.Stream.starLstream() (4), ко­
торая будет вызывать функцию обратного вызова несколько раз, пока эта функция не вер­
нет значение pyaudio.paComplete.
Чтобы поток оставался активным, основной поток не должен завершаться, например из­
за перехода в спящий режим (5).

1 82.2. Режим блокировки звукового ввода/вывода
"" "Пример PyAudio: Воспроизведение .wаv-файла." "''
import pyaudio
import wave
import sys
CHUN K = 1 024
if len(sys.argv) < 2:
print("Plays а wave fi le.\n\nUsage: %s f1lename.wav" % sys.argv[O])
sys.exit(-1 )
wf = wave.open(sys.argv[1 ], 'гЬ')
# инстан цировать PyAudio (1 )
р = pyaudio. PyAudio()
# запустить поток (2)
stream = p.open(format = p.get_format_from_width(wf.getsampwidth()),
channels = wf.getnchannels(),
rate = wf.getframerate(),
output = True)
# считывание дан н ых
data = wf. readframes(CHUN K)
# воспроизвести поток (3)
while len(data) > О:
stream.write( data)
data = wf.readframes(CHU N K)
# остановить поток (4)
stream.stop_stream()
stream.close()
# закрыть PyAudio (5)
p.terminate()

Чтобы использовать PyAudio, для начала инстанцируйте его с помощью pyaudio.PyAu­
который устанавливает систему portaudio.
Для записи или воспроизведения звука откройте на нужном устройстве поток с нуж­
ными аудиопараметрами с помощью pyaudio.PyAudio.open() (2). При этом создается поток
pyaudio.Stream для воспроизведения или записи звука.
Воспроизведение звука осуществляется путем записи аудиоданных в поток с помощью
функции pyaudio.Stream.write() или считывания аудиоданных из потока с помощью функции
dio() (1),

pyaudio.Stream.read(). (3)

Обратите внимание, что в "блокирующем режиме" каждый pyaudio.Stream.write() или
блокируется до тех пор, пока не будут воспроизведены или записаны

pyaudio.Stream.read()

1 83.1 . Использование Shelve

607

все заданные/запрошенные кадры. В качестве альтернативы, чтобы генерировать аудиодан­
ные на выходе или немедленно обрабатывать записанные аудиоданные, используйте "ре­
жим обратного вызова" (см. предыдущий пример).
Для приостановки воспроизведения/записи используйте pyaudio.Stream.stop_stream(),
а для завершения потока - pyaudio.Stream.close(). (4)
Наконец, завершите сеанс portaudio с помощью pyaudio.PyAudio.terminate() (5)

Глава 1 8 3 . Модуль Shelve
Shelve ("полка") - это модуль Python, используемый для хранения объектов в файле. Этот
модуль реализует постоянное хранилище для произвольных объектов Python, которые мож­
но обрабатывать модулем Pickle, используя API, подобный словарю. Shelve может использо­
ваться как простой вариант постоянного хранилища для объектов Python, когда реляцион­
ная база данных является излишеством. Доступ к "полке" осуществляется по ключам, как в
словаре. Значения обрабатываются модулем Pickle и записываются в базу данных, создан­
ную и управляемую модулем anydbm.

1 83. 1 . Использование Shelve

Самый простой способ использования Shelve - через класс DЬfi lenameShelf. Он использует
модуль anydbm для хранения данных. Вы можете использовать класс напрямую или просто
вызвать shelve.open():
import shelve
s = shelve.open('tesLshelf.db')
try:
s['key1'] = { 'int': 1О, 'float':9.5, 'string':'Sample data' }
finally:
s.close()

Чтобы снова получить доступ к данным, откройте Shelve и используйте ее как словарь:
import shelve
s = shelve.open('test_shelf.db')
try:
existing = s['key1']
f1nally:
s.close()
print existing

Если запустить оба примера скриптов, то можно увидеть:
$ python shelve_create.py

$ python shelve_existing.py
{'int': 1О, 'float': 9.5, 'string': 'Sample data'}

Модуль dbm не поддерживает одновременную запись нескольких приложений в одну и
ту же базу данных. Если вы знаете, что ваш клиент не будет модифицировать shelve, вы мо­
жете указать, что следует открыть базу данных только для чтения.
import shelve
s = shelve.open('tesLshelf.db', flag ='r')
try:

608

Глава 1 83. Модуль Shelve

existing = s['key1 ']
final ly:
s.close()
print existing
Если ваша программа пытается модифицировать базу данных, когда она открыта только
для чтения, то генерируется исключение об ошибке доступа. Тип исключения зависит от мо­
дуля базы данных, выбранного модулем anydbm при создании базы.

1 83.2. Пример кода для Shelve
Чтобы поместить объект на Shelve, сначала импортируйте модуль, а затем присвойте значение объекта следующим образом:
import shelve
database = shelve.open(f1 lename.suff1x)
object = Object()
database['key'] = object

1 83.3. Обобщение информации о интерфейсе
Ключ - это строка, данные - произвольный объект:
import shelve
d = shelve.open(fi lename) # открыть -- файл может получить суффикс, добавлен н ы й
# низкоуровневой библиотекой
d[key] = data
data = d[key]
del d[key]
flag = key in d
klist = list(d.keys())

# хранить дан н ые по ключу (перезаписывает старые данн ые, если
# используется существующий ключ)
# получить копию данн ых, хранящихся по ключу (ошибка КеуЕггог
# если такого ключа нет)
# удалить данн ые, хранящиеся по ключу (вызы вает ошибку КеуЕггог
# если такого ключа нет)
# true если ключ существует
# список всех существующих ключей (работает медленно!)

# поскольку d был открыт БЕЗ writeback=True, остерегайтесь:
# это работает, как и ожидалось, но ...
d['xx'] = [О, 1 , 2]
# это нет -- d ['xx'] все еще [О, 1 , 2] !
d['xx'].append(З)
# откры в d без writeback=True, необходимо внимательно отнестись к коду:
temp = d ['xx']
# извлекает копию
# изменяет копию
temp.append(5)
# сохраняет копию обратно, чтобы сохранить ее
d['xx'] = temp
# или, d=shelve.open(f1 lename,writeback=True) позволит вам просто закодировать
# d['xx'].append(5) и заставить его работать как ожидалось, но это также
# будет занимать больше памяти и сделает операцию d.close() более медленной.
d.close()

# закрыть

1 83.4. Обратная запись (writeback)
По умолчанию Shelve не отслеживает изменения непостоянных объектов. Это означает,
что при изменении содержимого элемента, хранящегося в Shelve, необходимо явно обновить
Shelve, заново сохранив этот элемент.

1 83.4. Обратная запись (writeback)

609

import shelve
s = shelve.open('tesLshelf.db')
try:
print s['key1 ']
s['key1 ']['new_value'] = 'раньше этого здесь не было'
f1nally:
s.close()
s = shelve.open('tesLshelf.db', writeback=True)
try:
print s['key1 ']
f1nally:
s.close()
В данном примере словарь по адресу 'key1 ' не сохраняется, поэтому при повторном открытии "полки" изменения не сохранятся.
$ python shelve_create.py
$ python shelve_withoutwriteback. py
{'int': 1 О, 'float': 9.5, 'string': 'Sample data'}
{'int': 1 О, 'float': 9.5, 'string': 'Sample data'}
Для автоматического отслеживания состоянiя изменчивых объектов, хранящихся на
полке, откройте "полку'' с включенной функцией writeback. При включении функции write­
back Shelve запоминает все объекты, извлеченные из базы данных, с помощью кэша в памя­
ти. При закрытии каждый объект кэша также записывается обратно в базу данных.
import shelve
s = shelve.open('tesLshelf.db', writeback=True)
try:
print s['key1 ']
s['key1 'l['new_value'] = 'раньше этого здесь не было'
print s['key1 ']
f1nally:
s.close()
s = shelve.open('tesLshelf.db', writeback=True)
try:
print s['key1 ']
f1nally:
s.close()

Хотя это снижает вероятность ошибки программиста и делает сохранение объектов бо­
лее прозрачным, использование такого режима может быть нежелательным в любой си­
туации. Кэш потребляет дополнительную память, пока Shelve открыта, а пауза для записи
каждого кэшированного объекта обратно в базу данных при ее закрытии может занять до­
полнительное время. Поскольку нет возможности определить, были ли модифицированы
кэшированные объекты, все они записываются обратно. Если ваше приложение считывает
данные чаще, чем записывает, то обратная запись будет занимать больше времени, чем хо­
телось бы.
$ python shelve_create.py
$ python shelve_writeback. py
{'int': 1 О, 'float': 9.5, 'string': 'Образец данных'}
{'int': 1 О, 'new_value': 'раньше этого здесь не было', 'float': 9.5, 'string': 'Образец данных'}
{'int': 1 О, 'new_value': 'раньше этого здесь не было', 'float': 9.5, 'string': 'Образец данных'}

61 0

Глава 1 84. Про грамми ро вание "интернета вещей" (loT) с помощью Python и Raspberry Pi

Глава 1 84. Програм мирование
"интернета вещей" {loT) с помощью
Python и Raspberry Pi
1 84. 1 . Пример - датчик температуры

Рассмотрим подключение датчика DS18B20 к мини-компьютеру Raspberry Pi
DS1 8 B20
1

2

З

BOПOM VIEW

G N D DATA Vcc

Вы можете видеть, что у датчика есть три клеммы:
1. Vcc
2. Gnd
3. Для передачи данных (One wire, однопроводной протокол)

'Rl
J. • • •


.. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..
fritz.1119

Rl - сопротивление 4, 7 кОм для повышения уровня напряжения
1. Vcc должен быть подключен к любому из выводов 5 В или 3,3 В Raspberry Pi (PIN : 01,
02, 04, 17).
2. Gnd должен быть подключен к любому из выводов Gnd на Raspberry Pi (PIN : 06, 09, 14,
20, 25).
3. DATA должен быть подключен к (PIN : 07)
Вюnочение однопроводного интерфейса со стороны RPi
4. Войдите в систему Raspberry pi, используя putty или любой друrой терминал linux/unix.
5. После входа в систему откройте файл /Ьoot/config.txt file в любимом браузере.
папа /boot/conf1g.txt
6. Добавьте

строку dtoverlay=w1 -gpio в конец файла.
7. Теперь перезагрузите Raspberry pi: sudo reboot.

1 84.1 . Пример - датчик температуры

61 1

8. Войдите в систему Raspberry pi и выполните команду sudo modprobe g1 -gpio.
9. Затем выполните команду sudo modprobe w1 -therm.
10. Теперь перейдите в каталог /sys/bus/w1 /devices cd /sys/bus/w1 /devices.
11. Теперь вы обнаружите, что для вашего датчика температуры создан виртуальный ка­
талог, начинающийся с адреса 28-**-k-k>l-*-k-k.
12. Перейдите в этот каталог cd 28-**-k-k>l-*-k-k.
13. Теперь найдите там файл с именем w1 -slave, он содержит температуру и друrую ин­
формацию, например CRC: cat w1 -slave.
Теперь наШiшем модуль на языке Python для чremm температуры

import glob
import time

RATE = 30
sensor_dirs = glob.glob("/sys/bus/w1 /devices/28*")
if len(sensor_dirs) != О:
while True:
time.sleep(RAТЕ)
for d irectories in sensor_dirs:
temperature_f1 le = open(directories + "/w1_slave")
# Чтение файлов
text = temperature_fi le.read()
temperature_f1 le.close()
# Разделите текст на новые строки (\n) и выделите вторую строку.
second_line = text.split("\n")[1 ]
# Разделить строку на слова и выделить 1 0-е слово
temperature_data = second_l ine.spl it(" '')[9]
# будем считывать после игнорирования первых двух сим волов
temperature = float(temperature_data[2:])
# Теперь нормализуем температуру путем деления на 1 ООО.
temperature = temperature / 1 ООО
print 'Address : '+str(directories.split('/') [-1])+', Temperature : '+str(temperature)

Приведенный выше Руthоn-модуль выводит зависимость температуры от значения
течение бесконечного времени. Параметр RATE предназначен для изменения или
настройки частоты запросов температуры от датчика.
Схема входов-выходов GPIO: https://www.element1 4.com/community/servlet/JiveServlet/pre­

Address в

viewBody/73950-102-11-339300/piЗ_gpio.png

Глава 1 85. Kivy - кроссплатформенный
Руthоn-фрейм ворк для разработки
естественных пользовательских
интерфейсов (NUI)
NUI, Естественный пользовательский интерфейс (Natural User lnterface) - это система взаи­
модействия человека и компьютера, которой пользователь управляет с помощью интуитив­
ных действий, связанных с естественным, повседневным поведением человека.
Кivy - это библиотека на языке Python для разработки мультисенсорных приложений, ко­
торые моrут быть установлены на различных устройствах. Мультитач - это способность сен­
сорной поверхности (обычно это сенсорный экран или трекпад) обнаруживать или воспри­
нимать ввод от двух или более точек контакта одновременно.

61 2

Глава 1 85. Кivy - кроссплатформенный Руthоп-фреймворк для разработки естественных

1 85.1 . Первое приложение

Для создания приложения Кivy:
1. Подклассифицируйте класс арр.
2. Реализуйте метод build, который будет возвращать виджет.
3. Инстанцируйте класс и вызовите команду run.

from kivy.app import Арр
from kivy.uix.label import Label
class Test(App):
def build(self):
return Label(text='Hello world')
if _name_ == '_main_':
Test().run()

ПояснеIШе

from kivy.app import Арр

Приведенное выше утверждение импортирует родительский класс арр. Он будет присут­
ствовать в каталоге установки your_installation_directory/kivy/app.py.
from kivy.uix.label import Label

Приведенное выше утверждение импортирует их-элемент Label. Все их-элементы при­
сутствуют в каталоге установки your_installation_directory/kivy/uix/.
class Test(App):

Приведенное выше утверждение служит для создания приложения, а имя класса будет
именем вашего приложения. Этот класс наследуется от родительского класса приложения.
def build(self):

Приведенное выше утверждение переопределяет метод build класса арр. В результате бу­
дет возвращен виджет, который должен быть показан при запуске приложения.
return Label(text='Hello world')

Приведенное выше утверждение является телом метода build. Он возвращает Label с тек­
стом "Hello world".
if _name_ == '_main_':

Приведенное выше утверждение является точкой входа, с которой интерпретатор Python
начинает выполнение вашего приложения.
Test().run()

Приведенный выше оператор инициализирует класс Test, создавая его экземпляр, и вы­
зывает функцию класса приложения run(). Ваше приложение будет представлять собой
окно с выведенным сообщением "Hello world".

61 3

1 86.1 . Простое преобразование

Глава 1 86. Использование Pandas
transform: предварительное выполнение
операции с группами и конкатенация
результатов
1 86. 1 . Простое преобразование

Сначала создадим фиктивный датафрейм.
Предположим, что клиент может иметь п заказов, заказ может содержать m товаров, а товары могут быть заказаны несколько раз:
orders_df = pd.DataFrame()
orders_df['id_клиeнтa'] = [1 , 1 , 1 , 1 , 1 ,2,2,3,3,3,3,3]
orders_df['id_зaкaзa'] = [1 , 1 , 1 ,2,2,3,3,4,5,6,6,6]
orders_df['item'] = ['яблоки', 'шоколад', 'шоколад', 'кофе', 'кофе', 'яблоки',
'бананы', 'кофе', 'молочный коктейль', 'шоколад', 'клубника', 'клубника"]
# Вот как выглядит датафрейм:
print(orders_df}
#
id_клиента
id_заказа

1
1
#1
1
1
#2
1
1
#3
1
2
#4
1
2
#5
2
3
#6
2
3
4
#7
3
#8
3
5
6
#9
3
6
#10
3
#11
6
3

item
яблоки
шоколад
шоколад
кофе
кофе
яблоки
бананы
кофе
молочный коктейль
шоколад
клубника
клубника

Теперь мы воспользуемся функцией pandas transform для подсчета количества заказов на
одного клиента:
# Сначала оп ределим функцию, которая будет п рименяться для каждого id_клиента
count_number_of_orders = lambda х: len(x.unique())
# И теперь мы можем п реобразовать каждую группу, используя логику, определенную выше.
orders_df['чиcлo заказов на клиента'] = (
# Поместить результаты в новый столбец
'число заказов на клиента'
orders_df
# Взять исходный датафрейм
.gгоuрЬу(['id_клиента'])['id_заказа'] # Создаем отдельную груп пу для каждого
id_клиента и выбираем id_заказа
.transform(count_number_of_orders)) # Применить функцию к каждой груп пе отдельно
# П роверка результатов...
print(orders_df}
id_заказа
# id_клиента

1
1
#1
1
1
#2
1
1
2
#3
1
#4
1
2

item
яблоки
шоколад
шоколад
кофе
кофе

число заказов на клиента
2
2
2
2
2

61 4
#5
#6
#7
#8
#9
# 10
#11

Глава 1 86. Использование Pandas transform: предварительное вы полнение операций
2
2
3
3
3
3
3

3
3
4
5
6
6
6

яблоки
бананы
кофе
молочный коктейль
шоколад
клубника
клубника

1
3
3
3
3
3

1 86.2. Несколько результатов для одной группы

Используем функцm1 transfonn, возвращающие подвы'Шслеmm для каждой группы
В предыдущем примере мы имели один результат для каждого клиента. Однако можно
применять и функции, возвращающие разные значения для группы.
# Создадим фиктивный датафрейм
orders_df = pd.DataFrame()
orders_df['id_клиeнтa'] = [1 , 1 , 1 , 1 , 1 ,2,2,3,3,3,3,3]
orders_df['id_зaкaзa'] = [1 , 1 , 1 ,2,2,3,3,4,5,6,6,б]
orders_df['item'] = ['яблоки', 'шоколад', 'шоколад', 'кофе', 'кофе', 'яблоки', 'бананы', 'кофе',
'молочный коктейль', 'шоколад', 'клубника', 'клубника']
# Попробуем проверить, были ли товары заказаны более одного раза в каждом заказе
# Сначала определим функцию, которая будет применяться к каждой группе
def multiple_items_per_order(_items):
# Примените функцию .duplicated, которая будет возвращать True, если элемент встречается
#более одного раза
multiple_item_bool = _items.duplicated(keep=False)
return(multiple_item_bool)
# Затем преобразуем каждую группу в соответствии с заданной функцией
огdегs_df['дублирующийся товар в заказе'] = (
# Поместить резул ьтаты в новый столбец
# Возьмем датафрейм по заказам
orders_df
.groupby(['id_зaкaзa'])['item'] # Создаем отдельную группу для каждого id_заказа и
выбираем товар (item)
.transform(multiple_items_per_order)) # П рименить определенную функцию
к каждому элементу группы отдельно
# проверка результатов ...
print(orders_df)
# id_клиента
дублирующийся товар взаказе
id_клиента
item
1
1
False

яблоки
#1
True
шоколад
1
1
#2
True
шоколад
1
1
кофе
True
#3
1
2
#4
1
2
кофе
True
яблоки
2
False
#5
3
2
бананы
False
#6
3
False
кофе
#7
3
4
молочный коктейль
#8
False
3
5
False
б
шоколад
#9
3
б
# 10
клубника
True
3
#11
клубника
True
3
б

1 87.1 . 'in' со списками

61 5

Глава 1 87. Сходство в синтаксисе,
различия в значении: Python и JavaScript

Иногда случается, что два языка вкладывают в одно и то же или схожее по синтаксису
выражение разные смыслы. Когда оба языка представляют интерес для программиста, про­
яснение этих точек бифуркации помогает лучше понять оба языка в их основах и тонкостях.

1 87.1 . 'iп' со списками
2 in [2, З]

В Python это значение равно True, а в JavaScript - False. Это связано с тем, что в Python in
проверяет, содержится ли значение в списке, поэтому 2 находится в [2, З] как его первый эле­
мент. В JavaScript in используется с объектами и проверяет, содержит ли объект свойство с
именем, выраженным значением. Таким образом, JavaScript рассматривает [2, З] как объект
или карту "ключ-значение", наподобие:
{'О': 2, '1 ': З}

и проверяет, есть ли в нем свойство или ключ '2'. Целое число 2 преобразуется в строку '2'.

Глава 1 88. Вызов Python из С#
В документации приведен пример реализации межпроцессного взаимодействия между
сценариями на языках программирования С# и Python.

1 88. 1 . Руthоп-скрипт, который вызывается С#-приложением
import sys
import json
# загрузить входные аргументы из текстового файла
f1 lename = sys.argv[ 1 ]
with open( f1 lename ) as data_f1 le:
input_args = json. loads( data_f1 le.read() )
# приведение строк к типу float
х, у = [ float(input_args.get( key )) for key in [ 'х', 'у' ] ]
print json.dumps( { 'sum' : х + у , 'suЬtract' : х - у } )

1 88.2. Код на С#, вызывающий Руthоп-скрипт
using MongoDB . Bson;
using System;
using System.Diagnostics;
using System.l O;
namespace python_csharp
{
class Program
{

61 6



Глава 1 88. Вызов Python из С#

static void Main(string args)
{
// полны й путь к файлу .ру
string pyScriptPath = "...../sum.py";
// преобразование входных аргументов в строку JSON
BsonDocument argsBson = BsonDocument.Parse("{ 'х' : '1 ', 'у' : '2' }");
bool savelnputFile = false;
string argsFile = string.Format("{0}\\{1 }.txt", Path.GetDirectoryName(pyScriptPath), Guid.New­
Guid());
string outputString = null;
// создание нового процесса start info
ProcessStartlnfo prcStartlnfo = new ProcessStartlnfo
{
// полны й путь к интерпретатору Python 'python.exe'
FileName = "python.exe", // string.Format(@"'"'{0} "'"', "python.exe"),
UseShell Execute = false,
RedirectStandardOutput = true,
CreateNoWindow = false
};
tгу
{
// запись входных аргументов в файл .txt
using (StreamWriter sw = new StreamWriter(argsFile))
{
sw.Writeli пе( args Bson);
prcStartlnfo.Arguments = string.Format("{0} {1 }", string.Format(@"""{0}""", pyScriptPath),
string.Format(@"""{0}""", argsFile));
}
// запуск процесса
using (Process process = Process.Start(prcStartlnfo))
{
// чтение стандартной выходной JSОN-строки
using (StreamReader myStreamReader = process.StandardOutput)
{
outputString = myStreamReader.Read line();
process.WaitForExit();
}
finally
{
// удаление/сохранение временного файла .txt
if (!savelnputFile)
{
File.Delete(argsFile);
}
Console.Writeline(outputString);

1 89.1 . Массивы ctypes

61 7

Глава 1 89. Б иблиотека ctypes
ctypes - это встроенная библиотека Python, которая вызывает экспортируемые функции
из собственных скомпилированных библиотек.
ПримечаIШе: поскольку эта библиотека работает с компилированным кодом, она отно­
сительно зависима от ОС.

1 89. 1 . Массивы ctypes

Как известно любому хорошему программисту на языке С - то, что действительно помо­
жет продвинуться, так это массивы!
>» c_int * 1 б


Это не настоящий массив, но очень близко к нему! Мы создали класс, обозначающий массив из 16 целых чисел. Теперь осталось только инициализировать его:
»> агг = (c_int * 1 б)(*гапgе(1 б))
>>> агг


Теперь агг - это настоящий массив, содержащий числа от О до 1 5. Доступ к нему можно
получить так же, как и к любому списку:
»> агг[5]
5

»> агг[5] = 20
»> агг[5]
20

Как и любой другой объект ctypes, он также имеет размер и местоположение:
»> sizeof(arr)
64 # размер(с_iпt) * 1 б
»> hex(addressof(arr))
'0xc000IOff'

1 89.2. Обертывающие функции для ctypes

В некоторых случаях функция языка С принимает указатель функции. Как заядлые поль­
зователи ctypes, мы хотели бы использовать эти функции и даже передавать в качестве ар­
гументов функции Python.
Определим функцию:
»> def max(x, у):
return х if х >= у else у

Теперь эта функция принимает два аргумента и возвращает результат того же типа. Для
примера предположим, что этот тип - int.
Как и в примере с массивом, мы можем определить объект, обозначающий этот прототип:
»> CFUNCТYPE(c_int, c_int, c_int)


Этот прототип обозначает функцию, которая возвращает c_int (первый аргумент) и при­
нимает два аргумента c_int (остальные аргументы).
Теперь обернем функцию:
»> CFUNCTYPE(c_int, c_int, c_int)(max)


Прототипы функций имеют еще одно применение: Они могут обернуть функцию ctypes
(например, l ibc.ntohl) и проверить, что при вызове функции используются правильные аргу­
менты.

61 8

Глава 1 89. Библиотека ctypes

>» l ibc.ntohl()
»> CFUNCTYPE(c_int, c_int)(libc.ntohl)O
Traceback (most recent call last):
File "", l ine 1 , in
ТуреЕггог: this function takes at least 1 argument (О given) (эта функция принимает как минимум
1 аргумент (задано О))

1 89.3. Базовое использование
Допустим, мы хотим использовать функцию ntoh l в libc. Сначала мы должны загрузить
libc.so:
»> from ctypes import *
>» l ibc = cdll. Loadlibrary('libc.so.6')
»> l ibc

Затем мы получаем объект функции:
»> ntohl = libc.ntohl
>» ntohl

И теперь мы можем просто вызвать функцию:
>» ntohl(0xбC)
1 81 1 939328
»> hex(_)
'ОхбсОООООО'
и получить именно то, чего мы ожидали.

1 89.4. Основные "подводные камни"
Невозможность загрузки файла

Первая возможная ошибка - это невозможность загрузить библиотеку. В этом случае
обычно выдается ошибка OSError. Это происходит либо потому, что библиотека не существу­
ет (либо не может быть найдена ОС):
»> cdll. Loadlibrary("foobar.so")
Traceback (most recent call last):
File "", l ine 1 , in
File "/usr/liЬ/python3.5/ctypes/_init_.py", line 425, in Loadlibrary
return self._d l ltype(name)
File "/usr/liЬ/python3.5/ctypes/_init_.py", line 347, in _iniL
self._hand le = _d lopen(self._name, mode)
OSError: foobar.so: cannot open shared object f1 le: No such f1 le ог directory
Как видите, ошибка явная и довольно показательная.
Вторая причина заключается в том, что файл найден, но имеет неправильный формат.
»> cdll. Loadlibrary("libc.so")
Traceback (most recent call last):
File "", l ine 1 , in
File "/usr/liЬ/python3.5/ctypes/_init_.py", line 425, in Loadlibrary
return self._d l ltype(name)
File "/usr/liЬ/python3.5/ctypes/_init_.py", line 347, in _iniL
self._hand le = _d lopen(self._name, mode)
OSError: /usr/liЬ/i386-linux-gnu/libc.so: invalid ELF header
В этом случае файл является скриптовым файлом, а не .sо-файлом. Подобное может про­
изойти и при попытке открыть .dll-файл на Linuх-машине или 64-разрядный файл на 32-раз­
рядном интерпретаторе Python. Как видите, в этом случае ошибка более расплывчата и тре­
бует некоторого поиска.

1 89.5. Базовый объект ctypes

61 9

Невозможность доступа к функции

Если мы успешно загрузили файл .so, то нам нужно обратиться к нашей функции, как это
бьmо сделано в первом примере. При использовании несуществующей функции возникает
ошибка AttributeError:
»> libc.foo
Traceback (most recent call last):
File "", line 1, in
File "/usr/liЬ/python3.5/ctypes/_iniL.py", line 360, in _getattr_
func = self._getitem_(name)
File "/usr/liЬ/python3.5/ctypes/_iniL.py", line 365, in _getitem_
func = self._FuncPtr((name_or_ordinal, self))
AttributeError: /liЬ/i386-linux-gnu/libc.so.6: undefined symbol: foo

1 89.5. Базовый объект ctypes

Самым простым объектом является целое число:

»> obj = ctypes.c_int(1 2)
»> obj
c_long(1 2)

Теперь obj ссылается на участок памяти, содержащий значение 1 2. К этому значению
можно обращаться напрямую и даже модифицировать его:
»> obj.value
12
»> obj.value = 1 3
»> obj
c_long(1 3)

Поскольку obj ссьmается на участок памяти, мы также можем определить его размер и
местоположение:
»> sizeof(obj)

4

»> hex(addressof(obj))
'0xdeadbeef'

1 89.6. Комплексное использование

Объединим все приведенные примеры в один комплексный сценарий: использова­
ние функции libc lf1nd. Более подробно об этой функции можно прочитать на странице:
https://linux.die.net/man/3/lfind.

Для начала определим соответствующие прототипы:

»> compar_proto = CFUNCТYPE(c_int, POI NTER(c_int), POI NTER(c_int))
»> lfind_proto = CFUNCТYPE(c_void_p, c_void_p, c_void_p, POI NTER(c_uint), c_uint, compar_proto)

Затем создадим переменные:
»> key = c_int(1 2)
»> агг = (c_int * 1 6)(*range(1 6))
»> nmemb = c_uint(1 6)

А теперь определим функцию сравнения:
»> def compar(x, у):
return x.contents.value - y.contents.value

Обратите внимание, что х и у являются POI NTER(c_int), поэтому для реального сравнения со
значением, хранящимся в памяти, нам необходимо "разыменовать" их и взять их значения.
Теперь мы можем объединить все вместе:
»> lfind = lf1nd_proto(libc.lf1nd)
»> ptr = lf1nd(byref(key), byref(arr), byref(nmemb), sizeof(c_int), compar_proto(compar))

620

Глава 1 90. Написание расширений

ptr - возвращаемый указатель void. Если бы ключ не был найден в агг, то значение было
бы None, но в данном случае мы получили корректное значение.
Теперь мы можем преобразовать его и получить доступ к значению:
»> cast(ptr, POI NTER(c_int)).contents
c_long(1 2}

Также мы видим, что ptr указывает на правильное значение внутри агг:
»> addressof(arr) + 1 2 * sizeof(c_int) == ptr
True

Глава 1 90. Написание расширений
1 90.1 . "Hello World" расширение на С

Следующий файл на языке С (который мы для демонстрации будем называть hello.c) соз­
дает модуль расширения hel lo, содержащий единственную функцию greet() :
#include < Python.h>
#include
#if РУ_MAJOR_VERSION >= 3
#def1ne IS_PVЗ K
#endif
static PyObject *hello_greet(PyObject *self, PyObject *args)
{
const char *input;
if (! PyArg_ParseTuple(args, "s", &input)) {
return NU LL;
}
printf("%s", input);
Py_RETURN_NONE;



static PyMethodDef HelloMethods = {
{ "greet", hello_greet, M ETH_VARARGS, "Приветствовать пользователя" },
{ NULL, NULL, О, NULL }
};
#ifdef IS_PVЗK
static struct PyModuleDef hellomodule = {
PyModuleDef_HEAD_I N IТ, "hello", NULL, -1 , HelloMethods
};
PyMODI N IT_FUNC Pylnit_hello(void}
{
return PyModule_Create(&hellomodule};
}
#else
PyMODI N IT_FUNC inithello(void}
{
(void} Py_lnitModule("hello", HelloMethods};
}
#endif

1 90.2. Расширение на языке С с помощью С++ и библиотеки Boost

621

Чтобы скомпилировать файл с помощью компилятора gcc, выполните в своем любимом
терминале следующую команду:
gcc /path/to/your/fi le/hel lo.c -о /path/to/your/fi le/hel lo

Для выполнения функции greetO, которую мы написали ранее, создайте в том же катало­
ге файл и назовите его hel lo.py.
import hel lo # импортирует скомпилированную библиотеку
hel lo.greet("Hel lo!") # запуск функции greet() с аргументом "Hel lo!"

1 90.2. Расширение на языке С с помощью С++ и библиотеки Boost
Это базовый пример С-расширения на языке С++ с использованием библиотеки Boost.
Код на С++

Код на языке С++ помещен в файл hel lo.cpp:
#include
#include
#include
#include
// Возвращаем строку hel lo world.
std::string get_hel lo_function()
{
return "Hel lo world!";
}
// класс hel lo, который может возвращать список строк count hel lo world
class hello_class
{
puЬlic:
// Получение приветствия в конструкторе.
hel lo_class(std::string message) : _message(message) {}
// Возвращает счетчик количества сообщений в списке python.
boost::python::list as_l ist(int count)
{
boost::python::l ist res;
for (int i = О; i < count; ++i) {
res.append(_message);
}
return res;
}
private:
std::string _message;
};
// Определение руthоп-модуля с присвоением ему имени "hel lo".
BOOST_PYTHON_MODULE(hel lo)
{
// Здесь объя вляется, какие функции и классы должны быть открыты в модуле.
// Функция get_hel lo_function раскры вается в python как функция.
boost::python::def("get_hel lo", get_hel lo_function);

622

Глава 1 91 . Python Lex-Yacc

// Класс hello_class раскры вается в python как класс.
boost::python::class_("Hello", boost::python::init())
.def("as_list", &hel lo_class::as_list)
Для компиляции этого модуля в модуль Python вам понадобятся заголовки Python и би­
блиотека Boost. Данный пример был создан на UЬuntu 12.04 с использованием Python 3.4 и
gcc. Boost поддерживается на многих платформах. В случае Ubuntu необходимые пакеты
были установлены с помощью:
sudo apt-get instal l gcc libboost-dev libpythonЗ.4-dev
Компиляция исходного текста в .sо-файл, который впоследствии может быть импортиро­
ван как модуль, если он находится на пути к Python:
gcc -shared -о hel lo.so -fPIC -I/usr/include/pythonЗ.4 hel lo.cpp -lboost_python-py34 -lboost_system
-l:libpython3.4m.so
Код на языке Python в файле example.py:
import hello
print(hel lo.get_hel lo())
h = hello.Hel lo("World hel lo!")
print(h.as_list(З))
Исполнение pythonЗ example.py выдаст следующий результат:
Hello world!
['World hel lo!', 'World hel lo!', 'World hello!']

1 90.3. Передача открытого файла в С-расширения
Рассмотрим передачу открытого объекта файла из языка Python в код расширения на
языке С.
Преобразовать файл в целочисленный дескриптор файла можно с помощью функции
PyObject_дsFileDescriptor:
PyObject *fobj;
int fd = PyObject_дsFileDescriptor(fobj);
if (fd < О){
return NU LL;
Для преобразования целочисленного дескриптора файла обратно в Руthоn-объект ис­
пользуйте PyFile_FromFd.
int fd; /* Existing file descriptor */
PyObject *fobj = PyFile_FromFd(fd, "fi lename";'г",- 1 , N U LL,NULL,NU LL, 1 );

Глава 1 91 . Python Lex-Yacc
Python Lex-Yacc (PLY) - это Руthоn-реализация популярных утилит Lex и Уасс.

1 91 . 1 . Начало работы с Python Lex-Yacc
Чтобы установить P LY для Python2/3, выполните следующие действия:
1. Скачать исходный код можно отсюда: http://www.dabeaz.com/ply/ply-3.1 0.tar.gz.
2. Разархивируйте загруженный файл архива.
3. Перейдите в распакованную папку ply-3 .1 0.
4. Выполните в терминале следующую команду: python setup.py install.

1 91 .2. "Hello, World!" от PLY - простой калькулятор

623

Если вы выполнили все вышеперечисленные действия, то теперь вы можете исполь­
зовать модуль PLY. Вы можете протестировать его, открыв интерпретатор Python и набрав
import ply.lex.

Примечание: не используйте pip для установки PLY, это приведет к установке нерабоче­

го дистрибутива.

1 91 .2. "Hello, World!" от PLY - простой калькулятор

Продемонстрируем возможности PLY на простом примере: эта программа принимает на
вход строку с арифметическим выражением и пытается его решить.
Откройте свой любимый редактор и скопируйте следующий код:
from ply import lex
import ply.yacc as уасс
tokens = (
'PLUS',
'M I N US',
'ТI M ES',
'DIV',
'LPAREN',
'RPAREN',
'NUMBER',
t_ignore = ' \t'
t_PLUS = r'\+'
t_M INUS = г'-'
t_ТIMES = r'\*'
t_D IV = r'/'
t_LPAREN = г'\('
t_RPAREN = r'\)'
def t_N U M BER( t ) :
r'[0-9]+'
t.value = int( t.value )
return t
def t_newl ine( t ):
г'\п+'
t.lexer. lineno += len( t.value )
def t_error( t ):
print("lnvalid Token:",t.value[0])
t.lexer.skip( 1 )
lexer = lex. lexO
precedence = (
( 'left', 'PLUS', ' M I N U S' ),
( 'left', 'ТI M ES', 'DIV' ),
( 'nonassoc', 'UM I N US' )
def p_add( р ) :
'expr : expr PLUS ехрг'
р[О] = р[1 ] + р[З]

624

Глава 1 91 . Python Lex-Yacc

def p_sub{ р ) :
'ехрг : ехрг M I NUS ехрг'
р[О] = р[1 ] - р[З]
def p_expr2uminus( р ) :
'expr : M I N US expr %prec U M I N US'
р[О] = - р[2]
def p_mult_div( р ) :
"'ехрг : ехрг TIMES ехрг
1 ехрг DIV ехрг"'
if р[2] == '*' :
р[О] = р[1 ] * р[З]
else :
if р[З] == О :
ргiпt{"Невозможно разделить на О")
raise ZeroDivision Error('цeлoчиcлeннoe деление на О')
р[О] = р[1 ] / р[З]
def p_expr2NUM{ р ) :
'expr : NUMBER'
р[О] = р[1 ]
def p_parens( р ) :
'ехрг : LPAREN ехрг RPAREN'
р[О] = р[2]
def p_error( р ):
ргint{"Синтаксическая ошибка во входных данных! !")
parser = уасс.уасс()
res = parser.parse("-4*-{3-5)") # входные данные
print(res)

Сохраните этот файл под именем calc.py и запустите его. Получим:
-8

Что является правильным ответом для выражения -4 * - {З - 5).

1 91 .З. Часть 1 . Токенизация входных данных с помощью Lex

Код из первого примера выполнил два этапа: первый - это токенизация входных данных,
то есть поиск символов, составляющих арифметическое выражение, и второй - синтакси­
ческий разбор (парсинг), который заключается в анализе извлеченных лексем и оценке ре­
зультата.
В этом разделе приводится простой пример токенизации пользовательского ввода, а затем он разбирается построчно.
import ply.lex as lex
# Список имен токенов. Это всегда обязательно
tokens = [
'NUMBER',
'PLUS',
'M I N US',
TIM ES',
'DIVI D E',
'LPAREN',
'RPAREN',

1

1 91 .3. Часть 1 . Токенизация входных данных с помощью Lex

625

# Правила регулярных выражений для п ростых лексем
t_PLUS = г'\+'
t_M I N US = г'-'
t_TIMES = г'\*'
t_DIVI D E = г'/'
t_LPAREN = г'\('
t_RPAREN = г'\)'
# Правило регулярного выражения с некоторым кодом действия
def t_N U M BER(t):
г'\d+'
t.value = int(t.value)
return t
# Определите правило, чтобы мы могли отслеживать номера строк
def t_newline(t):
г'\п+'
t.lexer. lineno += len(t.value)
# Строка, содержащая игнорируемые символы (п робелы и табуляции)
t_ignore = ' \t'
# Правило обработки ошибок
def t_error(t):
print(" ll legal character '%s"' % t.value[O])
t.lexer.skip(1 )
# Построим лексер
lexer = lex. lex()
# Дадим лексеру вводные данные
lexer.input(data)
# Токенизация
while True:
tok = lexer.token()
if not tok:
break # Больше нет ввода
print(tok)

Сохраните этот файл под именем calclex.py. Мы будем использовать его при создании нашего парсера Уасс.
Рассмотрим происшедшее постадийно

1. Импорт модуля с помощью import ply.lex.
2. Все лексеры должны предоставлять список, называемый tokens, который определяет
все возможные имена токенов, которые могут быть созданы лексером. Этот список всегда
обязателен. tokens также может быть кортежем строк (а не строкой), где каждая строка, как
и раньше, обозначает токен.
3. Правило regex для каждой строки может быть задано как в виде строки, так и в виде
функции. В любом случае имя переменной должно быть дополнено символом t_, чтобы обо­
значить, что это правило для сопоставления лексем.
о Для простых лексем регулярное выражение может быть задано в виде строк:
о

t_PLUS = г'\+'

Если необходимо выполнить какое-либо действие, то токен-правило может
быть задано в виде функции:
def t_N UMBER(t):
r'\d+'
t.value = int(t.value)
return t

626

Глава 1 91 . Python Lex-Yacc
Обратите внимание, что в функции правило задается в виде doc string. Функ­
ция принимает один аргумент, являющийся экземпляром LexToken, выполняет
некоторое действие и возвращает аргумент.
Если вы хотите использовать внешнюю строку в качестве правила regex
для функции вместо того, чтобы указывать doc string, рассмотрим следующий
пример:
@TOKEN(identif1er) # идентификатор - это строка, содержащая regex
def t_lD (t):
. . . # действия

о

Экземпляр объекта LexToken (назовем этот объект t) имеет следующие
атрибуты:
1. t.type - тип токена (в виде строки) (например, 'NUMBER', 'PLUS' и т. д.).
По умолчанию t.type устанавливается в имя, следующее за t_ pref1x.
2. t.value - лексема (собственно текст, с которым происходит совпадение).
3. t.lineno - номер текущей строки (он не обновляется автоматически, так
как лексер ничего не знает о номерах строк). Обновление lineno производится
с помощью функции t_newline.
def Lnewline(t):
r'\n+'
t.lexer. l ineno += len(t.value)

о

4. t.lexpos, который представляет собой позицию лексемы относительно на­
чала входного текста.
Если из функции правила regex ничего не возвращается, то лексема
отбрасывается. Если необходимо отбросить маркер, то можно добавить
t_ignore_ pref1x в переменную правила regex вместо того, чтобы создавать
функцию для того же правила.
def t_COMM ENT(t):
г'\#.'1 Optional[int]
# сложный и ресурсоемкий код
if process_has_failed:
return None
return integer_output

И это может быть использовано следующим образом:
x S
if intensive_f(x) is not None:
print(intensive_f(x) / 2)
else:
print(x, "не удалось обработать")
print(x)
=

Хотя это и работает, но имеет проблему вызова intensive_f, что удваивает время выполнения кода. Лучшим решением было бы заранее получить возвращаемое значение функции.
x=S
result = intensive_f(x)
if result is not None:
print(result / 2)
else:
print(x, "не удалось обработать")

652

Глава 200. Общие ошибки

Однако более понятным и, возможно, более "питоничным" способом является использование исключений, например:
х=5
try:
print(intensive_f(x) / 2)
except TypeError: # Исключение, возникающее при попытке использования None + 1
print(x, "не удалось обработать")

В данном случае временная переменная не нужна. Часто вместо этого бывает предпочти­
тельнее использовать утверждение assert и перехватить AssertionError.
Ключи словаря
Частым примером того, как это можно сделать, является обращение к ключам словаря.
Например, сравните код:
Ьird_speeds = get_very_long_dictionary()
if "европейская ласточка" in Ьird_speeds:
speed = Ьiгd_sрееds["европейская ласточка"]
else:
speed = input("Kaкoвa скорость полета ласточки без груза?")
print(speed)

с кодом:
Ьird_speeds = get_very_long_dictionary()
try:
speed = Ьiгd_sрееds["европейская ласточка"]
except KeyError:
speed = input("Kaкoвa скорость полета ласточки без груза?")
print(speed)

В первом примере приходится дважды просматривать словарь, а так как словарь длин­
ный, то каждый раз это может занять много времени. Второй пример требует только одного
поиска по словарю, что значительно экономит процессорное время.
Альтернативой этому может быть использование dict.get(key, default), однако во многих слу­
чаях может потребоваться выполнение более сложных операций в случае отсутствия ключа.

Гл а ва 200 . Общие о ш и б ки
Python - это язык программирования, который задумывался понятным и читаемым, без
двусмысленностей и неожиданного поведения. К сожалению, эти цели достижимы не во всех
случаях, и поэтому есть несколько ситуаций, когда он может сделать не то, что ожидалось.
В этой главе будут рассмотрены некоторые проблемы, с которыми можно столкнуться
при написании кода на языке Python.

200. 1 . Умножение списков и общие ссылки

Рассмотрим случай создания структуры вложенного списка путем умножения:

□]

li = [ * З
print(li)
# Результат: [

□, D. Ш

200. 1 . Умножение списков и общие ссылки

653

На первый взгляд кажется, что мы имеем список, содержащий три разных вложенных
списка. Попробуем добавить 1 к первому списку:
li[0] .append(1 )
print(li)
# Резул ьтат: [[1 ], [1 ], [1 1]

1 добавлена ко всем спискам в списке li.
Причина в том, что выражение [□] * 3 не создает список из трех разных списков. Скорее,
создается список, содержащий три ссылки на один и тот же объект списка. Поэтому при
добавлении к li[0] изменятся все вложенные элементы списка li. Это эквивалентно записи:
li = □
element = [ □]
li = element + element + element
print(li)
# Резул ьтат: [ □, □. Ш
element.append(1 )
print(li)
# Резул ьтат: [[1 ], [1 ], [1 1]

Это можно подтвердить, если вывести адреса памяти вложенного списка с помощью
функции id:
li = [ □ ] * 3

print([id(inner_list) for inner_list in l i])
# Резул ьтат: [6830760, 6830760, 6830760]
Решением является создание внутренних списков с помощью цикла:

li =

Ш for _ in range(3)]

Вместо того чтобы создавать один список и затем делать на него три ссылки, мы теперь
создаем три разных списка. Это, опять же, можно проверить с помощью функции id:
print([id(inner_list) for inner_list in l i])
# Резул ьтат: [6331 048, 6331 528, 6331 488]

Также можно поступить нижеприведенным образом. При этом в каждом вызове append
будет создаваться новый пустой список:


»>
»>
»>

li = □
li.append( □)
li.append( □)
li.append( □)
for k in li: print(id(k))

431 5469256
431 5564552
431 5564808

Не используйте индекс для перебора последовательносm
Не делайте так:

for i in range(len(tab)):
print(tab[i])
А делайте так:

for elem in tab:
print(elem)

for позволит автоматизировать большинство итерационных операций.

Используйте enumerate, если вам действительно нужны и индекс, и элемент
for i, elem in enumerate(tab):
print((i, elem))

654

Глава 200. Общие ошибки

Будьте осторожны при использовании "==" для проверки на True ИJШ False
if (var == True):
# это будет выполнено, если var равно True или 1, 1.0, 1L
if (var != True):
# это будет выполнено, если var не является ни True, ни 1
if (var == False):
# это будет выполнено, если var равно False или О (или О.О, 0L, 0j)
if (var == None):
# выполняется только в том случае, если var равен None
if var:
# выполняется, если var - непустая строка/список/словарь/кортеж, не-О и т. д.
if not var:
# выполнить, если var имеет значения "'', {},

□, О, О, None и т. д.

if var is True:
# выполняется только в том случае, если var - булево True, а не 1
if var is False:
# выполняется только в том случае, если var - булево False, а не О
if var is None:
# то же самое, что var == Nопе

Не проверяйте, можете ли вы это сделать, просто сделайте и обработайте ошибку

Руthоn-программисты обычно говорят: "Легче попросить прощения, чем разрешения".
Не делайте так:

if os.path.isf1 le(f1 le_path):
fi le = open(f1 le_path)
else:
# сделать что-нибудь

А делайте так:
try:
fi le = open(f1 le_path)
except OSError as е:
# сделать что-нибудь

А еще лучше делать так (версии Python 2.6+):
with open(f1 le_path) as fi le:

Этот способ гораздо лучше, поскольку является более универсальным. Можно применять
Вам не нужно заботиться о том, что нужно сделать для пре­
дотвращения ошибки, достаточно просто обратить внимание на ошибку, которую вы риску­
ете допустить.
try/except практически ко всему.

Не проверяйте соответствие типу

Python динамически типизирован, поэтому проверка типа приводит к потере гибкости.
Вместо этого используйте "утиную типизацию" (duck typing), проверяя поведение. Если вы
ожидаете в функции строку, то используйте str() для преобразования любого объекта в стро­
ку. Если вы ожидаете список, то используйте list() для преобразования любого итерируемого
объекта в список.

200. 1 . Умножение списков и общие ссылки

655

Не делайте так:
def foo(name):
if isinstance(name, str):
print(name.lower())
def bar(listing):
if isinstance(listing, list):
listing.extend((1 , 2, 3))
return ", ".join(listing)

А делайте так:
def foo(name)
print(str(name).lower())
def bar(listing) :
1 = list(listing)
l.extend((1 , 2, З))
return ", ".join(I)

Если использовать последний способ, то foo будет принимать любой объект, а Ьаг - стро­
ки, кортежи, множества, списки и многое другое. Простой способ использования принципа
DRY (Don't repeat yourself, с англ. - "не повторяйся").
Не смепmвайте пробелы и табуляции
Используйте объект в качестве первого родителя

Это непросто, но по мере роста вашей программы это поможет вам. В Python 2.х есть ста­
рые и новые классы. Старые классы лишены некоторых возможностей и могут иметь неу­
добное поведение при наследовании. Чтобы быть пригодным для использования, любой из
ваших классов должен быть выполнен в "новом стиле". Для этого необходимо сделать так,
чтобы класс наследовался от object.
Не делайте так:
class Father:
pass
class Child(Father):
pass

А делайте так:
class Father(object):
pass
class Child(Father):
pass

В Python З.х все классы нового стиля, поэтому этого делать не нужно.
Не ШПЩИаJШЗИруйте атрибуты класса вне метода init

Программистам, пришедшим из других языков, это кажется заманчивым, потому что
именно так вы поступаете в Java или РНР. Вы пишете имя класса, затем перечисляете свои
атрибуты и задаете им значение по умолчанию. Кажется, что это работает и в Python, одна­
ко работает это не так, как вы думаете. При этом будут заданы атрибуты класса (статические
атрибуты), затем, когда вы попытаетесь получить атрибут объекта, он выдаст вам его значе­
ние, если только оно не пустое. В этом случае он вернет атрибуты класса. Это влечет за собой
две большие опасности:
• Если атрибут класса изменяется, то изменяется и начальное значение.
• Если задать изменяемый объект в качестве значения по умолчанию, то получится
один и тот же объект, разделяемый между экземплярами.

656

Глава 200. Общие ошибки

Не делайте так:
class Car(object):
color = "red"
wheels = [Wheel(), Wheel(), Wheel(), Wheel()]
А делайте так:
class Car(object):
def _init_(self):
self.color = "red"
self.wheels = [Wheel(), Wheel(), Wheel(), Wheel()]

200.2. Изменяемый аргумент по умолчанию
def foo(li=□):
li.append(1 )
print(li)
foo([2])

# Результат: [2, 1 ]
fоо([З])
# Результат: [3, 1 ]

Этот код ведет себя так, как и ожидалось, но что если не передавать аргумент?
foo()
# Результат: [1 ] Как и ожидалось . . .

foo()

# Результат: [1 , 1 ] Не так, как ожидалось . . .

Это связано с тем, что стандартные аргументы функций и методов оцениваются не во
время выполнения, а во время определения. Поэтому у нас всегда есть только один экзем­
пляр списка li. Способ обойти это - использовать для аргументов по умолчанию только не­
изменяемые типы:
def foo(li=None):
if not li:
li = □
li.append(1 )
print(li)
foo()

# Результат: [1 ]

foo()

# Результат: [1 ]

Хотя это и улучшение, и if not li, и многие другие объекты, например, последовательности
нулевой длины, корректно оцениваются в False, но приведенные ниже примеры аргументов
могут привести к непредвиденным результатам:
х=□
foo(li=x)
# Результат: [1 ]

foo(li="")

# Результат: [1 ]

foo(li=O)

# Результат: [1 ]

200.3. Изменение последовательности, над которой вы полняется итерация

657

Идиоматический подход заключается в непосредственной проверке аргумента на соответствие объекту None:
def foo(li=None):
if li is None:
li =
l i.append(1 )
print(li)
foo()
# Out: [1 ]



200.3. Изменение последовательности, над которой выполняется
итерация

Цикл for выполняет итерацию по последовательности, поэтому изменеIШе последова­
тельности внутри цикла может привести к неожндюmым результатам (особенно при до­
бавлении или удалении элементов):
alist = [О, 1, 2]
for index, value in enumerate(alist):
al ist.pop(index)
print(alist)
# Резул ьтат: [1 ]

Примечание: для удаления элементов из списка используется метод list.pop().
Второй элемент не был удален, так как итерация проходит по индексам по порядку. Приведенный выше цикл выполняет итерацию дважды, получая следующие результаты:
# 1 -я итерация
index = О
alist = [О, 1 , 2]
alist. pop(0) # удаляет 'О'
# 2-я итерация
index = 1
alist = [1 , 2]
alist. pop(1 ) # удаляет '2'
# цикл завершается, но alist не пуст:
alist = [1 ]

Эта проблема возникает из-за того, что при итерации в направлении увеличения индек­
са происходит изменение индексов. Чтобы избежать этой проблемы, можно выполнить ите­

рацmо в цикле в обратном направлении:

alist = [1 ,2,3,4,5,6,7]
for index, item in reversed(list(enumerate(alist))):
# удалить все четные элементы
if item % 2 == О:
alist.pop(index)
print(alist)
# Резул ьтат: [1 , 3, 5, 7]

Итерация в цикле, начиная с конца, по мере удаления (или добавления) элементов не из­
меняет индексы элементов, находящихся ранее в списке. Поэтому в данном примере будУТ
корректно удалены все четные элементы из alist.
Аналогичная проблема возникает при вставке шm: добавлении элементов в список, над
которым въшоJПIЯется итерация, что может привести к возникновению бесконечного цикла:
alist = [О, 1 , 2]
for index, value in enumerate(alist):

658

Глава 200. Общие ошибки

# break, чтобы избежать бесконечного цикла:
if index == 20:
break
alist.insert(index, 'а')
print(alist)
# Результат (сокращенны й): ['а', 'а', ..., 'а', 'а', О, 1 , 2]
Без условия останова (break) цикл будет вставлять 'а' до тех пор, пока на компьютере не
закончится память и программа не сможет продолжить работу. В подобной ситуации обыч­
но предпочтительнее создать новый список и добавлять элементы в него по мере выполне­
ния цикла по исходному списку.
При использовании цикла for нельзя измеIШТЪ элемеmы с1П1ска с помощью перемен­

ной-заместителя:

alist = [1 ,2,3,4]
for item in alist:
if item % 2 == О:
item = 'even'
print(alist)
# Результат: [1 ,2,3,4]
В приведенном выше примере изменение item фактически ничего не меняет в исход­
ном СШlске . Необходимо использовать индекс списка (alist[2]), и для этого хорошо подходит
функция enumerate():

alist = [1 ,2,3,4]
for index, item in enumerate(alist):
if item % 2 == О:
alist[index] = 'even'
print(alist)
# Результат: [1 , 'even', 3, 'even']
В некоторых случаях лучше использовать цикл while:
Если вы собираетесь удалить все элемеmы в списке:
zlist = [О, 1 , 2]
while zlist:
print(zlist[O])
zlist.pop(O)
print('After: zlist =', zlist)
# Результат:
О
#
1
#
2
# После: zlist =



Хотя простой сброс zlist приведет к тому же результату:
zlist =



Приведенный выше пример можно также комбинировать с функцией len() для остановки после определенного момента или для удаления всех элементов списка, кроме х:
zlist = [О, 1 , 2]
х=1
while len(zlist) > х:
print(zlist[O])
zlist.pop(O)
print('After: zlist =', zlist)
# Результат:
О
1
#
# После: zlist = [2]

200.4. Целочисленные и строковые тождества

659

Или для циклического просмотра списка с удалением элементов, удовлетворяющих
определенному условшо (в данном случае удаление всех четных элементов):
zlist = [1 ,2,3,4,5]
i=о
while i < len{zlist):
if zl ist[i] % 2 == О:
zl ist.pop(i)
else:
i += 1
print{zlist)
# Резул ьтат: [1 , 3, 5]

Обратите внимание, что после удаления элемента инкремент i не увеличивается. При
удалении элемента по адресу zlist[i] индекс следующего элемента уменьшился на единицу,
поэтому, проверяя zl ist[i] с тем же значением i на следующей итерации, вы будете правильно
проверять следующий элемент списка.
Противоположным способом удаления ненужных элементов из списка является добав­
ление требуемых элементов в новый список. Следующий пример является альтернативой
последнему примеру цикла while:
zlist = [1 ,2,3,4,5]



z_temp =
for item in zlist:
if item % 2 != О:
z_temp.append{item)
zlist = z.Jemp
print{zlist)
# Резул ьтат: [1 , 3, 5]

Здесь мы перегоняем желаемые результаты в новый список. При желании мы можем пе­
реназначить временный список исходной переменной.
При таком подходе можно задействовать одну из самых элегантных и мощных возмож­
ностей Python - генераторы списков, которые позволяют отказаться от временных списков и
расходятся с рассмотренной ранее идеологией изменения списков/индексов на месте.
zlist = [1 ,2,3,4,5]
[item for item in zlist if item % 2 != О]
# Резул ьтат: [1 , 3, 5]

200.4. Целочисленные и строковые тождества
Python использует внутреннее кэширование для диапазона целых чисел, чтобы умень­
шить ненужные расходы на их повторное создание. Это может привести к запутанному по­
ведению при сравнении целочисленных тождеств:
»> -8 is (-7 - 1 )

False

»> -3 is (-2 - 1 )

True

и другой пример:
»> {255 + 1 ) is {255 + 1 )
True
»> {256 + 1 ) is {256 + 1 )
False

Мы видим, что что операция тождества выдает True для одних целых чисел (-3, 256), но
не для других (-8, 257).

660

Глава 200. Общие ошибки

Если быть более точным, то целые числа в диапазоне [-5, 256] кэшируются при запуске
интерпретатора и создаются только один раз. Поэтому они идентичны и сравнение их иден­
тичности при помощи is дает True; целые числа за пределами этого диапазона обычно созда­
ются "на лету'', и проверка их тождественности выдает результат False.
Это распространенный "подводный камень", поскольку это обычный диапазон для те­
стирования, но достаточно часто код, прекрасно работавший в процессе разработки, без ви­
димых причин отказывает на дальнейших стадиях. Решение состоит в том, чтобы всегда
сравнив ать значения с помощью оператора равенства (==), а не оператора тождества (is).
В Python также хранятся ссьшки на часто используемые строки, что может привести к та­
кому же запутанному поведению при сравнении идентичности (т.е. использовании оперто­
ра is) строк.
>» 'python' is 'ру' + 'thon'
True

Строка 'python' является общеупотребительной, поэтому в Python есть один объект, кото­
рый используется всеми ссылками на строку 'python'. Для нестандартных строк сравнение
идентичности не удается, даже если строки те же.
»> 'this is not а common string' is 'this is not' + ' а common string'

False

»> 'this is not а common string' = = 'this is not' + ' а common string'

True

Поэтому, как и в случае с целыми числами, всегда сравнивайте строковые значения с
помощью оператора равенства (==), а не оператора тождества (is).

200.5. Словари неупорядочены

Можно было бы ожидать, что словарь Python будет сортироваться по ключам, как, например, std::map в языке С++, но это не так:
myDict = {'f1 rst': 1 , 'second': 2, 'third': З}
print(myDict)
# Результат: {'first': 1 , 'second': 2, 'third': З}
print([k for k in myDict])

# Результат: ['second', 'third', 'f1rst']

В Python нет встроенного класса, который бы автоматически сортировал свои элементы
по ключу. Однако если сортировка не является обязательным условием, и вы просто хотите,
чтобы ваш словарь запоминал порядок вставки пар ключ/значение, вы можете использо­
вать collections.Ordered Dict:
from collections import Ordered Dict

oDict = OrderedDict([('f1 rst', 1 ), ('second', 2}, ('third', З)])
print([k for k in oDict])

# Результат: ['f1rst', 'second', 'third']

Следует помнить, что инициализация Ordered Dict стандартным словарем никак не отсо­
ртирует словарь, а только сохр анит порядок вставки ключей.
В Python 3.6 реализация словарей бьша изменена с целью улучшения потребления ими
памяти. Побочным эффектом новой реализации является сохранение порядка аргументов
ключевых слов, передаваемых в функцию:

Версия Python З.х ;:,: 3. 6:
def func(,н,kw}: print(kw. keys())
func(a = 1 , Ь = 2, с = З, d = 4, е = 5)
dict_keys(['a', 'Ь', 'с', 'd', 'е']) # ожидаем ы й порядок

200.6. Утечка переменных в генераторах списков и циклах for

661

Предупреждение: следует иметь в виду, что "аспект сохранения порядка в этой новой реа­
лизации рассматривается как деталь реализации и на него не следует полагаться", посколь­
ку в будущем он может измениться (см. https://docs.python.org/3.6/whatsnew/3.6.html#new-dict­
implementation).

200.6. Утечка переменных в генераторах списков и циклах for
Рассмотрим следующий генератор списков:

Версия Python 2.х :,; 2. 7:
i=о
а = [i for i in range(3)]
print(i) # Выводит 2

Это происходит только в Python 2 из-за того, что в генераторе списка происходит "утеч­
ка" управляющей переменной цикла в окружающую область видимости (источник). Такое
поведение может приводить к трудноотлавливаемым ошибкам, и в Python 3 оно было ис­
правлено.

Версия Python 3.х � 3.0:
i=

о

а = [i for i in range(3)]
print(i) # Выводит О
Аналогично, циклы for не имеют частной области видимости для своей итерационной
переменной.
i=о
for i in range(3):
pass
print(i) # Выводит 2

Подобное поведение встречается как в Python 2, так и в Python 3.
Чтобы избежать проблем с утечкой переменных, используйте новые переменные в гене­
раторах списков и циклах for по мере необходимости.

200. 7. Цепочка операторов "or"

При проверке любого из нескольких сравнений равенства:
if а == 3 or Ь == 3 or с == 3:
возникает соблазн сократить его до
if а or Ь or с == 3: # Неправильно
Это неверно: оператор or имеет меньший приоритет, чем ==, поэтому выражение будет оце­
нено как if (а) or (Ь) or (с = = 3):. Правильным способом является явная проверка всех условий:
if а == 3 or Ь == 3 or с == 3: # Правильно
В качестве альтернативы вместо цепочки операторов or может быть использована встро­
енная функция апу():
if апу([а == 3, Ь == 3, с == 3]): # Правильно
Или, чтобы сделать его более эффективным:
if апу(х == 3 for х in (а, Ь, с)): # Правильно
Или, для краткости:
if 3 in (а, Ь, с): # Правильно

662

Глава 200. Общие ошибки

Здесь мы используем оператор in, чтобы проверить, присутствует ли значение в кортеже,
содержащем значения, с которыми мы хотим сравнить. Аналогично, неверна запись:
if а == 1 or 2 or 3:
что следует записать в виде:
if а in (1 , 2, 3):

200.8. sys.argv[0] - имя исполняемого файла

Первым элементом sys.argv[0] является имя исполняемого Руthоn-файла. Остальные
элементы являются аргументами скрипта.
# script.py
import sys
print(sys.argv[0])
print(sys.argv)
$ python script.py
=> script.py
=> ['script. py']
$ python script.py f1zz
=> script.py
=> ['script. py', 'f1zz']
$ python script.py fizz buzz
=> script.py
=> ['script. py', 'f1zz', 'buzz']

200.9. Доступ к атрибутам целочисленных литералов

Вы, наверное, слышали, что в Python все является объектом, даже литералы. Это означа­
ет, что, например, 7 тоже является объектом, а значит, у него есть атрибуты. Например, од­
ним из таких атрибутов является Ьit_length. Он возвращает количество битов, необходимых
для представления вызываемого значения.
х=7
x.Ьit_lengthO
# Результат: 3
Видя, что приведенный выше код работает, можно интуитивно подумать, что и 7.Ьit_lengthO
тоже будет работать, но вместо этого возникает SyntaxError. Почему? Потому что интерпрета­
тору необходимо провести различие между обращением к атрибуту и числом с плавающей
точкой (например, 7.2 или 7.Ьit_length()). Этого сделать ему не удается, потому и возникает ис­
ключение. Существует несколько способов доступа к атрибутам целочисленного литерала:
# кругл ы е скобки
(7).Ьit_length()
# пробел
7 .Ьit_length()
Использование двух точек (например, 7 .. Ьit_length()) в данном случае не работает, так как
создается flоаt-литерал, а у чисел с плавающей точкой нет метода Ьit_length(). При обращении
к атрибутам flоаt-литералов этой проблемы не существует, поскольку интерпретатор доста­
точно "умен", чтобы понять, что flоаt-литерал не может содержать, например, две точки:
7.2.as_integer_ratio()
# Результат: (81 06479329266893, 1 1 25899906842624)

200. 1 0. Глобальная блокировка интерпретатора (G I L) и блокировка потоков

200. 1 О. Глобальная блокировка интерпретатора {GIL)
и блокировка потоков

663

О глобальной блокировке интерпретатора (GIL) в Python написано достаточно много.
Иногда это может привести к путанице при работе с многопоточными (не путать с много­
процессорными) приложениями. Приведем пример:
import math
from threading import Thread
def calc_fact(num):
math.factorial(num)
num = 600000
t = Thread(target=calc_fact, daemon=True, args=[num])
ргiпt("П ристуnаем к вычислению: {}!".format(num))
t.startO
ргint("Вычисление ...")
t.join()
ргiпt("Вычислено")
Можно было бы ожидать, что "Вычисление ..." будет выведено сразу после начала потока,
ведь мы хотели, чтобы вычисление происходило в новом потоке ! Но на самом деле это сооб­
щение выводится после завершения вычислений. Это связано с тем, что новый поток опира­
ется на функцию языка С (math.factorial), которая блокирует GIL на время выполнения.
Существует несколько способов обойти эту проблему. Первый - реализовать функцию
факториала на родном языке Python. Это позволит главному потоку перехватить управле­
ние, пока вы находитесь внутри цикла. Недостатком такого решения является то, что оно бу­
дет намного медленнее, поскольку мы больше не используем функцию на языке С.
def calc_fact(num):
""" медленная версия факториала на "родном" Python """
res = 1
while num >= 1 :
res = res * num
num -= 1
return res
Перед началом выполнения можно также использовать функцию sleep в течение неко­
торого времени. Заметьте, что это не позволит вашей программе на самом деле прервать
вычисления, происходящие внутри функции С, но позволит вашему основному потоку про­
должить работу.
def calc_fact(num):
sleep(0.001 )
math.factorial(num)

200. 1 1 . Многократное использование оператора return
Допустим, что функция xyz возвращает два значения - а и Ь:
def xyz():
return а, Ь
Код, вызывающий xyz, сохраняет результат в одной переменной, предполагая, что функ­
ция возвращает только одно значение:
t = xyzO
Значение t на самом деле является кортежем (а, Ь), поэтому любое действие над t, предпо­
лагающее, что оно не является кортежем, может завершиться неудачей в глубШiе кода с не­
ожиданной ошибкой о кортежах.

ТуреЕпоr: type tuple doesn't define ... method (тип tuple не определяет ... метод)

664

Глава 201 . Скрытые возможности

В этом случае достаточно написать:
а, Ь = xyz()

Новичкам будет трудно понять причину этого сообщения, прочитав только сообщение
об ошибке кортежа!

200. 1 2. JSОN-ключи в Python
my_var = 'Ыа';
api_key = 'key';
...тут много кода...
params = {"language": "en", my_var: api_key}

Если вы привыкли к JavaScript, то оценка переменных в словарях Python будет не такой,
как вы ожидаете. Это утверждение в JavaScript привело бы к тому, что объект params выгля­
дел бы следующим образом:
{

"language": "en",
"my_var": "key"

Однако в Python это привело бы к созданию следующего словаря:
"language": "en",
"Ыа": "key"

Переменная my_var оценивается, и ее значение используется в качестве ключа.

Глава 20 1 . Скрытые возможности
201 . 1 . Перегрузка операторов

Все в Python является объектами. Каждый объект имеет некоторые специальные вну­
тренние методы, которые он использует для взаимодействия с другими объектами. Как пра­
вило, эти методы соответствуют соглашению об именовании. В совокупности это называет­
ся моделью данных Python (см. https://docs.python.org/2/reference/datamodel.html).
Вы можете перегрузить любой из этих методов. Это широко используется в перегрузке
операторов в Python. Ниже приведен пример перегрузки операторов с использованием мо­
дели данных Python. Класс Vector создает простой вектор из двух переменных. Мы добавим
соответствующую поддержку математических операций над двумя векторами с помощью
перегрузки операторов.
class Vector(object):
def _init_(self, х, у):
self.x = х
self.y = у
def _add_(self, v):
# Сложение с другим вектором.
return Vector(self.x + v.x, self.y + v.y)
def _sub_(self,v):
# Вычитание другого вектора
return Vector(self.x - v.x, self.y - v.y)

201 . 1 . Перегрузка операторов

665

def _mu l_(self, s):
# Умножение на скаляр.
return Vector(self.x * s, self.y * s)
def _div_(self, s):
# Деление на скаляр
float_s = float(s)
return Vector(self.x / float_s, self.y / float_s)
def _floordiv_(self, s):
# Деление на скаляр (с округлением в сторону уменьшения).
return Vector(self.x // s, self.y // s)
def _repr_(self}:
# В ы вести дружественное представление класса Vector. В противном случае оно будет
# вы глядеть так: .
return '' % (self.x, self.y, )
а = Vector{З, 5)
Ь = Vector{2, 7)
print а + Ь # Резул ьтат:
print Ь - а # Результат:
print Ь * 1 .3 # Резул ьтат:
print а // 1 7 # Резул ьтат:
print а / 1 7 # Результат:

Приведенный пример демонстрирует перегрузку основных числовых операторов. Более
подробную информацию можно найги здесь: https://docs.python.org/2/reference/datamodel.html?

object._getattr_&emulating-numeric-types.

Благодарности
Большое спасибо всем участникам сообщества Stack Overflow Documentation, которые по­
могли предоставить читателю эти примеры. Присылайте материалы для публикации или
обновления примеров на web@petercv.com.
t;:agatay Uslu Глава 20
2Cubed Главы 39, 122 и 147
4444 Глава 21
А. Ciclet Глава 196
А. Raza Глава 1
Aaron Christiansen Главы 40 и 109
Aaron Critchley Глава 1
Aaron Hall Глава 38
AЬhishek Kumar Глава 149
abukaj Глава 200
acdr Глава 21
Adam Brenecki Глава 85
Adam Matan Главы 84 и 104
Adam_92 Глава 40
adeora Глава 197
Aditya Главы 40, 53, 134, 195 и 200
Adrian Antunez Глава 88
Adriano Глава 33
afeique Глава 1
Aidan Глава 75
Ajean Глава 5
Akshat Mahajan Главы 33, 67, 120 и 146
aldanor Глава 40
Aldo Глава 103
Alec Глава 200
alecxe Главы 5, 40, 87, 92 и 93
alejosocorro Главы 1 и 75
Alex Gaynor Глава 55
Alex L Глава 16
Alex Logan Глава 1
AlexV Глава 33
Alfe Глава 88
alfonso.kim Глава 16
ALinuxLover Глава 1
Alireza Savи Глава 192
Alleo Главы 21 и 149
Alon Alexиer Глава 116
amЫina Главы 83, 85 и 129
Ami Tavory Глава 192
amin Глава 9
Amir Rachum Главы 19 и 39
Amitay Stern Главы 41 и 91
Anaphory Глава 159
anatoly techtonik Глава 160
Иrеа Глава 1
иrew Глава 131
Иrew Schade Глава 84
Иrii AЬramov Глава 1
Иrzej Pronobls Главы 8, 38 и 55
Иу Главы 1, 5, 7, 17, 33, 51, 82 и 88
Иу Hayden Главы 8, 33, 67, 73, 75, 77, 98 и 149
angussidney Глава 1
Ani Menon Главы 1, 4, 40, 149 и 154

Annonymous Главы 78, 87 и 199
Anthony Pham Главы 13, 15, 16, 19, 20, 30, 33, 43, 45,
55, 60, 70 и 179
Antoine Bolvy Главы 1 и 149
Antoine Pinsard Глава 39
Antti Haapala Главы 5, 16, 72, 87 и 106
Antwan Глава 149
APerson Главы 21, 69 и 72
Aquib Javed Кhan Глава 1
Ares Главы 1, 15, 20 и 41
Arkady Глава 33
Arpit Solanki Главы 1, 82 и 125
Artem Kolontay Глава 173
ArtOfCode Главы 67, 173 и 197
Arun Главы 65 и 108
Aryaman Arora Глава 179
ashes999 Глава 75
asmeurer Главы 45 и 47
atayenel Глава 124
Athari Глава 151
Avantol13 Глава 38
avb Глава 30
Ахе Главы 21 и 149
B8vrede Главы 1, 40, 75 и 103
Baaing Cow Главы 1 и 200
Bahrom Глава 8
Bakuriu Глава 149
balki Глава 53
Barry Глава 20
Bastian Глава 86
bbayles Глава 128
Beall619 Главы 74 и 101
Ьее Главы 161 и 164
Benedict Bunting Глава 99
Bharel Главы 30 и 149
Bhargav Глава 40
Bhargav Rao Главы 41, 149 и 200
Ьignose Глава 149
Billy Глава 200
Biswa_9937 Главы 178, 182 и 183
Ьitchaser Глава 149
bixel Глава 200
ЫuЬberdiЫub Глава 177
Ыueberryfields Глава 181
Ыueenvelope Главы 9 и 20
Вluethon Глава 149
boboquack Главы 10, 11 и 18
bogdanciobanu Глава 111
Bonifacio2 Глава 64
BoppreH Глава 19
Bosoneиo Глава 46
Bozo Stojkovic Главы 1, 14, 21, 33 и 149
bpachev Глава 53

Бл а год арности
Brendan АЬеl Глава 84
Ьrennan Глава 173
Brett Cannon Главы 11 и 43
Brian С Глава 1
Brien Глава 41
Bryan Р Глава 1
BSL Главы 1 и 197
Burhan Кhalid Глава 19
BusyAnt Главы 1, 20, 43, 60, 78 и 88
Buzz Глава 18
Cache Staheli Глава 41
CamelВackNotation Глава 33
Cameron Gagnon Глава 149
Camsbury Главы 9 и 33
caped114 Глава 41
carrdelling Глава 77
СЬеЬ24404 Глава 1
ceruleus Глава 1
cfi Главы 21, 26 и 69
Chиan Purohit Главы 20 и 33
ChaoticTwist Главы 21, 33, 41 и 107
Charles Главы 10, 21, 30, 41, 149 и 200
Charul Глава 167
Chinmay Hegde Главы 50, 94, 132, 164 и 171
Chong Tang Глава 21
Chris Hunt Глава 16
Chris Larson Глава 33
Chris Midgley Глава 1
Chris Mueller Глава 19
Christian Ternus Главы 1, 16, 43, 51 и 78
Christofer Ohlsson Глава 45
Christophe Roussy Глава 200
Chromium Глава 134
Cilyan Глава 170
Cimbali Глава 8
cizixs Главы 19, 20 и 128
cjds Главы 110 и 134
Cliodhna Глава 1
Claudiu Главы 1, 31, 67, 75, 80, 83, 88, 111, 118, 153,
192 и 193
Clayton Wahlstrom Глава 149
cledoux Глава 108
CodenameLamЬda Главы 1 и 67
Cody Piersall Глава 8
Colin Yang Глава 149
Comrade SparklePony Главы 180 и 181
Conrad.Dean Главы 1, 5, 21, 38 и 43
crhodes Глава 30
C-L-s- Главы 1, 149, 161 и 191
Dair Главы 11, 120 и 173
Daksh Gupta Главы 1 и 38
Dania Глава 1
danidee Глава 33
Daniel Глава 43
Daniil Ryzhkov Глава 173
Darkade Глава 173
Darth Kotik Глава 16
Darth Shadow Главы 1, 40, 75, 97, 149 и 173
Dartmouth Главы 1, 62, 149 и 150
Dave J Главы 40 и 149

667
David Главы 9, 33, 124 и 161
David Cullen Главы 30 и 121
David Heyman Главы 41 и 149
davidism Глава 13
DawnPaladin Глава 33
Dee Глава 186
deeenes Главы 1, 20 и 67
deepakkt Глава 14
DeepSpace Главы 9, 16, 77, 100, 149 и 200
Delgan Главы 1, 16, 20 и 40
denvaar Глава 167
depperm Главы 1, 3, 38, 41 и 99
DevD Глава 1
Devesh Saini Глава 115
DhiaTN Глава 16
dhimanta Главы 184 и 185
Dilettant Глава 200
Dima Тisnek Глава 21
djaszczurowski Глава 167
Doc Глава 143
dodell Глава 1
Doraemon Глава 29
Doug Henderson Глава 41
Douglas Starnes Глава 1
Dоv Глава 30
dreftymac Глава 40
driax Главы 39, 75 и 88
Duh Глава 149
Dunatotatos Глава 171
dwиerson Глава 149
eиersson Глава 127
edwinksl Глава 173
eenЬlam Глава 21
Elazar Главы 1, 7, 8, 13, 15, 16, 20, 21, 28, 38, 41, 67, 87,
111, 144 и 149
Eleftheria Глава 113
elegent Глава 33
Ellis Главы 20 и 45
Elodin Главы 33 и 195
Emma Главы 20 и 21
Enamul Hassan Глава 16
engineercoding Глава 192
Enrico Maria De Angelis Глава 1
enrico.bacis Главы 21, 56 и 149
erewok Глава 149
Eric Глава 107
Eric Finn Глава 16
Eric Zhang Глава 110
Erica Глава 1
ericdwang Глава 149
ericmarkmartin Главы 67, 98, 110 и 149
Erik Godard Глава 1
EsmaeelE Глава 1
Esteis Главы 13 и 21
ettanany Главы 27 и 149
Everyone_Else Глава 149
evuez Главы 7, 8, 14, 20, 40, 113 и 149
exhuma Глава 20
Fa.Ьio Perez Глава 31
Faiz Halde Главы 21, 114 и 119

668
FazeL Главы 111 и 148
Felix D. Глава 16
Felk Глава 21
Fermi paradox Глава 21
Fernиo Глава 173
Ffisegydd Главы 14, 38, 98, 108 и 193
Filip Haglund Глава 1
Firix Главы 1 и 150
flamenco Глава 56
Flickerlight Глава 20
Florian Bender Глава 21
FМс Глава 43
Francisco Guimaraes Глава 94
Franck Dernoncourt Глава 1
FrankBr Глава 36
frankyjuang Глава 181
Fred Barclay Главы 1 и 157
Freddy Глава 1
fredley Глава 45
freidrichen Глава 21
Frustrated Глава 74
Gal Dreiman Главы 16, 20, 21, 33, 40, 136 и 137
ganesh gadila Главы 20 и 41
Ganesh К Глава 148
Gareth Latty Глава 19
garglOmay Главы 21 и 149
Gavin Глава 149
Geeklhem Главы 14, 110 и 124
Generic Snake Глава 16
geoffspear Глава 149
Gerard Roche Глава 1
gerrit Глава 40
ghostarbeiter Главы 16, 21, 33, 41, 45, 88, 132 и 149
Giannis Spiliopoulos Глава 40
GiantsLoveDeathMetal Главы 40 и 164
girish946 Главы 31 и 154
giucal Глава 55
GoatsWearHats Главы 1, 16, 29, 41 и 72
goodmami Глава 75
Greg Глава 22
greut Глава 37
Guy Главы 16, 19 и 156
Н. Pauwelyn Глава 1
hackvan Глава 164
Hannele Глава 21
Hannes Karppila Главы 14 и 134
Harrison Глава 40
hashcode55 Глава 76
ha_1694 Глава 173
heyhey2k Глава 94
hiro protagonist Главы 54 и 200
HoverHell Глава 12
Hriddhi Dey Глава 105
Hurkyl Главы 21 и 33
hxysayhi Главы 66 и 168
Iаn Глава 1
IanAuld Главы 1 и 21
iankit Главы 21 и 37
iВelieve Глава 19
idjaw Глава 41

Бл а годарности
ifma Глава 108
Igor Raush Главы 1, 19, 20, 38, 39, 45, 67 и 98
Ша Barahovski Главы 32, 41 и 88
ilse2005 Глава 30
InЬar Rose Глава 16
Indradhanush Gupta Глава 49
Infinity Главы 19 и 115
InitializeSahiЬ Главы 38, 39 и 82
intboolstring Главы 10, 21 и 45
iScrE4m Глава 149
J F Главы 1, 9, 15, 20, 29, 33, 38, 88, 97, 111, 119, 125
и 149
Jбrn Hees Глава 137
JOHN Главы 21 и 67
j3485 Глава 20
jackskis Главы 53 и 67
Jacques de Hooge Главы 97 и 151
JakeD Глава 22
James Главы 8, 14, 19, 20, 33 и 100
James Elderfield Главы 20, 40, 45 и 149
James Taylor Глава 1
JamesS Глава 21
Jаn Глава 75
jani Глава 20
japborst Глава 86
Jean Главы 1 и 40
jedwards Главы 1, 102 и 149
Jeff Hutchins Глава 110
Jeffrey Lin Главы 1, 16, 75, 132 и 200
JelmerS Глава 102
JGreenwell Главы 3, 9, 33 и 37
JHS Глава 21
Jim Fasarakis Hilliard Главы 1, 16, 33, 41, 55, 87, 110,
149 и 200
jim opleydulven Глава 1
Jimmy Song Глава 149
jimsug Главы 1 и 20
jkdev Главы 20 и 38
jkitchen Глава 33
JL Peyret Главы 40 и 51
jlarsch Глава 38
jmunsch Главы 1, 47, 92, 125, 181 и 192
JNat Главы 10, 20 и 29
joel3000 Главы 21, 103 и 192
Johan Lundberg Глава 1
John Slegers Главы 1 и 149
John Zwinck Глава 11
Jonatan Главы 40, 64 и 87
jonrsharpe Главы 1, 75, 78 и 98
Joseph True Глава 1
Josh Глава 149
Jossie Calderon Глава 86
jrast Глава 16
JRodDynamite Главы 1, 21 и 40
jtbиes Глава 70
Juan Т Главы 1, 67, 90 и 149
JuanPaЬlo Глава 110
Julien Spronck Главы 53 и 75
Julij Jegorov Глава 188
Justin Главы 30, 33, 40, 74 и 149

Благодарности
Justin Chadwell Глава 125
Justin М. Ucar Глава 149
j_ Глава 41
Kable Глава 149
Kallz Глава 38
Kamran Mackey Глава 1
Karl Кnechtel Главы 67, 69 и 149
KartikKannapur Главы 20 и 38
kdopen Главы 19, 21 и 110
keiv.fly Глава 194
Kevin Brown Главы 1, 5, 14, 30, 53, 75, 146, 149 и 192
KeyWeeUsr Главы 80 и 153
IODJourney Глава 21
Кinifwyne Главы 43 и 193
Кiran Vemuri Глава 1
kisanme Глава 1
knight Глава 40
kollery Глава 156
kon psych Глава 47
krato Глава 83
Kunal Marwaha Глава 149
Kwarrtz Глава 21
L3viathan Главы 33 и 98
Lafexlos Главы 1, 9 и 20
LDP Глава 20
Lee Netherton Главы 21, 33 и 114
Leo Главы 49 и 97
Leo Thumma Глава 20
Lеоn Глава 1
Leon Z. Глава 74
Liteye Главы 21 и 38
loading ... Главы 83 и 114
Locane Глава 21
lorenzofeliz Глава 2
LostAvatar Главы 1 и 161
Luca Van Oort Глава 165
Luke Taylor Глава 70
lukewrites Глава 20
Lyndsy Simon Глава 21
machine yearning Главы 19, 53 и 67
magu_ Глава 77
Mahdi Главы 21, 82 и 120
Mahmoud Hashemi Глава 199
Majid Главы 19, 28, 77, 82, 98, 112 и 173
Malt Глава 200
mаnu Глава 1
МАNU Глава 1
Marco Pashkov Главы 29, 39, 40, 83 и 173
Mario Corchero Глава 192
Mark Глава 125
Mark Miller Глава 130
Mark Omo Глава 168
Markus Meskanen Глава 21
MarkyPython Глава 41
Marlon AЬeykoon Глава 79
Martijn Pieters Главы 1, 67, 77 и 97
Martin Valgur Глава 104
Math Глава 16
Mathias711 Глава 1
matsjoyce Главы 1 и 9

669
Matt Dodge Главы 149 и 200
Matt Giltaji Главы 33, 40, 173 и 193
Matt Rowlи Глава 149
MattCorr Главы 4 и 121
Mattew Whitt Главы 21, 37, 39, 110, 146, 173 и 192
mattgathu Главы 19, 117, 124 и 190
Matthew Глава 77
mах Глава 67
Мах Fеng Глава 14
mbrig Глава 21
mbsingh Глава 161
Md Sifatul Islam Главы 28 и 75
mdegis Глава 1
Mechanic Главы 1, 9, 19 и 28
mertyildiran Глава 1
MervS Глава 125
metmirr Глава 162
mezzode Глава 28
mgilson Глава 192
Michael Recachinas Глава 149
Michel Touw Глава 161
Mike Driscoll Главы 1 и 13
Miljen Mikic Глава 1
Mimouni Глава 1
Mirec Miskuf Глaвa 21
mnoronha Главы 1 и 149
Mohammad Julfikar Глава 123
moshemeirelles Глава 1
MrP0l Глава 20
mrtuovinen Глава 92
МSD Глава 1
MSeifert Главы 9, 16, 19, 21, 26, 33, 37, 41, 43, 45, 48,
55, 64, 67, 68, 69, 70, 71, 72, 73 и 200
muddyfish Главы 1, 20, 21, 33, 37, 57, 111 и 149
Mukunda Modell Глава 37
Muntasir Alam Глава 1
Murphy4 Глава 33
MYGz Глава 40
Naga2Raja Глава 150
Nиer Speerstra Главы 40, 75 и 116
naren Главы 42 и 89
Nathan Osman Глава 190
Nathaniel Ford Главы 1, 29 и 41
ncmathsadist Глава 200
nd. Глава 33
nehemiah Глава 173
nemesisfixx Глава 10
Ni. Главы 1 и 92
Nick Humrich Глава 139
Nicolas Глава 29
Nicole White Глава 5
niemmi Глава 149
niyasc Главы 1, 43, 45 и 149
nlsdfnbch Главы 5, 19, 28, 30, 53, 67, 117, 120 и 121
Nour Chawich Глава 40
noч:iлPлzeJJ Главы 1, 19, 21, 28, 88 и 149
Nuhil Mehdy Глава 173
numbermaniac Главы 1 и 9
obust Глава 54
Ohad Eytan Глава 5

670
ojas mohril Глава 38
omgimanerd Глава 200
Or East Главы 8, 21, 75, 112 и 189
OrangeTux Глава 149
Ortomala Lokni Глава 173
orvi Главы 1, 25, 41, 125, 133 и 134
Oz Bar Глава 20
Ozair Kafray Глава 30
Риа Глава 21
Parousia Главы 23 и 69
Pasha Главы 3, 20, 21, 33, 37, 38, 67, 70, 77, 83 и 149
Patrick Haugh Главы 1 и 200
Раul Глава 5
Paul Weaver Глава 88
paulmorriss Глава 5
Paulo Freitas Глава 149
Paulo Scardine Глава 39
Pavan Nath Главы 1, 20 и 179
pcurry Главы 29, 110 и 149
Peter Brittain Глава 77
Peter M0lgaard Pallesen Глава 73
Peter Shinners Глава 109
petrs Глава 77
philngo Глава 16
Pigman168 Глава 96
pistache Глава 38
pktangyue Глава 149
poke Глава 20
PolyGeo Глава 145
poppie Глава 149
ppperry Глава 55
Prem Narain Глава 59
Preston Главы 138 и 173
proprefenetre Глава 147
proprius Глава 5
РSN Глава 1
pylang Главы 1, 8, 21, 33, 38, 43, 53, 54, 73, 80, 128, 147,
173, 192 и 200
PYPL Глава 79
Pythonista Главы 32 и 149
pzp Главы 1, 29, 30, 33, 41, 49, 64 и 67
Quill Глава 1
qwertyuip9 Главы 161, 173 и 181
R Colmenares Глава 10
R Nar Глава 21
Rapli Иras Глава 82
Regis в. Глава 173
Raghav Глава 125
Rahul Nair Главы 1, 21, 43, 88 и 143
RahulНP Главы 5, 8, 45, 53, 64 и 112
rajah9 Главы 14, 16 и 45
Ram Grиhi Глава 1
RиomHash Глава 95
rassar Глава 172
ravigadila Главы 20, 60, 85, 91 и 104
Razik Глава 158
Rednivrug Главы 2, 35 и 63
regnarg Глава 75
Reut Sharabani Главы 64 и 200
rfkortekaas Главы 1 и 115

Бл а годарности
Ricardo Глава 157
Riccardo Petraglia Главы 21, 84 и 117
Richard Fitzhugh Глава 38
rlee827 Глава 62
rll Глава 21
Rob Н Глава 121
Rob Murray Глава 94
Ronen Ness Глава 30
ronrest Главы 19 и 41
Roy Iacob Глава 19
rrao Глава 1
rrawat Глава 161
Ryan Smith Главы 21 и 120
ryanyuyu Глава 21
Rу Глава 149
Sachin Kalkur Глава 125
sagism Глава 5
Saiful Azad Глава 43
Sam Кrygsheld Глава 1
Sam Whited Глава 111
Sangeeth Sudheer Глава 1
SaqiЬ Shamsi Глава 92
Sardathrion Глава 103
Sardorbek Imomaliev Глава 103
sarvajeetsuman Главы 9 и 16
SashaZd Главы 12, 14, 29, 64, 86, 134, 143, 144, 146 и 194
satsumas Глава 67
sayan Главы 1 и 96
Scott Mermelstein Главы 110, 135 и 149
Sebastian Schrader Глава 173
Selcuk Главы 1, 28, 149 и 201
Sempoo Глава 38
Serenity Главы 20, 30, 40, 43, 149 и 173
SerialDev Глава 82
serv Глава 40
Seth М. Larson Главы 54 и 87
Seven Главы 1, 11, 20 и 33
sevenforce Главы 16 и 67
ShadowRanger Глава 149
Shantanu Alshi Глава 173
Shawn Mehan Главы 10, 15, 19, 20 и 47
Shihab Shahriar Глава 200
Shijo Глава 198
Shoe Глава 21
Shrey Gupta Главы 41, 56 и 173
Shreyash S Sarnayak Глава 12
Shuo Глава 77
SiggyF Главы 16 и 111
Simon Fraser Глава 173
Simon НiЬЬs Глава 169
Simplans Главы 1, 9, 10, 14, 16, 19, 21, 28, 33, 37, 38, 41,
43, 44, 45, 50, 69, 75, 77, 82, 88, 99, 147, 149 и 173
Sirajus Salayhin Главы 5, 175 и 176
sisanared Глава 39
skrrgwasme Главы 16 и 99
SN Ravichиran KR Глава 75
solarc Главы 20 и 149
Soumendra Kumar Sahoo Главы 14 и 38
SouvikMaji Глава 1
sricharan Глава 149

Бл а год арности
StardustGogeta Глава 43
stark Глава 1
Stephen Nyamweya Глава 161
Steve Barnes Главы 33 и 82
Steven Maude Главы 11, 33, 47 и 92
sth Главы 91, 92 и 141
strpeter Главы 85 и 192
StuxCrystal Главы 21, 37, 43, 56, 76, 82, 121 и 142
Sudip Bhиari Глава 88
Sun Qingyao Глава 101
Sunny Patel Глава 21
SuperВiasedMan Главы 1, 4, 15, 16, 20, 21, 26, 29, 30,
33, 41, 43, 64, 69, 77, 80, 88, 132, 149 и 200
supersam654 Глава 70
surfthecity Глава 6
Symmitchry Главы 47 и 53
sytech Глава 92
Sнadoшfa Главы 1 и 134
Tadhg McDonald Глава 149
talhasch Главы 92, 93 и 164
Tasdik Rahman Глава 30
taylor swift Глава 1
techydesigner Главы 1, 38, 43 и 190
Teepeemm Глава 152
Tejas Jadhav Глава 201
Tejus Prasad Глава 1
TemporalWolf Глaвa 90
textshell Главы 16, 28, 33 и 121
TheGenie OfГruth Глава 1
theheadofabroom Главы 41, 49 и 64
the_cat_lady Глава 43
The_Curry_Man Глава 16
Thomas Ahle Главы 60 и 134
Thomas Crowley Глава 105
Thomas Gerot Главы 30, 43, 58, 61, 126 и 181
Thomas Moreau Глава 119
Thtu Глава 80
Tim Глава 149
Tim D Глава 200
tjohnson Глава 44
tlo Глава 82
toЬias_k Главы 28 и 40
Тоm Глава 16
Tom Barron Главы 1 и 21
Tom de Geus Глава 1
Tony Meyer Глава 43
Tony Suffolk 66 Главы 9, 10, 38 и 40
tox123 Глава 38
TuringTux Глава 4
Tyler Crompton Глава 86
Tyler Gubala Главы 119 и 122
Udi Глава 54
ШtraBob Глава 38
UmiЬozu Глава 30
Underyx Глава 49
Undo Глава 9
unutbu Глава 116
user2027202827 Глава 163
user2314737 Главы 8, 9, 13, 16, 20, 28, 30, 33, 38, 40,
41, 57, 60, 64, 69, 75, 88, 110, 134, 142, 149, 154,
161, 196 и 200

671
user2683246 Главы 43 и 187
user2728397 Глава 166
user2853437 Глава 1
user312016 Главы 1, 40 и 77
user3333708 Глава 33
user405 Глава 33
user6457549 Глава 20
Utsav Т Глава 20
vaichidrewar Глава 1
valeas Глава 161
Valentin Lorentz Главы 20, 21, 43, 110 и 149
Valor Naram Глава 43
vaultah Главы 20, 33, 43 и 77
Veedrac Главы 21, 33, 41 и 110
Vikash Kumar Jain Глава 174
Vin Главы 1 и 17
Vinayak Глава 149
vinzee Главы 99, 116 и 120
viveksyngh Главы 10 и 19
VJ Magar Глава 31
Vlad Bezden Глава 103
weewooquestionaire Глава 1
WeizhongТu Главы 41 и 149
Wickramaranga Глава 53
Will Главы 5, 15, 16, 21, 30, 33, 91, 116, 117, 121 и 164
Wingston Sharon Глава 155
Wladimir Palant Главы 21, 37 и 197
wnnmaw Главы 41, 43 и 53
Wоlf Глава 149
WombatPM Глава 30
Wombatz Глава 200
wrwrwr Глава 173
wwii Глава 14
wythagoras Глава 9
Xavier Combelle Главы 15, 19, 111 и 120
XCoder Real Глава 47
xgord Главы 14 и 30
XonAether Глава 52
xtreak Главы 67 и 149
Y0da Глава 85
ygram Глава 190
Yogendra Sharma Главы 1 и 117
yuriЬ Глава 64
Zach Janicki Глава 1
Zаgs Глава 1
Zaid Ajaj Глава 67
zarak Глава 149
Zaz Глава 21
zenlc2000 Глава 34
Zhanping Shi Глава 145
zmo Главы 83, 140 и 141
zondo Главы 75 и 83
zopieux Глава 173
zvone Главы 13, 37, 39 и 112
zxxz Глава 33
Zydnar Глава 81
КriSTOJ Глава 117
лuser Главы 67 и 77
Некто Главы 24, 37 и 128

Серия «Программирование от экспертов»
Справочное издание
Анықтамалық басылым

САМОЕ ПОЛНОЕ РУКОВОДСТВО
ПО РАЗРАБОТКЕ НА PYTHON В ПРИМЕРАХ
ОТ СООБЩЕСТВА STACK OVERFLOW
Оформление обложки С. А. Арутюнян
Ответственный за выпуск И. В. Резько
Подписано в печать 12.12.2023.
Изготовлено в 2024 г.
Формат 70х1001/16. Бумага офсетная. Печать офсетная. Гарнитура Noto Serif SemiCondensed.
Усл. печ. л. 51,6. Тираж
экз. Заказ №
Общероссийский классификатор продукции ОК-034-2014 (КПЕС 2008);
58.11.1 — книги, брошюры печатные
Произведено в Российской Федерации
Изготовитель: ООО «Издательство АСТ»
129085, Российская Федерация, г. Москва, Звездный бульвар, дом 21,
строение 1, комната 705, пом. I, этаж 7.
Адрес места осуществления деятельности по изготовлению продукции:
123112, Российская Федерация, г. Москва, Пресненская набережная, д. 6, стр. 2,
Деловой комплекс «Империя», 14, 15 этаж.
Наш электронный адрес: ask@ast.ru
Наш сайт: www.ast.ru Интернет-магазин: www.book24.ru
Өндіруші: «Издательство АСТ» ЖШҚ
129085, Ресей Федерациясы, Звёздный бульвары, 21-үй, 1-құрылыс, 705-бөлме, I үй-жай, 7-қабат.
Өнім өндіру қызметін жүзеге асыру мекенжайы:
123112, Ресей Федерациясы, Мәскеу, Пресненская жағ., 6-үй, 2-құр.,
«Империя» іскерлік кешені, 14, 15-қабат
Біздің электрондық мекенжайымыз: www.ast.ru E-mail: ask@ast.ru
Интернет-магазин: www.book24.kz
Интернет-дукен: www.book24.kz
Импортер в Республику Казахстан и Представитель по приему претензий
в Республике Казахстан — ТОО РДЦ Алматы, г. Алматы.
Қазақстан Республикасына импорттаушы және Қазақстан Республикасында
наразылықтарды қабылдау бойынша өкiл — «РДЦ-Алматы» ЖШС,
Алматы қ., Домбровский көш., 3«а», Б литерi офис 1.
Тел.: 8(727) 2 51 59 90,91, факс: 8 (727) 251 59 92 iшкi 107;
E-mail: RDC-Almaty@eksmo.kz, www.book24.kz
Тауар белгici: «АСТ» Өндiрiлген жылы: 2024
Өнімнің жарамдылық мерзімі шектелмеген
Ресей Федерациясында өндірілген

Многие специалисты едины во мнении о том, что один из самых
мощных и «дружелюбных» языков программирования - Python язык будущего.
В этой книге вы найдете множество практических примеров ко­
дов на этом языке, написанных ведущими разработчиками про­
граммного обеспечения со всего мира. Все программы сопрово­
ждаются подробными комментариями технических специалистов
сообщества Stack Overflow, изящно и профессионально решающих
самые сложные задачи при создании ПО.
Здесь представлен мощный и универсальный инструментарий
для профессиональной работы на Python в самых разных областях
применения: при сетевом программировании, визуализации дан­
ных, многопоточности и многопроцессорности, программировании
«интернета вещей» и многих других. Отдельное внимание уделено
повышению скорости работы Руthоп-кода и оптимизированию его
производительности.

11

рекомендовано
Библиотекой программиста

1< н и г и д л я л ю б о г о н а с т р о е н и я з д е с ь

ISBN 978-5-17-160252- 9

www.ast.ru I www.book24.ru

а vk.com/izdatelstvoast
О ok.ru/izdatelstvoast
ИЗДАТЕЛЬСКАЯ ГРУППА АСТ

9 7 8 5 1 7 1 602529