КулЛиб - Классная библиотека! Скачать книги бесплатно
Всего книг - 712479 томов
Объем библиотеки - 1400 Гб.
Всего авторов - 274473
Пользователей - 125061

Новое на форуме

Новое в блогах

Впечатления

Влад и мир про Владимиров: Ирландец 2 (Альтернативная история)

Написано хорошо. Но сама тема не моя. Становление мафиози! Не люблю ворьё. Вор на воре сидит и вором погоняет и о ворах книжки сочиняет! Любой вор всегда себя считает жертвой обстоятельств, мол не сам, а жизнь такая! А жизнь кругом такая, потому, что сам ты такой! С арифметикой у автора тоже всё печально, как и у ГГ. Простая задачка. Есть игроки, сдающие определённую сумму для участия в игре и получающие определённое количество фишек. Если в

  подробнее ...

Рейтинг: 0 ( 0 за, 0 против).
DXBCKT про Дамиров: Курсант: Назад в СССР (Детективная фантастика)

Месяца 3-4 назад прочел (а вернее прослушал в аудиоверсии) данную книгу - а руки (прокомментировать ее) все никак не доходили)) Ну а вот на выходных, появилось время - за сим, я наконец-таки сподобился это сделать))

С одной стороны - казалось бы вполне «знакомая и местами изьезженная» тема (чуть не сказал - пластинка)) С другой же, именно нюансы порой позволяют отличить очередной «шаблон», от действительно интересной вещи...

В начале

  подробнее ...

Рейтинг: +1 ( 1 за, 0 против).
DXBCKT про Стариков: Геополитика: Как это делается (Политика и дипломатия)

Вообще-то если честно, то я даже не собирался брать эту книгу... Однако - отсутствие иного выбора и низкая цена (после 3 или 4-го захода в книжный) все таки "сделали свое черное дело" и книга была куплена))

Не собирался же ее брать изначально поскольку (давным давно до этого) после прочтения одной "явно неудавшейся" книги автора, навсегда зарекся это делать... Но потом до меня все-таки дошло что (это все же) не "очередная злободневная" (читай

  подробнее ...

Рейтинг: +1 ( 1 за, 0 против).
DXBCKT про Москаленко: Малой. Книга 3 (Боевая фантастика)

Третья часть делает еще более явный уклон в экзотерику и несмотря на все стсндартные шаблоны Eve-вселенной (базы знаний, нейросети и прочие девайсы) все сводится к очередной "ступени самосознания" и общения "в Астралях")) А уж почти каждодневные "глюки-подключения-беседы" с "проснувшейся планетой" (в виде галлюцинации - в образе симпатичной девчонки) так и вообще...))

В общем герою (лишь формально вникающему в разные железки и нейросети)

  подробнее ...

Рейтинг: +1 ( 1 за, 0 против).
Влад и мир про Черепанов: Собиратель 4 (Боевая фантастика)

В принципе хорошая РПГ. Читается хорошо.Есть много нелогичности в механике условий, заданных самим же автором. Ну например: Зачем наделять мечи с поглощением душ и забыть об этом. Как у игрока вообще можно отнять душу, если после перерождении он снова с душой в своём теле игрока. Я так и не понял как ГГ не набирал опыта занимаясь ремеслом, особенно когда служба якобы только за репутацию закончилась и групповое перераспределение опыта

  подробнее ...

Рейтинг: 0 ( 0 за, 0 против).

Рython. Полное руководство [Д. М. Кольцов] (pdf) читать онлайн

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


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

Кольцов Д. М.

PYTHON

ПОЛНОЕ РУКОВОДСТВО

"Издательство Наука и Техника"
Санкт-Петербург

УДК 004.42
ББК 32.973
Кольцов Д. М.
РУтноN. ПОЛНОЕ РУКОВОДСТВО. -

СПБ.:

ИЗДАТЕЛЬСТВО НАУКА и ТЕХНИКА,

2022.

-480с., ил.

ISBN 978-5-94387-270-9
Эта книга поможет вам освоить язык программирования Python
практически с нуля, поэтапно, от простого к сложному. Первая часть
книги посвящена базовым основам языка: переменные и типы данных,
операторы, циклы и условные операторы, математические функции,
кортежи, множества и словари, итераторы и генераторы, модули и
пакеты, а также многое другое.
Во второй части книги перейдем к более сложным вещам
в
программирование,
объектно-ориентированное
Python:
метапрограммирование, многопоточность и масштабирование.
Отдельное внимание будет уделено документированию своего
проекта в Python, контролю и оптимизации кода. Теоретическая часть
книги сопровождается практическими примерами, позволяющими на
практике осваивать полученные теоретические знания.
Книга будет полезна как начинающим, так и тем, кто хочет улучшить
свои навыки программирования на Python.

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

1

ISBN 978-5-94387-270-9

9

8- 5- 943 7- 270- 9

Контакrные телефоны издательства:
(812)412 7026
Официальный сайт: www.nit.com.ru
© Кольцов Д. М.
© Издательство Наука и Техника (оригинал-макет)

СОДЕРЖАНИЕ
ВВЕДЕНИЕ.......................................................... 11
ГЛАВА 1. ОСНОВЫ. ПЕРВАЯ ПРОГРАММА......•.......•.. 17
1.1 . О ВЕРСИИ PVТHON ................................................................... 18
1.2. УСТАНОВКА PVТHON 3 .............................................................. 19
1.3. ПЕРВАЯ ПРОГРАММА НА PVТHON ............................................... 22
1.4. ПОДРОБНО О IDLE •..........................................................••.•.••• 24
1.4.1. Подсказки при вводе кода ................................................ 24
1.4.2. Подсветка синтаксиса ...................................................... 25
1.4.3. Изменение цветовой темы................................................ 25
1.4.4. Горячие клавиши ............................................................... 27
1.5. ПОМЕЩЕНИЕ ПРОГРАММЫ В ОТДЕЛЬНЫЙ ФАЙЛ.
КОДИРОВКА ТЕКСТА ..............•.....•...........•.•....••...••.....•....•.• 30
1.6. СТРУКТУРА ПРОГРАММЫ ..................................•....•.•...•.•....••..•. 31
1.7. КОММЕНТАРИИ .•..•.....•..••.•....•.....••.•.••.••.•.••..••.•.•..•..•..•....••.•.•.• 34
1.8. ВВОд/ВЫВОД ДАННЫХ ...•.••.•..•...•.•.•••••.•.•..•...•....•..•..............••. 36
1.9. ЧТЕНИЕ ПАРАМЕТРОВ КОМАНДНОЙ СТРОКИ ...........•.................... 38

ГЛАВА 2. ПЕРЕМЕННЫЕ И ТИПЫ ДАННЫХ ..••....••...•.. 41
2.1. ИМЕНА ПЕРЕМЕННЫХ .••..••.•.••.•..•..••••..•...••.•...•.•..•.....•.......•....•. 43
2.2. ТИПЫ ДАННЫХ ............................................................•..•.•....•. 48
2.3. ПРИСВАИВАНИЕ ЗНАЧЕНИЙ ...•.•..•.....••....•.•...•.•....••................... 51
2.4. ПРОВЕРКА ТИПА ДАННЫХ И ПРИВЕДЕНИЕ ТИПОВ ......................... 54
2.5. УДАЛЕНИЕ ПЕРЕМЕННОЙ ........................................••.•.•••.•..•.•..• 57

ГЛАВА З. ОПЕРАТОРЫ ......•.......••...•...•...•.•............• 59
3.1. МАТЕМАТИЧЕСКИЕ ОПЕРАТОРЫ И РАБОТА С ЧИСЛАМИ ..••.••••••..•.•.. 60
3.2. ОПЕРАТОРЫ ДЛЯ РАБОТЫ С ПОСЛЕДОВАТЕЛЬНОСТЯМИ ............... 66



3.3. ОПЕРАТОРЫ ПРИСВАИВАНИЯ •.••..•.....•....................................... 67

.....................................................................................

Python. Полное руководство

__________________________ jJw python

3.4. ДВОИЧНЫЕ ОПЕРАТОРЫ ........................................................... 68
3.5. ПРИОРИТЕТ ВЫПОЛНЕНИЯ ОПЕРАТОРОВ .................................... 69
3.6. ПРОСТЕЙШИЙ КАЛЬКУЛЯТОР .................................................... 70

ГЛАВА 4. ЦИКЛЫ И УСЛОВНЫЕ ОПЕРАТОРЫ ............ 73
4.1. УСЛОВНЫЕ ОПЕРАТОРЫ ........................................................... 74
4.1.1. Логические значения ................. :......................................74
4.1.2. Операторы сравнения ......................................................75
4.1.3. Оператор if..else ...............................................................77
4.1.4. Блоки кода и отступы ........................................................ 80
4.2. циклы .................................................................................. 81
4.2.1. Цикл for ............................................................................ 81
4.2.2. Цикл while ................................................ :........................ 84
4.2.3. Операторы break и continue .............................................. 85
4.2.4. Функция range() ................................................................ 86
4.3. БЕСКОНЕЧНЫЕ ЦИКЛЫ............................................................. 88
4.3.1. Бесконечный цикл по ошибке ........................................... 88
4.3.2. Намеренный бесконечный цикл ........................................ 91
4.4. ИСТИННЫЕ И ЛОЖНЫЕ ЗНАЧЕНИЯ ............................................. 93
4.5. ПРАКТИЧЕСКИЙ ПРИМЕР. ПРОГРАММА УРОВЕНЬ ДОСТУПА ............ 93

ГЛАВА 5. МАТЕМАТИЧЕСКИЕ ФУНКЦИИ ................... 97
5.1. ПОДДЕРЖИВАЕМЫЕ ТИПЫ ЧИСЕЛ ............................................. 98
5.2. ЧИСЛОВЫЕ ФУНКЦИИ ............................................................ 101
5.2.1. Округление числовых значений ...................................... 103
5.2.2. Форматирование чисел для вывода ................................ 104
5.3. МАТЕМАТИЧЕСКИЕ ФУНКЦИИ .................................................. 105
5.4. СЛУЧАЙНЫЕ ЧИСЛА. МОДУЛЬ RANDOM ..................................... 107
5.5. ЗНАЧЕНИЯ INFINIТY И NAN ....................................................... 110

...

5.6. ВЫЧИСЛЕНИЯ С БОЛЬШИМИ ЧИСЛОВЫМИ МАССИВАМИ.
БИБЛИОТЕКА NUMPY ........................................................ 110
.

.. .. .. .. . .. . . .. - .. -... ... - ... ·--- .. .......................... - --.---........... -·

lf!:�, python

Содержание

5.7. ПРОГРАММА "УГАДАЙ число·· ................................................. 112

ГЛАВА 6. СТРОКИ И СТРОКОВЫЕ ФУНКЦИИ ............ 117
6.1. ЧТО ТАКОЕ СТРОКА? ВЫБОР КАВЫЧЕК ...................................... 118
6.2. СОЗДАНИЕ СТРОКИ ............................................................... 121
6.3. ТРОЙНЫЕ КАВЫЧКИ............................................................... 123
6.4. СПЕ ЦИАЛЬНЫЕ СИМВОЛЫ...................................................... 124
6.5. ДЕЙСТВИЯ НАД СТРОКАМИ ..................................................... 125
6.5.1. Обращение к элементу по индексу ................................. 125
6.5.2. Срез строки .................................................................... 126
6.5.3. Конкатенация строк ........................................................ 126
6.5.4. Проверка на вхождение .................................................. 127
6.5.5. Повтор ............................................................................ 127
6.5.6. Функция lеп() .................................................................. 127
6.6. ФОРМАТИРОВАНИЕ СТРОКИ И МЕТОД FORMAT() ......................... 128
6.6.1. Оператор форматирования%......................................... 128
6.6.2. Методы выравнивания строки ........................................ 132
6.6.3. Метод format() ............................................................... 132
6.7. ФУНКЦИИ И МЕТОДЫ ДЛЯ РАБОТЫ СО СТРОКАМИ ...................... 135
6.8. НАСТРОЙКА ЛОКАЛИ.............................................................. 141
6.9. ПОИСК И ЗАМЕНА В СТРОКЕ .................................................... 141
6.10. ЧТО В СТРОКЕ? .................................................................... 143
6.11. ШИФРОВАНИЕ СТРОК........................................................... 144
6.12. ПЕРЕФОРМАТИРОВАНИЕ ТЕКСТА.
ФИКСИРОВАННОЕ ЧИСЛО КОЛОНОК..•........•..•...........•....•... 144

ГЛАВА 7. РЕГУЛЯРНЫЕ ВЫРАЖЕНИЯ ..................... 147
7.1. ВВЕДЕНИЕ В РЕГУЛЯРНЫЕ ВЫРАЖЕНИЯ .......•.....•.•.......•......•..•. 148
7.2. ФУНКЦИЯ COMPILE() И ОСНОВЫ РЕГУЛЯРНЫХ ВЫРАЖЕНИЙ ........ 149
7.3. МЕТОДЫ МАТСН() И SEARCH() .............................•.......•...........• 154
•4. МЕТОД FINDALL() ............•....•.....•..•.....•..•............................... 156


... - ·-·-········-·-· ------ --· -·-· ... .. ........ ·-···....·-·........................ ...

Python. Полное руководство

_______________ ................

ilь1python
· '.�

7.5.METOДSUB() .................••..•.••.•..•.••.•••••.••.••.••.•..•..••...•......••••. 156
7.6. РАЗЛИЧНЫЕ ПРАКТИЧЕСКИЕ ПРИМЕРЫ •.•..•.••.......•........•.•........ 158

ГЛАВА 8. СПИСКИ .............................................. 170
8.1. ЧТО ТАКОЕ СПИСОК? ..............•..•.........•.....•..•.•..•••.••.••.....•.••.•• 171
8.2. ОПЕРАЦИИ НАД СПИСКАМИ ......•..•..•...•....•.........•.•.....•......•••..• 173
8.3. МНОГОМЕРНЫЕ СПИСКИ .............................•.......................... 175
8.4. ПРОХОД ПО ЭЛЕМЕНТАМ СПИСКА ........•.....•..........•...•.•............ 176
8.5. ПОИСК ЭЛЕМЕНТА В СПИСКЕ ................................................... 177
8.6. ДОБАВЛЕНИЕ И УДАЛЕНИЕ ЭЛЕМЕНТОВ В СПИСКЕ ..................... 178
8.7. ПЕРЕМЕШИВАНИЕ ЭЛЕМЕНТОВ И ВЫБОР СЛУЧАЙНОГО ЭЛЕМЕНТА180
8.8. СОРТИРОВКА СПИСКА ............................................................ 180
8.9. ПРЕОБРАЗОВАНИЕ СПИСКА В СТРОКУ ...................................... 181
8.1О. ВЫЧИСЛЕНИSI С БОЛЬШИМИ ЧИСЛОВЫМИ МАССИВАМИ .•....•.•.. 182
8.11. ПРОГРАММА "ГАРАЖ •............................................................ 185

ГЛАВА 9. КОРТЕЖИ ............................................ 188
9.1. ПОНSIТИЕ КОРТЕЖА •...........•.•..•............................................. 189
9.2. СОЗДДНИЕ КОРТЕЖЕЙ ........................................................... 190
9.3. МЕТОДЫ КОРТЕЖЕЙ .............................................................. 191
9.4. ПЕРЕБОР ЭЛЕМЕНТОВ КОРТЕЖА.........................•.•..•..•............ 192
9.5. КОРТЕЖ КАК УСЛОВИЕ ..........................................•.•..••.•........• 192
9.6. ФУНКЦИSI L EN() И ОПЕРАТОР IN ................................................ 192
9. 7. НЕИЗМЕННОСТЬ КОРТЕЖЕЙ И CЛИSIHИSI ....................•.••.•........• 193
9.8. МОДУЛЬ ITERTOOLS............................................................... 193
9.9. РАСПАКОВКА КОРТЕЖА В ОТДЕЛЬНЫЕ ПЕРЕМЕННЫЕ.................. 195
9.10. СПИСКИ VS КОРТЕЖИ ........................................................... 200

....

.



... .... .... ...... .......... - -- --- ---- ··- - -- --· .....-· ........................... .

Содержание
ГЛАВА 1О. МНОЖЕСТВА И СЛОВАРИ ..................... 202
10.1. ПОНЯТИЕ СЛОВАРЯ ...... , ....................................................... 203
10.2. РАЗЛИЧНЫЕ ОПЕРАЦИИ НАД СЛОВАРЯМИ ............................... 206
10.2.1. Доступ к элементу ........................................................ 206
10.2.2. Добавление и удаление элементов словаря..................206
10.2.3. Перебор элементов словаря ......................................... 207
10.2.4. Сортировка словаря ..................................................... 207
10.2.5. Методы keys(), values() и некоторые другие ..................208
10.2.6. Программа Dict ............................................................. 209
10.3. ПОНЯТИЕ МНОЖЕСТВА ......................................................... 213
10.4. ОПЕРАЦИИ НАД МНОЖЕСТВОМ .............................................. 213
10.5. МЕТОДЫ МНОЖЕСТВ............................................................ 215

ГЛАВА 11. ПОЛЬЗОВАТЕЛЬСКИЕ ФУНКЦИИ............ 217
11.1. ОБЪЯВЛЕНИЕ ФУНКЦИИ ....................................................... 218
11.2. НЕОБЯЗАТЕЛЬНЫЕ ПАРАМЕТРЫ ФУНКЦИИ •..•..••••....••.•..••••.••.•. 220
11.3. ПЕРЕМЕННОЕ ЧИСЛО ПАРАМЕТРОВ •..•....•••..•••.•.•..•..•.•...•..•.•.•. 222
11.4. АНОНИМНЫЕ ФУНКЦИИ ........................................................ 223
11.5. ФУНКЦИИ-ГЕНЕРАТОРЫ .................•.........•.............•.............. 227
11.6. ДЕКОРАТОРЫ ..•..•..•.••..•..•.....................................•.............. 228
11.7. РЕКУРСИЯ ...............•........•............••.••.•..•.....•.••.••••..•..•.••.•.• 228
11.8. ГЛОБАЛЬНЫЕ И ЛОКАЛЬНЫЕ ПЕРЕМЕННЫЕ.....•....................... 229
11.9. ДОКУМЕНТИРОВАНИЕ ФУНКЦИЙ ...•............•.•......••..•.•..••••.••.•. 233
11.10. ВОЗВРАЩАЕМ НЕСКОЛЬКО ЗНАЧЕНИЙ ....••..••.........•.......••..... 233
11.11. ИМЕНОВАННЫЕ АРГУМЕНТЫ...•...•••...............•....••.•.•.•.•....•... 234
11.12. ПРАКТИЧЕСКИЙ ПРИМЕР: ПРОГРАММА ДЛЯ ЧТЕНИЯ RSS-ЛEHTЫ236

ГЛАВА 12. ДАТА И ВРЕМЯ .................................... 238
12.1. ПОЛУЧЕНИЕ ТЕКУЩЕЙ ДАТЫ И ВРЕМЕНИ•.•..•........................... 239

,

12.2. ФОРМАТИРОВАНИЕ ДАТЫ И ВРЕМЕНИ .................................... 241

.............� .................................................................. ..

Python. Полное руководство

-------------------------•python

12.3. МОДУЛЬ CALENDAR•••••...••••....•.•.••••••.••••..••••••••••••••••••••••••••••• 243
12.4. ФУНКЦИЯ SLEEP •••••••••••••••••••••••••••••••••••••••••••.••••••••••••.••••••••• 244
12.5. ИЗМЕРЕНИЕ ВРЕМЕНИ ВЫПОЛНЕНИЯ ФРАГМЕНТОВ КОДА ••••••••• 244
12.6. МОДУЛЬ DATETIME ...•••••.•••••••••••••••••••••••••••••••••••••••••••••••••••••• 247

ГЛАВА 13. МОДУЛИ И ПАКЕТЫ ..•...•...•.•.•••••••••..••.• 249
13.1. ПОНЯТИЕ МОДУЛЯ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• 250
13.2. ИНСТРУКЦИЯ IMPORT •.••••••••••••••••••••••••••••••••••••••••••••••••••••••••• 250
13.3. ИНСТРУКЦИЯ FROM .••....•••.•.•..•..•.•••••.••••••••••••••••••••••••••••••••••• 252
13.4. ПУТЬ ПОИСКА МОДУЛЕЙ ••••••••••••••••••••••••••••••••••••••••••••••••••••••• 253
13.5. ПОВТОРНАЯ ЗАГРУЗКА МОДУЛЕЙ••••••••••••••.•••••.•••••.•••••••••••••••• 254
13.6. ЕGG-ФАЙЛЫ •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••·•··- 254
13.7. РАЗДЕЛЕНИЕ МОДУЛЯ НА НЕСКОЛЬКО ФАЙЛОВ............... , ....... 255
13.8. СОЗДАНИЕ ОТДЕЛЬНЫХ КАТАЛОГОВ ИМПОРТА КОДА ПОД ОБЩИМ
ПРОСТРАНСТВОМ ИМЕН•••••••••••••••••••••••••••••••••••••••••••••••••••• 257
13.9. ПЕРЕЗАГРУЗКА МОДУЛЕЙ .••.•..••.••••••••••••••••••••••••••••�•••••••••••••• 260
13.1О. СОЗДАНИЕ КАТАЛОГА ИЛИ ZIP-APXИBA, ВЫПОЛНЯЕМОГО
КАК ГЛАВНЫЙ СЦЕНАРИЙ ..••.....••••••••••••••••••••••••••••••••••••••••• 262
13.11. ДОБАВЛЕНИЕ КАТАЛОГОВ В S YS.PATH ••••••••••••••••••••••••••••••••••• 263
13.12. РАСПРОСТРАНЕНИЕ ПАКЕТОВ •••••••••.•••••.•.•••••••••••••••••••••••••••• 264

ГЛАВА 14. ОБРАБОТКА ИСКЛЮЧЕНИЙ ..••.•••••••••••••• 267
14.1. ЧТО ТАКОЕ ИСКЛЮЧЕНИЕ? •..••••••••••••• � •••••••••••••••••••••••••.••••••••• 268
14.2. ТИПЫ ИСКЛЮЧЕНИЙ •••••••••••••••••••••••..••••••••••••••••••••••••••••••••••• 25g
14.3. ИНСТРУКЦИЯ TRY••EXCEPT••ELSE •• FINALLY ··················�·········•·· 274
14.4. ИНСТРУКЦИЯ WITH •• AS ••••••••••••••••••••••••••••••••••• : •••••••••••••••••••• 276
14.5. ГЕНЕРИРОВАНИЕ ИСКЛЮЧЕНИЙ ••••••••••••••••••••••••••••••••••• ; •••••••• 277

ГЛАВА 15. ФАЙЛОВЫЙ ВВОд/ВЫВОД ................... 279
15.1. РАБОТА С ФАЙЛАМИ ••••••••••••••••••••••••••••••••••••••••••.•••••••••••••••••• 28
«.
.....................................................................................

rJIJpython
---------.. ·---------.. -·
ww,

Содержание

15.1.1. Открытие файла ........................................................... 280
15.1.2. Методы для работы с файлами ..................................... 283
15.1.3. Функции для манипулирования файлами ...................... 286
15.2. РАБОТА С КАТАЛОГАМИ ......................................................... 289
15.3. РАБОТА С ФАЙЛАМИ В РАЗНЫХ ФОРМАТАХ .............................. 291
15.3.1. Работа с CSV................................................................. 291
15.3.2. Чтение и запись JSОN-данных ...................................... 293
15.3.3. ПарсингХМL-файлов .................................................... 294
15.3.4. Преобразование словаря вХМL .................................... 296
15.3.5. Модификация и перезаписьХМL-кода .......................... 299
15.3.6. Декодирование и кодирование
шестнадцатеричных чисел ....................................................... 301
15.3.7. Кодирование/декодирование Base64 ........................... 302

ГЛАВА 16. ООП И РУТНОN ................................... 303
16.1. ОСНОВЫ ОБЪЕКТНО-ОРИЕНТИРОВАННОГО
ПРОГРАММИРОВАНИЯ ...................................................... 304
16.2. ОПРЕДЕЛЕНИЕ КЛАССА И СОЗДАНИЕ ОБЪЕКТА .•.•.........•..•.••.••• 307
16.3. КОНСТРУКТОР И ДЕСТРУКТОР•.•.••.•.•..•....•.•....•.......•.••....•......• 308
16.4. НАСЛЕДОВАНИЕ ..•.•.••.•..•.•..•..............•.•..•....•..............•....•••. 309
16.5. СПЕЦИАЛЬНЫЕ МЕТОДЫ •.•.•.........................•.•.•.......•..•........ 310
16.6. СТАТИЧЕСКИЕ МЕТОДЫ •••••••••.••••••••.•••••••.••..•••••••••••••••.••••••••• 313
16.7. АБСТРАКТНЫЕ МЕТОДЫ ........................................................ 314
16.8. ПЕРЕГРУЗКА ОПЕРАТОРОВ .................................................... 314
16.9. СВОЙСТВА КЛАССА .............................................................. 317
16.10. ДЕКОРАТОРЫ КЛАССА ........................................................ 317

ГЛАВА 17. РАБОТА С ИНТЕРНЕТОМ ....................... 319
17.1. РАЗБИРАЕМ URL-AДPECA...................................................... 320
17.2. ДЕКОДИРОВАНИЕ СТРОКИ ЗАПРОСА ...................................... 322
17.3. РАЗБОР НТМL-ЭКВИВАЛЕНТОВ .............................................. 323

. . ...

17.4. ПРЕОБРАЗОВАНИЕ ОТНОСИТЕЛЬНЫХ ССЫЛОК ..........•...•.••.•.••. 324

,_

.. -........ -............ -. -. -...... -. -. -.... -. -. -- -. - - . -· ---- -- -- -- . - -- - - -

17.5. ОПРЕДЕЛЕНИЕ КОДИРОВКИ ......................•......................•.... 325
17.6. РЕАЛИЗАЦИЯ НПР-КЛИЕНТА ................................................. 326

ГЛАВА 18. ИТЕРАТОРЫ И ГЕНЕРАТОРЫ .................. 328
18.1. РУЧНОЕ ИСПОЛЬЗОВАНИЕ ИТЕРАТОРА •................................... 329
18.2. ДЕЛЕГИРОВАНИЕ ИТЕРАЦИИ ................................................. 331
18.3. СОЗДАНИЕ НОВОГО ШАБЛОНА ИТЕРАЦИИ
С ПОМОЩЬЮ ГЕНЕРАТОРОВ ...........•.................................• 332
18.4. РЕАЛИЗАЦИЯ ПРОТОКОЛА ИТЕРАТОРА .................................... 334
18.5. ИТЕРАЦИЯ В ОБРАТНОМ НАПРАВЛЕНИИ .................................. 336
18.6. ЭКСТРА-СОСТОЯНИЕ ФУНКЦИИ-ГЕНЕРАТОРА ........................... 338
18.7. ПРОПУСК ПЕРВОЙ ЧАСТИ ИТЕРИРУЕМОГО ......................•......•. 339
18.8. ИТЕРИРОВАНИЕ ПО ВСЕМ ВОЗМОЖНЫМ КОМБИНАЦИЯМ
ИЛИ ПЕРЕСТАНОВКАМ ...................................................... 341

ГЛАВА 19. ДОКУМЕНТИРОВАНИЕ ПРОЕКТА ............ 344
19.1. РЕКОМЕНДАЦИИ ОТНОСИТЕЛЬНО НАПИСАНИЯ
ТЕХНИЧЕСКОЙ ДОКУМЕНТАЦИИ ......................................... 346
19.2. СТРОКИ ДОКУМЕНТАЦИИ В РУТНОN .............•.......................... 350
19.3. ЯЗЫКИ РАЗМЕТКИ ДЛЯ ДОКУМЕНТАЦИИ ................................. 352
19.4. ПОПУЛЯРНЫЕ ГЕНЕРАТОРЫ ДОКУМЕНТАЦИИ ........................... 352
19.4.1. Использование Sphinx .................................................. 353
19.4.2. Использование MkDocs ................................................ 357

ГЛАВА 20. МЕТАПРОГРА ММИРОВАНИЕ ......•......•.... 362
20.1. ВВЕДЕНИЕ В МЕТАПРОГРАММИРОВАНИЕ ................................ 363
20.2. ДЕКОРАТОРЫ ...................................................................... 364
20.3. МЕТАКЛАССЫ .••.......•.........•...............•.........................•....... 368
20.3.1. Введение в метаклассы ................................................ 368
20.3.2. Пользовательские метаклассы ..................................... 371
20.3.3. Использование метаклассов вместо функций ............... 374

�дpython

Содержание

20.4. ГЕНЕРАЦИЯ КОДА .........••.••....•.•......•.•..•.•......•...................... 375

ГЛАВА 21. КОНТРОЛЬ КОДА ..................- .............. 386
21.1. ВВЕДЕНИЕ В СИСТЕМЫ КОНТРОЛЯ ВЕРСИЯМИ ........................ 387
21.2. ЗНАКОМСТВО С GIT .................................•......•.•.•..•..••.•......•. 393
21.3. УСТАНОВКА GIT .••.•.....•..•...•.........................•...•.•.••.••..•••..•.•. 396
21.4. ОСНОВЫ РАБОТЫ С GIT ..••..•.•.•..•.•...•...•.......................•.....•.. 397
21.5. ОСНОВНЫЕ ПОНЯТИЯ GIT .•.......•.•.•......•....•..•...•.•.••.......•........ 412
21.6. УПРАВЛЕНИЕ ФАЙЛАМИ ...................•..........•..•..•.•..•.....•.•..... 429

ГЛАВА 22. ОПТИМИЗАЦИЯ КОДА РУТНОN .............. 446
22.1. ПРОФИЛИРОВАНИЕ КОДА С ПОМОЩЬЮ CPROFILE .•.••.•.••..•.•..•.• 447
22.2. ПРАКТИЧЕСКИЙ ПРИМЕР: ВЫЧИСЛЕНИЕ СКОРОСТИ
ЗАГРУЗКИ САЙТА .................•.•...•..................................... 451
22.3. СОБЫТИЙНЫЕ ПРОФАЙЛЕРЫ .............................•.......•.......... 452
22.4. РУЧНОЕ ПРОФИЛИРОВАНИЕ .............•....•.•.•....•.•....•.....••...••.•. 453

ГЛАВА 23. МНОГОЗАДАЧНОСТЬ В РУТНОN ............. 455
23.1. ЕСТЬ ЛИ НЕОБХОДИМОСТЬ В МНОГОЗАДАЧНОСТИ? .................. 456
23.2.

многопоточность ............................................................ 458
23.2.1. Введение в многопоточность ........................................ 458
23.2.2. Кейсы, подходящие
для использования многопоточности ...................................... .462

23.3. ПРАКТИКА: СОЗДАНИЕ МНОГОПОТОЧНОГО ПРИЛОЖЕНИЯ ......... 464
23.3.1. Модуль thread ............................................................... 464
23.3.2. Модуль threading ........................................................... 466
23.3.3. Синхронизация потоков ................................................ 468
23.3.4. Многопоточная приоритетная очередь ......................... 470
23.4. ПРАКТИЧЕСКИЙ ПРИМЕР: МНОГОПОТОКОВЫЙ СЕТЕВОЙ СЕРВЕР 472



··················································································-4118

Python. Полное руководство

_________________________• python

ВВЕДЕНИЕ

Вкратце о Python
Python - одновременно мощный и простой язык программирования, раз­
работанный Гвидо ванн Россумом (Guido van Rossum) в 1991 году. С одной
стороны, язык довольно молодой (тот же С был разработан в 1972 году), с
другой стороны, ему уже 30 лет и за это время его успели довести до того
уровня, когда на нем можно написать проект любого масштаба, в том числе
коммерческие приложения, работающие с очень важными данными.
Python - это высокоуровневый язык программирования общего назна­
чения, ориентированный на повышение производительности разработчика
и читаемости кода. Другими словами, писать программы на Python доволь­
но просто, а код самих программ будет гораздо лучше восприниматься, чем
в случае с другими языками программирования.
Синтаксис ядра этого языка программирования минималистичен, однако,
стандартная библиотека содержит множество полезных функций.
Тех вольностей, которые себе программист мог позволить в других языках
программирования, например, в Pascal, С, Java или РНР, здесь непозволи­
тельны. Один лишний или недостающий отступ - и вы получите синтакси­
ческую ошибку. Так Python приучает программиста писать красивый и чи­
табельный код. С одной стороны, если я бы не советовал выбирать Python
в качестве первого языка программирования - вам будет сложно. С другой



--------------·······-····-·-·------·-·······················-···-··-·-···-·----·-·

IJ!@python
.

--------------

Введение

стороны, это как учиться ездить на автомобиле зимой в гололед. После та­
кой школы летом вы уж точно будете ездить - с легкостью сможете освоить
другие языки программирования.
Python поддерживает несколько парадигм программирования, в том числе
структурное, объектно-ориентированное, функциональное, императивное
и аспектно-ориентированное. Это означает, что вьi можете выбрать любой
стиль программирования. Новичкам, создающим относительно несложные
программы, подойдет функциональный стиль. А для серьезных проектов,
как правило, выбирают объектно-ориентированное и/или аспектно-ори­
ентированное программирование.

Кросс-платформенность
Python портирован и работает почти на всех известных платформах - от
карманных компьютеров и смартфонов до мейнфреймов. Существуют вер­
сии Python под Microsoft Windows, практически все варианты UNIX ( вклю­
чая FreeBSD и Linux), Plan 9, macOS и macOS Х, iPhone OS 2.0 и выше,
Palm OS, OS/2, Amiga, HaikuOS, AS/400 и даже OS/390, Windows Moblle,
Symblan и Android.
Важно понимать, что программы, написанные на Python, независимы от
платформы. Другими словами, вы можете написать программу, которая бу­
дет работать и на вашем macOS Х и на Windоws-компьютере приятеля и
даже на iPhone. Главное, чтобы на устройстве, на котором планируется за­
пускать программу, был установлен Python.

Python - один из самых простых
языков программирования
Когда-то давно программы писались с помощью переключения различных
разъемов на компьютерах, которые занимали целые залы. Затем появились
перфокарты - специальные карточки, используемые для ввода программы,
после - язык программирования Ассемблер, позволяющий манипулиро­
вать данными прямо в регистрах процессора. Это пример низкоуровнего
языка. Даже самая простая программа на этом языке представляла доволь­
но большой, сложный и непонятный непосвященному человеку кусок кода.
С появлением высокоуровневых языков, таких как С, J ava все изменилось.
Такие языки программирования ближе к человеческому языку, чем к ма­
шинному. Python делает синтаксис еще проще. Его правила приближают-

'--... -·..--..........---.................................... -. --..-....-... -... ---4111

Pythor1. Полное руководство

...........• python

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

Популярность Python
Если вы раньше ничего не слышали о Python и думаете, что он н:епопулярен,
то вы ошибаетесь. Его используют разработчики со всего мира, ним поль­
зуются крупнейшие корпорации, такие как Google, NASA, Red Hat, Yahoo!,
Xerox, IBM, Microsoft и др. Так, Google предпочитает C++,Java и Python1 , а
Microsoft даже открыла Python Developer Center2
Популярные программные продукты Yahoo, в том числе Django3, TurboGe­
ars4 и Zope5 написаны на Python.
Некоторым начинающим программистам, которые только выбирают язык
программирования, программы на Python могут показаться неказистыми.
Порой кажется, что ничего серьезного на этом языке не разработаешь, и он
больше приближен к сценариям оболочки, чем к полноценным языкам
программирования, таким как С#.
Это заблуждение. Многие популярные игры от ЕА Games, 2К Games и
Disney Interactive, написаны на Python:

https.//gamedev.stackexchange.com/questions/5035/Jamous-games-written-in­
python
Игра - это одно из самых сложных приложений, поскольку оно сочетает
работу с графикой, музыкой, сложную логику и т.д.

Сколько это стоит?
Интерпретатор Python абсолютно бесплатен и распространяется свободно.
Чтобы использовать его, вам не нужно ничего платить, но зато вы можете
абсолютно свободно продавать свои программы, написанные на Python это позволяет лицензия, по которой распространяется этот интерпретатор.
1
2

https://www.quora.com/Which-programming-languages-does-Google-use-internally
https:j/azure.microsoft.com/en-us/develop/python/

3
4
5

https://en.wikipedia.org/wiki/Django_(web_framework)
https:j/en.wikipedia.org/wiki/ТurboGears
https://ru.wikipedia.org/wiki/Zope


-----------------------------··----·--··············································

фpython

--------------

Введение

Такие условия лицензии способствуют популярности этого языка програм­
мирования. Ведь для старта вам не нужно ничего и никому платить. Вы мо­
жете использовать Python, как для обучения программированию, так и для
создания коммерческих программ. В отличие от других языков программи­
рования, где за использование среды программирования нужно выложить
кругленькую сумму, например, за Visual Studio придется выложить более
500 евро, а стоимость некоторых сторонних компонентов превышает 1500
евро (например, DevExpress).
Понятно, что это останавливает многих программистов-одиночек или
заставляет их использовать пиратские версии. А на Python вы можете
программировать с чистой совестью - все изначально доступно бесплатно.

Философия Python
Чтобы введение не было однотипным и скучным, расскажу вам о филосо­
фии Python. Python - это не простой язык программирования, у него есть
своя философия, разработанная Тимом Петерсом.
Философия выводится один раз за сессию при вводе команды
import this

Результат выполнения этой команды изображен на рис. В.1.


� Python 3.6.S Shell

х

file .Edit She!I .Qebug Qptions :Window .l:!elp

pytbon 3.6.5 (v3.б.5:f59с0932Ь4, Mar 28 2018, 16:07:46) [МSС v.1900 32 Ьit (Inte
l)] on win32
Туре "copyright", "credits" or "license()" for more informatiort.
>>> .i.mpo.r:t this
The zen of Python, Ьу Tim Peters
вeautiful is Ьetter than ugly.
Explicit is Ьetter than iщplicit.
simple is Ьetter than complex.
Complex is Ьetter than complicated.
Flat is Ьetter than nested.
Sparse is Ьetter than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Altbough practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of amЬiguity, refuse the temptation to guess.
There should Ье one-- and preferaЫy only one --obvious way to do it.



Рис. В. 1. Философия Python

·------------------------------------------------------------------------------------

Python. Полное руководство

-----------�1python
_,;:;

Вот текст философии в переводе с английского языка:
Красивое лучше, чем уродливое.
Явное лучше, чем неявное.
Простое лучше, чем сложное.
Сложное лучше, чем запутанное.
Плоское лучше, чем вложенное.
Разреженное лучше, чем плотное.
Читаемость имеет значение.
Особые случаи не настолько особые, чтобы нарушать правила.
При этом практичность важнее безупречности.
Ошибки никогда не должны замалчиваться.
Если не замалчиваются явно.
Встретив двусмысленность, отбрось искушение угадать.
Должен существовать один - и, желательно, только один
очевидный способ сделать это.
Хотя он поначалу может быть и не очевиден, если вы не
голландец.
Сейчас лучше, чем никогда.
Хотя никогда зачастую лучше, чем прямо сейчас.
Если реализацию сложно объяснить - идея плоха.
Если реализацию легко объяснить - идея, возможно, хороша.
Пространства имён - отличная штука! Будем делать их побольше!

Приятного чтения!

...

. . ....... . .. .... ..... . ....... .... ........... ....... ... . . ........... ... ...... ....



..

ГЛАВА 1.
OCHOBbl. ПЕРВАЯ
ПРОГРАММА

Python. Полное руководство

------------------ tJЬpython
""'

1 . 1 . О версии Python
На момент написания этих строк текущей является версия 3.9.2. Однако
параллельно с веткой 3.х доступна и ветка 2. 7 (хотя ее поддержка завершена
в прошлом, 2020-ом, году). Какую версию выбрать - 3.х или 2.7?
В Интернете вы можете часто найти рекомендации бывалых программи­
стов, рекомендующих использовать версии 2.7. Они мотивируют это нали­
чием огромного количества кода, написанного под версию 2.7, своим опы­
том разработки в версии 2. 7 и, конечно же, нежеланием переходить на ветку
3.0 (тогда придется переписать много кода).
Однако посмотрите на дату, когда были написаны эти рекомендации. Ско­
рее всего, это будет 2009 или 2012 год максимум. Первая версия Python 3.0
появилась в декабре 2008 года, с тех пор уже много воды утекло, и большая
часть их рекомендаций уже потеряла свою актуальность.
Если вы - начинающий программист и только хотите начать осваивать
Python, вам следует выбрать самую новую версию - 3.9.
Совсем другое дело, когда вы - уже состоявшийся программист, знаете дру­
гие языки программирования (например, С++, РНР) и вам необходимо
изучить Python, поскольку вам предложили место в компании, которая ве­
дет разработку на этом языке программирования, тогда все зависит от ком­
пании.



411!1--. -........ -. -. -. -.. -. -. -. -.----.-...-. --. -· ..-............-............ -.. -. -. -..



---------------

i6python

Глава 1. Основы. Первая программа

Если компания использует версию 2.7, тогда выбора у вас нет - его за вас
сделала компания, а переписывать весь код на 3.х - нет времени. Подробно
о разнице между версиями 2.7 и 3.0 рассказано здесь:
https:j/wiki.python.org/moin/Python2orPython3ё
Во всех остальных случаях нет смысла говорить о ветке 2.7, так как ее под­
держка завершена и больше нет смысла ее использовать.

1.2. Установка Python 3
Как вы уже догадались, далее будет рассматриваться только ветка 3.х как
самая актуальная на данный момент. Скачать инсталлятор Python можно
по адресу:
https:j/www.python.org/downloads/
Обратите внимание, что Python - это кросс-платформенный язык, и есть
его версии для Linux и MacOS. Далее все иллюстрации будут соответство­
вать Windows 10, поскольку эта ОС используется большинством пользова­
телей в данный момент.

--

· wot�

.'C21·J.N3

lookinq for а s�ciflc rele�e?



Рис. 1. 1. Страница загрузки Python

·----------------------------------------------------------------------·-········--4111

Python. Полное руководство

6python

------------------ щ;,

Скачайте и запустите инсталлятор (файл python-3.9.2amd64.exe). Первым
делом нужно выбрать определиться, что вы хотите сделать - или устано­
вить с установками по умолчанию (кнопка Install Now) или кастомизиро­
вать инсталляцию. Стандартные установки использовать не рекомендуется
хотя бы по той причине, что Python 3.9.2 устанавливается в домашний ката­
лог пользователя, а обычно нужно сократить путь к интерпретатору, поэто­
му лучше выбрать Customize installation, а перед этим включить флажок
Add Python 3.9 to РАТИ, чтобы инсталлятор сам добавил путь к интерпре­
татору в переменную РАТН.

i:» Python 3.9.2 (64-blt) Setup

о

х

lnstall Python 3.9.2 (64-Ьit)
Select lnstall Now to install Python with default settings, or choose
Customtze to еnаЫе or disaЫe features.

➔ lnstalJ Now

C:\Users\Den\AppData\local\Programs\Python\Python39
lncludes IOLE. pip and documentation

➔ Cцstomize instaflation
Choose location and features

python
windows

Jnsta!i Jauncher tor all users (recommended)
G2] [Add Eython 3.9 to РдПiJ

Рис. 1.2. Тип установки

Далее нужно выбрать, какие компоненты Python нужно установить (рис.
1.3). Как правило, здесь нужно просто нажать кнопку Next.

'1о Python 3.9.2 (64-Ьit) Setup

х

Optional Features

0 !Qocumentation!

!nstalls the Python documentation file.

0oip

Jr1stnBs pip, which сап down!oad and iristaH oth€-r Python packages..

G2] td/!k and JDLE
!nstalls tkinter эnd the !DLE development envircnment
G2] Python 1est suite
t,,stзlls the stai,dard Пbr.ary test suite.

i1

for oiJ users (requires elevaticn)
ру !auncher
Use ?rograms and feзtures to remove the 'ру' launcher.

Рис. 1.3. Выбор компонентов Python



------·········································-----·-······---------·-·······-·---·

6'3 python_______________

Глава 1. Основы. Первая программа

Затем нужно выбрать каталог для установки (рис. 1.4). Как уже было отме­
чено, по умолчанию Python устанавливается в домашний каталог пользова­
теля, вызвавшего установку, но лучше установить его в корневой каталог
(например, в C:\Python) - там вам будет удобнее работать с интерпретато­
ром.
х

� Python 3.92 (64-Ьit) Setup

Advanced Options

О lnstall for 211 users

0 Associate jiles with Python (requires the ру launcher)

� Create shortcuts for installed applications
� Add Python to gnvironment variaЫes

О frecompile standard Пbrary
О Download debugging wroЬols
О Download debug Ьinaries (requires VS 2017 or later)
Customize install location

python

! [ok�
�-------------�
You wi!I req;,.1ire wлte pennis.sions fur the- se!ected !ocation.

j E:\Python39

windows
Рис. 1.4. Выбор каталога для установки

При выборе каталога для установки есть возможность выбрать различные
опции вроде ассоциации файлов .ру с Python, добавления Python в пере­
менные окружения и т.д. Установите опции по своему усмотрению.
После нажатия кнопки Install начнется процесс установки Pythoo и нуж­
но будет немного подождать. По окончанию этого процесса нужно нажать
кнопку Close (рис. 1.5).
х

\;i, Python 3.9.2 (64-Ьit) Setup

Setup was successful
New to Python? Start with the о..�6.!!! and
dccumeш:ation. At your termina� type ·ру· to launch Python,
or search for Python in your Start menu.
See � in this release. or find more info aЬout �
python оп Windows.

python



foc

windows
Рис. 1.5. Python 3.9.2 установлен

Python. Полное руководство

;!IJpython

------------------ ½15

Поздравляю! Вы только что установили Python и можете приступить к его
изучению.
Технические подробности. При установке инсталлятор связыва­
ет расширение .ру с программой python.exe, а расширение .pyw
с программой pythonw.exe. Первая программа используется для
выполнения консольных приложений, а вторая - оконных.

В Linux в большинстве случаев Python уже будет установлен. Если это не
так, то с помощью команды sudo apt install python (команда уста­
новки, как и название самого устанавливаемого пакета, может отличаться в
зависимости от используемого дистрибутива) это можно легко исправить.

1 .3. Первая программа на Python
Для запуска вашей первой программы вы можете запустить или программу
python.exe ( она находится в каталоге, в который вы установили Python 1)
или запустить среду разработки IDLE (это можно сделать с помощью меню
Пуск) - см. рис. 1.7.
Если вы добавили Python в РАТН, то можно просто ввести команду pythoп

--

Рис. 1.6. Проrраммаруthоп.ехе



-. -.. -. -... -... -.... -... -... -.... - ... -...... -... -.. -... -. -... -.. -... - . -... -.... - ..

iJJpython
'"'�

------ .. ·-------

Глава 1. Основы. Первая программа


х

f:i!e fdit She!! Qeoog Qptions '!:J.indow Jje!p
.
Pythoг. 3.9.2 (tags/v3.9.2:1a7978S, Feb 19 2021, 13:44:sS") "{МS'ё···v·:i92iГ"i4""••ьit"• {AМ. А
D64) J on win32
Туре "help", "copyright", "credits" or "licenset)" for rnor:e infornatior:.
>'>> help О
Welccme tc Python 3.9'.s help t1t.;.lityi
!f this is: your first ti."ne usir:q Python, you shotlld definite.ly check ou:
r .hec. tuto. .::ialvn the Int.err.et ,;,t: https: / /d-:)cs. pythor,.m:g/З. 9/r.1lt.cr.1.a.l/.
Ente.r the narne of any module, keywcrd 1 or -ropic to get help on w.ritirig
Python prcgrams a:-i.d ш>iпq Pythoп 1r.od'Ulr:>s. То quit. t:h.i..s }:elp utill.r.y a.nd
1·eturr: to tГ,е inteтpreter. j1.1st type " c1uit".
T·:J get а li.st of availaЫe mcdules, k1..'yw,.н:ds, :syrrl.Jc-ls, ,:,н- �opics, type
"modules", "keywoгds", ''symЬols", cr "topic�"- Each шvdt>> p1:int{"He1.l::;, -;,;;;r:1d!")
Hellc, wcrld!
>>> �rint ("E,0:.ll('")

Рис. 1. 12. Ошибка: неожиданный отступ

х



....................................................................................

Глава 1. Основы. Первая программа

ф python__ "-___________

Хорошо, что интерпретатор подсказывает, что не так, и даже показывает по"
зицию ошибки.
Во многих языках программирования (РНР, С, Pascal, Perl,Java и др.) каж­
дая инструкция должна завершаться точкой с запятой. В Python точка с за­
пятой необязательна. Но и ее наличие не вызовет ошибку, если вы вдруг
поставили ее по привычке. Поэтому следующие два варианта инструкции
- правильные с точки зрения синтаксиса:
print("Hi ! ")
print("Hi ! ");

Концом инструкции в Python считается конец строки (символ EOL). Одна­
ко точка с запятой обязательна, если вы хотите поместить в одну строку
несколько инструкций, например:
>>> а = 2; Ь
>>> print(x)
14

=

3; с

=

4; х

=

а + Ь * с;

Также в других языках программирования мы привыкли видеть фигурные
скобки, которые разграничивают инструкции внутри блока. Например, вот
код на РНР:
$х = О;
while ($х < 5) {
echo "$х \n";
$х++;

echo "все";

А вот аналогичный код на Python:
х = о
while х < 5:
print(x)
х += 1
print("Все")

•·------------------·-········-········-·-···-······-·-······-·······-····-·-·-·-··-

Pythor1. Полное руководство

, _________________• python

Перед всеми инструкциями блока должно быть расположено одинаковое
количество пробелов. Так Python распознает, какая инструкция и к какому
блоку относится. При написании кода в IDLE одинаковое количество про­
белов проставляется автоматически, а для завершения блока при переходе
на следующую строку вы должны нажать Backspace, а затем - Enter. При
написании кода в редакторе количество пробелов нужно учитывать само­
стоятельно. Обычно используется 4 пробела. Если количество пробелов
внутри блока - разное, Python выведет фатальную ошибку и выполнение
программы будет остановлено. Сначала вам будет непривычно, но спустя
месяц программирования на Python вы научитесь писать понятный и кра­
сивый код. Ведь в других языках программирования вы можете написать
хоть все инструкции программы, в том числе вложенные, в одну строку.
Главное, чтобы было правильно с точки зрения синтаксиса. В результате
читать такую "кашу" из кода не очень удобно. В Python такого бьгrь не мо­
жет - хочешь не хочешь, а придется создавать понятный код.
Если весь ваш блок состоит всего из одной инструкции, разрешается раз­
местить ее на одной строке с основной инструкцией, например:
for х in range (1 , 10): print(x)

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

=

а + Ь \
* с

х

=

(а + Ь
* с)

# Comment

В первом случае никакие другие символы не разрешаются, в том числе и
комментарии.

1 . 7. Комментарии
Как вы уже успели понять, комментарии в Python начинаются с решетки.
Комментарий может начинаться, как с новой строки, так и помещен после
инструкции:



-----·························-·------·-············································

О python...............

Глава 1. Основы. Перва� программа

# Это комментарий
рrint("Привет") # Это тоже комментарий

Если решетка размещена перед инструкцией, то инструкция считается ком­
ментарием и не будет выполнена:
# print("Привет")

Также # не считается символом комментария, если он находится внутри ка­
вычек или апострофов, например:
print("# Комментарий")

В Python нет многострочного комментария, поэтому можете или использо­
вать несколько комментариев:
# Многострочный
# комментарий

Можно также использовать тройные кавычки:
Многострочный
комментарий

Данная конструкция не игнорируется интерпретатором. Он создает строко­
вой объект, но так как все инструкции внутри тройных кавычек считаются
текстом, никакие действия производиться не будут.
Однако знайте, что при частом использовании такого подхода в большой
программе будет наблюдаться нерациональное использование памяти, по­
скольку обычные комментарии игнорируются, а в случае с тройными ка­
вычками созда.ется строковой объект.
С одной стороны, объемом оперативной памяти в 16 Гб сегодня никого не
удивишь, и те несколько килобайтов перерасхода памяти в очень большой
программе особой роли не сыграют. С другой стороны, нужно привыкать к
рациональному использованию ресурсов компьютера. Сегодня вы исполь­
зовали тройные кавычки, чтобы закомментировать несколько килобайтов
текста, завтра - забудете закрыть соединение или освободить память. Ин-



•..................................................

······························-

Python. Полное руководство

'.::...... _ ...........• python

терпретатор Python по окончанию работы сценария автоматически закры�
вает все выделенные программе ресурсы (в том числе файлы и соединения),
но такой подход считается очень плохим тоном среди программистов.
Тройные кавычки удобно использовать, чтобы временно закомментировать
какой-то участок кода (чтобы не дописывать в начале каждой строки#, а
потом не удалять ее, когда вам вновь понадобится этот фрагмент кода).
Использовать или нет тройные кавычки - решать вам. Я вам показал, как
их можно использовать и рассказал о преимуществах и недостатках этого
способа.

1.8. Ввод/вывод данных
В этом разделе мы поговорим о вводе и выводе данных. Ранее было пока­
зано, что для вывода данных используется инструкция (функция) print().
Полный синтаксис pri,nt() выглядит так:
print ( [][, sep = ' '] [,end='\n'] [,file= sys.stdout])

Разберемся с параметрами функции. Первый параметр - это набор объек­
тов, которые нужно вывести. Список объектов разделяется запятыми. На­
пример:
>>> а = 1; Ь = 2;
>>> print(a, Ь)

1 2

Как видите, между объектами автоматически вставляется разделитель - по
умолчанию Пробел. Задать собственный разделитель можно с помощью па­
раметра sep. Например, вы можете задать символ табуляции:
>>> print(a, Ь, sep = '\t')
1 2



.....................................................................................

О python...............

Глава 1. Основы. Первая программа

Параметр end задает конец строки. По умо.лчанию использует символ '\n',
который в Windows автоматически преобразуется в последовательность
'\r\n' (перевод каретки и новая строка). Обычно вам не нужно изменять
этот параметр при выводе на экран, но может понадобиться его изменение
при выводе в файл - все зависит от синтаксиса (формата) файла.
Последний параметр задает файл, в который осуществляется вывод. По
умолчанию вывод осуществляется в файл sys.stdout, что означает стандарт­
ный вывод и обычно соответствует экрану (консоли). О работе с файлами
мы пока не говорим, просто знайте, что функция print() умеет выводить
данные не только на экран, но и в файл.
Вызов функции print() без параметров позволит просто перевести строку
(выводится пустая строка):
print()

Некоторые программисты вместо функции print() предпочитают использо­
вать метод write объекта sys.stdout. Например:
import sys;
sys.stdout.write("Пpивeт")

При первомиспользовании метода write() нужно сначала импортировать
модуль sys, в котором определен этот метод.

Особенность этого метода в том, что он не добавляет символ конца строки,
поэтому его нужно при необходимости добавить самостоятельно:
sys.stdout.write("Hello\n")

Для ввода данных в Python 3 используется функция input(). Использовать
ее можно так:
[ =] input ( [ ] )

Небольшой пример:



name = input("Как ·тебя зовут? ")

··········-···--·····-····-········-·-·-·-·-·----·-·---------------------·---·--·---

Python. Полное руководство

------------------•pyt'"'n

рrint( " Привет, ", name)

Работа с программой в IDLE:
>>> name = input("Kaк тебя зовут? "1
Как тебя зовут? Марк
>>> рrint("Привет, ", name)
Привет, Марк

>>>

Обратите внимание, что функция input() не выводит после сообщения­
приглашения никаких символов, поэтому ввод начнется сразу после вы­
веденного функцией сообщения, что не очень удобно. Поэтому в конец
сообщения принято добавлять или пробел или символ новой строки, чтобы
ввод начался с новой строки:
>>> name = input("Kaк тебя зовут?\n")

Как тебя зовут?
Марк

Если пользователь нажмет Ctrl + Z или будетдостигнут конец файла (в
данном случае речь идет о файле стандартного ввода - stdin ), будет сгене­
рировано исключение EOFError и программа завершит свою работу. Чтобы
этого не произошло, нужно произвести обработку этого исключения:
try:

name = input("Как тебя зовут? ")
print(name)
except EOFError:
print("EOFError raised")

Подробно обработка исключений рассмотрена не будет, а пока вам нужно
знать, как обработать только одно из них - EOFError.

1.9. Чтение параметров командной
строки
Вашей Руthоn-программе, как и любой другой программе, можно передать
параметры командной строки. Они хранятся в списке argv модуля sys. Вот
как можно вывести все переданные программе параметры:



------------------------------------------------------------------------------------·



python_______________

Глава 1 . Основы. Первая программа

import sys
args = sys.argv[:]
for n in args:
print(n)

Передать параметры программе можно так:
python program.py argl arg2

В данном случае будет запущен интерпретатор Python, который начнет об­
работку программы program.py, а самой программе при этом будут переда­
ны параметры arg1 и arg2.
На этом все. В следующих главах будут рассмотрены переменные и типы
данных в Python.



·-----------------------------------------------------. ----------------------------

ГЛАВА 2.
ПЕРЕМЕННЬIЕ И ТИПЬI
ДАННЫХ

Python. Полное руководство

Теперь, когда вы знаете, как создать и запустить программу, можно присту­
пить к изучению синтаксиса языка. Прежде, чем мы перейдем к рас­
смотрению переменных� вы должны знать, что типизация в Python динами­
ческая, то есть тип переменной определяется только во время выполнения.
Данное отличие может поначалу сбивать с толку, особенно, если вы
программировали на языках, где требуется сначала указать тип перемен­
ной, а потом уже присваивать ей значение (С, Pascal и др.). В то же время
языки с динамической типизацией тоже не редкость - типичный пример
РНР, где не нужно объявлять тип переменной перед присвоением значения.
Тип будет определен автоматически - по присвоенному значению. С одной
стороны, так проще. С другой - это требует от программиста постоянно сле­
дить за данными, которые он присваивает переменной, поскольку одна и та
же переменная в разные моменты времени может содержать данные разных
типов.
В Python имеются встроенные типы: булевый, строка, Unicode-cтpoкa,
целое число произвольной точности, число с плавающей запятой, ком­
плексное число и некоторые др. Из коллекций в Python встроены: список,
кортеж (неизменяемый список), словарь, множество и др. Все значения яв­
ляются объектами, в том числе функции, методы, модули, классы.

....

'

.

.... ... . . .. ····-······-· .................... - . -··..........------------------·-··

41 python....................

Глава 2. Переменные и типы данных

Все объекты делятся на ссылочные и атомарные. К атомарным относятся
int, long (в версии Python 3 любое число является int, так как, начиная с
этой версии, нет ограничения на размер), complex и некоторь1е другие.
При присваивании атомарных объектов копируется их значение, в то вре­
мя как для ссылочных копируется только указатель на объект, таким об­
разом, обе переменные после присваивания используют одно и то же значе­
ние. Ссылочные объекты бывают изменяемые и неизменяемые. Например,
строки и кортежи являются неизменяемыми, а списки, словари и многие
другие объекты - изменяемыми. Кортеж в Python является, по сути, неиз­
меняемым списком. Во многих случаях кортежи работают быстрее списков,
поэтому если вы не планируете изменять последовательность, то лучше ис·
пользовать кортежи.
Далее мы поговорим обо всем этом подробнее. В этой главе мы рассмотрим
основные операции с переменными и поговорим детальнее о типах данных.

2. 1. Имена переменных
В Python, как и в остальных языках программирования, есть переменные.
Переменные в Python представлены объектами. Точнее для доступа к
объекту используются переменные. При инициализации переменной (ко­
торая происходит при первом присваивании значения) в самой перемен·
ной сохраняется ссылка на объект - адрес объекта в памяти.
У каждой переменной должно быть уникальное имя, позволяющее одно·
значно идентифицировать объект в памяти. Имя переменной может со­
стоять из латинских букв, цифр и знаков подчеркивания. Не смотря на то,
что имена переменных могут содержать цифры, они не могут начинаться с
цифры.
Также в именах переменной нужно избегать использования знака подчер­
кивания в качестве первого символа имени, поскольку такие имена имеют
специальное значение. Имена, начинающиеся с символа подчеркивания
(например, �name), не импортируются из модуля с помощью инструкции
from module import *, а имена, имеющие по два символа подчеркивания (на·
пример, _name_) в начале и конце, имеют особый смысл для интерпре­
татора.
В качестве имени переменной нельзя использовать ключевые слова.
Просмотреть список ключевых слов можно с помощью следующих
инструкций (рис. 2.1):



··················································································-

Python. Полное руководство

____________________О

python

>>> import keyword
>>> keyword.kwlist

о

[� IDLE Shell 3.9.2

х

file .Edit She!I .Qebug Qptions Window !::lelp

Python 3.9.2 (tags/v3.9.2:la79785, Feb 19 2021, 13:44:55) [MSC v.1928 64 bit (АМ •�
D64)] on win32
Туре "help", "copyright", "credits• or "license()" for more information.
»> impor:t keyword
>>> keyword.kwlist
1
and 1 , 'as•, 'assert', 'async.' ,. 'awa
['False', 1 No.ne 1 , 'True', '_peg_parser_
it', 'break', 'class', 'c.ontinue', 'def', 'del', 'elif', 'else', 'except', 'fina
lly', 'for', 'from', 'global', 1 if 1 , 'import', 'in', 'is', 'lamЬda', 'nonlocal',
'not', 'or', 'pass',- 'raise', 'return', 'try', 'while', 1 wit.h', 'yield']
»>

..,

Ln: 6 Cot:4

Рис. 2. 1. Ключевые слова Python

Кроме ключевых слов в качестве имени переменных не нужно использо­
вать встроенные идентификаторы. Конечно, такие идентификаторы можно
переопределить, но конечный результат будет не таким, как вы ожидаете.
Получить список встроенных идентификатор можно с помощью следую­
щих команд:
>>> import builtins
>>> dir(builtins)
['ArithmeticError', 'AssertionError', 'AttributeError',
'BaseException', 'BlockingIOError', 'BrokenPipeErro:rf',
'BufferError', 'BytesWarning', 'ChildProcessError',
'ConnectionAbortedError', 'ConnectionError',
'ConnectionRefusedError', 'ConnectionResetError',
'DeprecationWarning', 'EOFError', 'Ellipsis',
'EnvironmentError', 'Exception', 'False', 'FileExistsError',
'FileNotFoundError', 'FloatingPointError', 'FutureWarning',
'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning',
'IndentationError', 'IndexError', 'InterruptedError',
'IsADirectoryError', 'KeyError', 'Keyboardinterrupt',
'LookupError', 'MemoryError', 'NameError', 'None',
'NotADirectoryError', 'Notimplemented', 'NotimplementedError',

....

-...... -....... - - . -- - ........... - -.........-- -.. -..... - -....... -- . -- - -- . --- . ,

_.'

ф pyt hon___________________ _

Глава 2. Переменные и типы данных

'OSError', 'OverflowError', 'PendingDeprecationWarning',
'PerrnissionError', 'ProcessLookupError', 'ReferenceError',
'ResourceWarning', 'RuntirneError', 'RuntirneWarning',
'Stopiteration', 'SyntaxError', 'SyntaxWarning',
'SysternError', 'SysternExit', 'TabError', 'TirneoutError',
'True', 'TypeError', 'UnboundLocalError',
'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError',
'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning',
'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError',
irnport
build class
doc
_debug
loader
narne
_package
spec
'abs',
'all', 'any', 'ascii', 'Ьin', 'bool', 'bytearray', 'bytes',
'callaЬle', 'chr', 'classrnethod', 'cornpile', 'cornplex',
'copyright', 'credits', 'delattr', 'dict', 'dir', 'divrnod',
'enurnerate', 'eval', 'ехес', 'exit', 'filter', 'float',
'forrnat', 'frozenset', 'getattr', 'globals', 'hasattr',
'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance',
'issubclass', 'iter', 'len', 'license', 'list', 'locals',
'rnap', 'rnax', 'rnernoryview', 'rnin', 'next', 'object', 'oct',
'open', 'ord', 'pow', 'print', 'property', 'quit', 'range',
'repr', 'reversed', 'round', 'set', 'setattr', 'slice',
'sorted', 'staticrnethod', 'str', 'surn', 'super', 'tuple',
'type', 'vars', 'zip']

Итак, если подытожить, то можно выделить следующие правила:
• Имя (идентификатор) может начинаться с латинской буквы любого ре­
гистра, после которой можно использовать цифры. Пример правильных
имен переменных: q1, result1, а, Х, MyVar
• Имя переменной не может начинаться с цифры. Пример неправильных
имен переменных: 1q, 1result
• Имя переменной может начинаться с символа подчеркивания, но такие
имена имеют специальное значение для интерпретатора. Примеры таких
имен: _resource, resource
• Имя переменной не может быть ключевым словом
• Лучше не переопределять встроенные идентификаторы
• Имя переменной должно быть уникальным в пределах пространства
имен

•. -----· ------------·-..-------·-·------.-------.---------------.---.--------·-·----c:EI

Python. Полное руководство

Примечание. Теоретически, имя переменной может содержать
символы национальных алфавитов, но лучше такие имена не
использовать.
Немного забегая вперед, хочется поговорить о пространствах имен. В лю­
бой точке программы есть доступ к трем пространствам имен (namespaces):
локальному, глобальному и встроенному. В других языках программирова­
ния они также называются областями видимости.
Сложность скриптовых (интерпретируемых) языков программирования
вроде РНР и Python заключается в том, что переменную можно объявить
в любой части программы. В результате вы можете не заметить, как пере­
менные в разных пространствах имен просто перемешались, не забыть об
объявлении переменной и т.д. Например, в том же языке Pascal перемен­
ные объявляются в блоке Var, который вынесен за пределы основного блока
кода. Рассмотрим небольшой пример программы на Pascal:
program VarTest;
var
а, Ь : real;
function sum(a
var
res : real;
begin
res ·= а + Ь;
res;
sum
end;

real; Ь

real) : real;

begin
а : = 2; Ь : = 2;
writeln(sum(a, Ь);
end.

Четно видно, что переменные а и Ь являются глобальными, поскольку
определены за пределами какой-либо функции, а переменная res является
локальной, поскольку она объявлена в функции sum. В принципе, без нее
можно было бы обойтись, но нужен был пример локальной переменной, а
усложнять код не хотелось.
В Python также есть понятие глобальной и локальной переменной. Локаль­
ными считаются переменные, объявленные внутри функции (о функциях
мы поговорим позднее). Простота Pascal (и многих других переменных) в

----



............................................................................... .

Глава 2. Переменные и типы данных

�}python

том, что переменные объявляются в одном блоке (var), при этом жестко
определен тип переменной.
В Python (как и в РНР) все иначе. Какого-либо оператора или блока объ­
явления переменной просто не существует. Объявлением переменной счи­
тается присваивание ей значения. Вот у вас может быть программа на 2000
строк и переменная res, хранящая результат, может встречаться лишь в
предпоследней строке. И это будет правильно с точки зрения Python.
С типом переменной тоже не все так просто. Если в С типы определяются
жестко - при объявлении переменной, то в Python типы плавающие:
>>> х = 1
>>> х = "test"
>>> print(x)
test
>>>

Сначала переменная х была у нас целым числом. Затем она превратилась в
строку со значением"test". Попробуйте вы проделать такое в Pascal или С вы получите ошибку несоответствия типа - ну нельзя переменной, которая
была изначально запланирована для хранения числа, присвоить строку. В
Python такое возможно, что тоже не добавляет ясности вашим программам.
Поэтому при использовании переменных в Python нужно быть очень вни­
мательным.
Я рекомендую, особенно начинающим программистам, объявлять все не­
обходимые переменные в начале вашей программы путем присваивания им
начальных значений. Если начального значения нет, используйте О для чис­
ла или"" для строки. Также комментируйте назначение переменных, если
по их имени нельзя однозначно сказать, для чего они предназначены. Так
вам будет гораздо проще и вы привыкните к некоторой дисциплине.
Также рассмотрим некоторые рекомендации, позволяющие навести поря­
док в вашем коде и сделать его более удобным для чтения и поддержки в
будущем:
1. Хотя переменную можно объявить в любом месте программы, но до
первого использования, рекомендуется объявлять переменные в начале
программы. Там же можно произвести инициализацию переменных. Об
этом мы только что говорили.
2. Неплохо бы снабдить переменные комментариями, чтобы в будущем не
забыть, для чего используется та или иная переменная.

'----

....... . .

------ ................ ....... --- . ......................--..------

. ...

/!#ipython

Python. Полное руководство

3. Имя переменной должно описывать ее суть. Например, о чем нам гово­
рит переменная s? Это может быть все, что угодно и сумма (summ), и
счет (score) и просто переменная-счетчик, когда вы вместо i почему-то
используете s. Когда же вы называете переменную score, сразу становит­
ся понятно, для чего она будет использоваться.
4. Придерживайтесь одной и той же схемы именования переменных. На­
пример, если вы уже назвали переменную high_score (нижний регистр и
знак подчеркивания), то переменную, содержащую текущий счет поль­
зователя называйте user_score, но никак не userScore. С синтаксической
точки зрения никакой ошибки здесь нет, но код будет красивее, когда
будет использоваться одна схема именования переменных.
5. Традиции языка Python рекомендую начинать имя переменной со строч­
ной буквы и не использовать знак подчеркивания в качестве первого
символа в имени переменной. Все остальное - считается дурным тоном.
6. Не создавайте слишком длинные имена переменных. В таких именах
очень легко допустить опечатку, что не очень хорошо. Да и длинные
имена переменных сложно "тянуть" за собой. Если нужно использовать
в одной строке несколько переменных, то длинные названия будут вы­
ходить за ширину экрана. Максимальная рекомендуемая длина имени
переменной - 15 символов.

2.2. Типы данных
В Python есть типы данных. При присвоении переменной значения тип дан­
ных выбирается автоматически, согласно присваиваемому значению. Тип
данных может меняться на протяжении протяжение программь�колько
раз - столько раз, сколько ей присваивают значения разных типов. Под­
держиваемые типы данных приведены в таблице 2.1.
Таблица 2. 1. Типы данных в Python

Тип данных

Описание

bool

Логический тип данных. Может содержать только два
значения - tme (истина) илиfаlsе (ложь), что соответствует числам 1 и О

....



.. ..... .. .. ········ - . - ....... . - . - ... ........ .......... -............ .... ··-- ..... .

;:Jдpython
Q�



--------------------

Глава 2. Переменные и типы данных

hytearray

Изменяемая последовательность байтов

bytes

Неизменяемая последовательность байтов

complex

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

dict

Словарь. Похож на ассоциативный массив в РНР

ellipsis

Используется для получения среза. Определяется или
ключевым словом Ellipsis или тремя точками

float

Вещественные числа

frozenset

Неизменяемое множество

function

Функция

int

Целые числа. Размер числа ограничен только размером
доступной оперативной памяти

list

Список. Аналогичен массивам в других языках программирования

module

Модуль

NoneType

Пустой объект, объект без значения (точнее со значением
None, что в других языках соответствует null)

set

Множество (набор уникальных объектов)

str

U nicode-cтpoкa

tuple

Кортеж

·-----------------------------·······-···············--·---·-·--·-·-·-----·--·-------

Python. Полное руководство

type

____________________.&.python
.-r
Типы и классы данных

Узнать тип данных можно с помощью функции type():
>>> type(x)

>>> х = "аЬс"
>>> type(x)

Все типы данных в Python можно разделить на неизменяемые и изменяемые.
К неизменяемым типам данных относятся числа, строки, кортежи и bytes. К
изменяемым относятся списки, словари и bytearray.
Также можно говорить о последовательностях и отображениях. К последо­
вательностям относятся строки, списки, кортежи, типы bytes и bytearray. К
отображениям -.словари.
Последовательности и отображения поддерживают механизмы итераторов,
который позволяет произвести обход всех элементов с помощью метода _
next_() или функции next. Пример:
>>> m = [1, 2, 3)
>>> i = iter(m)
>>> i. next ()

1

>>> next(i)
2
>>> next(i)
3
>>>
Использование метода _next_() и функции next() на практике на­

блюдается редко. Чаще всего используется цикл for in:
>>> for i in m:
print(i)
1

2
3
>>>



С8:··-------------------------------·---····-·-···················-·-······--··-····

.python

F

--------------------

Глава 2. Переменные и типы данных

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

2.3. Присваивание значений
Для присваивания значения используется оператор =. Переменной, как и в
другом языке программирования, вы можете присвоить:
• Обычное значение (константу):
х = 1 # Переменной х присвоено значени'е 1 (число)
FirstName = "Denis" # Переменной присвоена
строковая константа "Denis"
• Значение другой переменной
а = х
• Результат вычисления выражения
у =

х

*

а

+

х

• Результат вычисления функции
res = func(y)
Как уже отмечалось, в Python используется динамическая типизация, то
есть тип данных переменной изменяется в зависимости от присваиваемо­
го ей значения. После присваивания значения в переменной сохраняется
ссылка на объект, а не сам объект. Это обязательно следует учитывать при
групповом присваивании. Групповое присваивание можно использовать
для чисел, строк и кортежей, но этого нельзя делать для изменяемых объ­
ектов. Рассмотрим небольшой пример. Судя по следующему коду, мы соз­
дали два разных объекта:
>>> а = Ь = (5, 4, 3]
>>> а, Ь

((5, 4, 3], (5, 4, 3])

А теперь попробуем изменить объект а:
>>> a[l] = 6
··················································································-Cl8

Python. Полнее руководство

l!tд1python

-------------------- IJ,.

>>> а, Ь
( [5, 6, 3], . [5, 6, 3])

Как видите, в переменной хранится только ссылка на объект, а не сам
объект. Поэтому в переменных а и Ь содержится ссылка на один и тот же
объект. Следовательно, изменение одной переменной приводит к измене­
нию значения и другой переменной, точнее к изменению объекта, на кото­
рого ссылается вторая переменная.
С числами, которые являются неизменяемыми объектами, вполне можно
использовать групповое присваивание и получить ожидаемый результат:
>>>
>>>
>>>
(2'

а = ь = 1
а = 2
а, ь
1)

Проверить, ссылаются ли переменные на один и тот же объект, можно с по­
мощью оператора is. Например:
>>> а = Ь = [5, 4, 3]
>>> а is Ь
True
>>> Ь is а
True

Как видите, оператор is вернул значение True, что означает, что переменные
а и Ь ссылаются на один и тот же объект в памяти. А теперь не будем ис­
пользовать групповое присваивание, но присвоим переменным а и Ь одно
и то же значение:
>>> а
>>> ь
>>> а
False
>>> ь
False

= [5, 4' 3]
= (5, 4' 3]
is ь
is а

Теперь переменные а и Ь ссылаются на разные объекты в оперативной па­
мяти. Просмотреть, сколько ссылок есть на тот или иной объект, можно с
помощью метода sys.getrefcount():
>>> а = 5; Ь = 5; с = 5;
>>> sys.getrefcount(5)
105



ta·-·······················-·-····························-·-··-·-·-·-·--·····-·-·-·



Jllapython

--------------------

Глава 2. Переменные и типы данных

Когда число ссылок на объект станет равно О, объект будет удален из памяти.
Кроме группового присваивания в Python поддерживается позиционное
присваивание, когда нужно присвоить разные значения сразу нескольким
переменным, например:
>>> а, Ь, с
>>> а, Ь, с
(5, 4, 3)

=

5, 4, 3

По обе стороны оператора = можно указать последовательности (строки,
списки, кортежи, bytes и bytearray), но такие сложные операторы присва­
ивания встречаются в "природе" довольно редко и я бы рекомендовал из­
бегать их использования, если вы хотите сделать программу понятной и
читаемой:
>>> а, Ь, с = "аЬс"
>>> х, У, z
( 'а', 'Ь', 'с')
>>> а, Ь, с = [1, 2, 3]
>>> а, Ь, с
(1, 2, 3)
>>> [а, Ь, с]
(1, 2, 3)
>>> а, Ь, с
(1, 2, 3)

Количество элементов слева и справа должно совпадать, иначе вы получите
сообщение об ошибке:
>>> а, Ь, с = 1, 2
Traceback (most recent call last):
File "", line 1, in
а, Ь, с = 1, 2
ValueError: need more than 2 values to unpack
>>>

Если справа от оператора = указано больше значений, чем переменных сле­
ва, все лишние элементы могут быть помещены в последнюю переменную.
Для этого перед этой переменной нужно указать звездочку(*):



>>> а, Ь, *с = 1, 2, 3, 4

·------------------------------------------------------------------------------------

Python. Полное руководство
>>> а, Ь, с
(1,

>>>

2,

[3,

--------------------•python

4])

Однако такая возможность появилась в Python 3 и в Python 2.7 она не под­
держивается.
Примечание. Звездочку можно указать только перед одной пе­
ременной, иначе получите следующую ошибку:
SyntaxError: two starred expressions in
assignment

2.4. Проверка типа данных и
приведение типов
Как уже отмечалось ранее, функция type() позволяет определить тип пере­
менной. Например:
>>> а = "1"
>>> type(a)

>>>

Данную функцию можно использовать не только для вывода типа, но и для
сравнения возвращаемого нею значения с названием типа данных:
>>> if type(a) == str:
print("String");
String

Иногда нужно преобразовать один тип данных в другой. Эта операция на­
зывается приведением типа. Стоит отметить, что далеко не всегда можно
преобразовать один тип данных в другой без потери самих данных. Неко­
торые типы данных вообще не совместимы. Например, никак нельзя преоб­
разовать вещественное число в целое без потери данных. А при преобразо­
вании строки в число вы вообще увидите сообщение об ошибке:

---



- -- ......... - -... - . ---... -...............-- -...-- ....... - . -...................... .

О python____________________

Глава 2. Переменные и типы данных

>>> int("String")
Traceback (most recent call last):
File "", line 1, in
int("String")
ValueError: invalid literal for int() with base 10: 'String'

В таблице 2.2 перечислены функции приведения типов, а также примеры
их использования.
Таблица 2.2. Функции приведения типов

Функция

Описание

>>> bool(1)
True

bool()

Преобразование объекта в лоrический тип данных

int()

Преобразование объекта в целое >>> int(5.5)
число. Обратите внимание: дробная 5
часть потеряна

float()

Преобразование целого числа в вещественное

>>> float(5)
5.0

str()

Преобразование объекта в строку

>>> str( (5, 4, 3])
'(5, 4, 3]'

bytes()

Преобразует строку в объект типа
bytes(). Первый параметр - это >>> bytes("String",
строка, второй - кодировка, третий "utf-8")
параметр необязательный и может b'String'
указывать способ обработки ошибок
(strict, replace, ignore)

bytearray()

Преобразует строку в объект типа bytearray("Hpllo",
"utf-8")
bytearray

list()

Используется для преобразования
последовательности в список

>>>

bytearray(b'Hello')

.,. - - - - --- - - - - - - - - - - - - -

Пример

-

. .

.

-

>>> list("Hello")
[ 'Н', 'е'' '1',
'1', 'о']

- - -- - - -- -- -- - - -- - - - . - -- . - . - -- - - - . - . - -- . - - -- - -- - --.- - -- -

....

Python. Полное руководство

Преобразует последовательность в
кортеж

tuple()

.python

••••••••••••••••�••• ·Ш

>>> tuple("Hello")
( 'Н''

' 1' '

'е',

'о')

' 1' '

Зачем необходимо преобразование типов, если в Python используется ди­
намическая типизация и типы приводятся автоматически? Далеко не всег­
да функции возвращают значения в ожидаемом типе. Представим, что нам
нужно написать простейшую программу, вычисляющую сумму двух чисел,
введенных пользователем:
х = inрut("Введите х = \n")
у = inрut("Введите у
\n")
print(x + у)
input().

Запустите программу и введите числа 5 и 7. Хотя вы ввели числа, функция
input() всегда возвращает введенное значение, как строку. В результате вме­
сто числа 12 вы увидели строку 57 (рис. 2.2).


i i IDLE Shell 3.9.2
Eile !;dit She!I ,Qebug

х

Qptions ';Xindow !!elp

Python 3.9.2 (tags/v3.9.2:la79785, Feb 19 2021, 13:44:55) [MSC v.1928 64 Ьit (АМ
D64)] on '"'in32
Туре "help", "copyright", "credits" or "license()" for more information.
>>> х = input("Bseдитe х ·,, \п")
Введите х =
5

>>> у = inрutС'3в(:-дите у ;;;; \ri н )
введите у =
7

>>> print(x + у)

57

>>>

Рис. 2.2. Неожиданный результат работы программы

Изменим программу так:
float(input("Bвeдитe х = \n"))
х
\n"))
у = float(input("Bвeдитe у

са----------. ----. ---..----. -. ·-......... -...-........ -.--. --.....-----.-. -. _._.--_,

IJ,'4 python

--------------------

Глава 2. Переменные и типы данных

print(x + у)
input()
Здесь мы приводим введенное пользователем значение к типу float, а затем
вычисляем сумму двух вещественных чисел. В результате получаем значе­
ние 15.0, что соответствует нашим ожиданиям (рис. 2.3).
-

LЙ!, IDLE Shell 3.9.2
file fdit
LJI"�

>>> х

=

SheJI Qebug

1 --1-'

введите х

5
>>> у

f

Qptions Window tlelp

\...U,l-J,Y.l...L.YH'-

input("BвeДJ1.тe л

=

= inрut("Нве.дите
Введите у=

,

....

1............u-1.1..-.,

\n")

UL

..L..1..\...C.11..::)C\1

LUL



х

н1uтe--I"11.1-u.1..111a.1....1..uн •

у .... \n")

7

>>> print(x + у)
57

= float (input ("Введите
Введите х =
>>> х
5

)(

= float (input ("Введите у
введите у =
>>> у

=

\n")}

=

\n"}}

7

>>> print(x + у}
12.0
»> 1

"

ln: 19

Col:4

Рис. 2.3. Теперь программа работает, как нужно

2.5. Удаление переменной
Для удаления переменной используется инструкция del:
del [, ..., ]
Пример:
>>> z = 1
>>> print(z)
1
>>> del z
>>> print(z)
Traceback (most recent call last):
File "", line 1, in
print(z)
NameError: name 'z' is not defined
>>>

•.

. . ... . .. ... ... . . .. ..... . . .. . . .. . . ..... ... ... . .......... ... ... ... .. . . . .. .... .. .. ..

...

Python. Полное руководство

____________________О

python

Чтобы удалить несколько переменных, просто перечислите их в инетрук­
ции del через запятую:
del а, Ь, с



.....................................................................................

ГЛАВА 3.
ОПЕРАТОРЬI

Python. Полное руководство

•ipython

................................... ,'}ii!

Операторы производят определенные действия с данными. Например, ма­
тематические операторы выполняют арифметические вычисления, двоич­
ные операторы - производят манипуляции с отдельными битами данных
и т.д.

3. 1. Математические операторы и
работа с числами
3.1.1. МАТЕМАТИЧЕСКИЕ ОПЕРАТОРЫ
Числа - немаловажный элемент любой компьютерной программы. Можно
сказать, что ни одна мало-мальски полезная программа не обходится без
применения чисел.
Как и в любом другом языке программирования (ну, почти в любом), в
Python имеются следующие базовые математические операторы (табл. 3.1 ).



С:8------. -...... -.-... --.-........... -........ -.. -... -..-.-... -... -..................

r':.1 python

Глава 3. Операторы

Таблица З. 1. Математические операторы в Python

Оператор

Действие

+

Сложение

-

Вычитание

*

Умножение

/

Обычное деление

//

Деление с остатком

%

Остаток от деления

**

Возведение в степень

Результатом оператора/ всегда является вещественное число, даже если вы
делите два целых числа и нет остатка. Вам кажется, что так и должно быть?
В принципе да, в Python 3 так и есть. А вот в Python 2 при делении двух
целых чисел возвращалось целое число, а остаток просто отбрасывался. В
Python 3 оператор деления работает, как обычно.
Теперь рассмотрим примеры использования математических операторов.
Обязательно обратите внимание на используемый тип данных операндов и
тип данных возвращаемого результата:
>>> 2 + 2
# Два целых числа, результат - целое число
4
>>> 2.5 + 2
# Одно целое, одно вещественное, результат вещественное
4.5
>>> 2.5 + 2.5 # Два вещественных, результат - вещественное
5.0
>>> 100 - 20
80
>>> 100.5 - 80.5
20.0
>>> 5 * 5
25
>>> 5.25 * 5.25
27.5625
>>> 5 * 2.5
12.5

•. ·------------------------------. ----.------------------.......------.-.. ·----. -----C!I

Python. Полное руководство

•••••••••••••••••••

# Обратите внимание на разницу между операторами/

8'.!lpython
·,W

и//

>» 100 / 20
5.0
»> 100 / 33
3.0303030303030303
»> 100 / / 5
20
>>> 100 // 33
3
>>> 100 % 33
1
>>> 2 * 2
4
>>> +100, -20, -5.0
(100, -20, -5. О)
>>>

При выполнении операций над вещественными числами нужно учитывать
точность вычислений, иначе вы можете получить довольно неожиданные
результаты. Например:
>>> 0.5 - 0.1 - 0.1 - 0.1
О.20000000000000004
>>> 0.5 � 0.1 - 0.1 - 0.1 - 0.1 - 0.1
2.7755575615628914е-17

В первом случае результат вполне предсказуем - мы получили 0.2. А вот во
втором случае мы ожидали О, а получили значение, отличное от нуля.
Поэтому если вы разрабатываете финансовые приложения на Python, где
важна точность , лучше использовать модуль Decimal:
>>> from decimal import Decimal
>>> Decimal("0.5") - Decimal("0.1") - Decimal("0.l") Decimal("0.l") - Decimal("0.l") - Decimal("0.l")
Decimal( ' О. О ' )

3. 1.2. ПРИМЕР: ВЫЧИСЛЕНИЕ ВРЕМЕНИ В ПУТИ

...

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

• python...................

Глава 3. Операторы

Листинг 3.1. Вычисление времени в пути
dist = О
speed = О

# Расстояние, которое нужно проехать
# Средняя скорость авто, км /ч

dist = int(input("Paccтoяниe: "))
speed = int(input("Плaниpyeмaя средняя скорость: "))
time = dist * 60 / speed
print("Bpeмя в пути ", time, " минут.")

Посмотрим, что есть в нашей программе. Первым делом мы инициализиру·
ем две переменные - dist и speed. Python не требует обязательной иници·
ализации переменной. Мы сделали это так, чтобы добавить комментарий и
знать, для чего используется та или иная переменная.
Далее мы получаем расстояние и среднюю скорость:
dist = int(input("Paccтoяниe: "))
speed = int(input("Плaниpyeмaя средняя скорость: "))

Обратите внимание: мы используем преобразование типа и явно указыва·
ем, что прочитанное значение должно быть типа int.
Затем мы вычисляем время движения автомобиля по формуле:
time

=

dist * 60 / speed

60 здесь - количество минут в одном часе. После того, как время вычислено,
мы его выводим.
о

Q IDLE Shell З.92

х

file fdit She!I QeЬug Qptions Window J:\elp

Python 3.9.2 (tags/v3.9.2:la79785, Feb 19 2021, 13:44:55) [MSC v.1928 64 Ьit (АМ А.
D64)] on win32
•�},.
Туре "help", "copyright", "credits" or "license()" for m ore information.
>>>
------------------- RESTART: C:\temp\py\time.py =========== \\
Расстояние: 120
Планируемая средняя скорость: ВО
Время в пути 90.0 минут.

>» 1



Рис. З. 1. Результат работы программы

·-·····-----------------------------------------------------------------··········-

Python. Полное руководство

________.__________О

python

. 3.1.З. ПРИМЕР: ВЫЧИСЛЕНИЕ РАСХОДА ТОПЛИВА
Данный пример демонстрирует работу с дробными числами. Ранее мы вы­
числяли время в пути и вводили два целых параметра. Теперь мы будем так­
же вводить два параметра, но они с большей долей вероятности могут быть
дробными.

Листинг 3.2. Вычисления расхода топлива
# Средний расход 10.5 л/100 км
# Расстояние, км

consum
О
dist = О

consum
float(input("Cpeдний расход топлива л/100 км: "))
dist = float(input("Paccтoяниe, км:"))
result = consum * dist / 100
рrint("Необходимо ", result, " л.")

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

лой и дробной части используется точка, а не запятая! То есть,
если вы введете 10.5, программа будет работать, а если вы вве­
дете 10,5, то получите сообщение об ошибке:


� IDLE Shell 3.92

х

file fdit She!I Qebug Qptions Window Help
....
r=;;;;;;:;;;;;;;:;;;;;;;:;;;;;;;:;;;;:;;;;;;;;;:;;;;;;;:;;;;;;;:;_;;_;;;_�;;;'·"jjREsтART�:-·· �c-:\-tem
_ _p�\p
- ,\
y Ьcl:.ppy
, ��;;;;;;;;;;;:;;;;;;;;;;;;;;;;;:;;;;;;;;;;;;:;;;;;;�
Средний расход топлива л/100 км: 11
Расстояние, км:660
Необходимо 72.б л.
>>>
=====-��= RESTART: C:\temp\py\bcl.py ========
Средний расход топлива л/100 км: 11
Расстояние, км:80
необходимо 8 . 8 л.
>>>
------------�= RESTART: с:\ temp\py\bcl .ру
Средний расход топлива л/100 км: 18
Расстояние, км:100
необходимо 18.О л.
>>>

Рис. 3.2. Программа вдействии



----------············--·--·---------------------------------------------····-···-·

/!!l!python
4!5

•••••••••••••••••••

Глава 3. Операторы

Traceback (most recent call last):
File "E:/Python39/samples/3-2.py", line 4, in
consum = float(input("Cpeдний расход топлива л/100 км: "))
ValueError: could not convert string to float: '10,5'

Данное сообщение говорит о том, что невозможно конвертировать строко­
вое значение "10,5" в flоаt-значение.

3.1.4. ВЫБОР ПРАВИЛЬНОГО ТИПА ДАННЫХ
В предыдущих примерах мы явно применяли int() иfloat() для приведения
прочитанного с ввода значения к числовому типу. А что будет, если не вы­
полнять приведения типа? Давайте посмотрим. Напишем программу, под­
считывающую стоимость содержания автомобиля.
Листинг 3.3. Стоимость, содержания автомобиля
service = inрut("Стоимость ТО: ")
fuel = inрut("Стоимость топлива: ")
tax = inрut("Транспортный налог: ")
tuning = inрut("Тюнинг и прочие доработки: ")
insurance = input("OCAГO: ")
total = service + fuel + tax + tuning + insurance
print("Bceгo: ", total)

Вывод изображен на рис. 3.3. Явно не то, что мы хотели. По умолчанию все
введенные значения считаются строковыми, и интерпретатор просто скле­
ил строки в одну большую строку.
=========== RESTART: C:\temp\py\cost.py ===
стоимость то: 15000
стоимость топлива: 154000
Транспортный налог: 32000
тюнинг и прочие доработки: 50000
ОСАГО: 6000
Всего: 1500015400032000500006000
>>>



Рис. 3.3. Стоимость содержания автомобиля. Ошибка!

....... ................... --.······-. - ..········- ........... ·-. --··-- ... - ··-

... .....

Pythori. Полное руководство

................... +

python

Именно поэтому нам нужно явно указывать тип прочитанного значения.
Исправим ошибку (рис. 3.4).
service = float(input("Cтoимocть ТО: "))
fuel = float(input("Cтoимocть топлива: "))
tax = float(input("Tpaнcпopтный налог: "))
tuning = float(input("Tюнинг и прочие доработки: "))
insurance = float(input("OCAГO: "))
total = service + fuel + tax + tuning + insurance
print("Bceгo: ", total)

Теперь, думаю, вы понимаете, зачем мы использовали int() иfloat() в преды­
дущих примерах.

=��

�' IDLE Shell 3.92



х

file fdit She!I Qebug Qptions Window J::!elp
'тОимость·-тu: 1SiJUu- -- - -·- � ���-�������-�-����� ��,:/'\<
""
стоимость топлива: 154000
1j¾:
Транспортный налог: 32000
'½:"
очие доработки: 50000
���
:��
го: 1500015400032000500006000
:��
W?
---------------- RESTART: C:\temp\py\cost.py
;i--Стоимость ТО: 15000
Стоимость топлива: 154000
Транспортный налог: 32000
тюнинг и прочие доработки: 50000
ОСАГО: 6000
257000.0

,J:'

Рис. 3.4. Стоимость содержания автомобиля. Правильная версия

3.2. Операторы для работы с
последовательностями
Операторы для работы с последовательностями используют в качестве сво­
их операндов последовательности - строки, списки, кортежи. К этим опера­
торам относят следующие:
• + - конкатенация;



.....................................................................................

-�

flмpython

-------------------

Глава 3. Операторы

• * - повторение;
• in - проверка на вхождение.
Примеры использования операторов:
>>> "Hello, " + "world!"
'Hello, world!'
>>> [l, 2, 3) + [ 4, 5, 6]
[1, 2, 3, 4, 5, 6]
>>> (1, 2) + (3, 4)
(1, 2, 3, 4)

# Строки
# Списки
# Кортежи

Оператор + объединяет две последовательности.
>>> "а" * 5
'ааааа'
>>> [1] * 5
[1, 1, 1, 1, 1)
>>> (1, 2) * 3
(1, 2, 1, 2, 1, 2)

Оператор * создает новую последовательность. В качестве исходной после­
довательности используется последовательность, заданная слева, а операнд
справа задает количество повторов указанной последовательности.
>>> "s" in "String"
False
>>> "s" in "string"
True
>>> 3 in [1, 2' 3)
True
>>> 4 in (5, 5, 5)
False

Как видите, оператор вхождения (in) возвращает True, если операнд слева
входит в состав последовательности, указанной операндом справа. В про­
тивном случае оператор возвращает False.

3.3. Операторы присваивания

...

Операторы этой группы используются для сохранения значения в перемен­
ной:

•.

• = - присваивает переменной значение
. . . . .. . . .. . . . .. .. . . . . . . . .. .. .. . . . . . . . . . .. . . . . . . . .. . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . .

rfп python

Python. Полное руководство

• + = - увеличивает значение переменной на указанную величину (или
производит конкатенацию� для строк)
• -= - уменьшает значение переменной на указанную величину
• * = - умножает значение переменной на указанную величину (для строк
этот оператор означает повтор)
• / = - делит значение переменной на указанную величину
• //= - то же, что и/=, но деление происходит с округлением вниз и при­
сваиванием
• % = - деление по модулю и присваивание
• **= - возведение в степень и присваивание
Примеры (следите за возвращаемым значением):
>>> а = 10; а
10
>>> а += 5; а
15
>>> s = "Hel"; s + = "lo"; s # Для с·трок конкатенация
'Hello'
>>> а
5; а
10
>>> а *= 2; а
20
>>> s * = 2; s
# Для строк - повтор
'HelloHello'
>>> а/ = 2; а
10.0
>>> а//= 3; а
3.0
>>> а % = 2; а
1.0
>>> а **= 5; а
# Возведение в степень 1 л 5
1. О

1

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

....

-·. - -- .------. - ------... -- . - -- ---- ------ -- ---- --------- -- -- ·- ------- ---- . --- ---

•-·

lf;л python

Глава 3. Операторы

• - - двоичная инверсия (значение бита изменяется на противоположное:
1 на О,О на 1)
• & - двоичное И
двоичное ИЛИ



1 -



л -

двоичное исключающе ИЛИ

• > - сдвиг вправо (сдвигает двоичное представление числа вправо на
один или несколько разрядов,разряды слева заполняются нулями, если
число положительное, а если число отрицательное - единицами)

3.5. Приоритет выполнения
операторов
Последовательность вычисления выражений зависит от приоритета выпол­
нения операторов. Все мы знаем,что сначала выполняются умножение и де­
ление, а потом уже сложение и вычитание, поэтому результат следующего
выражения будет 6,а не 8:
а = 2 + 2 * 2

Это основы. Но в Python операторов гораздо больше,чем в математике,по­
этому нужно учитывать приоритет каждого оператора. Далее приведены
операторы в порядке убывания приоритета. Операторы одного приоритета
выполняются слева направо:
1. -х, +х, -х, **
2. * ,%,/,//
3. + ' 4.
5. &
6. л
7.



8. =, +=, -=, *=, /=, //=, %=, **=

.....................................................................................

Python. Полное руководство

iJ1python

------------------- .,,,;"'!

Если вам сложно запомнить приоритет операторов, хочется большей одно­
значности или нужно изменить приоритет выполнения, используйте скоб­
ки. Результатом следующего выражения будет уже 8, а не 6:
а = (2 + 2) * 2

Сначала будет вычислено значение в скобках (4), а потом уже будет произ­
ведено умножение на 2.

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

Листинг 3.4. Калькулятор
print("*" * 15, " Калькулятор " "*" * 10)
print("Для выхода введите q в качестве знака операции")
while True:
s = input("Знaк (+,-,*,/): ")
if s == 'q': break

---

- . - -- - - . ---- -- - - -- -- - -- . - - - . - - - - - - -- . - . - . - - . - . - . - - - - - - - - - - - - - - - - - - - . - - - . - - - - - - -

_,

l!й1python



-------------------

Глава 3. Операторы

if s in ('+','-','*','/')
х = float(input("х="))
у = float (input("у="))
if s == '+':
print("%.2f" % (х+у))
elif s == '-':
print("%.2f" % (х-у))
elif s == '*':
print("%.2f" % (х*у))
elif s == '/':
if у ! = О:
print("%.2f" % (х/у))
else:
рrint("Деление на ноль!")
else:
рrint("Неверный знак операции!")

Посмотрим на вывод программы. Обратите внимание, как она реагирует на
неверный ввод, например, если введено число вместо знака операции, или
был введен О вместо у при делении:
*************** Калькулятор **********
Для выхода введите q в качестве знака операции
Знак (+,-,*,/): +
х= 12
у = lЗ
25.00
Знак ( +,-,*,/)
x = l00
у=25
75.00
Знак (+, -,*,/) /
х= 9
у =З
3.00
Знак (+, -,*,/) /
х= 9
у= О
Деление на ноль!
Знак ( +,-,*,/): \
Неверный знак операции!
Знак ( +, -,*,/): *
x = l.25
у=4

,

................................................................................ ...

Python. Полное руководство

5.00

Знак ( +, - , *, /) : q

>>>

�. . python

ГЛАВА 4.
ЦИКЛЫ И УСЛОВНЫЕ
ОПЕРАТОРЬI

Python. Полное руководство

......................................

i!@ python
"::1Н

4. 1 . Условные операторы
4.1.1. ЛОГИЧЕСКИЕ ЗНАЧЕНИЯ
В любой программе (если не считать самых простых) встречаются услов­
ные операторы. Данные операторы позволяют выполнить отдельный
участок программы (или наоборот, не выполнить) в зависимости от значе­
ния логического выражения. Логические выражения могут вернуть только
два значения: True (истина) или False (ложь), которые ведут себя как числа
1 и О соответственно.
Логическое значение можно хранить в переменной:
>>> а = True; Ь
>>> а, Ь
(True, False)

False;

Логическим значением True может интерпретироваться любой объект, не
равный О, не пустой. Числа, равные О или пустые объекты, интерпретиру­
ются как False.

• python _ ..........•........

Глава 4. Циклы и условные операторы

4.1.2. ОПЕРАТОРЫ СРАВНЕНИЯ
В логических выражениях Python используются следующие операторы
сравнения:
• ==-равно;
• != -не равно;
• -больше;
• =-больше или равно;
• in - проверяет вхождение элемента в последовательность, возвращает
True, если элемент встречается в последовательности;
• is -проверяет, ссылаются ли две переменные на один и тот же объект.
Если переменные ссылаются на один и тот же объект в памяти, оператор
возвращает True.
Внимание/ Условные операторы в Pythoп могут сравнивать не
только числа, но и строки, например audi< bmw, поскольку audi
находится по алфавиту раньше, чем bmw. Но не все в Pythoп
можно сравнить. Объекты разных типов, для которых не опре­
делено отношение порядка, нельзя сравнить с помощью опе­
раторов=. Например, вы не можете сравнить число
и строку. Если вы попытаетесь это сделать, получите огромное
сообщение об ошибке.

Примеры (обратите внимание на возвращаемые значения True и False):
5
>>> 5
True
>>> 5 != 6
True
>>> 5
6
False
»> 100 > 99
True
>>> 100< 99
False
>>> 100 >> 100 >= 101
False
>>> 2 in [1, 2, 3]
True
>>> а = Ь = 100
>>> а is Ь
True
>>>
Значение логического выражения можно инвертировать с помощью опера­
тора nоt:
>>> а = Ь = 100
>>> not (а == 100)
False
Если нужно инвертировать значение оператора in, оператор not нужно ука­
зывать непосредственно перед in - без скобок:
>>> 2 not in [1, 5, 7]
True
При необходимости инвертирования оператора is, оператор not указывается после этого оператора:
>>> а is not Ь
False
При необходимости, можно указывать несколько условий сразу:
>>> 2 < 5 < 6
True
С помощью операторов and (И) и or (ИЛИ) можно объединить несколько
логических выражений:
х and у
х or у
В первом случае, если х = False, то будет возвращен х, в противном случае
-у :



.....................................................................................

,6python
'11§

с

-------------------

Глава 4. Циклы и условные операторы

>>> 2 < 5 and 2 < 6
True
>>> 2 < 5 and 6 < 2
False
Во втором случае если х = False, то возвращается у, в противном случае - х:
>>> 2 < 5 or 2 < 6
True
>>> 2 < 5 or 6 < 2
True
>>> 2 < 1 or 6 < 2
False
Далее перечислены операторы сравнения в порядке убывания приоритета:
1. , =, ==, ! =, , is, is not, in, not in

2. not - логическое отрицание
3. and - логическое И

4. or - логическое или

4.1.3. ОПЕРАТОР IF.. ELSE
Оператор if"else называется оператором ветвления. Он, в зависимости от
значения логического выражения, может выполнить или, наоборот, не вы­
полнить какой-то участок программы. Формат этого оператора следующий:
if :

[е 1 i•f :

[else:

Напомню, что блоки в составной конструкции выделяются одинаковым ко­
личеством пробелов. Конец блока - инструкция, перед которой расположе­
но меньшее число пробелов.
Рассмотрим небольшой пример. Сейчас мы напишем программу, которая
будет запрашивать число N у пользователя. Далее, программа проверяет

•.

-........................... ---.. - .-..-----..... -- .-......... -- .... - - ..-

. . .. . ...

Python. Полное руководство

______________________• python

введенное значение - оно больше или меньше ста - и выводит соответству­
ющее сообщение (листинг 4.1 ).
Листинг 4.1. Пример использования оператора if..else
n = int(input("Bвeдитe N: "));
if n < 100:
print("n < 100")
else:
print("n > 100")
=================

Введите N: 5
n < 100

>>>

Введите N: 567
n > 100

RESTART. C:\temp\py\if.py ------------------­

RESTART: C:\temp \py\if .py

»> 1

Рис. 4. 1. Результат выполнения листинга 4. 1

У нас очень простая программа, в которой каждый блок состоит из одной
инструкции, поэтому ее можно переписать так, как показано в листинге 4.2.
Листинг 4.2. Пример использования оператора if.. else - 2
n = int(input("Bвeдитe N: "));
if n < 100: print("n < 100")
else: print("n > 100")

Однако не нужно злоупотреблять этим подходом. На практике лучше ис­
пользовать подход, представленный в листинге 4.1. Так ваша программа
будет более читабельной.
Оператор if .. else позволяет указывать несколько условий с помощью бло­
ков elif. Пример использования такого условного оператора приведен в ли­
стинге 4.3.
Листинг 4.3. Проверка нескольких условий

...

рrint("""Выберите ваш браузер:
1 - Google Chrome
2 - Firefox

--------------------------------------------------------------------------------'

"'*

python___________________

Глава 4. Циклы и условные операторы

3 - MS Internet Explorer

4

-

Opera

5 - Safari
6 - Другой""");

browser = int(input(""));
if browser == 1:
print("Chrome");
elif browser == 2:
print("Firefox");
elif browser == 3:
print("MS IE");
elif browser == 4:
print("Opera");
elif browser == 5:
print("Safari");
elif browser == 6:
print("Other");
RESTART: C:\temp\py\4 4.ру

1

2

з
4

5

6

7
8

9
все.
>>>

Рис. 4.2. Результат работы программы из листинга 4.3

Недостаток нашей программы - то, что она никак не реагирует, если поль­
зователь введет число, отличное от 1 до 6. Исправить это можно с помощью
еще одного блока else:
if browser == 1:
print("Chrome");
elif browser == 2:
print("Firefox");
elif browser == 3:
print("MS IE");
elif browser == 4:
print("Opera");
elif browser == 5:

••

··----··----------·-·--···-···-·---····-·---···--················-········-···--··-81

Pytho11. Полное руководство

Apython

---------------------- '4W

print("Safari");
elif browser == 6:
print("Дpyгoй");
else:
рrint("Неправильное значение")

Не забывайте указывать блок else, если нужна реакция на неопределенное
в блоках elif значение.
Примечание. Если вы программировали на других языках, то
вам наверняка знаком оператор switch"case. Смысл этого опе­
ратора в следующем: в switch задается выражение, значение ко­
торого сравнивается со значениями, заданными в блоках case.
Если значение совпало, то выполняются операторы, указанные
в этом блоке case. К сожалению, в Python нет такого операто­
ра, и вам придется строить конструкции if"elif"else. Некоторые
прогр_аммисты предлагают использовать словари вместо switch"
case, но данный подход не универсальный и подойдет далеко не
всегда.

4. 1.4. БЛОКИ КОДА И ОТСТУПЫ
Рассмотрим следующий условный оператор:
if age < 18:
рrint("Извините, вы не можете использовать эту программу!")

Обратите внимание, что вторая строка написана с отступом. Отступ превра­
щает наш код в блок. Блок - это одна или несколько идущих подряд строк с
одинаковым отступом. Блок - единая конструкция.
Блоки используются, когда в случае выполнения условия нужно выпол­
нить несколько операторов:
if age < 18:
рrint("Извините, вы не можете использовать эту программу!")
print("Kaк только исполнится 18, возвращайтесь!")

На другом языке программирования блоки кода, как правило, заключают в
фигурные скобки:
if ($age < 18) {
echo " Извините, вы не можете использовать эту программу!";
echo " Как только исполнится 18, возвращайтесь!";



--------------------------------·-------------------------------------------------·

APY,thon
lii

-------------------

Глава 4. Циклы и условные операторы

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

4.2. Циклы
Если проанализировать все программы, то на втором месте, после условно­
го оператора, будут операторы цикла. Используя цикл, вы можете повто­
рить операторы, находящиеся в теле цикла. Количество повторов зависит
от типа цикла - можно даже создать бесконечный цикл. В этом и есть неко­
торая опасность циклов - если не предусмотреть условие выхода из цикла,
то может произойти зацикливание программы, когда тело цикла будет вы­
полняться постоянно.

4.2.1. ЦИКЛ FOR
Цикл for в других языка называют еще циклом со счетчиком, поскольку он
позволяет повторить тело цикла ( инструкции внутри цикла) определенное
количество раз. В Python цикл for больше похож на цикл foreach языка
РНР - он позволяет перебрать элементы последовательности.
Формат цикла for следующий:
for in

[else:

]

Здесь элемент - это переменная, через которую будет доступен текущий
элемент итерации. Последовательность - объект, поддерживающий меха­
низм итерации - строка, список, кортеж, словарь и т.д. Тело цикла - опера­
торы, которые будут выполняться при каждой итерации цикла.

....

Изюминка цикла for в языке Python - наличие блока else, который задает
операторы, которые будут выполнены, если внутри цикла не использовался

,

__ - -.

- .. - -- - - -- - -- - - . -.----- - -- - - -- - -- - -- - --- - --... - ... -.. - .. - -- ---- - . -- -- -- -- -

......................+

Python. Полное руководство

python

оператор break. Данный блок не является обязательным; но вы МО)Кете его
использовать в контексте, показанном в листинге 4.4.
Листинг 4.4. Пример использования блока e/se в цикле for
for i in range(l, 10):
print(i)
else:
print("Bce.")

Результат выполнения этого кода приведен на рис. 4.3. Как видите, сцена·
рий вывел числа от 1 до 9 и в конце работы цикла вывел сообщение "Все."
Теперь переделаем цикл так, чтобы внутри был оператор break, который
прерывает работу цикла (лист. 4.5). Результат изображен на рщ:. 4.4. Как
видите, если выполнение цикла прерывается оператором break, то операто·
ры из блока else не выполняются.
1

RESTART: C:\temp\py\4 4.ру

2

з
4

5
б

7
8

9
Все.

>>>

Рис. 4.3. Результат выполнения кода из листинга 4.4

========== RESTART: C:\temp\py\4-5.py ==========
1
2

з
4

5
б
>>>

Рис. 4.4. Результат выполнения кода из листинга 4.5




....................................................................................

0PYthon
5:
print (k)
k = k + 1

Здесь цикл будет выполняться, пока k больше 5. Изначально k у нас больше
5, далее значение k только увеличивается, поэтому мы получим бесконеч­
ный цикл - программа будет бесконечно увеличивать значение k и выво­
дить его:
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403

...





- - - - - - - -- - . - . - . - .... -- ..... - -- . -.. - ..... - -- . -.... -. -. - ·- .. -- -. - -- . --- .... -. -.....

,Ьpython
W

Глава 4. Циклы и условные операторы

_ ...................................

Прервать выполнение зацикленной программы можно с помощью комби­
нации клавиш Ctrl + С:
Traceback (most recent call last):
File "E:/Python39/loop.py", line 3, in
print(k)
File "E:\Python39\lib\idlelib\PyShell.py", line 1344, in
write
return self.shell.write(s, self.tags)
Keyboardlnterrupt
>>>
Как исправить бесконечный цикл. Здесь нужно или редактировать условие
или же процесс изменения значения управляющей переменной. Например,
можно сделать декремент управляющей переменной:
k = k - 1
Тогда программа выведет 5 чисел и завершит свою работу:
10

9
8
7

6

Если разложить наш цикл на итерации, то получится табличка, приведен­
ная ниже (табл. 4.1)
Таблица 4. 1. Итерации цикла
Номер итерации

Значение k

Проверка
условия

1

10

true

2

9

true

Действия

print(k)

# 10

k = k - 1 # 9
print(k)

# 9

k = k - 1 # 8

•...................................................--....-.....----.-.-----.--..---са

l',python

Python. Полное руководство

з

8

true

4

7

true

5

6

true

6

5

false

print(k)

# 8

k = k - 1 # 7
print(k)

# 7

k = k - 1 # 6
print(k)

# 6

k = k - 1 # 5
-

Можно было бы изменить и условие, например:
k = 10
while k < 15:
print (k)
k = k + l
Тогда программа,выведет:
10
11

12

13

14

Типичная ошибка новичков - многие вообще забывают изменять перемен­
ную в цикле. Например:
# Внимание! Код содержит ошибку!
k = 1
while k >

5

Логин: root
Пароль: 1234
Доступ запрещен!
>>>

В первом случае были введены правильные логин и пароль. Программа
сообщила уровень доступа. Во втором случае логин был введен правильно,
а пароль - нет. Программа сообщила, что доступ закрыт.

•.

--

- ------- --------------- ---- - . - . - . - ..... - .. -. - .. --..-. - . --- . - ---. - . - . -- - . - - . - . - .

-

ГЛАВА 5.
МАТЕМАТИЧЕСКИЕ
ФУНКЦИИ

Python. Полное руководство

.,,..... .., .....,..,,.., .. ,..,.. .,.,., •.,.,

,lii;python
.·.y;s

5. 1. Поддерживаемые типы чисел
Python поддерживает следующие типы чисел: int, float, complex. Как вы уже
знаете из предыдущих глав, это целые, вещественные и комплексные числа
соответственно. При операции с числами нужно помнить, что результатом
операции является число бо'лее сложного типа. Например, вы хотите умно­
жить целое число на вещественное, тогда результатом будет вещественное
число.
Самым простым числовым типом является целое число. Чуть сложнее
- вещественное, поскольку у него есть дробная часть. Конечно же, самым
сложным типом является комплексное число.
Создать числовой объект можно так же, как и объекты остальных типов:
>>> а
>>> с

=

5; Ь = 2
а * Ь

С помощью префиксов ОЬ (ОБ), Оо (00) и Ох (или ОХ) можно указать числа
в двоичной, восьмеричной и шестнадцатеричной системах счисления соот­
ветственно:



----·--···-···-··················-··---····-············-·--········-------·-------··

f!мpython
и

---------------�----

Глава 5. Математические функции

>>> а = ОЫ1110000
>>> ь
00555
>>> с = Oxfff
Вещественные числа могут быть представлены в экспоненциальной форме
- с точкой и буквой Е, например:
>>> а 5е10
>>> Ь = 2.5е-5
Вещественные числа записываются в виде:
Вещественная часть+мнимая частьJ
Например:
>>> а = 3+4J
Для выполнения операций повышенной точности над вещественными чис­
лами нужно использовать модуль decimal, например:
>>> from decimal import Decimal
>>> Decimal("0.2") - Decimal("O.l") - Decimal("O.l")
Decimal ( 'О.О' )
Модуль Decimal реализует "Общую спецификацию десятичной арифмети­
ки" IBM. Само собой, разумеется, есть огромное число параметров кон­
фигурации, которые выходят за рамки этой книги.
Новички в Python могут использовать модуль Decimal, чтобы избежать
проблем с точностью, которые имеются при работе с типом данных float.
Однако здесь важно понять, а нужна ли вам такая точность. Тут все зависит
от вашего приложения. Если вы решаете научные или технические задачи,
занимаетесь компьютерной графикой или решаете большинство задач на­
учной природы, вам будет вполне достаточно обычного типа float. В мире
существует очень мало вещей, для которых будет недостаточно обеспечива­
емой этим типом данных 17 -значной точности. Таким образом, крошечные
ошибки, имеющиеся в вычислениях, просто не имеют значения. К тому же
производительность вычислений с типом данных float (в отличие от моду­
ля Decimal) - на высоте.



. .... .... ··········-··.......·-·...·······-·.... --·-·-- -..................... - -- -

....

Python. Полное руководство

·

tflдx python

- - .... --- - - .. - - - - - - - .. - - - - '\t

Исходя из всего сказанного, основное применение модуля decimal - в
финансовых программах. В таких программах необходимо чрезвычайно
точное вычисление и даже малейшие ошибки недопустимы. Таким образом,
deciшal позволяет избежать таких ошибок. Также объекты deciшal принято
использовать при взаимодействии с базами данных, особенно при доступе
к финансовым данным.
Модуль fractions обеспечивает поддержку рациональных чисел:
>>> from fractions import Fraction
>>> Fraction("0.2") - Fraction("O.l") - Fraction("O.l")
Fraction(O, 1)

Модуль fractions может быть использован для осуществления математиче­
ских операций с дробями. Например:
>>> from fractions import Fraction
>>> а = Fraction(б, 4)
>>> Ь = Fraction(7, 12)
>>> print(a + Ь)
25/12
>>> print(a * Ь)
7/8
>>>#Получение числителя/знаменателя
>>> с = а * Ь
>>> c.numerator
7
>>> c.denominator
8
>>>#Конвертирование в float
>>> float(c)
0.875
>>>#Ограничение значения знаменателя
>>> print(c.limit denominator(8))
7/8
>>>#Преобразование из float в дробь
>>> х = 5.75
>>> у = Fraction(*x.as integer ratio())
>>> у
Fraction(23, 4)
>>>

----

- ---- -



---· --- - - --·-.... . .. ····· · ......................................... ....... .

;!Ьpy
� thon

Глава 5. Математические функции

-.-----.-.-------------.

5.2.,Числовые функции
В таблице 5.1 представлены встроенные числовые функции, имеющиеся в
Python.
Таблица 5. 1. Встроенные числовые функции
'•

Функция

Описание

Возвращает абсолютное значение числа:
abs()

>>> abs(-5), abs(-7.5)
(5, 7.5)

Преобразует десятичное число в двоичную систему, возвращает строку:

Ьin()

>>> Ьin(О), bin(333)
( 'оьо', 'ОЫ01001101')

Возвращает кортеж из двух значений - ( а // Ь,
а% Ь)

divmod(а, Ь)

Преобразует целое число или строку в вещественное число:
float()

>>> float(3), float( 11 2.2 11 ) ,
float( 11 13. 11 ).
(3.0, 2.2, 13.0)

Преобразует десятичное число в шестнадцатеричную форму, возвращает строку

hех()

Преобразует объект в целое число. Второй параметр позволяет указать систему счисления:
16 - шестнадцатеричная
1 О - десятичная ( по умолчанию)
int(
счисления])

[,

система

8 - восьмеричная
2 -двоичная
Пример:



>>> int(5.5), int( 11 50 11 , 10),
int( 11 0xfff 11 , 16), int( 110o555 11, 8)
(5, 50, 4095, 365)

·---------------·-·-···-·----·----------------··--··········-·--·--------·-·······-

Python. Полное руководство

max()
min()

oct()

роw(, [,

К])

...................................tl1python
,,-,:;,,·

Возвращают максимальное/минимальное зна­
чение из заданного списка. Список задается
через запятую:
>>> max(4, 7, 5), min(l, 4, 9)
(7, 1)

Преобразует десятичное число в восьмерич­
ную систему, возвращает строку
Возводит указанное число в указанную сте­
пень. Последний параметр задает остаток от
деления, то есть если он указан, то возвращает­
ся остаток от деления ( число возводится в сте­
пень, делится. на К и возвращается остаток).
Например:
>>> pow (5, 2), pow (1О, 2, 2),
pow (1О, 2, 3)
(25,

round([, NJ)

О,

1)

Округляет число до ближайшего меньшего
целого для чисел с дробной частью меньше 0.5
или до ближайшего большего целого для чи­
сел с дробной частью больше 0.5. Если дробная
часть равна 0.5, округление производится до
ближайшего четного числа. Второй необяза­
тельный параметр N задает число знаков после
точки. Пример:
>>> round(0.33), round(l.7),
round(0.51)
(О, 2, 1)

sum(
[, NJ)

Возвращает сумму значений элементов после­
довательности плюс N (N - это начальное зна­
чение). Примеры:
>>> s um ( [ 1, 2, 3 ] ), s um ( [ 1 , 2, 3 ] ,
1)
( 6, 7)



t:ID--·················································································

О python____________________

Глава 5. Математические функции

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

5.2.1. ОКРУГЛЕНИЕ ЧИСЛОВЫХ ЗНАЧЕНИЙ
Часто нужно округлить число с плавающей запятой к числу с фиксирован­
ным числом десятичных знаков. Для простого округления можно исполь­
зовать встроенную функцию round(value, ndigits). Например:
>>> round(l.24, 1)
1.2
>>> round(l.28, 1)
1.3
>>> round(-1.29, 1)
-1.3
>>> round(l.25371,3)
1.254

>>>

Функция round() округляет промежуточные значения к ближайшей четной
цифре. То есть значения, такие как 1.5 или 2.5 будут округлены к 2. Число
разрядов, передаваемых функции round() может быть отрицательным, ког­
да округление имеет место для десятков, сотен, тысяч и т.д. Например:
>>> а = 2625531
>>> �ound(a, -1)
2625530
>>> round(a, -2)
2625500
>>> round(a, -3)
2626000
>>>

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



·-------------····-·····------------·-···--···-·-·-····-···-·-·-···----·-···-------

Python. Полное руководство

____ ..............................

f!J;python
·,,4

>>> х = 1.234567
>>> format(x, '0.2f')

'1.23'

>>> format (х, 'О.3f')

'1.235'

>>> 'nшnЬer - {:0.3f}' .format(x)

'number - 1.235'
>>>

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

а = 3.1
Ь = 4.2
с = а + Ь
с

7.300000000000001

>>> с = round(c, 2)
>>> с

7.3
>>>

Для большинства приложений, работающих с плавающей точкой, просто
рекомендуется это сделать. Если для вас важно предотвращение таких оши­
бок (например, в финансовых приложениях), рассмотрите использование
модуля decimal.

5.2.2. ФОРМАТИРОВАНИЕ ЧИСЕЛ ДЛЯ ВЫВОДА
Для форматирования одного числа для вывода, используется встроенная
функция format(). Например:
>>> х = 9876.54321

>>>#Два десятичных места точности

>>> format(х, 'О.2f')

'9876.54'
>>>#Выравнивание по правому краю, 10 символов, 1 разряд
точности
>>> format(x, '>10.lf')

9876.5'
>>>#Выравнивание по левому краю, 1 разряд

>>> format(х, '>>#Выравнивание по центру



411D" ---------------------------------------------------------------------------------.


r!:.
t python
№J

--------------------

Глава 5. Математические функции

>>> format(х, '"10.lf')

9876.5
>>>#Добавление разделителя тысяч

>>> format(x, ',')

'9,876.54321'

>>> format(x, '0,.lf')

'9,876.5'
>>>

Если вы хотите использовать экспоненциальную запись, измените f на е
или Е, в зависимости от регистра, который вы хотите использовать для экс­
поненциального спецификатора. Например:
>>> format(x, 'е')

'9.876543е+03'

>>> format(x, '0.2Е')

'9.88Е+03'
>>>

Общая форма ширины и точности в обоих случаях - '[ л]?width[,]?(.
digits)?', где width (ширина) и digits (разряды) - целые числа, а? показы­
вает дополнительные части.
Форматирование значений с разделителем тысяч тоже не проблема. Одна­
ко сам разделитель зависит от настроек лакали, поэтому желательно иссле­
довать функции из модуля locale. Вы можете заменить символ разделителя,
используя метод translate() строки. Например:
>>> separators = { ord('.'): ',', ord(','):'.' }
>>> format(x, ',') .translate(separators)

'9.876,54321'
>>>

5.3. Математические функции
Математические функции содержатся в модуле math, поэтому перед их ис­
пользованием вам нужно импортировать этот модуль:
import math



··················································································-tD

Python. Полное руководство

Для работы с комплексными числами нужно импортировать модуль cmath:
irnport crnath
В модуле math можно найти следующие константы:
• pi - возвращает число Пи
• е - возвращает значение константы е
Пример:
>>> irnport rnath
>>> rnath.pi
З.141592653589793
>>> rnath.e
2.718281828459045
Математические функции приведены в таблице 5.2.
Таблица 5.2. Математические функции (модуль math)

Описание

Функция
atan2(x, у)

Аналогично atan(x/y). Если у равен О, то возвращается
pi/2

ceil(x)

Возвращает наименьшее вещественное число с нулевой
дробной частью - большее, чем число х

ехр(х)

Возвращает е**х

fabs(x)

Возвращает абсолютное значение числа х

floor(x)

Наибольшее вещественное число с нулевой дробной частью - меньшее, чем число х

fmod(x, у)

Возвращает остаток от деления х на у и эквивалентно х%у

hypot(x, у)

Возвращает длину гипотенузы прямоугольника со сторонами длиной х и у и эквивалентно sqrt(x*x+y*y)



418-··:···············································································

Глава 5. Математические функции

,.,.1 python

log(x), log10(x)

Натуральный и десятичный логарифм числах

modf(x)

Возвращает кортеж из пары вещественных чисел - дробной и целой части х

sin(x), cos(x),
tan(x), asin(x),
acos(x), atan(x)

Всем известные стандартные и обратные тригонометрические функции ( синус, косинус, тангенс, арксинус, арккосинус, арктангенс). Значение возвращается в радианах

sinh(x),
cosh(x),

Гиперболические синус, косинус, тангенс числах

sqrt(x)

Корень квадратный числа х

t,шh{x)

Поскольку функции не являются встроенными, использовать их нужно так:
import math
math.log(10)
2.302585092994046
math.logl0(l0)
>>> math.logl0(l0)
1. О

>>>
>>>

5.4. Случайные числа. Модуль
random
Модуль random() содержит функции для работы со случайными числами:
import random
Функции, предоставляемые этим модулем, приведены в таблице 5.3.

Таблица 5. З. Функции для работы со случайными числами
Функция

Описание

random()

Возвращает случайное вещественное
число r, находящееся в диапазоне О.О <
г > irnport randorn
>>> randorn.randorn()
О.9922129256765113
>>> randorn.uniforrn(l,100)
64.5126755129645
>>> randrange(l,100,1)
Traceback (rnost recent call last):
File "", line 1, in
randrange(l,100,1)
NarneError: narne 'randrange' is not defined
>>> randorn.randrange(l,100,1)
54
Обратите внимание: если вызвать функцию без названия модуля, то вы по­
лучите сообщение об ошибке. Понимаю, что не очень хочется "таскать" за
собой название модуля (пакета). Поэтому можете использовать конструк­
цию:



О python____________________

Глава 5. Математические функции

from random import *
После этого можно использовать функции как обычно:
rando_m()
О.9942452546319136

>>>

Аналогично, вы можете импортировать все функции из math и использо­
вать их подобно встроенным функциям.
Функция random.choice() может использоваться для выбора случайного
элемента последовательности:
>>>
>>>
>>>

import random
seq = [8, 7, 6, 5, 4' 3, 2, 1]
random.choice(seq)

>>>

random.choice(seq)

>>>

random.choice(seq)

5
1

7

Случайная выборка из N элементов с использованием random.sample():
random.sample(seq, 3)
[6' 7, 5)
>>> random.sample(seq, 3)
[2, 3, 4)
>>>

Если вам нужно просто перемешать элементы последовательности, исполь­
зуйте random.shuffle():
>>>
>>>
[5,

random.shuffie(seq)
seq
1,

6, 8,

3,

2,. 4, 7)

Чтобы создать случайные целые числа, используйте random.randint():
>>>

random.randint(0,100)

>>>

random.randint(0,100)

47

97

Модуль random вычисляет случайные числа, используя алгоритм Вихря
Мерсенна (Mersenne Twistei-). Это - детерминированный алгоритм (то есть
его не нужно предварительно инициализировать, как в РНР), но вы можете



·----------------------------------------------------------------------------------

'1

уtlюп, Полное PYKOБf•iJCТ!,J

....................................... ....... .python
.
··

настроить генератор случайных чисел на другу� последовательность, ис­
пользуя функцию random.seed():
random. seed ()

5.5. Значения lnfinity и NaN
В Python есть два специальных значения:
• inf - бесконечность
• NaN (Not а Nymber) - не число
У Python нет специального синтаксиса для представления этих специаль­
ных значений с плавающей запятой, но они могут быть созданы с помощью
функцииflоаt(). Например:
>>> а = fl.oat( 'inf')
>>> Ь = float('-inf')
>>> с = float('nan')
>>> а
inf
>>> ь
-inf
>>> с
nan

>>>

Для проверки на присутствие таких значений используйте функции math.
isinf() и math.isnan(). Например:
>>> math.isinf(a)
True
>>> math.isnan(c)
True

>>>

5.6. Вычисления с большими
числовыми массивами. Библиотека
NumPy
_____________________ , ________________________________________________ , ____________

,

О python__ __________________

Глава 5. Математические функции

Иногда .появляется необходимость производить вычисления с огромны­
ми наборами данных, представленными в виде массивов или таблиц. В
Python для этого принято использовать библиотеку NumPy.
Основное назначение NumPy - то, что она предоставляет объект массива,
который более эффективен и лучше подходит для математических вычис­
лений, чем стандартный список Python.
Рассмотрим простой пример, иллюстрирующий важные различия между
массивами NumPy и списками:
>>># Списки Python
>>> х = [1, 2, 3, 4]
>>> у = [5, 6, 7, 8]
>>> х * 2
[1, 2, 3, 4, 1, 2, 3, 4]
>>> х + 10
Traceback (most recent call last):
File "", line 1, in
TypeError: can only concatenate list (not "int") to list
>>> х + у
[1, 2, 3, 4, 5, 6, 7, 8)
>>>#Массивы Numpy
>>> import numpy as np
>>> ах = np.array([l, 2, З, 4])
>>> ау= np.array([S, 6, 7, 8])
>>> ах * 2
array([ 2, 4, 6, 8))
>>>_ах + 10
array( [11, 12, 13, 14))
>>> ах + ау
array([ 6, 8, 10, 12))
>>> ах * ау
array([ 5, 12, 21, 32))
>>>

Как видите, основные математические операции с массивами ведут себя
иначе. В частности, скалярные операции (например, ах* 2 или ах+ 10) при­
меняются к массиву поэлементно (в случае с обычным списком нужно было
писать цикл и добавлять в цикле 10 к каждому значению списка). Кроме
того, математически операции, когда оба операнда являются массивами,
применяются к каждому элементу и в результате создается новый массив.
Библиотека NumPy просто огромна и можно ей посвятить отдельную книгу. Посетите сайт http.//www.numpy.org для дополнительной информации.



·---------------------········-···-··········-··-·······-··-·-·-·-···--·-·-·-·-··--

Python. Полное руководство

f6python

--------------------- U,.;

5.7. Программа "Угадай число

п

5.7.1. ПОСТАНОВКА ЗАДАЧИ
Сейчас мы напишем программу "Угадай число", которая будет демонстри­
ровать следующее:
• Работу с генератором случайных чисел
• Использование цикла while
• Прерывание итерации
Работа с циклами была рассмотрена ранее, а сейчас, так сказать, мы теорию
закрепим практикой.
Алгоритм работы будет такой:
• В цикле мы "загадываем" случайное число от 1 до 10
• Затем просим пользователя отгадать это число
• Если число правильное, мы выводим соответствующее сообщение и уве­
личиваем значение переменной score
Также будет показано, как исправить логическую ошибку в программе.

5. 7 .2. РАБОТА С ГЕНЕРАТОРОМ СЛУЧАЙНЫХ ЧИСЕЛ
Для подключения генератора случайных чисел нужно импортировать мо­
дуль random:
import random

Далее нужно вызвать функцию randint(), передав ей начальное и конечное
значение. Возвращенное случайное число будет лежать в диапазоне между
ними:
random.randint(l i 10)

В модуле random также есть функция randrange(), возвращающая случай­
ное целое число в промежутке от О до преданного в качестве параметра знаt:ID---·········--·-················································--·-··········----·

Глава 5. Математические функции

чения (но, не включая само значение), то есть вызов randrange(10) вернет
числа от О до 9 включительно.
Как по мне, то проще использовать randint(), чем randrange(). Но это смо­
тря, что вам нужно.

5.7.3. КОД ПРОГРАММЫ
Код программы, д�йствительно, очень прост (лист. 5.1).

Листинг 5.1. Код программы "Угадай число"
import random
print("*" * 10, "Угадай число", "*" * 10)
рrint("Компьютер выберет случайным образом число от 1 до 10.
Попробуй угадать это число. Для выхода введите 0")
answer = 1;
score = О;
i = о
while answer:
i = i + 1
rand = random.randint(l, 10)
answer = int(input("Bвeдитe число: "))
if answer == rand:
score = score +1
рrint("Правильно! Ваш счет: ", score, " из "
else:
рrint("Попробуйте еще раз!")

i)

print("Дo встречи!")

Программа ничего сверхъестественного не делает. В цикле while она про­
веряет введенное пользователем значение. Если оно совпадает со сгене­
рированным в начале итерации случайным значением, значит, выводится
соответствующее сообщение и увеличивается значение переменной score.
Параллельно мы ведем счетчик итераций, чтобы знать, сколько попыток со­
вершил пользователь (переменная i).
Посмотрим на вывод программы:

•.

********** Угадай число **********

- . - . - -.. -- . - ·_· - .. -- - - ... - . - - -- -- . -..... - . - - - .. -.. -.. - ........... -... - . -.. -- -... -

.....

_____________________О

Python. Полное руководство

python

Компьютер выберет случайным образом число от 1 до 10. Попробуй
угадать это число. Для выхода введите О
Введите число: 9
Попробуйте еще раз!
Введите число: 8
Правильно! Ваш счет: 1 из 2
Введите число: 5
Правильно! Ваш счет: 2 из 3
Введите число: 2
Попробуйте еще раз!
Введите число: о
Попробуйте еще раз!
До встречи!

5. 7 .4. ИСПРАВЛЕНИЕ ЛОГИЧЕСКОЙ ОШИБКИ В
ПРОГРАММЕ
Все бы хорошо, но в нашей программе есть одна логическая ошибка и как
минимум одна недоработка. Для выхода пользователь должен ввести О. Но
посмотрите, что происходит при этом.
Программа считает О ... еще одним вариантом, но никак не признаком вы­
хода, поэтому она сообщает, что введенный вариант неправильный. Но он
и не может быть правильным, поскольку случайные числа генерируются в
диапазоне от 1 до 100.
Исправить эту ошибку можно, если добавим конструкцию:
if answer == О:
break
Данную инструкцию нужно добавить в самое начало тела цикла. Если поль­
зователь введет О, выполнение будет прервано. Также было бы неплохо, что­
бы программа выводила статистику по окончанию игры:
рrint("Общий счет: ", score, " из ", i)
Измененный код приведен в листинге 5.2.

Листинг 5.2. Окончательный вариант
import random
print("*" * 10, "Угадай число", "*" * 10)



..................................................... � ............................. .

tfjjpython
¼!1.,
- - - - - - - - - - - - -·- - - - - - -

Глава 5. Математические функции

рrint("Компьютер выберет случайным образом число от 1 до 10.
Попробуй угадать это число. Для выхода введите 0")
answer = 1·;
score = О;
i = о
while answer:
rand = randorn.randint(l, 10)
answer = int(input("Bвeдитe число: "))
О:
if answer
break
if answer ==,rand:
score = score +1
рririt("Правильно!")
else:
рrint("Попробуйте еще раз!")
i = i + 1
print("Общий счет " score, " из "
print("Дo встречи!")

i)

Вывод программы будет следующим:
********** Угадай число **********
Компьютер выберет случайным образом число от 1 до 10. Попробуй
угадать это число. Для выхода введите О
Вве�ите число: 7
Попробуйте еще раз!
Введите число: 5
Попробуйте еще раз!
Введите число: 4
Попробуйте еще раз!
Введите число: о
Общий счет 1 из 3
До встречи!

Бот теперь все правильно и работает как нужно!
Примечание. При чтении данных мы не производим проверку
их·корректности. Если пользователь введет строку вместо чис­
ла, то выполнение программы будет установлено, а на консоли

•. --. -. -.-----.......·....................-...-.....................................tD

Python. Полное руководство

____________________О

python

будет изображено сообщение об ошибке. Для обработки таких
ситуаций используются блоки try.. except, которые мы пока рас­
сматривать не будем.



tllt·····················------------------------------------------------------------·

ГЛАВА 6.
СТРОКИ И CTPOKOBblE
ФУНКЦИИ

Python. Полное руководство

- ·---· -----------------

i!il python
"ft)

6.1. Что такое строка? Выбор кавычек
Строка - это упорядоченная последовательность символов. Можно даже
сказать, что строка - это массив символов, поскольку массив - это и есть
упорядоченная последовательность.

Строки поддерживают обращение по индексу, конкатенацию(+ ), повторе­
ние(*), проверку на вхождение(in).
В Python строковые значения принято заключать в кавычки - двойные или
одинарные. Компьютеру все равно, главное, чтобы использовался один и
тот же тип открывающейся и закрывающейся кавычки, например:
рrint("Привет")
print('Привет')

Эти операторы выведут одну и ту же строку. При желании можно, чтобы
строка содержала,кавычки обоих типов:
print("Привет, 'мир'!")



t:ID------------------------.--------------. --------.----.. --.---. --------.-. --.---.-..

8'�python
{\\J

-------··---------

Глава 6. Строки и строковые функции

Здесь внешние кавычки (двойные) используются для ограничения строко­
вого значения, а внутренние выводятся как обычные символы. Внутри этой
строки вы можете использовать сколько угодно одинарных кавычек.
Можно поступить и наоборот - для ограничения использовать одинарные
кавычки, тогда внутри можно будет использовать сколько угодно двойных
кавычек:
рrint('Привет, "мир"!')

Использовав кавычки одного типа в роли ограничителей, вы уже не сможе­
те пользоваться ими внутри строки. Это целесообразно, ведь второе по по­
рядку вхождение открывающей кавычки компьютер считает концом стро­
ки.
Функции print() можно передать несколько значений, разделив их запяты­
ми:
рrint("Привет",
"мир!")

Иногда такой прием используют, чтобы сделать код более читабельным.
Если вы внимательно читали предыдущие· главы, то знаете, что строки яв­
ляются неизменяемыми типами данных. Именно поэтому почти все строко­
вые методы в качестве значения возвращают новую строку, а не изменяют
существующую. С одной стороны, это хорошо - вам не нужно беспокоить­
ся, что что-то пойдет не так. С другой, при работе с большими объемами
данных можно столкнуться с нехваткой памяти.
Помните, что вы можете получить символ строки по индексу, но изменить
строку, то есть изменить этот символ, как можно было в других языках
программирования, нельзя:
>>> str = "Hello"
>>> str(l]
'е'
>>> str(l] = "r"
Traceback (most recent call last):
File "", line 1, in
str(l] = "r"
TypeError: 'str' object does not support item assignment
>>>

•. -.-.. -------.---.-..--.--................... -......-.-. -.-.-...-..-...-........ -. -811)

Python. Полное руководство

,8:,tpython

Python поддерживает следующие строковые типы: str, bytes и bytearr. Пер­
вый тип - это обычная Unicode-cтpoкa. Символы хранятся в некоторой
абстрактной кодировке, а при выводе вы можете указать нужную вам коди­
ровку с помощью метода encode():
>>> s = "Привет"
>>> s.encode(encoding="utf-8")
b'\xd0\x9f\xdl\x80\xd0\xb8\xd0\xb2\xd0\xb5\xdl\x82'
>>> s.encode(encoding="cpl251")
b'\xcf\xf0\xe8\xe2\xe5\xf2'

Тип bytes - это неизменяемая последовательность байтов . Каждый элемент
такой последовательности может хранить целое число от О до 255, обозна­
чающее код символа. Этот тип поддерживает большинство строковых мето­
дов, однако при доступе по индексу возвращается целое число, а не символ:
>>> s = bytes("hello", "utf-8")
>>> s[O], s[l], s[2]
(104, 101, 108)
>>> s
b'hello'

Некоторые строковые функции некорректно работают с типом bytes. На­
пример, функция len() возвращает количество байтов, которые занимает
строка в памяти, а не количество символов:
>>> len("hello")
5
>>> len(bytes("Привет", "utf-8"))
12

В кодировке UTF-8 для кодирования одного символа используется два бай­
та, поэтому результат - 12, а не 6.
Тип bytearray - это изменяемая последовательность байтов. Данный тип
аналогичен типу bytes, но вы можете изменять элементы такой строки по
индексу. Также этот тип содержит дополнительные методы, которые позво­
ляют добавлять и удалять элементы:
>>> s = bytearray ("hello", "utf-8")
>>> s[O] � 50; s
bytearray(b'2ello')

�,python

Глава 6. Строки и строковые функции

6"2. Создание строки
Создать строку можно, указав ее между апострофами или двойными кавыч­
ками, как уже было показано выше:
>>> а = "hello"; а
'hello'
"hi!"; Ь
>>> Ь
'hi ! '

Данные строки ничем не отличаются, и вы можете использовать любой
способ, какой вам больше нравится. В РНР есть разница между строками,
заключенными в кавычки и в -апострофы. В Pythoв разницы никакой нет.
Мой совет следующий: если строка содержит апострофы, заключайте ее в
кавычки, если же строка содержит кавычки, то заключайте ее в апострофы.
Все специальные символы в этих строках (что в кавычках, что в апостро­
фах) интерпретируются, например,\t - это символ табуляции,\в - символ
новой строки и т.д. Если нужно вывести символ\ как есть, его нужно экра­
нировать:
>>> s = "Hello\\nworld"; s
'Hello\\nworld'

При использовании кавычек и апострофов вы не можете разместить объект
на нескольких строках. Если нужно присвоить переменной многострочный
текст, используйте тройные апострофы:
>>> s = '''Hello,
****
world ! **** '''

Также создать строку можно с помощью функции str():
str(, , )

Преимущество этой функции - вы сразу можете указать кодировку, в кото­
рой находится текст:
>>> s = str(b'\xd0\x9f\xdl\x80\xd0\xb8\xd0\xb2\xd0\xb5\xdl\
х82', 'utf-8'); s
'Привет'

Python. Полное руководство

�-

.··.
6python
--�-------------------

Обратите внимание: ранее мы использовали пример, в котором мы пере­
менной str присваивали значение. Этим мы переопределили идентифика­
тор str и при вызове функции str вы можете получить сооfiптРние:
Traceback (most recent call last):
File 11 11 , line 1, in
s = str(b'\xd0\x9f\xdl\x80\xd0\xb8\xd0\xb2\xd0\xb5\xdl\
х82', 'utf-8');
TypeError: 'str' object is not callaЫe

По привычке многие из нас используют идентификатор str для хранения
какой-то промежуточной строки. В отличие от РНР, где можно использо­
вать переменную $str и функцию str(), в Python этого лучше не делать, ина­
че вы не сможете использовать функцию str(). Да, РНР более гибкий язык
и в нем вы легко можете использовать следующий код:


Одни сплошные идентификаторы str, но в Python так делать нельзя. Зато
в Python есть строки документирования, которые сохраняются в атрибуте
_doc_. Пример:
>>> def funcl () :
1111 11
Краткое описание
pass

>>> print(funcl. doc )
Traceback (most recent call last):
File 11 11, line 1, in
print(funcl._doc )
AttributeError: 'function' object has по attribute ' doc '
>>> print(funcl. doc
Краткое описание
>>>



CD-------------------------. ------. -·-. ---. -----------------------------------------.

+python__________________

Глава 6. Строки и строковые функции

Обратите внимание, что имя атрибута содержит четыре знака подчеркива­
ния - два до doc и два после, иначе вы получите ошибку.
Перед некоторыми строками необходимо разместить модификатор r. Спе­
циальные символы внутри строки будут выводиться как есть, например, \t
не будет преобразован в символ табуляции. Такой модификатор будет поле­
зен, если вы работаете с регулярными выражениями, а также при указании
пути к файлу и каталогу:
>>> print(r"c:\test\test.py")
c:\test\test.py
Если модификатор не указать, то слеши нужно экранировать:
>>> prini("c:\\t�st\\test.py")
c:\test\test.py

6.3. Тройные кавычки
Иногда есть большой фрагмент текста, который нужно вставить в програм­
му как есть, и вывести в неизменном виде. Конечно, для этого лучше ис­
пользовать файлы - записать текст в файл, потом в программе прочитать
текст из файла и вывести -его. Но не все программисты хотят усложнять
программу- и если программа несложная, то можно весь код хранить в од­
ном файле, чтобы ничего не потерялось.
Для вывода текста как есть используются тройные кавычки. В листинге 6.1
мы рассмотрим небольшую программу, выводящую инструкцию по исполь­
зовании абстрактной программы. Когда мы будем изучать функции, дан­
ный вывод можно будет оформить в отдельную функцию, а пока разберем­
ся, как работают тройные кавычки.
Листинг 6.1. Вывод многострочного текста прямо из
программы
print("""
Использование: program· -if [-of ]
-if: входной файл
-of: результирующий файл. Если не указан, будет использован
стандартный вывода



·········································-········································at

Python. Полное руководство

·__ • ___________________• python

''"")

# Ждем, пока пользователь нажмет Enter
inрut("\Нажмите Enter для выхода\n") # Это тоже комментарий
Текст, заключенный между парой тройных кавычек(""" текст""" ) выводит­
ся, как есть - сохраняется форматирование, переносы строк и т.д. Тройные
кавычки существенно облегчают вывод многострочного текста.

6�4" Специальные символы
Внутри строк в Python можно использовать специальные символы, то есть
комбинации символов, которые обозначают служебные или непечатаемые
символы, которые нельзя вставить обычным способом. Наверняка, вам зна­
комы эти символы по другим языкам программирования'(см. табл. 6.1 ).
Таблица 6. 1. Специальные символы

Специальный символ

Что представляет

\r

Возврат каретки

\n

Перевод строки

\t

Табуляция

\v

Вертикальная табуляция



Забой

\f

Перевод формата



. Звонок(BELL)



Нулевой символ

\'

Апостроф

\"

Кавычка

\n

n - восьмеричное значение (код символа),
например, \50(символ 2)






at-------------------·-·················-·---····--··--·-·····--·--·--·····-·-------·

ф python_________________ _

Глава 6. Строки и строковые функции

\хо

·n - hех-значение символа (код символа), например, \xSb соответствует символу [

\\

Обратный слеш

\uxxxx

16-битный символ Unicode

\Uxxxxxxxx

32-битный символ Unicode

6.5. Действия над строками
Строки поддерживают следующие операции:
• Обращение к элементу по индексу;
• Срез;
• Конкатенация;
• Проверку на вхождение;
• Повтор.

6.5.1. ОБРАЩЕНИЕ К ЭЛЕМЕНТУ ПО ИНДЕКСУ
Ранеебыло показано, как обратиться к отдельному символу строки. Нуме­
рация символов начинается с О:
>>> s = "123"
>>> s[O], s[l], s[2]
( 11', 121, 131)

Если обратиться к несуществующему символу строки, получите следую­
щую ошибку:
>>> s[3]
Traceback (most recent call last):
File "", line 1, in
s[3]
IndexError: string index out of range

• -- --- -- -.-- --

- - - - - - - - - - . - . - -- . - - -- - -- - -- - --........... -- ............ -- - .... - .. -- .

-

'

Python. Полное руководство

�. ½ ................................................

Apython
,&'>"J

Вы можете указать отрицательное значение индекса. В этом случае отсчет
будет с конца строки:
»> s[-1], s[-2], s[-3]
('3', '2', '1')

6.5.2. СРЕЗ СТРОКИ
Очень интересной является операция среза строки. Ее формат следующий:
[::]

Интересна она хотя бы даже тем, что все три параметра являются необяза­
тельными. Например, если не указан параметр , то по умолчанию
будет использовано значение О. Если не указан параметр , то будет
возвращен фрагмент до конца строки. И, если не указан , будет ис­
пользоваться шаг 1. В качестве всех tpex параметров :можно· указать отри­
цательные значения.
>>> s = "Hello"
>>> s [:]
# Фрагмент от позиции О до конца строки
'Hello'
>>> s[:-1:]
# Отрезаем последний символ строки
'Hell'
>>> s[l::2]
# Начиная с позиции 1 до конца строки, шаг 2
'el'
>>> s[1::]
# Отрезаем первый символ
'ello'

Поэкспериментируйте с операцией среза - я уверен, что вам она понра­
вится.

6.5.3. КОНКАТЕНАЦИЯ СТРОК
Конкатенация строк бывает явной и неявной. Явная - это использование
оператора+, а неявная - это указание двух или более строк рядом - через
пробел:
>>> print("l" + "2")
12
>>> print("l" "2")
12

...

.- .-.- -- -.- ----.--.- .-.-. - -- . -- "

"

"."

•.

-- ------ --- - -- -- ---------- ------ --- ----."

"

"

---------- -- ---- - - -

Глава 6. Строки и строковые функции

•python

Преобразовать несколько строк в кортеж можно с помощью запятой, например:
>>> s = "1", "2"
>>> type(s)



Как видите, мы получили тип tuple - кортеж, а не строку. Вот только пом­
ните, что вы не можете выполнить неявную конкатенацию строки и пере­
менной, например:
>>> print("3" s),

SyntaxError: invalid syntax

6.5.4. МРОВЕРКА НА ВХОЖДЕНИЕ
Проверить, входит ли подстрока в строку, можно с помощью оператора in:
>>> "hell" in "Hello"

False

>>> "hell" in "hello"

True

Оператор in, как вы уже успели заметить, чувствителен к регистру символов.

6.5.5. ПОВТОР
Оператор * позволяет повторить строку определенное число раз, например:
>>> print("*" * 20)

********************

6.5.6. ФУНКЦИЯ LEN()
Функция len() возвращает количество символов в строке. Напомню, что с
байтовыми строками эта функция работает некорректно и возвращает ко­
личество байтов, которые занимает строка:

······-···········································································-

Python. Полное руководство

6python
---------------------- тю

>>> len("123456")
6
Функцию len() можно использовать для перебора всех символов строки:
>>> s = "123456"
>>> for i in range(len(s)): print(s[i], end=" ")
1 2 3 4 5 6

Помните, что в случае с Uniсоdе-строками количество байтов, необходи­
мых для хранения символов строки, превышает само число символа, и вы
можете получить ошибку выхода за пределы диапазона.

6.6� Форматирование строки и метод

format()

6.6.1. ОПЕРАТОР ФОРМАТИРОВАНИЯ%
Программистам, знающим язык С, з1-щкомая функция printf(), выводящая
строку в определенном формате. Язык Python также поддерживает форма­
тирование строки. На данный момент в Python подд�рживается два способа
форматирования текста:
• Оператор%
• Метод format()

В следующей версии Python оператор % могут удалить, поэтому настоя­
тельно рекомендуется использовать методfоrтаt(). Но не рассмотреть, хотя
бы вкратце, оператор % мы не можем, поскольку все еще есть множество
кода, написанного с использованием этого оператора. Формат оператора%
следующий:
%
Синтаксис описания формата такой:
% [ ()] [] [] [.]
Пример использования оператора форматирования:



at--------------······--------·-·--·--···-····-···-···--------------·----·------·-·-·

О python__________________

Глава 6. Строки и строковые функции

>>> "%s/%s/%s" % (30, 10, 2020)
'30/10/2020'

Рассмотрим параметры формата. Первый параметр - . Он задает
ключ словаря,. если он задан, то в параметре нужно указать
словарь, а не кортеж. Вот пример:
>>> "%(car)s - %(year)s" % {"car"
'nissan "'" 2021'

"nissan", "year"

2021}

Параметр - это флаг преобразования, который может содержать
следующие значения:
• # - для восьмеричных значений добавляет в начало символы Оо, для
шестнадцатеричных - Ох, для вещественных чи.сел - будет выводиться
точка, даже если дробная часть равна О
• О - если указан, будут выводиться ведущие нули для числового запол­
нения
• - - задает выравнивание по левой границе области
• пробел - добавляет пробел перед положительным числом, перед отри­
цательным будет выводиться
• + - обязательный вывод знака, как для отрицательных, так и для поло­
жительных чисел
Примеры:
>>> "%#х %#х" % (0xfff, 100)
'0xfff Ох64'
>>> "%+d %+d" % (�3, 3)
'-3 +3'

Параметр определяет минимальную ширину поля, но если
строка не помещается в указанную ширину, то значение будет проигнори­
ровано и строка будет выведена полностью. Пример:
>>> "'%10d' - '%-l0d'" % (5, 5)
'"
5' - '5

"'

'- -- - - - - - - - - - - - - - - - _._ - - - - - - - - - - - - - - - - - - - - - - - - - - - -... - -.-.-.-.- -..- -.......- -.-....

-

Python. Полное руководство

tf,;python

' ........................................ 'xiw

Параметр задает количество знаков после точки для веще�
ственных чисел:
>>> from math import *
>>> "%s %f %.2f" % (е, е, е)

'2.718281828459045 2.718282 2.72'

Последний параметр является обязательным и может
содержать следующие значения:
• а - пытается преобразовать любой объект в строку, используя функцию
ascii();
• с - выводит одиночный символ или преобразует число в символ;
• d (i) - возвращает целую часть числа;

• е - вещественное число в экспоненциальной форме (буква "е" в нижнем
регистре);
• Е- вещественное число в экспоненциальной форме (буква "е" в верхнем
регистре);
• f (F) - используется для вывода вещественного числа;
• g - то же самое, что f или Е (используется более короткая запись числа);
• G - то же самое, что F или Е (используется более короткая запись чис­
ла);
• s - пытается преобразовать любой объект в строку (с помощью функции
str());
• r - то же, что и s, но для преобразования в строку вместо функции str()
будет использоваться функция repr();
• о - выводит восьмеричное значение;
• х - шестнадцатеричное значение в нижнем регистре;
• Х - шестнадцатеричное значение в верхнем регистре.
Ранее было продемонстрировано использование модификаторов d, • f, s, х.
Остальные модификаторы используются аналогично. Вместо множества
примеров, которые вы и сами можете провести, я подскажу, как правильно
нужно использовать модификаторы формата и оператор %.

---

--



- - - . - - - - - - ..... - .. - . .......... -................. -.. - ............ -....... -.. .... .

• python__________________

Глава 6. Строки и строковые функции

Представим, что у нас есть НТМL-шаблон, который нужно заполнить дан­
ными. В этом случае идеально подходит оператор% (лист. 6.2).
Листинг 6.2. Правильное использование оператора %
template = """

%(title)s


%(text)s

"""
data = 1 "title": �мой сайт",
"text": "Контент"}
print(template % data)

Переменная template содержит код шаблона, а переменная data - данные
шаблона. Затем последним оператором мы заполняем наш шаблон данны­
ми. Результат изображен на рис. 6.1.
х

о

[i& IDLE Shell З.92
[ile fdit She/1 Oebug Qptions .. Window Help

Python З.9.2 (tags/vЗ.9.2:la79785, FеЬ 19 2021, 13:44:55) [MSC v.1928 64 Ьit (АМ
D64)] on win32
ТуРе "help",. •copyright", •credits• or "license () • for more information.
>>>
--------------- RESTART: C.\temp\py\6-2.py ------------------

Moй caйт


Контент


>» 1



Рис. 6. 1. Результат работы проrраммы из пистинrа 6.2.






·--------------------------------···-·-····-·----·- ·-·····--·-·-·---···-····-·····-

Python. Полное руководство

if,1, python

•••••••••••••••••••••• '>> s = "Hello"
>>> s.center(20)
Hello
>>> s.center(20, '*')
'*******Hello********'
>>> s.ljust(20)
'Hello
>>> s.rjust(20)
Hello'

6.6.3. МЕТОД FORMAT()
Метод format() используется для выравнивания строк, начиная с версии
Python 2.6. Пока еще оператор % оставлен из соображения обратной совме­
стимости с тоннами уже написанного кода, но в скором времени останется
лишь метод format().
Использовать этот метод нужно так:
= .format(*args, **kwargs)

Синтаксис строки формат следующий:
{ [] [!] [:] }

Начнем с параметра . В нем можно указать индекс позиции или
ключ. Помните, что нумерация начинается с О. Также можно комбинировать



.....................................................................................

Глава 6. Строки и строковые функции

,Ъpython

т,

------------------

именованные й ii:озици-6нные параметры. В этом случае в методе format()
именованные параметры используются в конце:
> » 11 О } /{1)/ {2} 11 fоrmat(3О , 1 О, 2О2О)
'30/10/2020'
>>> 11 {0}/{1)/(2) 11 .format(*arr)
'30/10/2020'
>>> 11 {firstname} {lastname} 11 format(firstname = 11 Ивaн 11
lastname= 11 Пeтpoв 11
'Иван Петров'
>>> 11 {lastname}, {0} 11 .format( 11 Ивaн 11 lastname= 1 Пeтpoв 11
'Петров, Иван'
{



,



)

,

1

)

Параметр позволяет задать функцию, с помощью которой об­
рабатываются данные перед их вставкой в строку. Если указано значение
"s", то данные обрабатываются функцией str(), если указано значение "r" функцией repr(), если "а", то- ascii(). По умолчанию используется функция
str().
В параметре указывается значение, имеющее формат:
[[]][][#][О][][,]
[.] []
По умолчанию выравнивание выполняется по правому краю, но вы можете
изменить это поведение с помощью параметра . Вы може­
те указать следующие значения:
• < - по левому краю;
• > - по правому краю;
• л - по центру;
• = - знак числа выравнивается по левому краю, а само число- по правому.
Пример:
>>> 1 {0:15)"'.format('Hello', 'Hello')
' '
'Hello
Hello 111
1

1

11

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

........ , .............·········· .................................................. -4IEEt

Python. Полное руководство

.

- -·- ....... - - .... -........ - .... - - -

f!apython
,.J,

>>> "'{0:*15)'".format('Hello', 'Hello')
"'Hello**********' ' &&&&&&&&&&Hello'"

Параметр управляет отображением знака числа:
• + - знак будет отображаться, как для положительных, так и для отрица­
тельных чисел;
• - - знак будет выводиться только для отрицательных чисел (по умолча­
нию);
• пробел - вместо + для положительных чисел будет вы�одиться пробел,
для отрицательных - минус.
Как и в случае с оператором %, параметр задает минимальную
ширину поля. Если строка не помещается в указанную ширину, то значение
будет проигнорирована и строка будет выведена полностью.
Переходим к самому важному параметру - . Для целых
чисел можно использовать следующие модификаторы формата:
• Ь - двоичное значение;
• с - преобразует целое число в соответствующий ему символ;
• d - десятичное значение;
• n - выводит десятичное значение с учетом настроек лакали;
• о - восьмеричное значение;
• х - шестнадцатеричное значение в нижнем регистре;
• Х - шестнадцатеричное значение в верхнем регистре.
Для вещественных чисел можно использовать эти модификаторы:
• f и F - вещественное число;
• е - вещественное число в эксnоненциальной форме (буква "е" в нижнем
регистре);
• Е- вещественное число в экспоненциальной форме (буква "е" в верхнем
регистре);
• g - то же самое, что f или Е (используется более короткая запись числа);
• G - то же самое, что F или Е (используется более короткая запись чис­
ла);






418----------------------------------------------------------------------------------·

• python______ 0 __________ _

Глава 6. Строки и строковые функции

• n - аналогично g, но с учетом настройки локали;
• % - умножает число на 100 и добавляет символ процента в конце.
Пример:
>>> "'{0:G}' '{1:e}'".format(pi, pi)
"'3 .14159' '3 .14159Зе+ОО'"

6.7. Функции и методы для работы со
строками
В Python очень много функций и методов для работы со строками. Данная
книга не является справочным руководством по Python (хорошо, что такое
руководство легко найти в Интернете), поэтому мы рассмотрим только ос­
новные функции и методы (табл. 6.1 и табл. 6.2). Некоторые из этих мето­
дов мы рассмотрим в этом разделе, некоторые - далее в этой главе.
Таблица 6. 1. Функции для работы со строками

Синтаксис

Описание

str( [])

Преобразует любой объект в строку. Если параметр не
указан, то возвращается пустая строка. Данная функция
используется функцией print() и некоторыми другими
функциями для вывода объектов

repr( )

Возвращает строковое представление объекта. Используется в IDLE для вывода объектов

ascii( )

Возвращает строковое представление объекта, при выводе объекта используются только АSСП-символы

len( )

Возвращает количество символов в строке

Примеры:



>>> str("Hello")

·---------------------------------------------------·····-····-·-·····--···--------

Python. Полное руководство

,!J� python

..... .;. __________________

·@}

'Hello'
>>> repr ("Hello")
"'Hello'"
>>> ascii("Hello")
"'Hello'"
>>> аsсii("Привет")
"'\\u041f\\u0440\\u0438\\u0432\\u0435\\u0442'"
>>> lеn("Привет")
6

Таблица 6.2. Строковые методы (основные)
Синтаксис
capitalize()

Описание
Делает прописной первую букву строки

center(width, [fill])

Возвращает отцентрованную строку, по
краям которой стоит символ fill (пробел по
умолчанию)

count(str, [start],[end])

Возвращает количество непересекающихся
вхождений подстроки в диапазоне [ начало,
конец] (О и длина строки по умолчанию)

endswith(str)

Заканчивается ли строка S шаблоном str

expandtabs( [tabsize])

Возвращает копию строки, в которой все
символы табуляции заменяются одним или
несколькими пробелами, в зависимости от
текущего столбца. Если TabSize не указан,
размер табуляции полагается равным 8 пробелам

find(str, [start],[end])

Поиск подстроки в строке. Возвращает номер первого вхождения или -1

fonnat(*args, **kwargs)

Форматирование строки



t:Ea-------·-------·--------·--···---·-······-·-·-··--··-------··-···-·-······------···

ф python__________________

Глава 6. Строки и строковые функции

index(str, [start],[end])

Поиск подстроки в строке. Возвращает номер
первого вхождения или вызывает ValueError

isalnum()

Состоит ли строка из цифр или букв. Возвращает true, если это так

isalpha()

Состоит ли строка из букв

isdigit()

Состоит ли строка из цифр

islower()

Состоит ли строка ИЗ СИМВОЛОВ в нижнем регистре

isspace()

Состоит ли строка из неотображаемых символов (пробел, символ перевода страницы
('\f), "новая строка" ('\n'), "перевод каретки" ('\r'), "горизонтальная табуляция" ('\t')
и "вертикальная табуляция" ('\v'))
"

istitle()
isupper()

join()

Начинаются ли слова
буквы

в строке с заглавной

Состоит ли строка из символов в верхнем регистре
Преобразует последовательность в строку.
Элементы разделяются через указанный разделитель. Метод используется так:
= .
jоi�()

ljust(width, fillchar= " ")

Делает длину строки не меньшей width, по
необходимости заполняя последние символы cимвoлoмfillchar
Переводит все символы строки в нижний регистр

•·-------------------------------------·---------------------------------------------lower()

Pytho11. Полное руководство

lstrip([])

partition( )

__ : _______......___...• python

Удаляет указанные или пробельные символы в начале строки
Находит первое вхождение разделителя в
строку и возвращает кортеж из трех элементов:
Фрагмент до разделителя
Символ-разделитель
Фрагмент после разделителя

replace(old, new [,rnax])

Возвращает строку, в которой вхождения
строки old заменены строкой new. Необязательный параметр max устанавливает наибалее возможное количество замен

rfind(str, [start],[end])

Поиск подстроки в строке. Возвращает номер последнего вхождения или -1

rindex(str, [start],[end])

Поиск подстроки в строке. �озвращает номер последнего вхождения или вызывает
ValueError

rjust(width, fillchar= " ")

Делает длину строки не меньшей width, по
необходимости заполняя первые символы
cимвoлoмfillchar

rpartition( )

То же, что и partition, но поиск разделителя
осуществляется справа налево

rsplit([[,
]])

То же, что и split, но поиск разделителя осуществляется справа налево

rstrip([])

Удаляет указанные или пробельные символы в конце строки



.....................................................................................

Глава 6. Строки и строковые функции

split( ([,
]])

startswith(str)

Разделяет строку на подстроки по указанному разделителю. Если не указан первый
параметр (или передано значение None), то
вместо разделителя используется пробел.
Второй параметр ограничивает
количество подстрок
Начинается ли строка S с шаблона str

strip([])

Удаляет указанные символы в начале и конце
строки. Если символы не указаны, удаляются пробельные символы: пробел, табуляция
(\t, \v), возврат каретки (\r), перенос строки
(\n)

swapcase()

Своп-регистра: верхний регистр заменяется
на нижний и наоборот

title()

Делает прописной первую букву каждого
слова

upper()
zfill(width)

Переводит все символы в верхний регистр
Делает длину строки не меньшей width, по
необходимости заполняя первые символы
нулями

Примеры:
# Обрезаем пробельные символы
>>> s = " Hello \n"
>>> s.strip()
'Hello'
# Методы работают с копией строки, поэтому исходная строка
# не изменяется
>>> s
Hello \n'
>>> s.lstrip ()
'Hello \n'
>>> s. rstrip ()
Hello'

•...............··-....-···-···--·-·-·---····-·-·-·---··-.............········-···--tв

Python. Полное руководство

/Apython

---------·------------- ''е(1/'

# Метод split() без параметров
s = "полеl поле2 полеЗ"
s.split()
[ 'полеl', 'поле·2', 'полез']
# Параметры None, 1
>>> s.split(None, 1)
[ 'полеl', 'поле2 полез']

>>>
>>>

# Метод partition()
s.partition(" ")
('полеl',
'поле2 полез')
>>> s.rpartition(" ")
('полеl поле2', ' ' 'полез')

>>>

# Пример использования join()
sep = " "
>>> sl = sep.join(["пoлel", "поле2"])
>>> sl
'полеl поле2'

>>>

# Функции изменения регистра
s = "hello, world"
s.capitalize()
'Hello, world'
>>> s.title()
'Hello, World'
>>> s. upper()
'HELLO, WORLD'
>>> s. lower()
'hello, world'
>>> s.swapcase()
'HELLO, WORLD'
>>.>
>>>

Также вы можете использовать функции chr() и ord(). Наверняка вы зна­
комы с ними, если знаете другие языки программирования. Первая возвра­
щает символ, соответствующий заданному коду, а вторая - возвращает код
символа:
>>>

chr(55)

>>>

ord('7')

>>>

chr (5055)

'7'

55

·�·



a!t--. -------.. -.. -....-. -..........-.-. -......-. -......-... -. -.-..-. -. -.-.. -..--. -� -.

;J:;python
дЕ

-->---------------

Глава 6. Строки и строковые функции

6.8. Настройка локали
Локаль - это совокупность локальных настроек системы ( формат даты,
формат времени, кодировка, форматирование денежных единиц и чисел
и т.д.). Для установки лакали используется функция setlocale() из модуля
locale.
Синтаксис функции следующий:
sеtlосаlе(, )

Категория задает категорию настроек, которые будут изменены после вы­
зова setlocale():
• LC_ ALL с все настройки;
• LC_MONETARY - для денежных единиц;
• LC_TIME - настройки, влияющие на форматирование даты и времени;
• LC_NUMERIC - настройки, влияющие на форматирование чисел.
Пример:
import locale
locale. setlocale(locale. LC_ALL, ('russian'))

6.9. Поиск и замена в строке
Для поиска и замены в строке в Python используется множество методов.
Начнем с метода.fiпd(), формат которого выглядит так:
.find([, [, ]])

Метод осуществляет поиск подстроки в строке. Последние два параметры
необязательны. Если они указаны, то производится операция среза строки:
[:]

Метод возвращает номер позиции, с которой начинается вхождение под­
строки в строку:
>>> s = "hello, world"
>>> num = s.find("world")
>>> num

7

'-

- .. - - -... - ................................................. - . - ... - .............. ...

Pytho11. Полное руководство

..•'· ••.. _ .....•.. _ •....• python

Если подстрока не входит в строку, возвращает -1.
Метод index() похож на методji,пd(), но возвращает исключение ValueError,
если подстрока в строку не входит.
Следующий метод - ifind():
.rfind([, [, ]])

Похож на методji,пd(), но возвращает позицию последнего вхождения под­
строки в строку или -1, если подстрока не входит в строку.
Метод count() возвращает число вхождения подстроки в строку. Если под­
строки нет в строке, метод возвращает О. Метод зависит от регистра сим­
волов. Синтаксис метода такой:
.соunt([, [, ]])

Метод startswith() возвращает True, если подстрока есть в строке, или же
False, если подстрока не входит в строку. Синтаксис:
.startswith([, [, ]])

Метод endswith() возвращает True, если строка заканчивается указанной
подстрокой. Если подстроки нет в строке, возвращается False:
>>> s.startswith( 'world')
False
>>> s.endswi th('world')
True

Метод replace() реализует замену всех вхождений подстроки в строку на
другую подстроку. Замена:
.rерlасе(, [,
])

Параметр необязательный и ограничивает максимальное количество замен.
>>> s.replace('world', 'reader! ')
'hello, reader ! '




•··················································································

фpython

Глава 6. Строки и строковые функции

------------------

6. 1 О. Что в строке?
В Python есть ряд методов, позволяющих проверить тип содержимого стро­
ки. Вы можете использовать их для проверки ввода пользователя (табл.
6.3). Аналогичные методы есть в других языках программирования, напри­
мер, в РНР.
Таблица 6.3. Методы проверки типа содержимого строки

Синтаксис

'

Описание

isdigit()

Возвращает Trne, если строка состоит только из цифр

isdecimal()

Возвращает Trne, если строка содержит только десятичные символы

isnumeric()

Возвращает Trne, если строка содержит только числовые
символы. К числовым символам относятся не только десятичные цифры, но и дробные числа, римские числа и
т.д.

isalpha()

Метод возвращает Trne, если строка содержит только буквы

isspace()

Trne, если строка состоит только из пробельных символов

isalnum()

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

islower()

Возвращает True, если строка содержит все символы в
нижнем регистре

isupper()

Возвращает True, если строка содержит все символы в
верхнем регистре

Все эти методы возвращают False, если строка не прошла соответствующую
проверку. Например:

'----------------------------------------------------------------------------------

Python. Полное руководеrво

-- .. - - .. - - .. - ... - - ........ - - - - -

6python
.?i"f

>>> age = input('Baш возраст: ')
Ваш возраст: 37
>>> if age.isdigit() == True:
print('Ok')
else:
print('Ошибка')
Ok

6" 11" Шифрование строк
Модуль hashlib содержит функции, позволяющие зашифровать строки.
Этот модуль очень и очень полезный. Пароли пользователей принято хра­
нить в базе данных в зашифрованном виде. Чтобы не изобретать колесо за­
ново, вы можете использовать модуль hashlib, содержащий функции md5(),
sha1(), sha256(), sha384, sha512(). Названия функций соответствуют алго­
ритмам шифрования.
Пример шифрования пароля с использованием алгоритма MDS:
>>> import hashlib
>>> hash = hashlib.md5(b"secret")
>>> hash.digest()
b'л\xbe"\x94\xec\xd0\xe0\xf0\x8e\xabv\x90\xd2\xa6\xeei'
>>> hash.hexdigest()
'5ebe2294ecd0e0f08eab7690d2aбee69'

В базе данных сохраняется, как правило, значение, возвращаемое методом
hexdigest().

6" 12. Переформатирование текста.
Фиксированное число колонок
Для переформатирования текста для вывода используется модуль textwrap.
Например, представим, что у нас есть следующие строки:
s = " Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Quisque sit amet diam quis risus interdum sollicitudin. In
vestibulum, libero id maximus lacinia, leo massa pharetra mi,



Clt--------. ----. -. -. ---. --. -. -----. -. ----. ---. --. -. -. -. ---. --. -. -. -. ----. ---. -. -----.

6python



------------------

Глава 6. Строки и строковые функции

vitae posuere libero dui пес lectus. Ut ас nunc sagittis,
consequat eros eget, dignissim ех. Vivamus ас nisl felis. In
accumsan tristique aliquet. Morbi eu varius urna. Lorem ipsum
dolor sit amet, consectetur adipiscing elit. Curabitur пес
urna sit amet libero tempor rhoncus. Nulla fringilla erat sit
amet augue laoreet volutpat. Etiam quis molestie risus. Morbi
sapien nisi, scelerisque sed finibus non, placerat sed elit.
Sed placerat turpis ас varius tincidunt. Morbi turpis metus,
molestie eu erat eu, ultricies sollicitudin lorem."

Далее вы можете использовать модуль textwrap для переформатирования
текста различными способами:
>>> import textwrap
>>> print(textwrap.fill(s, 70))
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Quisque sit
amet diam quis risus interdum sollicitudin. In vestibulum,
libero id
maximus lacinia, leo massa pharetra mi, vitae posuere libero
dui пес
lectus. Ut ас nunc sagittis, consequat eros eget, dignissim
ех.
Vivamus ас nisl felis. In accumsan tristique aliquet. Morbi eu
varius
urna. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
CuraЬitur пес urna .sit amet libero tempor rhoncus. Nulla
fringilla
erat sit amet augue laoreet volutpat. Etiam quis molestie
risus. MorЬi
sapien nisi, scelerisque sed finibus non, placerat sed elit.
Sed
placerat turpis ас varius tincidunt. MorЬi turpis metus,
molestie eu
erat eu, ultricies sollicit�din lorem.
>>>
>>> print(textwrap.fill(s, 40))
Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Quisque sit amet diam
quis risus interdum sollicitudin. In
vestibulum, libero id maximus lacinia,
leo massa pharetra mi, vitae posuere
libero dui пес lectus. Ut ас nunc
sagittis, consequat eros eget, dignissim
ех. Vivamus ас nisl felis. In accumsan

, _ -.-.---------------.-.----------.-.------. ---.------.... -. --.-........ -........ -са

Python. Полное руководство

______________________• python

tristique aliquet. Morbi eu varius urna.
Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Curabitur nec urna sit
amet libero tempor rhoncus. Nulla
fringilla erat sit amet augue laoreet
volutpat. Etiam quis molestie risus.
Morbi sapien nisi, scelerisque sed
finibus non, placerat sed elit. Sed
placerat turpis ас varius tincidunt.
Morbi turpis metus, molestie eu erat eu,
ultricies sollicitudin lorem.
>>> print(textvrap.fill(s, 40, initial indent=' '))
Lorem ipsum dolor sit amet,
consectetur adipiscing elit. Quisque sit
amet diam quis risus interdum
sollicitudin. In vestibulum, libero id
maximus lacinia, leo massa pharetra mi,
vitae posuere libero dui nec lectus. Ut
ас nunc sagittis, consequat eros eget,
dignissim ех. Vivamus ас nisl felis. In
accumsan tristique aliquet. Morbi eu
varius urna. Lorem ipsum dolor sit amet,
consectetur adipiscing elit. Curabitur
пес urna sit amet libero tempor rhoncus.
Nulla fringilla erat sit amet augue
laoreet volutpat. Etiam quis molestie
risus. Morbi sapien nisi, scelerisque
sed finibus non, placerat sed elit. Sed
placerat turpis ас varius tincidunt.
Morbi turpis metus, molestie eu erat eu,
ultricies sollicitudin lorem.

Модуль textwrap - простой способ подготовить текст для вывода, особен­
но, если вы хотите аккуратно вывести его на терминал. Получить количе­
ство колонок терминала можно, используя метод os.get_temzinal_size(). На­
пример:
>>> import os
>>> os.get_terminal_size() .columns
80

>>>

У мeтoдafi,ll() есть несколько дополнительных параметров, управляющих
табуляцией, окончанием предложений и т.д.

---------·---·-------- --------------------------------------------------------· ----·

ГЛАВА 7.
РЕГУЛЯРНЬIЕ
ВЬIРАЖЕНИЯ

Python. Полное руководство

e,python

7. 1 " Введение в регулярные
выражения
В этой книге мы уже рассмотрели функцию поиска подстроки в строке
- методjiпd(). Реализовать поиск нужной нам строки в файле с помощью
этой функции не составит проблем. Все, что нам нужно - это прочитать
файл в одну большую строку (благо, размер строки в Python не ограничен,
в отличие от некоторых других языков программирования, где размер стро­
ки ограничен 255 символами) и вызвать методjiпd(), передав ей искомую
строку.
Но наша задача существенно усложняется, если мы знаем, только, к�к долж­
на выглядеть искомая строка, но мы не знаем точно, какую строку мы ищем.
Сейчас поясню, что имеется в виду. Представим, что у нас есть текстовый
файл, в котором нужно найти все адреса e-mail. Мы не знаем, какие именно
адреса будут в этом файле, поэтому мы не можем указать точную строку
для ее передачи методуjind(). Мы только лишь знаем, как будут выглядеть
искомые строки: строка без пробелов@имя домена1.имя домена2 ...имя
-

-

.

411:Et-"""""" """""""" ........ " ................................... " .. ······ ......... ······
-

-

flшpython
.'>> import re
# Игнорируем регистр
>>> р = re.compile(r"л[a-z]+$", re.I)
>>> print("Haйдeнo" if p.search("ABC") else "Not found")
# Найдено, хотя в строке символы в верхнем регистре, а в
шаблоне - в нижнем
Найдено
# Регистр символов не игнорируется
>>> р = re.compile(r"л[a-z]+$")
>>> print("Haйдeнo" if p.search("ABC") else "Не найдено")
# Не найдено
Не найдено
>>> р = re.compile(r"л.$")
>>> print("Haйдeнo" if p.search("\n") else "Не найдено")
Не найдено

Посмотрите на примеры - до строки, содержащей регулярное выражение,
указывается модификатор r. Мы используем неформатированные строки если модификатор не указывать, то все слэши нужно будет экранировать.
Например, строку:

...

.. ... - -. - ". --- - -.....---... - -.-- --. -.- ---.

"

"

"

"

" "

"

"

"

"

"

•.

..-.-. ..- -.-. - ... .... -

"."

"

"

"

ф pyt hon________ • ___________

Глава 7. Регулярные выражения

р = re.compile(r"л\a+$")
нужно будет записать так:
р = re.compile("л\\a+$")
Внутри регулярного выражения символы., л, $, *, +, ?, {, [, ),\, 1, ( и ) имеют
специальное значение. Если эти символы должны трактоваться как есть, то
их следует экранировать с помощью слэша. Некоторые специальные сим­
волы теряют свое особое значение , если их разместить внутри квадратных
скобок.
Например, символ "точка" по умолчанию соответствует любому символу,
кроме символа перевода строки. Если необходимо найти точку, то перед ней
нужно указать\ или разместить точку внутри скобок - [.].
Рассмотрим небольшой пример:
dt = "01.01.2021"
»> р = re.compile(r"л [0-3) [0-9). [01) [0-9). [12) [09) [0-9) [09) $")
>>> if p.search(dt): print("+")
+
>>>

В этом примере мы использовали методы л и $. Кроме этих символов есть
метасимволы\А и\Z. Назначение этих метасимволов следующее:
• л - привязка к началу строки или подстроки (зависит от флагов М или
S)
• $ - привязка к концу строки или подстроки (зависит от флагов Мили S)
• \А- привязка к началу строки (без зависимости от модификатора)
• \Z - привязка к концу строки (без зависимости от модификатора)

Примеры:
# Точка не соответствует \n
>>> р = re.compile(r"л.+$")
>>> p.findall("s1\ns2\ns3")
[]
# Точка соответствует \n
>>> р =l re.compile(r"л.+$", re.S)
>>> p.findall("sl\ns2\n\s3")
['s1\ns2\n\\s3']

'-------. --- .----. ---. --------. ---. --------. ------. -. --.. -. ----.....- . -.--.- -. ,·----t:D

Python. Полное руководство

�,,python

Привязка к началу или концу строки используется, только если строка
должна полностью соответствовать регулярному выражению. Давайте на­
пишем программу, которая проверяет, является ли строка числом:
import re
р = re.compile(r"л[0-9]+$", re.S)
num = "123"
snum = "s123"
if p.search(num):
print("Чиcлo")
else:
print("Строка")
if p.search(snum):
print ("Число")
else:
print ("Строка")

Результат выполнения этого модуля будет следующим:
Число
Строка

Сначала мы передаем строку, которая содержит только число, поэтому бу­
дет выведено Number. Далее мы передаем строку, которая не полностью со­
стоит из числа, поэтому будет выведено String.
В квадратных скобках указываются символы, которые могут встречаться на
этом месте в строке. Диапазон символов перечисляется через тире. Примеры:
• [13] - соответствует числу 1 или 3
• [0-9] - соответствует числу от О до 9
• [аЬс] - соответствует буквам "а", "Ь" или "с"
• [a-z] - соответствует буквам от "а" до "z"
• [a-zA-Z] - соответствует любой букве английского алфавита
• [0-9a-zA-Z] - любая буква или любая цифра



CD----------------------------------------------------------------- , --------------...

'""'

--------------------

6python

Глава 7. Регулярные выражения

С помощью символа л можно инвертировать значение, указанное в скобках.
Вы можете указать символы, которых не должно быть на этом месте в стро­
ке, например, [л 13] - символов 1 и 3 не должно быть в строке.
Если не хочется указывать символы, то можно указать сразу классы симво­
лов (табл. 7.1).
Таблица 7. 1. Классы символов

Класс символов

Что означает

\d

Соответствует любой цифре

\w

Соответствует любой букве, цифре и знаку подчеркивания. Эквивалентно [a-zA-Z0-9_] при указании флага А

\s

Любой пробельный символ. При указании флага А
эквивалентно [\t\n\r\t\v]

\D

Не цифра. При указании флага А эквивалентно [лО-9]

\W

Не буква, не цифра и не символ подчеркивания. Эквивалентно [лa-zA-Z0-9_]

\S

Не пробельный символ, эквивалентно [л\t\n\r\t\v]

Количество вхождения символа в строку задается с помощью квантифика­
торов:
• {n} - n вхождений символа в строку. Например, r" л [О-9]{3}$" соответствует трем вхождениям любой цифры
• {n,} - минимум n (n или больше) вхождений символа
• {n,m} - от n до m вхождений
• * - ноль или больше вхождений символа в строку. Эквивалентно {О,}
• + - одно или больше число вхождений символа в строку. Эквивалентно
{ 1,}
• ? - ни одного или одно вхождение в строку. Эквивалентно комбинации
{0,1}

•.---------.-.-.....---..........................-..--.........................-..--св

Python. Полное руководство

ilJpython

_ .................................

/%/

Регулярные выражения по умолчанию "жадные". То есть ищут самую длин­
ную последовательность, которая соответствует шаблону, и не учитывают
более короткие соответствия. Рассмотрим следующий пример:
>>> s

"ОдинДваТри"

>>> р
re.compile(r".*", re.S)
>>> p.findall(s)
['ОдинДваТри']

Как видите, была найдена вся строка. Это немного не то, что мы хотели.
Чтобы ограничить "жадность" регулярных выражений, нужно использо­
вать символ?:
>>> р = re.compile(r".*?", re.S)
>>> р.findall(s)
[ 'Один', 'Два', 'Tpи']
>>>

Теперь вместо одного большого соответствия у нас есть три меньших.
Мы только что рассмотрели основы синтаксиса регулярных выражений
Perl. Дополнительную информацию вы можете получить по адресу:
http.j/www.boost.org/doc/libs/1_43_0/libs/regex/doc/html/boost_regex/
syntax/perl_syntax.html

7"3" ме�rодь1

atch() и search()

Метод match() проверяет соответствие с началом строки. Формат метода
следующий:
match([, [, ]])

Если соответствие найдено, то возвращается объект Match, в противном
случае - None. Пример:
>>> р = re.compile('[0-9]+')
>>> print ("Найдено" if p.match("s123") else "Не найдено")
Не найдено




&:t··················································································

О python____________________

Глава 7. Регулярные выражения

>>> print("Haйдeнo" if p.match("123s") else "Не найдено")
Найдено
>>>

Метод search() проверяет соответствие с любой частью строки:
search([, [, ]])

Пример:
>>> р = re.compile(' [0-9)+')
>>> print("Haйдeнo" if p.search("s123") else "Не найдено")
Найдено
>>> print("Haйдeнo" if p.search("123s") else "Не найдено")
Найдено

Объект Match,. возвращаемый методами match() и search() имеет следующие свойства и методы:
• re - ссылка на скомпилированный шаблон, указанный в методах match()
и search() Через нее доступны следующие свойс!ва и методы:
• groups - количество групп в шаблоне
• goupindex - словарь с названиями групп и их номерами
• string - значение параметра
• pos - значение параметра
• endpos - значение параметра
• lastindex - номер последней группы или значение None
• lastgroup - название последней группы
• start([]) - индекс начала фрагмента.
Если параметр не указан, то фрагментом является полное соответствие
с шаблоном, в противном случае - соответствие с указанной группой.
Если соответствия нет, то возвращается значение -1
• end([]) - индекса конца фрагмента. Если
параметр не указан, то фрагментом является полное соответствие с ша­
блоном, в противном случае - с указанной группой. Если соответствия
нет, то возвращается -1
• expand() - производит замену в строке согласно указанного
шаблона

•.-----------.-... -..-...-.-.-....-..-....-....-.-...-......-........-.-........-.-tlD

Python. Полное руководство

_________________ .. __ • python

В качестве примера работы с регулярными выражениями рассмотрим код,
проверяющий правильность введенного e-mail (лист. 7.1).
Листинг 7.1. Код, проверяющий правиnьность e-mail
import re
email = "mark@sales.example.com"
pattern = r"л([a-z0-9 .-]+)@(([a-z0-9-]+\.)+[a-z]{2;6})$"
р
re.compile(pattern, re.I I re.S)
m

,p.search(email)

if not m:
print("He совпадает")
else:
рrint("Совпадает")

7.4. Метод findall()
Meтoд.findall() ищет все совпадения с шаблоном. Формат метода:
findall([, [, ]])

Если найдены соответствия, то возвращается список с фрагментами, в про­
тивном случае возвращается пустой список. Если внутри шаблона есть
несколько групп, то каждый элемент списка будет кортежем, а не строкой.
Рассмотрим пример:
>>> р = re.compile(r"[a-z]+")
>>> p.findall("abc, Ьса, 123, dsf")
['аЬс', 'Ьса', 'dsf']
>>> p.findall("1234, 12345, 123456")
[]

7.5. МетодsuЬ()
Метод sub() используется для поиска всех совпадений в строке с шаблоном
и для их замены указанным значением. Если совпадения не найдены, будет
возвращена исходная строка. Синтаксис метода следующий:



------------------------------········-······-·-········----------------------------·

.

llr,python

--------------------

Глава 7. Регулярные выражения

suЬ(,, [, ])

Внутри нового фрагмента можно использовать обратные ссылки \номер \
gНомер и \gНазвание. Обратные ссылки предоставляют удобный способ
идентификации повторяющегося символа или подстроки в строке. Напри­
мер, если входная строка содержит несколько экземпляров произвольной
подстроки, то можно найти первое вхождение с помощью группы записи, а
затем использовать обратную ссылку для поиска последующих вхождений
подстроки. Чаще всего используются ссылки типа \номер. Номер - это
порядковое положение группы записи, определенное в шаблоне регуляр­
ного выражения. Например, \4 соответствует содержимому четвертой за­
хватываемой группы.
В качестве первого параметра можно использовать ссылку на функцию. В
функцию будет передан объект Match, соответствующий найденному фраг­
менту. Результат, возвращаемый этой функцией, служит фрагментом для
замены. Для примера найдем все числа в строке и умножим их на 2 (лист.
7.2)

Листинг 7.2. Использование метода sub
import re
def mult(m) :
х = int(m.group(O))
х *= 2
return "{0}".format(x)
р = re.compile(r"[0-9]+")
# умножаем все числа на 2
print(p.sub(mult, "2, 3, 4, 5, 6, 7"))
# умножаем первые три числа
print(р. sub(mult, "2, 3, 4, 5, 6, 7", 3))

Результат:
4, 6, 8, 10, 12, 14
4, 6, 8, 5, 6, 7

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

•.

- -. -.......... -- -. -..-- .. -. -......................................... -

..............

Python. Полное руководство

__________________ 0 _.

python

7.6. Различные практические
примеры
7.6.1. РАЗДЕЛЕНИЕ СТРОК С ИСПОЛЬЗОВАНИЕМ
РАЗДЕЛИТЕЛЕЙ
Представим, что у нас есть строка и нам ее нужно разделить на поля, ис­
пользуя разделители, например, пробелы. Чтобы задача была менее триви­
альной, будем считать, что разделители и пробелы вокруг них противоречи­
вы по всей строке.
В самых простьiх случаях можно использовать метод split() объекта строки.
Но он не позволяет использовать несколько разделителей и учитывать воз­
можное пространство вокруг разделителей. В нашем случае нужна большая
гибкость, поэтому нам нужен использовать метод split() из модуля re:
>>> line = 'asdf fjdk; afed, fjek,asdf; foo·'
>>> import re
>>> re.split(r' [;,\s]\s*', line)
['asdf', 'fjdk', 'afed', 'fjek', 'asdf', 'foo']

Метод re.split() полезен, потому что вы можете определить многократ­
ные образцы для разделителя. например, как показано в решении, разде­
литель может быть или запятой(,) или точкой с запятой(;) или пробелом,
сопровождаемым любым количеством дополнительных пробельных симво­
лов. Каждый раз, когда найден образец, все соответствия ему становятся
разделителем между любыми полями, лежащими по обе стороны от соот­
ветствия. Результат - список полей, как и в случае с str.split().
При исп�льзовании re.split(} нужно быть осторожным, образец регулярно­
го выражения должен содержать группу захвата, заключенную в круглые
скобки. Если используются группы захвата, то соответствующий текст так­
же будет включен в результат. Например:
>>> fields = re.split(r' (;1,1\s)\s*', line)
>>> fields
['asdf', ' ', 'fjdk', ';', 'afed', ',', 'fjek',
',', 'foo']

...
>>>

'

' '
1

'asdf',



. - - - - --- -- - -.. - .. -- -. -. - .... -. -. -. -... -.. -. -. -.... -. -. -...-.. -. -. -. -.-. - -- -..-.- -- .

.python

--------------------

Глава 7. Регулярные выражения

Получение символов разделителя может быть полезным в определенных
контекстах. Например, возможно, вы нуждаетесь в символах разделителя,
чтобы преобразовать выходную строку:
>>> values = fields[::2]
>>> delimiters = fields[1: :2] + ['']
>>> values
['asdf', 'fjdk', 'afed', 'fj ek', 'asdf', 'foo' ]
>>> delimiters
[' ', ';', ',', ',', ',', '']
>>>#Преобразуем строку с испопьзованиеи �ех же раздеп�епей
>>> ''.join(v+d for v,d in zip(values, delimiters))
'asdf fjdk;afed,fjek,asdf,foo'
>>>

Если вы не хотите получить символы разделителя в результате, но вам все
еще нужны круглые скобки для группировки частей образца регулярного
выражения, убедитесь, что вы используете группы не захвата, которая опре­
деляется как(?...). Например:
>>> re.split(r'(?:,l;l\s)\s*', line)
[ 'asdf', 'fjdk', 'afed', 'fjek', 'asdf', 'foo']
>>>

7.6.2. ИСПОЛЬЗОВАНИЕ МАСКИ ОБОЛОЧКИ
Этот пример показывает, как найти соответствие текста с использованием
масок, которые часто используются при работе с Uniх-оболочкой (напри­
мер, *.ру, Dat[0-9]*.csv и т.д.).
Для решения поставленной задачи мы используем модуль fnmatch, предо­
ставляющий две функции - fnmatch() и fnmatchcase(),кoтopыe могут ис­
пользоваться для решения поставленной задачи. Использование функций
очень простое:
>>> from fnmatch import fnmatch, fnmatchcase
>>> fnmatch('report.txt', '*.txt')
True
>>> fnmatch('rep.txt', '?oo.txt')
False
>>> fnmatch('Data45 .csv', 'Data[0-9] *')
True



··················································································•

Pytl10n. Полное руководство

•••,.,.,.,...,,.,. ••,...,,.,.,.,.,.,.

,il:;python
>> m.group(2)
'19'
>>> m.group(З)
'2021'
>>> m.groups О
('12', '19', '2021')
>>> month, day, year = m.groups()
>>>
>>>#Поиск всех соответствий
>>> text
'Сегодня 12/19/2021. Завтра 12/20/2021.'
>>> datepat.findall(text)
[('12', '19', '2021'), ('12', '20', '2021')]
>>> for month, day, year in datepat.findall(text):
... print(' {}-{}-{}' .format(year, month, day))



·-······-·-----·-------------------······-·-············-······-·-·----·-·---·-···-t!D

Python. Полно� руководство

. .
.
+python
------------------�-

2021..,.12-19
2021-12-20
>>>

Метод findall() ищет текст и находит все соответствия, возвращая йх как
список. Если вы хотите найти соответствия итеративно, используйте метод
finditer(). Например:
>>> for m in datepat.finditer(text):
... print(m. groups ())
('12', '19', '2021')
('12', '20', '2021')
>>>

Обсуждение теории регулярных выражений выходит за рамки этой книги.
Однако этот пример иллюстрирует абсолютные основы использования мо­
дуля re. Сначала нужно скомпилировать образец, используя re.compile(), а
затем использовать методы, такие как match(),findall() илиfinditer().
При указании шаблонов,как правило,принято и:спользовать обычные стро­
ки, такие как r'(\d+)/(\d+)/(\d+)'. В таких строках обратный слеш остает­
ся неинтерпретируемым, что может быть полезно в контекстерегулярных
выражений. Иначе вам придется использовать двойной обратный слеш,на­
пример,'(\\d+)/(\\d+)/(\\d+)'.
Знайте, что метод match() проверяет только начало строки. Возможно, это
немного не то,что вы ожидаете. Например:
>>> m = datepat.match('ll/27/202laЬcdef')
>>> m

>>> m.group()
'11/27/2021'
>>>

Если вам нужно точное совпадение, убедитесь, что шаблон в конце содер­
жит маркер$,например:
>>> datepat = re.compile(r'(\d+)/(\d+)/(\d+)$')
>>> datepat.match('ll/27/2021aЬcdef')
>>> datepat.match('ll/27/2021')

>>>

-

................................... -............................................

'

.python
\mJ
••••••••••••••••••••

Глава 7. Регулярные выражения

Наконец, если вы просто выполняете простой поиск текста, вы можете ча­
сто пропустить шаг компиляции и использовать функции уровня модуля в
модуле re вместо этого. Например:
>>> re.findall(r'(\d+)/(\d+)/(\d+) ', text)
[('12', '19', '2021'), ('12', '20', '2021')]
>>>

Знайте, тем не менее, что если вы собрались произвести значительное соот­
ветствие или поиск по большому тексту, обычно имеет смысл сначала ском­
пилировать образец, чтобы использовать его снова и снова. Так вы получите
значительный прирост производительность.

7 .6.5. ПОИСК И ЗАМЕНА ТЕКСТА
В самых простых случаях·используйте метод str.replace(). Например:
>>> text = 'yeah, but no, but yeah, but no, but yeah'
>>> text. replace(' yeah', 'уер')
'уер, but no, but уер, but no, but уер'
>>>

Для более сложных образцов используйте функцию/метод sub() в моду­
ле re. В качестве примера представим, что нужно перезаписать даты вроде
"11/27/2021" так, чтобы они выглядели так: "2021-11-27".
Вот пример того, как это можно сделать:
>>> text = 'Сегодня 11/27/2021. Завтра 3/13/2021.'
>>> import re
>>> re.suЬ(r'(\d+)/(\d+)/(\d+) ', r'\3-\1-\2', text)
'Сегодня 2021-11-27. Завтра 2021-3-13.'
>>>

Первый аргумент sub() - это шаблон, которой должен быть найден в тексте,
а второй аргумент - это шаблон замены. Цифры с обратными слешами
(например, \3) используются для группировки чисел в образце.
Если вы собираетесь выполнить повторные замены с тем же образцом, за­
думайтесь о его компиляции для лучшей производительности. Например:



··················································································•

Pytho11. Полное руководство

....................О python

>>> import.re
>>> datep�t = re.compile(r'(\d+) /(\d+)/ (\di)')
>>> datepat.suЬ(r'\3-\1-\2', text)
'Сегодня 2021-12-19. Завтра 2021-12-20.'
>>>

Для более сложных замен можно использовать саllЬасk·функцию замены.
Например:
>>> from calendar import month_aЬbr
>>> def change_date(m):
mon_name = month_aЬbr[int(m.group(l))]
return '{} {} {}'.format(m.group(2), mon_name, m.group(З))
>>> datepat.suЬ(change_date, text)
'Сегодня 19 Dec 2021. Завтра 20 Dec 2021.'
>>>

В качестве ввода в саllЬасk·функцию замены передается объект, возвращен­
ный функциями match() илиfiпd(). Используйте метод .group() для извле­
чения определенных частей соответствия. Функция должна вернуть текст
замены.
Если вы знаете, сколько замен было сделано в дополнение к получению
· текста замены, используйте вместо этоп;> re.subn(). Например:
>>> newtext, n = datepat.suЬn(r'\3-\1-\2', text)
>>> newtext
'Сегодня 2021-12-19. Завтра 2021-12-20.'
>>> n

2
>>>

Для выполнения нечувствительных к регистру операций над текстом вам
нужно использовать модуль re и установить флаг re.IGNORECASE. Этот
флаг нужно устанавливать отдельно для каждой операции над текстом.
Например:
>>> text = 'UPPER PYTHON, lower python, Мixed Python'
>>> re.findall('python', text, flags=re. IGNORECASE)
[ ' PYTHON' , '.python' , ' Python' ]
>>> re.suЬ('python' , 'snake', text, flags=re. IGNORECASE)
'UPPER snake, lower snake, Mixed snake'

...
>>>

.



----- - -- - -- . -... - - - -- - -- . - ---- - - -- - -- . - -- -- - -- . - -- - - .. --- ... -... - . - . -- -- . -- - . ---·

О python____________________

Глава 7. Регулярные выражения

Последний пример показывает ограничение, что текст замены не будет со­
ответствовать регистру в исходном тексте. Если вам нужно исправить это,
вам, возможно, придется использовать функцию поддержки, например:
def matchcase(word):
def replace(m):
text = m.group()
if text.isupper():
return word.upper()
elif text.islower():
return word.lower()
elif text[0] .isupper():
return word.capitalize()
else:
return word .
return replace

Вот пример использования этой последней функции:
>>> re.suЬ('python', matchcase( 'snake' ) , text, flags=re.
IGNORECASE)
'UPPER SNAKE, lower snake, Mixed Snake'

>>>

7 .6.6. УДАЛЕНИЕ НЕЖЕЛАТЕЛЬНЫХ СИМВОЛОВ ИЗ
СТРОКИ
Часто требуется удалить нежелательные символы, например, пробелы в на­
чале и конце строки.
Метод strip() может быть использован для удаления символов в начале или
конце строки. Функции lstrip() или rstrip() осуществляет обрезку символов
слева или справа соответственно.

-

По умолчанию эти методы удаляют пробельные символы, но вы можете за­
дать любые другие символы. Например:

•.

- - -. -.......... - -- ...... -.-- - --....-- -... -...... -. -...... -... -...................

lf,python

Python. Полное руководство

•••••••••••••••••••• ·Ю

>>>#Обрезаем пробельные символы

>>> s = ' привет мир \n'
>>> s.strip О

'привет мир'

>>> s. lstrip ()

'привет мир \n'

>>> s. rstrip ()

' привет мир'
>>>

>>>#Удаляем заданные символы

>>> t = '-----привет='
>>> t.lstrip('-')

'привет====='
>>> t.strip ( •-=')
'привет'
>>>

Различные striр()-методы обычно используются при чтении и очистке дан­
ных для дальнейшей обработки. Например, вы можете использовать их,
чтобы избавиться от пробелов, удалить кавычки и выполнить другие зада­
чи. Знайте, что striр()-методы не применяются к любому тексту в середине
строки. Например:
>>> s = ' привет \n'
>>> s = s.strip()
>>> s

'привет мир'
>>>

Если вам нужно сделать что-то во внутреннем пространстве, вам нужно ис­
пользовать другую технику, например, использовать метод replace() или
замену с использованием регулярных выражений. Например:
>>> s.replace (' '
'приветмир'
>>> import re
>>> re.suЬ('\s+' ,
'привет мир'
>>>

'' )

' '

s)



--·-············-··-·····-········-······-··········-·-·-···-··-·-----·--·-----·--·

.,python
Щ!

--------------------

Глава 7. Регулярные выражения

Часто нужно объединить строковые операции разделения (splitting) с неко­
торым другим видом итеративной обработки, например, чтение строк дан­
ных из файла. Если это так, то вам пригодится использование генератора.
Например:
with open(filename) as f:
lines = (line.strip() for line in f)
for line in lines:

Здесь выражение lines = (line.strip() for line in f) действует как, своего рода,
преобразование данных. Это эффективно, потому что оно, фактически, сна­
чала не считывает данные в какой-либо вид временного списка. Мы
просто создаем итератор, где все ко всем считанным строкам сразу приме­
няется strip().



·---------------------------------------------------------------------------------CD

ГЛАВА 8.

списки

6python
,№У

•••••••••••••••••••••

Глава 8. Списки

8. 1 . Что такое список?
Список - это нумерованный набор объектов. Каждый элемент набора со·
держит только ссылки на объект. Именно поэтому список может содержать
объекты произвольного типа данных и иметь неограниченную степень вло·
женности.
Списки поддерживают обращение к элементу по индексу (нумерация эле·
ментов списка начинается с О), получение среда, конкатенацию, повторе·
ние, а также проверку на вхождение.
Ранее было отмечено, что списки относятся к изменяемым типам данных
(в отличие от кортежей). Это означает, что вы можете не только получить
элемент по индексу, но и изменить его. Например:
>>>
>>>
2
>>>
>>>
[ 1,
>>>



1st = (1, 2, 3, 4]
1st [ 1]
7
1st [ 1]
1st
7, 3, 4]

.....................................................................................

_____.....___........• python

Создать список можно разными способами. Напри�ер, можно использовать
функцию list(), которой нужно передать посл�до:щ1тельность, по которой и
будет создан список. Если вы ничего не передаете, будет создан пустой спи­
сок:
>>> 1st = list('Hello')
>>> 1st
['Н', 'е', '1', '1', 'о']

Можно указать все элементы списка в квадратных скобках, как уже было
показано. Обратите внимание, что элементы могуt быть разных типов:
>>> 1st = ["а", "Ь", 1)
>>> 1st
['а', ' Ь', 1)

Третий способ заключается в поэлементном формировании списка с помо­
щью метода append:
>>>
>>>
>>>
>>>
[1,
>>>

1st = []
lst.append(l)
lst.append(2)
lst.append(З); 1st
2, 3)

В РНР можно использовать такую конструкцию для добавления элементов
в список:
1st[] = новый_элемент

В Python ее использовать нельзя, вы получите сообщение об ошибке. Также
нужно быть осторожнее со следующей констру;цией:
>>>
>>>
' а'
>>>
>>>
1

.

>>>

а = ь = ["а", "b"J
a[OJ
Ь[О]
а[О]

1



-------------------·-······················-····················-··················

,т,

ilJ;python

---------------------

Глава 8. Списки

Как видите, при создании списка сохраняется ссылка на объект, а не сам
объект. Поэтому нам кажется, что мы якобы создали два списка, а на самом
деле обоим переменным была присвоена ссылка, указывающая на объект.
Напомню, что проверить, ссылаются ли переменные на один и тот же объ­
ект, можно с помощью оператора is, например(если оператор возвращает
Trne, то переменные ссылаются на один и тот же объект):
>>> а is Ь
True

Если вам нужно создать вложенные списки, то это лучше делать с помощью
метода append(), например:
>» 1st = []
>>> for i in range(З): lst.append([])
>>> lst[O] .append(l)
>>> 1st
[ [1] , [], []]
>>>

8.2. Операции над списками
Над списками можно выполнить множество операций. Начнем с доступа к
элементу. Для этого используются квадратные скобки([]), в которых ука­
зывается индекс элемента. Нумерация элементов списка начинается с О.
Примеры использования [] уже были показаны.
Примечание. Поскольку нумерация элементов списка начинает­
ся с О, то индекс последнего элемента будет меньше на единицу
количества элементов.

Оператор присваивания можно использовать как для присваивания значе­
ния всему списку, так и отдельному элементу:
>>> 1st = (1, 2, 3]
>>> lst[l] = 6

. ..

В Python 3 при позиционном присваивании перед одной из переменных

слева от оператора = можно указать звездочку. В этой переменной

,_ -- -- ---·- --

... -.. - .. -- . -

-- - ·-------.... --....... - -.. - . -....... - -...-..... � - . -

Python. Полное руководство

.....................• pyth()n

будет сохраняться список, состоящий из "лишних" элементов. Если таких
элементов нет, то список будет пустым:
>>> а, Ь, *с = [1, 2, 3, 4]

Если вы попытаетесь получить доступ к несуществующему элементу, вщ
получите сообщение об ошибке IndexError.
>>> lst[4]
Traceback (most recent call last):
File "", line 1, in
lst[4)
IndexError: list index out of range

В качестве индекса можно использовать отрицательные значения, в этом
случае смещение будет отсчитываться с конца списка:
>>> 1st = [1, 2, 3, 4, 5, 6, 7]
»> lst[-2]
6

Кроме обращения к элементу по индексу списки поддерживают операцию
извлечения среза, которая возвращает указанный фрагмент списка:
[::]

Все три параметра являются необязательными. Если не указан первый па­
раметр, то используется значение О. Если не задан второй параметр, то счи­
тается, что нужно возвратить фрагмент до конца списка. Если не задан
последний параметр, то используется значение 1.
Операция среза очень интересная и мощная. Вот, например, как можно вы­
вести элементы списка в обратном порядке:
>>> lst[::-1]
[7, 6, 5, 4, 3,, 2, 1]

Вот еще несколько примеров:
>>> lst[:-1]

# Без последнего элемента




... - - - -.- • - - - - - - - - - - - - - - - - - - - - - -•• - - -• - - - - -• -•••••••••••••••••• - ••••••• с ••••••• -•••••

.python
@�

---------------------

Глава 8. Списки

[1, 2, 3, 4, 5, 6]
lst[l:]
# Без первого элемента
[2, 3, 4, 5, 6, 7]
>>> lst[0:3]
# Первые три элемента
[1, 2, 3]
>>> lst[-1:]
# Последний элемент
>>>

[7].

Срез позволяет даже изменять элементы списка, например:
»> lst[l:3] = [8, 9]
1st
[1, 8, 9, 4, 5, 6, 7]

>>>

Только будьте осторожны с этой операцией!
Срез - это не единственная полезная операция над списком. Вы можете вы­
полнить конкатенацию списков, используя оператор +:
1st
[1, 8, 9, 4, 5, 6, 7]
>>> lst2 = [10, 11, 12]
>>> lstЗ = 1st + lst2; lstЗ
[1, 8, 9, 4, 5, 6, 7, 10, 11, 12]
>>>

Если нужно добавить элементы в текущий список, можно использовать
оператор +=:
>>>

[1,

1st

8, 9, 4, 5, 6, 7]
1st += [10, 11, 13]; 1st
[1, 8, 9, 4' 5, 6, 7, 10, 11, 13]

>>>

8.3. Многомерные списки
Любой элемент списка может содержать любой объект, в том числе и дру­
гой список, кортеж, словарь и т.д. Вот как можно создать список, состоящий
из трех списков:



>>>

1st = [[1, 2, 3], ['а','Ь','с'], [9, 8, 7]]

.....................................................................................

Python. Полное ру,·сводство

.,.python

--------------------- w6

>>> 1st
[[l, 2, 3], ['а', 'Ь', 'с'], [9, 8, 7]]
Чтобы добраться к элементам вложенного списка, нужно указывать два ин­
декса, например:
>» lst[1][2]
'с'
В свою очередь элементы вложенного списка также могут быть списками
и т.д. Количество вложений не ограничено, поэтому у вас могут быть вот
такие странные индексы:
lst[l][2][3][4]...
Если того не требует решаемая задача, я бы не советовал увлекаться вло­
женными списками - так вы сделаете программу сложнее, чем она- могла
бы быть.

8.4. Проход по элементам списка
Вот как можно перебрать все элементы списка:
>>> 1st
((1, 2, 3], ['а', 'Ь', 'с'], [9, 8, 7]]
>>> for i in 1st: print(i, end=" ")
(1, 2, 3] ['а', 'Ь', 'с'] (9, 8, 7]
>>>
Также для перебора списка можно использовать функцию range():
range([,] [, ])
Пример:
>>> 1st
[1, 2, 3, 4]
>>> for i in range(len(lst)): print(lst[i], end=" ")
1 2 3 4

-

- -..... -........ - .......................... -� .......... -............ -·· .... -.



.,

.python

w

---.------------------ .

Глава 8. Списки

Данный способ можно использовать не только для итерации по одномер­
ным спискам, как вы бы могли подумать, например:
>>> 1st = [[l, 2, 31, ['а','Ь','с'], [9, 8, 7]]
>>> for i in range(len(lst)): print(lst[i], end=" ")
[1, 2, 3] ['а', 'Ь', 'с'] [9, 8, 7]

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

8.5. Поиск элемента в списке
Определить, есть ли элемент в списке, можно с помощью оператора in, на­
пример:
>>> 1st
[[1, 2, 3], ['а', 'Ь', 'с'], [9, 8, 7]]
>>> 2 in 1st
False
>>> 1st = [1, 2, 3, 4]
>>> 2 in 1st
True

Как видите, данный способ не работает с многомерными списками, поэтому
для поиска элемента в таких списках правильнее использовать перебор эле­
ментов. Хоть медленно, но зато работает.
Однако оператор in сообщает только, если ли элемент в списке, но он не
сообщает его позицию. Для этого можно использовать метод index:
>>> lst.index(3)
2

Здесь видно, что элементу 3 соответствует индекс 2. Если указанного элемента нет в списке, вы получите ошибку ValueError.
>>> lst.index(7)
Traceback (most•recent call last):
File "", line 1, in
lst.index(7)
ValueError: 7 is not in list



.....................................................................................

Python. Полное руководство

fljpython
1,,;,,_�

• ., .. ч ·----------------

Посчитать количество элементов с определенным значением позволяет ме­
тод count():
>>>
>>>
2
>>>
1
>>>

о

1st = [1, 2, 2, 3, 4]
lst.count(2)
lst.count(3)
1st.count(7)

Данный метод можно также использовать в качестве основного метода
поиска элемента - если количество равно О, то и элемента в списке нет. Вам
не нужно обрабатывать никакие исключения, просто анализируйте количе­
ство элементов и все.
Функции min() и тах() позволяют найти минимальный и максимальный
элемент списка соответственно:
>>> 1st = [1, 2, 2, 3, 4]
>>> min(lst); max(lst)
1
4

8.6. Добавление и удаление
элементов в списке
Для добавления и удаления элементов создано множество методов. Начнем
с метода append( ), позволяющего добавить элемент в конец спи­
ска:
>>>
>>>
>>>
[1,

1st = [1, 2, 3, 4]
lst.append(5)
1st
2, 3, 4, 5]

Также добавить элементы в конец списка можно и с помощью оператора+=,
например:
>>> 1st += [6, 7]
>>> 1st
[1, 2, 3, 4, 5, 6, 7]

---



------------------------····----···-···········-······ ........................... .

Глава 8. Списки

,!;i python

Метод insert() вставляет объект в указанную позицию. Синтаксис следую­
щий:
insert(, )
Примеры:
>>>
[1,
>>>
>>>
[О,
>>>
>>>
[О,

1st
2, 3, 4, 5, 6, 7]
# Вставили ноль в начало списка
lst.insert(O, О)
1st
1, 2, 3, 4, 5, 6, 7]
# Вставили 8 в позицию 8
lst.insert(8, 8)
1st
1, 2, 3, 4, 5, 6, 7, 8]

Для удаления элементов можно использовать методы рор(), remove() и опе­
ратор del. Первый удаляет элемент с указанным индексом и возвращает
его. Если индекс не указан, удаляется и возвращается последний элемент списка:
>>> 1st

[О, 1, 2, 3, 4, 5, 6, 7, 8]
>>> lst.pop(O)

о

>>> 1st
[1, 2, 3, 4, 5, 6, 7, 8]
>>> 1st.рор ()
8
>>> 1st
[1, 2, 3, 4, 5, 6, 7]
Оператор del ничего не возвращает, а просто удаляет элемент. Например:
>>> del lst[6]
>>> 1st
[1, 2, 3, 4, 5, 6]
>>>
Удалить элемент, содержащий определенное значение, можно с помо­
щью метода remove():

'

>>> lst.remove(5)
>>> 1st
[1, 2, 3, 4, 6]

.............................................. ................................. ...
"



---------------------i!x python

Python. Полное руководство

е

еш вание элементов и
йн
емента

Функция shujfle() из модуля random используется для перемешивания спи­
ска случайным образом. Функция перемешивает сам список и ничего не
возвращает. Пример:
>>>
>>>
[1,
>>>
>>>
[4,
>>>

import random
1st
2, 2, 3, 4]
random.shuffie(lst)
1st
2, 2, 1, З]

Выбрать случайный элемент со списка можно с помощью функции choice()
из того же модуля:
>>> random.choice(lst)
4
>>> random.choice(lst)
2

Изменить порядок следования элементов можно с помощью метода
reverse(). В отличие от среза, данный метод работает с самим списком, а не с
его копией:
>>>
[4,
>>>
>>>
[З,
>>>

1st
2, 2, 1, З]
lst.reverse()
1st
1, 2, 2, 4]

списка
Для сортировки списка используется метод sort(), синтаксис которого сле­
дующий:
sort( [ key=None] [, reverse = False])

-----------------.----

tllJJpython



Глава 8. Списки

Оба параметра являются не обязательными. Метод изменяет текущий спи­
сок и ничего не возвращает. Попробуем отсортировать список, используя
параметры по умолчанию:
>>>
>>>
>>>
[1,
>>>

1st = [2, 3, 7, 5, 6, 1, 4)
lst.sort()
1st
2, 3, 4, 5, 6, 7)

Для сортировки в обратном порядке укажите параметр reverse= True:
>>> lst.sort(reverse= True)
>>> 1st
[7, 6, 5, 4, 3, 2, 1)
>>>

Иногда нужно не учитывать регистр символов, для этого нужно вызвать
sort() так:
lst.sort(key= str.lower)

Помните, что метод sort() изменяет список, а в некоторых случаях этого не
нужно делать. Для таких случаев предназначена функция sorted():
sоrtеd([, key=None] [, reverse= False])

Первый параметр - это последовательность, которую нужно отсортировать,
остальные параметры - такие же, как у метода sort(). Данная функция
возвращает отсортированный список и не изменяет исходный.

8.9. Преобразование списка в строку
Для преобразования списка в строку используется методjоiп(). Вызывать
его нужно так:
= .jоin()

Пример:
>>> 1st = ['h', 'е', '1', '1', 'о']
>>> s = "".join(lst)
>>> s
'hello'

, __ ---------. -.........-.............................................. -------.. ---CD

Python. Полнuе руководство

i/lJ;python

.........
,........................ '@!У

Здесь в качестве разделителя мы используем пустую строку, поэтому, по
сути, разделителя нет.

8. 1 О. Вычисления с большими
числовыми массивами
NumPy 1 - основа для огромного числа научных и технических библиотек
Python. Но в то же время NumPy - один из самых больших и самых слож­
ных в использовании модулей. Однако вы можете выполнить полезные
вещи с NumPy, начиная с простых примеров и экспериментируя с ними.
Нужно сделать только одно примечание относительно использования
NumPy. Относительно распространено использовать оператор import
numpy as np, что и показано в далее. Это просто сокращает название моду­
ля - так удобнее, если часто приходится обращаться к нему.
Представим, что вам нужно произвести вычисления с огромными число­
выми наборами данных, например, с массивами или сетками (таблицами).
Для любых "тяжелых" вычислений с использованием массивов нужно ис­
пользовать библиотеку NumPy. Основ_ное назначение NumPy - то, что она
предоставляет Python объект массива, который более эффективен и луч­
ше подходит для математических вычислений, чем стандартный список
Python. Вот небольшой пример, иллюстрирующий важные поведенческие
различия между массивами NumPy и списками:
>>>#Списки Python

>>> а = [2, 2, 2, 2]
>>> Ь = [3, 3, 3, 3]
>>> а * 2

[2, 2, 2, 2, 2, 2, 2, 2]

>>> а + 10
Traceback (most recent call last):
File "", line 1, in
а + 10
TypeError: can only concatenate list (not "int") to list
>>> а + Ь
[2, 2, 2, 2, 3, 3, 3, 3]
>>>

>>>#Массивы Numpy

>>> import numpy as np
1

http://www.numpy,org

tD---.-...... -... -.... -.... ---..... -... -......-...-. --.........-..-.......-. -....-..



--------·-····---····

•python

Глава 8. Списки

>>> an = np. a,rray( (2, 2, 3, 3])
>>> bn = np.array( (5, 5, 7, 7])
>>> an * 2

array([4, 4, 6, 6])
>>> an + 10
array([12, 12, 13, 13])
>>> an + bn
array([ 7, 7, 10, 12])
>>> an * bn
array([ 10, 10, 21, 31])

>>>

Как видите, основные математические операции с массивами ведут себя
иначе. В частности, скалярные операции (например, an * 2 или bn + 10)
применяются к массиву поэлементно ( в случае с обычным списком нужно
было писать цикл и добавлять в цикле 10 к каждому значению списка). Кро­
ме того, математически операции, когда оба операнда,являются массивами,
применяются к каждому элементу и в результате создается новый массив.
Библиотека NumPy предоставляет набор "универсальных функций", кото­
рые также доступны для работы с массивами.
Данные функции представляют собой замену обычных функций, которые
вы можете найти в модулеmаth. Например:
>>> np.sqrt (an)
array( [1.41421356 , 1. 41421356, 1. 73205081, 1. 73205081 ] )
>>> np.cos (an)
array([-0.41614684, -0.41614684, -0.9899925 , -0.9899925))

>>>

Использование универсальных функций может быть в сотни раз быстрее,
чем перебор :массива в цикле поэлементно и выполнение необходимых опе­
раций над каждым элементом отдельно. Поэтому если вам нужно выпол­
нить операции с использованием функций из модуля math над элементами
'массива, взгляните на аналогичные функции модуля np. Вы должны пред­
почесть их; если это возможно.
"За кулисами" массивы NumPy выделяются таким же способом, как и в С
или Fortran. А именно они являются большими, непрерывными областями
памяти, состоящие из однородного типа данных. Именно поэтому вы мо­
жете сделать массивы просто огромными, гораздо больше, чем щ,1 себе мо­
жете представить. Например, если вы желаете сделать двумерную таблицу
10 ООО х 10 ООО, состоящую из чисел с плавающей запятой, это не проблема.



·····-············-·······························································---

Python. Полное руководство

_____________________• python

>>> qrid = np.zeros(shape=(l0000,10000), dtype=floa t)
>>> qrid
array( [ [ о.' о.' о.' . . . ' о.' о.' о.]'
[ о.' о.' о.' . . . , о.' о.' о� ] ·,
[

о.' о.' о.' . . . , о.' о.' о.]'

...,

о.' о.' о.' . . . , о.' о.' о.]'
о.' о.' о.' . . . , о.' о.' о.]'
о.' о.' о.' . . . , о.' о.' о.]])

>>>

Все обычные операции все еще применяются ко всем элементам одновременно:
>>> qrid += 100
>>> qrid
array([[ 100., 100.,
[ 100., 100.,
[ 100., 100.,
...,
100., 100.,
100., 100.,
100., 100.,

100.,
100.,
100.,

...,
...,
...,

100., 100., 100.],
100., 100., 100.],
100., 100., 100.],

100.,
100.,
100.,

...,
...,
...,

100., 100., 100.],
100., 100., 100.],
100., 100., 100.]])

Один чрезвычайно известный аспект NumPy _:_ способ, которым она рас­
ширяет список Python, индексирующий функциональность - особенно
при работе с многомерными массивами. Чтобы проиллюстрировать это,
сделайте простую двумерную матрицу и попробуйте выполнить некоторые
эксперименты:
>>> a = np.array([[l, 2, З, 4), [5, б, 7, 8), [9, 10, 11, 12)))
>>> а
array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
>>>#Ряд О
>>> a[l]
array([l, 2, 3, 4])
>>>#Колонка 1
>>> а[:,1)
array([ 2, 6, 10])

..

>>>#Выбираем фрагмент массива и изменяем его




--· .....................................................···.··.................... .

Глава 8. Списки

,i8, python

>>> a[l:3, 1:3]
array( [ [ 6, 7],

[ 10, 11]])

>>> a[l:3, 1:3] += 10
>>> а
array([[ 1, 2, 3, 4],

[ 5, 16, 17, 8],
[ 9, 20, 21, 12]])

8" 11 * Проrра
Теория - это хорошо. Теперь продемонстрируем полученные знания на
простом примере, который оформлен в виде листинга 8.1.
Листинг 8.1. Проход по списку автомобилей
cars = ["audi", "vw", "lexus"]
print("Haши машины: ")
for item in cars:
print(item)

К списку можно применять функцию len():
cars = ["audi", "vw", "lexus", "gtr", "m5"]
print("Bceгo ", len(cars), " машин(ы) в гараже")

Вывод программы:
Всего 5 машин(ы) в гараже
Оператор in можно использовать для поиска по списку. Рассмотрим не­
большой пример:
cars = ["audi", "vw", "lexus", "gtr", "m5"]
car = inрut("Введите название авто: ")
if car in cars:
print("Y вас есть авто из списка!")
else:
print("У вас нет машины из списка : (")
Вывод программы:

,

Введите название ато: vm
У вас нет машины из списка: (
__

------- -- ------------ . . ---- " " -- .-- ---- ·- . " . . ------

""

. ........ .

- -- .
"

"""

--- - . .

" ,..

Python. Полное руководство

........................................

fЪpython
'-%7

Введите название авто: vw
У вас есть авто из списка!

Как уже было сказано, списки индексируются. Ничего нового нет. Индек­
сация начинается с О (то есть первый элемент списка имеет индекс О), под­
держиваются положительные и отрицательные индексы.
Пример работы с индексами:
cars = ["audi", "vw", "lexus", "gtr", "m5"]
start = -len(cars)
end
len(cars)
for i in range(start,end,1):
print("cars[", i, "] = "

cars[i]).

Вывод программы:
cars[
cars[
cars[
cars[
cars[
cars[
cars[
cars[
cars[
cars[

-5 ]
-4 ]
-3 ]
-2 ]
-1 ]
о ]
1 ]

2 ]
3 ]

4 ]

audi

vw

lexus
gtr
m5
audi

vw

lexus
gtr
m5

Сначала мы получаем начало ( -len()) и конец (len()) диапазона. Потом про­
ходимся по списку и указываем в качестве индекса переменную i, которая
изменяется от start до end с приростом в 1.
Из вывода видно, что первому элементу сnиска присвоен индекс О. Также
к нему можно обратиться по индексу -len(cars), который в нашем случае
равен -5.
Теперь мы можем приступить к разработке самой программы "Гараж".
Программа Гараж демонстрирует практически все операции над списком:
• Добавление и удаление элементов
• Вывод списка
• Сортировка списка



-----------------------------------------------------------------------------------·

Глава 8. Списки
• Поиск элемента в списке
Листинг 8.2. Программа Гараж
cars = []
print("*" * 10, " Гараж v.0.0.1 "

"*" * 10)

responce = 1
while responce:
рrint("""Выберите действие:
1 - Добавить авто
2 - Удалить авто
3 - Вывести список авто
4 - Поиск
5 - Сортировка гаража
О - Выход""")
int(input(">> "))
responce
if responce == 1:
car = input("Hoвoe авто: ")
cars.append(car)
elif responce == 2:
car = input("Удалить авто: ")
cars.remove(car)
elif responce == 3:
print (cars)
elif responce == 4:
car = input("Пoиcк: ")
if car in cars:
print("Taкaя машина есть в гараже!")
else:
print("Heт такой машины в гараже!")
elif responce == 5:
cars. sort ()
рrint("Отсортировано!")
else:
print("Пoкa!")

Программа работает так. В цикле мы выводим подсказку-меню. Пользова­
тель вводит свой выбор, программа выполняет действия в зависимости от
введенного номера действия. Предполагается, что пользователь будет вво­
дить только цифры от О до 5. Обработка некорректного ввода не добавля­
лась для упрощения кода.

•·-------.............-...................................--·...................----са

ГЛАВА 9.

КОРТЕЖИ

t8python

Глава 9. Кортежи

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

9.1" Пон
В отличие от списка, кортежи относятся к неизменяемым типам данным.
Это означает, что вы можете получить документ по индексу, но не можете
его изменить. Небольшой пример, чтобы вы понимали, о чем речь:
>>> tup =се ( 1, 2, 3, 4)
>>> tup[2]
3
>>> tup[2] = 5
Traceback (most recent call last) :
File "", line 1, in
tup[2] = 5
TypeError: 'tuple' object does not support item assignment
>>>

•.---------------------------------------------·----...... --------.--... "-----··-.--С'!)

Python. Полное руководство

Apython
---------------------·-

\':j;D

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

9.2. Создание кортежей
Создать кортеж можно несколькими способами. Первый способ был уже
нами рассмотрен - это перечисление элементов внутри круглых скобок че­
рез запятую, например:
tup
tup
tup
tup

()
(1,)
(1, 2, 3)
(1, "str", 3)

#
#
#
#

Создан пустой кортеж
Кортеж из одного элемента
Кортеж ИЗ трех элементов
Кортеж ИЗ трех элементов разного типа

Также можно использовать функцию tuple(), которая преобразует передан­
ную ей последовательность в кортеж. Пример:
tup
tup
tup

tuple ()
tuple ( 'Hello')
tuple ([ 1, 2, 3] )

# Пустой кортеж
# Преобразуем строку в кортеж
# Преобразуем список в кортеж

Вообще-то кортеж формируют запятые, а не круглые скобки. Скажем, вы
можете создать кортеж так:
>>> tup = 1, 2, 3
>>> tup
(1, 2, 3)

Позиция элемента в кортеже задается индексом. Нумерация элементов на­
чинается с О (как и в случае со списком). Как и все последовательности,
кортежи поддерживают обращение к элементу по индексу, получение среда,
конкатенацию ( + ), проверку на вхождение (in) и повторение (*). Рассмо­
трим несколько примеров:

....

>>> tup = (1, 2, 3, 4, 5)
>>> tup[4]
# доступ по индексу
- . - . -- - - - " - """"" - - " - " - - ""- - " - " - " - " - "" - - "" - " - "" -- - - - - - " - " - - "" - "" - " - " - """" - - - " - -

"'

;f?J
python -•••••••••••••••••••••
d

5
>>> tup[::-1]
#
(5, 4' 3, 2' 1)
>>> tup[2:3]
#
(3,)
>>> tup[l:3]
#
(2' 3)
>>> 7 in tup
#
False
>>> 2 in tup
True
>>> tup * 2
#
(1, 2' 3, 4' 5, 1, 2,
>>> tup + (1, 2, 4) #
(1, 2' 3, 4' 5, 1, 2'
>>>

Глава 9. Кортежи

обратный порядок
срез
еще срез
проверка на вхождение

повтор
3, 4' 5)
конкатенация

4)

Примеры ранее были рассчитаны на интерактивную работу с IDLE. Теперь
посмотрим, как можно создать кортеж в Руthоn-файле:
cars = ("Nissan", "Toyota", "Lexus")
drivers = ()
# Создает пустой кортеж

Первая строка создает непустой кортеж, состоящий из трех элементов. Вто­
рая строка создает пустой кортеж.

9.3. Методы кортежей
Кортежи поддерживают всего два метода:
indех([, [, ]])
соunt()

Первый метод возвращает индекс элемента с указанным значением. Если
такого элемента нет в кортеже, то генерируется исключение ValueError.
Если не заданы второй и третий параметры, то поиск будет производиться
с начала кортежа.
>>> tup = (1, 2, 3, 4' 5, 2, 7)
>>> tup.index(2)
1
>>> tup.index(2, 2)
5

•. . . . -----..-. -. -. -..-... -. -. -. --. -. -.-. --.-. -. -.-. ----.-..... -. --.---. -. -.-.. ---. -CD

Python. Полное руководство

........ - " .., _____ ., __ .,. ______ ,. __

6python
,,т;

Второй метод подсчитывает количество элементов в кортеже с указанным
значением:
>>> tup.count(2)
2

Чтобы не обрабатывать исключение ValueError, проверяйте сначала коли­
чество элементов методом count() - если оно отличное от О, тогда вычис­
ляйте позиции элементов методом index().
Других методов у кортежей нет, но вы можете использовать функции, пред­
назначенные для работы с последовательностями.

9.4" Перебор элементов кортежа
Вывести содержимое кортежа можно функцией print():
print(cars)

Перебрать все элементы кортежа и что-то сделать с ними:
for item in cars:
print(item)

9.5. Кортеж как условие
Кортеж можно использовать как условие, например:
if not cars:
print("У вас нет машины!")

Пустой кортеж интерпретируется как ложное условие (False), а кортеж, со­
держащий хотя бы один элемент - как истинное. Поскольку пустой кортеж
интерпретируется как False, то условие not cars оказывается истинным,
поэтому программа выведет строку "У вас нет автомобиля".

9"6" Функция len() и оператор in
К кортежам может применяться функция len(), возвращающая число эле­
ментов кортежа:



ID-·····················-········-········-·-·-········-·-······-·-·-········-·-·-···

i!&xpython••••••••••••••••••••••

а,

Глава 9. Кортежи

print("Bceгo машин: ", 1•n(cars))

Проверить существование элемента в кортеже можно так:
if "Nissan" in cars:
print("Y вас есть Nissan!")

9.7" Неизменность кортежей
слияния
О кортежах вам нужно знать еще две вещи. Первая - они, как и строки, не­
изменяемые:
>>> cars = ("nissan", "toyota")
>>> print(cars[O])
nissan
>>> cars[O] = "ford"
Traceback (most recent call last):
File "", line 1, in
cars [О] = "ford"
TypeError: 'tuple' object does not support item assignment
>>>

То есть вы не можете присвоить другое значение элементу кортежа.
Вторая вещь - кортежи поддерживают слияния. Слияние кортежей еще на­
зывают сцеплением. Чтобы выполнить сцепление кортежей, нужно исполь­
зовать оператор+.

9.8. Модуль itertools
Модуль itertools содержит функции, позволяющие генерировать различные последовательности на основе других последовательностей, произво­
дить фильтрацию элементов и др. Подключить модуль можно так:
import itertools

•. . . . ---. -. -----. -------------------------------------------------------. ----------а)

Python. Полное руководство

. � ..............•..... �. python

Далее можно использовать функции этого модуля. Начнем с функции

count(), которая создает бесконечную нарастающую последовательность.
Синтаксис:
count( [start=0] [, step=l] )

Первый параметр задает начальное значение, а второй - шаг. Пример:
>>> import itertools as it
>>> for i in it.count():
if i > 15: break
print(i, end=" ")
О 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Просто так вызвать count() вы не можете, поскольку функция создает бес­
конечную последовательность и вы получаете бесконечный цикл.
Функция repeat() возвращает объект указанное количество раз. Функции
передаются два параметра - объект и количество повторений. Если: коли­
чество повторений не указано, то объект 'будет возвращатi.'ся бесконечно.
Пример:
>>> list(it.repeat('*', 10))
['*',

# Список из '*'
'*']

'*', '*', '*', '*' '*' '*' '*'' '*',

# Комбинация функций zip() и repeat()
>>> list(zip(it.repeat(3), "аЬс"))
[(3, 'а'), (3, 'Ь'), (3, 'с')]
>>>

В предыдущем примере для создания комбинаций мы использовали функ­
цию zip(), которая не является частью itertools, но в самом модуле itertools
есть подобная функциональность:
>>> list(it.comЬinations('abc', 2))
[('а', 'Ь'), ('а', 'с'), ('Ь', 'с')]

Функция comЬinations_with_replacement(iteraЬle, r) создает комбинации
длиной r из iteraЬle с повторяющимися элементами. Пример:
>>> list(it.comЬinations_with_replacement ([1,2,3], 2))
[(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]

••

.....................................................................................

iJ!Jpython
'i?W:1
----------------------

Глава 9. Кортежи

Для создания перестановок используется функция permutations():
>>> list(it.permutations('abc', 2))
[('а', 'Ь'), ('а', 'с'), ('Ь', 'а'), ('Ь', 'с'), ('с', 'а'),
('с', 'Ь')]

Функция cycle(iteraЬle) - возвращает по одному значению из последова­
тельности, повторенной бесконечное число раз.
Функция chain(*iteraЫes) - возвращает по одному элементу из первого ите­
ратора, потом из второго, до тех пор, пока итераторы не кончатся.
Функция dropwhile(junc, iteraЫe) возвращает элементы iteraЫe, начиная с
первого, для которого func вернула ложь. Пример:
>>> list(it.dropwhile(lambda х: х < 5, (1,4,6,4,1]))
[ 6, 4, 1]
>>>

Функция tee(iteraЬle, п =2) создает кортеж из n итераторов:
it . tee( [ 1, 2, 3,4], 2)
(, )
>>>

Модуль itertools очень удобен, когда нужно создать перестановку, комби­
нацию. В этом случае не нужно изобретать колесо, а использовать функции
модуля itertools.

9.9. Распаковка кортежа в отдельные
переменные
Представим, что у нас есть кортеж из N элементов, который вы хотите "рас­
паковать" в набор из N переменных.
Любая последовательность может быть распакована в переменные с ис­
пользованием простой операции присваивания. Требование только одно:
чтобы число переменных соответствовало числу элементов в структуре.
Например:

Python. Полное руководство
»>
>>>
>>>
4
>>>
5
>>>

------------- . ---------

,!;, python
".<
'(" />·

р = (4, 5)
х, у = р
х
у

>>> data = [ 'Den', 50, 91.1, (2022, 12, 21)
>>> name, shares, price, date = data
>>> name
'Den'
>>> date
(2022, 12, 21)
data
>>> name, shares, price, (year, mon, day)
>>> name
'Den'
>>> year
2022
>>> mon
12
>>> day
21
>>>

Если будет несоответствие в числе элементов, то вы получите ошибку.
Например:
»> р = (4, 5)
>>> х, у, z = р
Traceback (most recent call last):
File "", line 1, in
ValueError: need more than 2 values to unpack
>>>

Фактически, распаковка работает с любым объектом, который является
итерируемым, а не только с кортежами или списками. К таким объектам 9.Т­
носятся строки, файлы, итераторы и генераторы. Например:
>>> s = 'Hello'
>>> а, Ь, с, d, е = s
>>> а
'Н'

............
>>> ь

········ ..................... -·-··· ............ -· -· --· .................

'

• python______________________

Глава 9. Кортежи

'е'
>>> е
'о'
>>>

При распаковке иногда бывает нужно отбросить определенные значения.
У Python нет специального синтаксиса для этого, но вы можете просто ука­
зать имя переменной для значений, которые нужно отбросить. Например:
>>> data = [ 'Den', 50, 91.1, (2022, 12, 21) ]
= data
>>> , shares, price,
>>> shares
50
>>> price
91.1
>>>

Однако убедитесь, что имя переменной, которое вы выбираете, не используется для чего-то еще.
Ситуация усложняется, когда вам нужно распаковать N элементов из ите­
рируемого объекта, который может быть длиннее, чем N, что вызывает ис­
ключение "too тапу values to unpack".
Для решения этой задачи может использоваться "звездочка". Например,
пр�дположим, что в конце семестра вы решаете отбросить первые и послед­
ние классы домашней работы и выполнить только их среднюю часть. Если
классов только четыре, то можно распаковать все четыре, но, что если 24?
Тогда все упрощает "звездочка":
def drop_first_last (grades):
first, *middle, last = grades
return avg(middle)

Рассмотрим и другой вариант использования. Предположим, что у вас есть
записи, состоящие из 'имени пользователя и адреса электронной почты,
сопровождаемые произвольным числом телефонных номеров. Вы можете
распаковать эти записи так:
>>> record = ('Mark', 'mark@nit.center', '25-333-26', '888-12-11')
user record
>>> name, email, *phone_numЬers
>>> name
'Mark'



·--------···--··-····-···--·-·-·-··-·········-···-······························-·-С8

Python. Полное руководство

r!,ti python

>>> email
'mark@nit.center'
>>> phone_numЬers
888-12-11']
[ '25-333-26',
>>>
1

1

Стоит отметить, что переменная phone_numbers всегда будет списком, неза­
висимо от того, сколько телефонных номеров распаковано (даже если ни
один). Таким образом, любой код, использующий phone_numbers, должен
всегда считать ее списком или хотя бы производить дополнительную про­
верку типа.
Переменная со звездочкой может также быть первой в списке. Например,
скажем, что у вас есть последовательность значений, представляющая объ­
емы продаж вашей компании за последние 8 кварталов.
Если вы хотите видеть, как самый последний квартал складывается в сред­
них по первым семи кварталам, вы можете выполнить подобный код:
*trailing_qtrs, current_qtr = sales record
trailing avg = sum(trailing qtrs) / len(trailing qtrs)
return avg comparison(trailing_avg, current qtr)

А вот как выглядит эта операция из интерпретатора Python:
>>> *trailing, current = [10, 8, 7, 1, 9, 5, 10, 3]
>>> trailing
[10, 8, 7, 1, 9, 5, 10]
>>> current
3

Расширенная распаковка итерируемого объекта нестандартна для распа­
ковки итерируемых объектов неизвестной или произвольной длины. Часто
у таких объектов есть некоторый известный компонент или образец в их
конструкции (например, "все, что после элемента 1 считать телефонным но­
мером") и распаковка со звездочкой позволяет разработчику усиливать те
образцы, чтобы получить соответствующие элементы в итерируемом объ­
екте.
Стоит отметить, что *-синтаксис может быть особенно полезным при ите­
рации по последовательности кортежей переменной длины. Например,
возможно у нас есть последовательность теговых кортежей:
records = [



а)-........ -. -. -. --. ---... -.............................. -. ----.. -.... ----------. ---.

• python______________________

Глава 9. Кортежи

('foo', 1, 2) ,
('bar', 'hello') ,
('foo', 3, 4) ,
def do_foo(x, у):
print (' foo', х, у)
def do_bar(s):
print ('bar', s)
for tag, *args in records:
if tag == ' foo':
do_foo(*args)
elif tag == 'bar':
do_bar(*args)

Распаковка со звездочкой может также быть полезной, когда объединена с
определенными видами операций обработки строк, например, при разделе­
нии строки (splitting). Например:
>>> line = 'nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false'
>>> uname, *fields, homedir, sh = line.split(': '}
>>> uname
'nobody'
>>> homedir
'/var/empty'
>>> sh
'/usr/Ьin/false'
>>>

Иногда нужно распаковать значения и отбросить их. Вы не можете указать
пустое место с * при распаковке, но вы можете использовать звездочку
вместе с переменной _. Например:
>>> record = ('Den', 50, 123 .. 45, (17, 03, 2021) )
(* , year) = record
>>> name, *
>>> name
'Den'
>>> year
2021
>>>



·---·-············································································..:ID

Python. Полное руководство

_______________________• python

Есть определенная схожесть между звездообразными функциями распа­
ковки и обработки списков различных функциональных ящ,1ков. Напри­
мер, если у вас есть список, вы можете легко разделить его на компоненты
головы и хвоста. Например:
>>> iterns = (1, 10, 7, 4, 5, 9]
>>> head, *tail = iterns
>>> head

1

>>> tail
[ 10, 7, 4 , 5, 9]
>>>

Можно было предположить написание функций, выполняющих такое раз­
деление, в виде некоторого умного рекурсивного алгоритма. Например:
>>> def surn(iterns):

head, *tail
iterns
return head + surn(tail) if tail else head

>>> surn(iterns)

36

>>>

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

9. 1 О. Списки vs кортежи
Мы только что рассмотрели, как работать с кортежами в Python. Возникает
закономерный вопрос - когда лучше использовать списки, а когда - кор­
тежи? Понятно, что списки - лучше кортежей, поскольку можно изменять
элементы списка.
Но не спешите отказываться от кортежей.У них есть следующие преиму­
щества:
• Кортежи работают быстрее. Система знает, что кортеж не изменится,
поэтому его можно сохранит так, что операции с его элементами будут
выполняться быстрее, чем с элементами списка. В небольших программа



ED-··················································································

deJpyth
on ........................................ .
'..№;t

Глава 9. Кортежи

эта разница в скорости никак не проявит себя. Но при работе с больши­
ми последовательностями разница будет ощутимой.
• Неизменяемость кортежей позволяет использовать их как константы.
• Кортежи можно использовать в отдельных структурах данных, от кото­
рых Python требует неизменимых значений.
• Кортежи потребляют меньше памяти. Рассмотрим пример:
>>> а = (1, 2, 3, 4, s; 6)
>>> ь = [1, 2, 3, 4, 5, 6)
>>> а. sizeof
()

36

>>>

44

ь.

sizeof

()

• Кортежи можно использовать в качестве ключей словаря:
>>> d = { (1, 1, 1) : 1)
>>> d

{ (1, 1, 1): 1)

>>> d = { [ 1, 1, 1)

: 1)
Traceback (most recent call last):
File "", line 1, in
d= {[1, 1, 1) : 1)
TypeError: unhashaЫe type: 'list'

.

··········-·······································································-

ГЛАВА 10.
МНОЖЕСТВА И СЛОВАРИ

ф python__________________

Глава 1 О. Множества и словари

1 О. 1. Понятие словаря
Словарь - это набор объектов, доступ к которым осуществляется не по ин­
дексу, а по ключу (аналог ассоциативных_массивов в РНР). Словари могут
содержать данные разных типов и иметь неограниченную степень вложен­
ности.
Элементы в словарях находятся в произвольном порядке. Для доступа к
элементу нужно использовать ключ, нет никакого способа обратиться к эле­
менту в порядке добавления.
Словари, как тип данных, относятся не к последовательностям, а к отобра­
жениям. Именно поэтому операции, которые были применимы к последо­
вательностям (конкатенация, повторение, срез и т.д.), к словарям не при­
м·енимы.
Существует несколько способов создания словаря. Первый способ - это
использование функции dict().
dict(= [ , ... , =]
diсt()
diсt()



·---------------------------------------------------------------------·-·-··--------

Python. Полное руководство

________________________• python

Рассмотрим несколько примеров:
»> d = dict()
>>> d = dict(name='Ивaн', surname='Ивaнoв'); d
{ 'name': 'Иван', 'surname': 'Иванов'}
>>> d = dict({"name": "Иван", "surname": "Иванов"}).; d
{ 'name': 'Иван', 'surname': 'Иванов'}
>>> d = dict([["name", "Иван"], ["surname", "Иванов"]]); d
{ 'name': 'Иван', 'surname': 'Иванов'}
>>> d = dict([("name", "Иван"), ("surname", "Иванов")]); d
{'name': 'Иван', 'surname': 'Иванов'}
>>>
Первый оператор создает пустой словарь. Второй - создает словарь по па­
рам Ключ= Значение. Третий оператор - создает словарь по словарю, да и
в качестве параметров функции dict() мы передали уже готовый словарь.
Четвертый оператор создает словарь по списку списков, а пятый - по спи­
ску кортежей. Как видите, существуют различные способы создания·слова­
рей, и вы можете выбрать тот, который вам больше нравится.
В создании словаря может участвовать и функция zip(). Она может объ­
единить два списка в список кортежей, а затем созданный нею список мы
можем передать в функцию dict(). Например:
>>> keys = ("name", "surname")
>>> values = ("Иван", "Иванов")
>>> list(zip(keys, values))
[('name', 'Иван'), ('surname', 'Иванов')]
>>> kv = list(zip(keys, values))
>>> d = dict(kv); d
{'name': 'Иван', 'surname': 'Иванов'}
>>>
У нас есть два списка - keys (ключи) и values (значения). Мы комбинируем
их функцией zip и создаем общий список kv, который мы потом передаем в
функцию dict и получаем такой же словарь, как и раньше.
Также создать словарь можно, заполнив его поэлементно, например:
>>> d = {}
>>> d["name"] = "Иван"
>>> d["surname"] = "Иванов"
>>> d
{'name': 'Иван', 'surname': 'Иванов'}
>>>



.....................................................................................

Глава 1 О. Множе�;тва и словари

Если вам удобно, вы можете указать все элементы словаря в фигурных
скобках:
»> d = {}
>>> d = {"name": "Иван", "surname": "Иванов"}; d
{'name': 'Иван', 'surname': 'Иванов'}
>>>

При создании словаря нужно помнить, что в переменную сохраняется не
сам словарь, а только ссылка на него, что нужно учитывать при группо­
вом присваивании. Если вам нужно скопировать словарь, то вам нужно ис­
пользовать не оператор присваивания, а метод сору(). Рассмотрим пример:
">>> d = {"name": "Иван", "surname": "Иванов"}; d
{'name' : 'Иван', 'surname': 'Иванов'}
>>> d2 = d
>>> d2 is d
True
>>> d2 � d.copy()
>>> d2 is d
False
>>>

Если присвоить d переменной d2, то оператор is сообщит, что обе перемен­
ные ссылаются на один и тот же объект в памяти (True). Если же скопи­
ровать словарь через метод сору(), то будет создана независимая копия в
памяти (оператор is вернет False). Однако метод сору() делает только по­
верхностную копию словаря, для создания полной копии лучше использо­
вать функцию deepcopy():
>>> import сору
>>> d2 = copy.deepcopy(d); d2
{'name': 'Иван', 'surname': 'Иванов'}
>>> d2 is d
False



··················································································-

Python. Полное руководство

------------------------.python

10.2. Различные операции над
словарями
10.2. 1. ДОСТУП К ЭЛЕМЕНТУ
Начнем с доступа к элементу. Доступ осуществляется по ключу:
>>> d["name"]
'Иван'
При обращении к несуществующему элементу будет сгенерировано исклю­
чение:
>>> d["lastname"]
Traceback (most recent call last): ·
File "", line 1, in
d.["lastname"]
KeyError: 'lastname'
Проверить наличие ключа можно с помощью оператора in:
>>> "surname" in d
True
>>> "lastname" in d
False
>>>
Узнать, сколько ключей есть в словаре можно с помощью функции ler,,():
>>> len(d2)
2

>>>

10.2.2. ДОБАВЛЕНИЕ И УДАЛЕНИЕ ЭЛЕМЕНТОВ
СЛОВАРЯ
Добавить элемент в словарь можно так:
>>> d
{'name': 'Иван', 'surname': 'Иванов'}
>>> d["lastname"] = "Иванов"
>>> d
{'lastname': 'Иванов', 'name': 'Иван', 'surname': 'Иванов'}



--------------------------------------------------------------··-···--··-·---····-··

,t!Ьpython
·•···•············
�w

Глава 1 О. Множества и словари

Если ключ есть в словаре, то ему будем присвоено новое значение. Если
ключа нет, то он будет добавлен в словарь.
Удалить ключ из словаря можно с помощью оператора del:
>>> d
{'lastname': 'Иванов', 'name': 'Иван', 'surname': 'Иванов'}
>>> del d["lastname"]; d
{'name': 'Иван', 'surname': 'Иванов'}

10.2.З. ПЕРЕБОР ЭЛЕМЕНТОВ СЛОВАРЯ
Перебрать все элементы словаря можно так:
>>> for key in d.keys():
print("({O} => {l})".format(key, d[key]), end=" ")
(name => Иван) (surname => Иванов)

10.2.4. СОРТИРОВКА СЛОВАРЯ
Словарь - это неупорядоченная структура данных. Поэтому при выводе
словаря его ключи выводятся в произвольном порядке. Вы же можете от­
сортировать словарь по ключам. Для этого нужно получить сначала список
всех ключей, а затем использовать метод sort():
>>> keys = list(d.keys())
>>> keys. sort()
>>> for key in keys:
print("({O}=> {l))".format(key, d[key]), end=" ")
(name => Иван) (surname => Иванов)

Данный пример не очень удачен, поскольку и до сортировки ключи в сло­
варе находились в отсортированном порядке (так получилось), но если до-

•.

. . . . . . . .. . .. . . . . . . . . . . . - - .. - - . - . - . - .. - -- . - . - . - . -- . - --· - -- . - . - - - - - - - - . - - - . - -

. .... ...

Python.

---••·••----------------•··python

Гiолнсе PVKOROД� 0 В0

бавить·в словарь н6вый элемент и повторить пример, все будет работать как
нужно:
>>> d["lastname"]="Ивaнoв"
>>> d["zip"]="109011"
>>> d
{'zip': '109011', 'lastname': 'Иванов', 'name': 'Иван',
'surname': 'Иванов'}
>>> keys = list(d.�eys())
>>> keys.sort()
>>> for key in keys:
print("({0}=> {lJj".format(key; d[key]), end=" ")
(lastname=> Иванов) (name=> Иван) (surname=> Иванов) (zip=> 109011)

10.2.5. МЕТОДЫ KEYS(), VALUES() И НЕКОТОРЫЕ
ДРУГИЕ
Метод keys(), как вы уже заметили, возвращает объект dict_keys, содержа­
щий все ключи словаря. Данный объект поддерживает итерации, а также
операции над множествами.
Аналогично, метод values() возвращает объект dict_values, содержащий.все
значения словаря. Данный объект также поддерживает итерации. Пример:
>>> values = d.values()
>>> list(values)
['109011', 'Иванов', 'Иван', 'Иванов'·]

Также у словарей есть много дополнительных и бессмысленных методов.
Например, метод get() возвращает значение элемента, но его и так можно
получить:
>>> d.get("lastname")
'Иванов'
>>> d·[ "lastname"]
'Иванов'





at-----·····················-·········································-····-·····--··

Глава 1 О. Множества и словари

.python____ �-____________

Особого tмысла в этом методе нет,, как и в методе clear(), который очищает
словарь. А вот метод рор() может пригодиться. Он удаляет элемент и
возвращает его значение:
>>> d.pop("lastname")
'Иванов'
>>> d
{'zip': '109011', 'name': 'Иван', 'surname': 'Иванов'}

10.2.6. ПРОГРАММА DICT
Продемонстрируем полученные знания на примере простой программы­
сло�аря. В .этой программе мы· будем активно использовать оператор in,
чтобы выяснить, есть ли слово в словаре или нет:
if "bus" in dict:
print(dict["bus"])
else:
print("Cлoвa нет в словаре!")
Поскольку при обращении к несуществующему элементу словаря генери­
руется ошибка, то перед обращением неплохо бы проверить его наличие с
помощью оператора in. Напишем простейшую программу поиска по слова­
рю.

Листинг 10.1. Словарь v0.1
dict = {
"apple" : "яблоко",
"bold" : "жирный",
"bus"
"автобус",
"cat"
"кошка",
"car"
"машина"}
print("=" * 15, "Dict", "=" * 15)
word = ""
while word != "q":
word = inрut("Введите слово или
if word != " q":



q

для выхода: ")

·------------------------------------------------------------------------------------

Pr1l10,1. Полнре ру�оводстrю
if word in dict:
print(dict[word])
else:
print("He найдено")

--·----_.__....._.______+

pyttion

Программа осуществляет поиск по словарю. Посмотрим, как она органи­
зована. Сначала мы определяем переменную word. Цикл while будет рабо­
тать, пока эта переменная не равна "q".
В цикле пользователю предлагается ввести слово. Если слово не равно "q",
то мы начинаем поиск по словарю. Если слово найдено, мы выводим его
значение, если нет - то строку "Не найдено".
Если пользователь введет "q", то в цикле мы ничего не делаем, а при следу­
ющей итерации цикл будет прекращен.
Продолжим разработку нашего Словаря. Попробуем модифицировать ис­
ходную программу так, чтобы она поддерживала добавление и удаление
элементов словаря, а также некоторые другие возможности.
Листинг 10.2. Словарь v 0.1
# Словарь заполнен по умолчанию
dict = {
"apple" : "яблоко",
"bold" : "жирный",
мьus"
"автобус",
"cat"
"кошка",
"car"
"машина"}
print("=" * 15, "Dict v �-2", "=" * 15)
# Справка. Будет выведена по команде h
"""
help_message
s - Поиск
а - Добавить новое слово
r - Удалить слово
k - Показать все слова
d - Показать весь словарь
h - Справка
q - Выход

"""

choice = ""
while choice != "q":
choice = input("(h - help)>> ")

�-- -- . -.. - . - . - . - . -- -- . -...........................................................

'

фpython................. ..

Глава 1 О. Множества и словари

if choice == "s":
word = inрut("Введите слово: ")
res = dict.get(word, "Не найдено!")
print(res)
elif choice == "а":
word = inрut("Введите слово: ")
value = inрut("Введите перевод: ")
dict[word] = value
print("Cлoвo добавлено!")
elif choice == "r":
word = inрut("Введите слово: ")
del dict[word]
print("Cлoвo удалено")
elif choice == "k":
print(dict.keys())
elif choice == "d":
for word in dict:
print(word, ": ", dict[word])
elif choice == "h":
print(help_message)
elif choice == "q":
continue;
else:
рrint("Нераспознанная команда. Введите h для справки")
Основной цикл программы:
choice = ""
while choice != "q":
choice = input("(h - help)>> ")
При каждой итерации мы выводим подсказку (h - справка)>> и читаем
ввод пользователя. Справка, а именно доступные команды отображаются
по команде h.
Поиск слова в словаре мы производим с помощью метода get():
if choice == "s":
word = inрut("Введите слово: ")
res = dict.get(word, "Не найдено!")
print(res)
Добавление осуществляется так:



elif choice == "а":
word = inрut("Введите слово: ")

··················································································•

.python

- ... ------------ ... --- ....., •1,

value = inрµt("Введите перевод: ")
>> msum(y= З, х= 2)
5

Если значения параметров, которые планируется передать в функцию, со·
держатся в кортеже или списке, то перед объектом следует указать символ

>>> 11 = (2, 3)
>>> msum(*ll)
5

· Количество элементов в словаре должно быть равно количеству параме­
тров, которые может принимать функция.
Если значения параметров содержатся в словаре, то перед именем с�оваря
нужно указать две звездочки**:
>>>· d = {"х": 5, "у": 6)
>>> msum(**d)
11

Если вы указываете переменную в качестве значения параметра функции и
передаваемый объект относится к неизменяемым типам, то, если функция
изменяет значение параметра, это никак не отобразится на исходной пере­
менной:
>>> def test(x):
х = 1
return х
>>> у = 2
>>> test(y)
1
>>> у

,

-- --

........ � ... - - - -- -

-- ... - . - .. - -- . - . - . -.... - . - ... - . - - . - . - - - ... - .. -... - . ---- . -

....

Python. Полное руководсrво

---------------------···. python

2
>>>

Однако функция может изменять значения объектов изменяемых типов, к
которым относятся списки и словари.

11.3. Переменное число параметров
Представим, что вам нужно написать функцию, принимающую любое чис­
ло параметров. Для этого используйте аргумент*. Пример:
def avg(tirst, *rest):
return (first + sum(rest)) / (1 + len(reit)i

Рассмотрим пример использования функции:
prin_t(avg(l, 2))
# 1.5
print(avg(l, 2, 3, 4))
# 2.5

В данном случае rest - это кортеж, содержащий все дополнительно пере­
данные аргументы. Наш код считает его последовательностью и работает
как с последовательностью.
Чтобы принять любое число именных параметров (keyword arguments), ис­
пользуйте параметр, который начинается с**. Например:
import html
def make_element(name, value, **attrs):
keyvals = [' %s="%s"' % item for item in attrs.items()]
attr_str = '' .join(keyvals)
element = '{value}'.format(
name=name,
attrs=attr_str,
value=html.escape(value.))
return element

Примеры использования:



------·······················-·------------------------------------·------------·-·

; • python _ _ •_____ •..__ •.•

Гл;�ва 11.

ПользОВЛГЕjЬСКИЕ Ф\'Нl(ЦИ,1

# Создаем 'Bus'
rnake_elernent('itern', 'Bus', size='large', quantity=б)
# Создаем '&lt;Car&gt;'
rnake_elernent('р', '')

Здесь attrs - словарь, который хранит переданные именные аргументы
(если они были переданы, конечно).
Если вы хотите написать функцию, которая сможет принимать любое чис­
ло позиционных и именных параметров, используйте* и** вместе. На­
пример:
def anyargs(*args, **kwargs):
print(args) # Кортеж
print(kwargs) # Словарь

С этой функцией все позиционные аргументы будут помещены в кортеж
args, а все именные аргументы будут помещены в словарь kwargs.
Параметр * может быть указан исключительно как последний позицион­
ный параметр в определении функции. Параметр** тоже может появиться
как последний параметр. Тонкий аспект определения функции - это то,
что параметры могут все еще появиться после параметра*:
def а(х, *args, у):
pass
def Ь(х, *args, у, **kwargs):
pass

11 .4. Анонимные функции
Некоторые функции, например, функции сортировки, подразумевают пе­
редачу в качестве параметров пользовательских функций, определяющих
порядок сортировки или что-либо еще. В таких случаях удобнее использо­
вать короткие встроенные функции, а не создавать полноценные функции
оператором def.



...•••...••.......... ····························································�-at

Простые функции, которые делают ни что иное, как просто вычисляют вы�
ражение, могут быть заменены выражением lambda. Например:
>>> add = lamЬda х, у: х + у
>>> add(2,2)
4
>>> add('hello', 'world')
'helloworld'
>>>

Использование lambda здесь аналогично следующим кодом:
>>> def add(x, у):
return х + у
>>> add(2,2)
4
>>>

Как правило, larrzbda используется в контексте некоторой другой операции,
такой как сортировка или сокращение данных:
>>> names = ['John', 'Den', 'Mark', 'Jane']
>>> sorted(names, key=lamЬda name: name.split() [-1] .lower())
['Den', 'Jane', 'John', 'Mark']
>>>

Хотя lambda позволяет вам определять простую функцию, определять та­
кую функцию не рекомендуется. В частности, может быть определено толь­
ко единственное выражение, результатом которого является возвращаемое
значение. Это означает, что никакие другие функции языка, включая мно­
гократные операторы, условные выражения, итерация и обработка исклю­
чений не могут быть включены в эту функцию.
Вы можете написать много Python-кoдa, вообще не используя lambda. Од­
нако вы будете иногда встречаться с ней в программах, где кто-то пишет
много крошечных функций, которые вычисляют различные выражения
или в программах, которые требуют, чтобы пользователи лредоставили
саllЬасk-функции.




•··················································································

----------------

.
•pytllon

.

Глава 11. ПопьзсвдтЕr.1оскиЕ Q)11"'�,·

Рассмотрим поведение следующего кода:
>>> х
>>> а
>>> х
>>> ь
>>>

=

10
lamЬda у: х + у
20
lamЬda у: х + у

Теперь задайте себе вопрос. Каковы значения а(10) и Ь(10)? Если вы дума­
ете, что 20 и 30, то вы ошибаетесь.
>>> a(l0)
30
>>> Ь(10)
30
>>>

Проблема здесь в том, что значение х в выражении lambda является свобод­
ной переменной, которая связывается во время выполнения, а не во время
определения. Поэтому значение х в lатЬdа-выражении - то же, что и значе­
ние переменной х во время выполнения. Например:
>>>
>>>
25
>>>
>>>
13
>>>

х = 15
а(10)
х = 3
а(10)

Если вы хотите написать анонимную функцию, которая получает значение
на момент определения и хранить ее, добавьте значение как значение по
умолчанию, например:
>>>
>>>
>>>
>>>
>>>
20



х
а
х

10
lamЬda у, х=х: х + у
20
ь lamЬda у, х=х: х + у
а(10)

•·············································. ·····················.

·············-

Python. Полное руководство

-------·------------ ------f6python


»> b(lO)
30
>>>

Теперь поговорим о переносе дополнительного состояния в функциях об­
ратного вызова. Представим, что вы написали код, который основывается
на использовании саllЬасk-функций (например, обработчики событий), но
вы хотите, чтобы саllЬасk-функция хранила дополнительную информацию
о состоянии для использования внутри функции.
Этот пример связан с использованием функций обратного вызова, которые
используются во многих библиотеках и структурах - особенно связанных
с асинхронной обработкой. Для иллюстрации и из соображений тестирова­
ния определим следующую функцию, которая вызывает функцию обратно­
го вызова:
def apply_async(func, args, *, callback):
# Вычисляем результат
result = func(*args)
# Вызываем саllЬасk-функцию и передаем ей результат
callback(result)

На практике такой код мог бы выполнять сортировку с использованием по­
токов, процессов и таймеров, но здесь мы не. об этом. Вместо этого мы
просто фокусируемся на вызове саllЬасk-функции. Вот пример, показыва­
ющий, как можно использовать предыдущий код:
>>> def print result(result):
print('Result: ', result)
>>> def add(x, у):
return х + у
>>> app ly_ async(add, (2, 3), callback=print_result)
Got: 5
>>> apply_async(add, ('hello', 'world'), callback=print_result)
Result: helloworld
>>>

Как видите, функция print_result() принимает только один аргумент, кото­
рый является результатом. Никакая другая информация не передается в
нее. Такой недостаток информации иногда может представлять проблему,



f!J------.-----.. ----..........--.... --........ -................................ -. -...

• python................

Глава 11.

ПользовдТЕЛЬСКИЕ ФУНКЦИИ

например, когда вы хотите, чтобы функции обратного вызова взаимодей·
ствовала с другими переменными или частями среды.
Один из способов хранить дополнительную информацию в функции: об·
ратного вызова - использовать метод (и, соответственно, класс) вместо
функции. Например, следующий класс хранит внутренний номер последо­
вательности, который вызывается при каждом вызове метода handler():
class ResultHandler:
def
init (self):
self.sequence = О
def handler(self, result):
self.sequence += 1
print(' [{}] Результат: {}'.format(self.sequence, result))

Чтобы использовать этот класс, вам нужно создать экземпляр и использо­
вать связанный метод handler в качестве функции обратного вызова:
>>> r = ResultHandler()
>>> apply_async(add, (2, 3), callback=r.handler)

[1] Result: 5

>>> apply�async(add, ('hello', 'world'), callback=r.handler)

[2] Result: helloworld

>>>

11.5. Функции-генераторы
Функция-генератор - это функция, которая может возвращать одно зна­
чение из нескольких значений на каждой итерации. Превратить функцию
в генератор позволяет ключевое слово yield. Рассмотрим пример простой
функции-генератора:
def gen(х, у):
for i in range(l, x+l):
yield i + у

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



·------------------------------------------------------------------------------------

Python. Полное руководс1во
>>>
>>>
4
>>>
5
>>>
6

s = gen(З,3)
print(s. next

- ------- . -------------·- - ...
� python

() )

print(s.

next

() )

print(s.

next

() )

>>> print(s. next ())
Traceback (most recent call last):
File "", line 1, in
print(s. next ())
Stopiteration
>>>

Как видите, при каждом следующем запуске функция увеличивает резуль-.
тат предыдущей операции на 1. Максимальное число итераций устанавли�
вается вторым параметром, а первый параметр - начальное число, которое
будет постепенно увеличиваться на 1. Четвертый вызов функции завер­
шился с ошибкой Stoplteration, поскольку второй параметр. задает макси­
мальное число итераций, равное трем.

11.6. Декораторы
Основное назначение декораторов - выполнить какие-либо действия перед
выполнением функции. Рассмотрим пример:
def deco(f):
print("my_func is running")
return f
@deco
def my_fuлc(х):
return х * 2
print(my_func(S))

11 . 7. Рекурсия
Рекурсия - это явление, когда функция вызывает саму себя. В современном
программировании рекомендуется не использовать рекурсию и пытаться
любой рекурсивный алгоритм заменить нерекурсивным. Опасность рекур­
сии в том, что вы можете забыть предусмотреть условие выхода из рекур-

--

............... � .......... -..........--.. -...... -... ........ -..........--- . --·

•..

•python---------------•,

Глава 1 1.

ПользОВАТЕЛЬСКИ� ФУНКЦИИ

сии, и тогда функция будет запускать себя снова и снова и ничего хорошего
из этого не получится.
Классическим примеров рекурсивной функции является функция вычис­
ления факториала:
def fact(n):
if n == О or n == 1: return 1
else:
return n * fact(n - 1)

Как видите, мы предусмотрели условие выхода из рекурсии - если n = О
или n = 1, то функция просто возвращает 1. В противном случае функция
вызывает саму себя, передав значение n, уменьшенное на 1. Следовательно,
при каждом вызове функции значение параметра будет уменьшаться, а ког­
да оно станет равно 1, функция просто вернет 1.
Ради справедливости нужно отметить, что функция вычисления факториа­
ла называетсяfасtоriаl() и имеется в модуле math.

11.8. Глобальные и локальные
переменные
Глобальные переменные - это все переменные, объявленн�це за пределами
функции. Локальные переменные - это переменные, объявленные в самой
функции.

11.8.1. ИНКАПСУЛЯЦИЯ
А зачем функции возвращаю значения? Посмотрим на такую функцию:
def fun():
res = 10
return res

Почему бы нам не обратиться к переменной res напрямую - в коде нашей
программы? Спешу вас огорчить: потому что нельзя. Переменная res не



··················································································-а»

Python. Полное руководство

_______________________• python

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

11.8.2. ОБЛАСТЬ ВИДИМОСТИ. КЛЮЧЕВОЕ СЛОВО
GLOBAL
Благодаря инкапсуляции, функции как бы закрыты от основной програм­
мы и от других функций. Пока мы знаем только единственный механизм
обмена информацией между ними - это параметры и возвращаемые значе­
ния. Однако есть еще и другой способ - глобальные переменные.

Область видимости - это способ представления разных частей програм­
мы, отделенных друг от друга.
Рассмотрим небольшой пример:
def funl ():
res = 10
print(res)
def fun2 ()
res = 20
print(res)
res = 30
print(res)
funl ()
fun2 ()
print(res)

Вывод программы будет таким:

О python________________

Глава 11.

ПользовдТЕЛЬСКИЕ ФУНКЦИИ

30
10
20
30

Сначала мы определили две функции, внутри каждой из них переменной
res присваивается разное значение - 1 О и 20 соответственно. Далее в основ­
ной программе мы определили переменную res со значением 30.
Сначала мы выводим значение переменной res до запуска функций. Затем
запускаем обе функции и снова выводим значение переменной res, чтобы
убедиться, что ни одна из функций его не изменила.
Как видите, программа и две наших функции выводят собственные значе­
ния переменной res, а все потому что у нас есть целых три области видимо­
сти - одна глобальная (программа) и две локальные - по одной для каждой
из функций.
Любая переменная, созданная в глобальной области видимости, называет­
ся vюбалъной. Переменная, объявленная в локальной области, назьшается
локальной.
Если вам нужно получить доступ к глобальной переменной, тогда нужно
использовать ключевое слово global. Изменим нашу программу так:
def funl():
global res
print(res)
def fun2 ():
res = 20
print(res)
res = 30
print(res)
funl ()
fun2 ()
print(res)

Теперь вывод программы будет такой:
30
30
20



·-------------------------------------------------------------------------------------

Python. Полное руководство

·-·-··-----------------•python
,

-

_

,

30

Посмотрим, что произошло. Сначала мы вывели значение res, определен­
ное в основной программе. Затем мы вызвали функцию fun 1 (), которая бла­
годаря ключевому слову global получила доступ к глобальной переменной
res и вывела ее значение. Функцияfип2() вывела собственное значение res.
Далее мы отобразили значение переменной res из глобальной области.
Использование ключевого слова global позволяет не только читать, но и за­
писывать, то есть изменять значение глобальной переменной. Рассмотрим
следующий пример:
def funl():
global res
res = 50
print(res)
def fun2 () :
global res
print(res)
res =' 30
print(res)
funl ()
fun2 ()
print(res)

Изначально значение глобальной переменной res было 30. Затем в функ­
ции fип 1 () мы изменили его на 50. Функцияfип2(), поскольку она вызы­
вается после функции/ип 1 (), получает уже новое значение - 50. Поскольку
функция fun 1 () изменила значение переменной res() в глобальной области,
то последний оператор print() отобразит также 50. В итоге вывод програм­
мы будет таким:
30
50
50
50

11.8.З. СТОИТ ЛИ ИСПОЛЬЗОВАТЬ ГЛОБАЛЬНЫЕ
ПЕРЕМЕННЫЕ?
Да, в Python вы можете использовать глобальные переменные. А нужно ли?
Ведь, по сути, тогда вы лишаетесь преимуществ инкапсуляции - вам нужно
будет следить за значением переменной. Одна логическая ошибка в боль-

f!8--· · · · · · · · -· · · · · · · · · · · --· · -· · · · · · · · · · · · · · · · · · · -'· · · · · · · · · · · · · · · · · · · · · · ·· · · · · · · · ·· ·'

ф python................

Глава 11. ПользовдтЕльскиЕ ФУнкции

шой программе приведет к непоправимым последствиям и многочасовой
отладке. Нужно ли вам это? Глобальные переменные только запутывают
код, поскольку за их постоянно меняющимися значениями сложно следить.
Поэтому постарайтесь ограничить их использование по максимуму. Основ­
ной девиз должен быть таким: если можно обойтись без глобальной пере­
менной, сделайте это.

11.9. Документирование функций
Очень полезно документировать функции по мере их написания. Пройдет
время и вы даже не сможете вспомнить, как работает та или иная функция,
какие параметры она должна принимать. Конечно, в простом случае до­
статочно взглянуть на ее код и все станет понятно, но есть ситуации, когда
функции содержат сотни строк кода. В таких ситуациях на помощь прихо­
дит документирование.
В Python есть особый механизм, который называется документирующими
строками. Такие строки представляют собой строку в тройных кавычках. В
блоке кода документирующая строка должна обязательно идти первой по
порядку. Рассмотрим пример:
def warning(message):
""" Выводит сообщение, заданное параметром message,
обрамленное символами * для привлечения. внимания """
print ("*" * 10, message, "*" * 10)

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

11.1 О. Возвращаем несколько
значений"
Иногда нужно, чтобы функция вернула несколько значений. Для этого
просто возвращайте кортеж, например:



def fun():

···································-··············································-с8

Pytlюn. Полное руководство

,. ___ ·-----______________• py1:hon

return 1, 2, 3
а, Ь, с

=

fun()

print(a, Ь, с)

В результате будет выведено
1 2 3

Хотя кажется, что функция fun() возвращает несколько значений, на самом
деле она возвращает одно значение, но в виде кортежа. Это выглядит немно­
го странным, но кортеж формирует запятая, а не круглые скобки. Пример:
>>> а
>>> а

=

(1, 2)

>>> Ь
>>> ь

=

1, 2

( 1,

2)

(1,

2)

>>>

# Со скобками
# Без скобок

При вызове функций, которые возвращают кортеж, принят.о присваивать
результат несколько переменным, как было показано выше. Это просто распаковка кортежа. Возвращаемое значение можно также присвоить од­
ной переменной.
>>> х
>>> х
(1,

>>>

2,

=

fun ()
3)

11 . 11 . Именованные аргументы
Давайте рассмотрим функцию hello, использующую самый простой способ
передачи параметров:
def hello(name, city):
рrint("Привет, ", name, "! Мы едем в ", city)

f!lt--.-....-... -........-.-....-...-. -.-..-. -.-.-. -.. -. -.-. -.----. -.-.-...--•.-.---. _,

Глава 11.

ПользОВАТЕЛЬСКИЕ ФУНКЦИИ

Ничего сложного. Мы просто определили два параметра - name и city. Пер­
вый - имя, второй город. Такие параметры называются позиционными, по­
скольку строго определен порядок их следования. ·значения функция при­
нимает в том же порядке, что и указаные позиционные параметры.
Рассмотрим вызов функции:
hello("Mapк", "Санкт-Петербург")

Вывод будет таким:
Привет, Марк! Мы едем в Санкт-Петербург

А что будет, если программист перепутает порядок следования аргументов:
hello("Санкт-Петербург", "Марк")

Тогда параметру name будет передано значение "Санкт-Петербург", а пара­
метру city - "Марк". В итоге вывод будет не таким, как мы ожидали:
Привет, Санкт-Петербург! Мы едем в Марк

Это у нас еще простой случай, а представьте, если бы второй параметр был
числом и далее шла его обработка как числа. Тогда мы бы получили ошибку
и выполнение программы было бы остановлено!
Специально для таких случаев предназначены именованные аргументы.
Они позволяют указывать аргументы в любом порядке при условии, что мы
задаем имя аргумента. Вызовем функцию так:
hello(city

"Санкт-Петербург", name

"NoName")

Преимущества использования именованных аргументов следующие:
• Ясность - вы знаете, какое значение и какому параметру передаете. Даже
если вы будете указывать параметры в том же порядке, в котором они и
объявлены, использование именованных аргументов добавляет прозрач­
ности в вашу программу.

-

• Возможность изменения порядка следования параметров - если вы на­
мерено изменили порядок следования аргументов ( потому что вам так
захотелось) или случайно перепутали его, ничего страшного не произой-

,_

......... -. -- - -- -.-- - - -. - - . - --.-- -. -.. -. -. -... -....... -- ... -· ......... ---- -.. - -

Pytho11. Полное руководство

........................• python

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

11. 12. Практический пример:
программа для чтения RSS-ленты
Формат новостной ленты RSS довольно популярен во всем мире, особенно
на новостных сайтах и всевозможных форумах. Многие пользователи пред­
почитают читать RSS-ленту, а не заходить на сайт. Почему? Да потому что в
RSS-ленту не попадает реклама и прочий не нужный пользователю контент
- он видит только текст новости и некоторые другие служебные данные
(дату и время публикации, имя автора и т.д.)
Сейчас мы попробуем написать программу, которая будет читать RSS но­
востную ленту (лист. 11.1).

Листинг 11.1. Программа для чтения RSS
def rss reader(url):
from urllib.request import urlopen
from xml.etree.ElementTree import parse
# Загружаем RSS-ленту и парсим ее
= urlopen(url)
doc = parse(u)
# Извлекаем и выводим итересующие теги
for item in doc.iterfind('channel/item'):
title = item.findtext('title')
date
item.findtext('pubDate')
link = item.findtext('link')
и

print(title)
print(date)
print(link)
print()
rss reader('http://example.com/rss.php')

Итак, у нас есть функция rss_reader(), которой нужно передать адрес но­
востной ленты. Посмотрим, что происходит внутри функции. Первым де­
лом импортируются модули urlopen и parse. Первый нужен.для открытия



fJ!!t----------------------------------------------------------------------------------·

6python

:·�. F. ,, '

' .... - .... - ............. ---

Глава 11 . ПользовдтЕльскиЕ ФУнкции

удаленного докумецта, второй - для разбора (парсинга ХМL-формата, в
котором и распространяется новостная лента).
Далее в переменной u мы читаем новостную ленту, адрес которой задается
параметром url. После мы выполняем парсинг этой ленты и результат
сохраняем в doc.
Переменная doc содержит уже "разобранную" новостную ленту. Функция
xml.etree.ElementTree.parse() парсит весь ХМL-документ в объект докумен­
та. Вы можете использовать методы вpoдeftnd(), iterfind() иfindtext() для
поиска определенных ХМL-документов. Аргументы к этим функциям имена определенных тегов, вроде channel/item или title.

При определении тегов вы должны принять полную структуру докумен­
та во внимание. Каждая операция find работает относительно начального
элемента. Ащuюгично, имя тега, которое вы передаете каждой операции,
тоже указывается относительно начального элемента. В примере вызов к
doc.iterfind('channel/item') находит все элементы "item", которые находят­
ся внутри элемента "channel". "doc" представляет верхнюю часть документа
(элемент "rss"). Более поздние вызовы itemfindtext() будут иметь место от­
носительно найденных элементов "item".
Далее в цикле мы просто находим и выводим элементы title, pubDate, link.
Как правило, элементы с такими названиями есть в большинстве случаев,
но вы можете просмотреть код RSS-ленты и изменить названия элементов,
если в коде используются другие.



···········································--·--·-································88

ГЛАВА 12.
ДАТА И ВРЕМЯ

r', python

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

12. 1. Получение текущей даты и
времени
В Python для работы с датой и временем используются модули time,
datetime, calendar, timeit. Первый модуль позволяет получить текущую
дату и время, а также произвести форматированный вывод времени/даты.
Второ� модуль используется для манипулирования датой и временем.
Третий модуль позволяет вывести календарь в виде простого текста или
НТМL-кода. Последний модуль позволяет измерять время выполнения
фрагментов кода - он используется для оптимизации программы.
Начнем с модуля time. Функция time позволяет получить число секунд,
прошедшее с 1 января 1970 года (эта дата считается началом эпохи UNIХ­
систем, поэтому во многих языках программирования считается началь­
ной):
>>> import time
>>> time.time()
1528374820.9274228

•.

- - - - - - - - - - - - - - - - - - - - - - - - - - - ... - - - - - . - . - . - - - - ... - . - - - - - .. - - - .. - - . - - . - - - - - ..... - ..

----

Python. Полное JJуководr�в"

______________ и. ________ •

pythan

Понятно, что такая дата будет удобна·лишь самому компьютеру, но не чело�
веку. Пока вы высчитаете, какому времени соответствует выданный функ­
цией результат, время существенно изменится. Поэтому для работы с датоi1
и временем лучше использовать другую функцию, например, gmtime(), ко­
торая возвращает объект strnct_time, представляющий универсальное время
UTC. Функции передается единственный параметр - количество секунд,
прошедшее с 1 января 1970 года, то есть то, что возвращает функция time():
>>> t = time.time()
>>> time.gmtime(t)
time.struct_time(tm_year=2018, tm_mon= б, tm_mday=7, tm_
hour= l2, tm_min =ЗЗ, tm_sec =54, tm_wday= З, tm�yday�l58, tm
isdst= O)

Как видите, это структура. Использовать структуру нужно так:
>>> tm = time.gmtime(t)
>>> tm.tm hour
12

Если нужно не UТС-время, а ме�тное время, тогда нужно использовать
функцию localtime(), которая также принимает также количество секунд,
прошедшее с 1 января 1970 года. Если нужно работать с текущим временем,
то ни в localtime(), ни в gmtime() не нужно вообще ничего передавать - по
умолчанию будет использовано локальное время. Функция localtime(), как
и gmtime(), возвращает объект strnct_time, содержащий следующие атрибу­
ты:
• tm year- О - год;
• tm_mon - 1 - месяц (число от 1 до 12);
• tm_mday- 2 - день месяца (число от 1 до 31);
• tm_hour- 3 - час (число от О до 23);
• tm_min- 4 - минуты (число от О до 59);
• tm_sec- 5 - секунды (число от О до 59);
• tm wday - б - день недели (число от О (для понедельника) до 6 (для вос­
кресенья));



CD-··················································································

+•python..................... .

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

• ·tm yday· · 7 � количество дней, прошедшее с начала года (число от 1 до
366);
• tm_isdst · 8- флаг коррекции летнего времени (значения О, 1 или ·1).

12.2. Форматирование даты и
времени
Понятно, что можно проанализировать объект scruct_time и отобразить
информацию о времени/дате так, как вам нужно. Но гораздо проще не за­
ниматься этим вручную, а поручить форматирование даты и времени спе­
циальным функциям. Например, функция strftime() возвращает строковое
представление даты в соответствии с заданной строкой формата:
strftime([, ])

Первый параметр - это строка формата, мы ее рассмотрим чуть позже. Вто­
рой параметр - необязательный. Если он не указан, то будет использована
текущая дата (время). Как именно будет отображаться дата и время, зави­
сит от локали. Рассмотрим несколько примеров:
>>> time.strftime("%d.%m.%Y")
'22.03.2021'
>>> time.strftime("%H:%M:%S")
'15:34:56'

Для обратного преобразования, то есть строки, содержащей дату в объект
struct_time, используется функция st7Ptime():
strptime([, ])

Формат можно не указывать (второй параметр). Первый параметр - это
строка с датой/временем, которая будет преобразована в объект struct_time,
который и будет возвращен функцией. Если строка не соответствует фор­
мату, вы получите исключение ValueErтor.
В строке формата могут использоваться следующие модификаторы:



......... ···························································-··········.

Pythor1. Полное руководство

• %а - аббревиатура дня недели в зависимости от настроек лакали;
• %А - название дня недели в зависимости от настроек лакали;
• %m - номер месяца с предваряющим нулем (от "01" до "12");
• %Ь - аббревиатура месяца в зависимости от настроек лакали (например,
"feb" для января);
• %В - название месяца в зависимости от настроек лакали (например,
"March");
• %d - номер дня в месяце с предваряющим нулем (от "01" до "31 ");
• %j - день с начала года (от "001" до "366");
• % U - номер недели в году (от "00" до "53"). Неделя начинается с воскре�
сенья. Все дни с начала года до первого воскресенья относятся к неделе
с номером О;
• %W - номер недели в году (от "00" до "53"). Неделя начинается с по­
недельника. Все дни с начала года до первого понедельника относятся к
неделе с номером О;
• %w - номер дня недели ("О"- для воскресещ,я, "6"-для сурботы); ·
• %И - часы в 24-часовом формате (от "00" до "23");
• %1 � часы в 12-часовом формате (от "01" до "12");
• %М - минуты (от "00" до "59");
• %S - секунды (от "00" до "59");
• %р - эквивалент значениям АМ и РМ в текущей лакали;
• %с -· представление даты и времени в текущей лакали;
• %х - представление даты в текущей лакали;
• %Х - представление времени в текущей лакали;
• % у - год из двух цифр (от "00" до "99");
• %У - год из четырех цифр (например, "2021");
• %Z - название часового пояса или пустая строка;
• %% - символ "%".
Прежде, чем форматировать дату и время, нужно импортировать локаль:




fJ!t·········-···················-·s·················································•

··python.......................

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

>>> irnport locale
>>> locale.setlocale(locale. LC_ALL, ('russian'))
'Russian Russia.1251'

После этого можно работать с датой и временем:
>>> print(tirne.strftirne("%A %d %Ь %У %H:%M:%S\n%d.%rn.%Y"))
пятница 19 мар 2021 18:42:47
19.03.2021

12.3. Модуль calendar
Модуль calendar можно использовать для вывода календаря в текстовом
формате или в HTML. В этом модуле вы найдете следующие классы:
• Calendar - базовый класс, который наследуют все остальные классы
• TextCalendar - текстовый календарь
• НТМLCalendar - календарь в формате HTML
• LocaleTextCalendar - позволяет вывести календарь на языке указанной
лакали
• LocaleНТМLCalendar - то же самое, что и LocaleHTMLCalendar, но вы·
водит календарь в формате HTML
Каждому из этих классов нужно передать первый день недели. "Локализи·
рованным" классам нужно также еще передать название лакали.
>>> irnport calendar
>>> с = calendar.LocaleTextCalendar(O, "Russian_Russia.1251")
>>> print(c.forrnatyear(2021))

Данный код выводит календарь на 2021 год на русском языке в текстовом
формате. Результат работы программы приведен на рис. 12.1.

-

Аналогично, вы можете использовать класс LocaleHTMLCalendar, чтобы
создать календарь в формате HTML.



. ................. . .................. . ...........................................
'

Python. Полное nуководr:пзr-

i6python

' - - - - - - - - - - - - - - - - - - - -__., ',Ш!!

о

i)�J IDLE Shell 3.9.2

х

file fdit Shell Qebu9 Qptions Window !::!elp

Апрель
пн вт ер Чт пт
1 2
5 6 7 8 9
12 13 14 15 16
19 20 21 22 23
26 27 28 2930

Сб
3
10
17
24

Вс
4
11
18
25

Июль
Пн Вт ер Чт Пт Сб Вс
1 2 3 4
�,,6 .7,.8 ,910,,11,
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
октябрь
ПН Вт ер Чт Пт
1
4 5 6 7 8
11 12 13 14 15
18 19 20 21 22
25 26 27 28 29

еб
,2
9
16
23
30

Вс
З
10
17
24
31

Пн Вт ер Чт Пт,еб·Ва•
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31

пн Вт
1
7 8
14 15
21 22
28 29

июн ь
Ср·ЧТ nт
2 3 4
9 10 11
16 17 18
23 24 25
30

еб
5
12
19
26

Вс
б
13
20
27

Август
Пн Вт ер Чт Пт еб Вс
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28, 29
30 31

сент'ябрь
nн Вт ер Чт Пт
1 2 3
б 7 8 9 10
13 14 15 16 17
20 21 22 23 24
27 26 29 30,

Сб
4
11

25

Вс
5
12
19
26

Ноябрь
Ср Чт Пт
3 4 5
10 11 12
17 18 19
24 25 26

Декабрь
пн вт ер чт nт·
1 2 :З
6 7 8 9 10
13 14 15 16 17
20 21 22 23 24
27 28 29 30 31

еб
4
11
18
25

вс
5
12
19
26

Май

Пн
1
8
15
22
29

Вт
2,
9
16
23
30

Сб
6
13
20
27

Вё
7
14
21
28

Рис. 12. 1. Календарь в текстовом виде

12.4. Функция sleep
В модуле time есть очень полезная функция - sleep(), позволяющая прио­
становить выполнение сценария на указанное в секундах время. Например:
import time as t
t.sleep(lO)

В данном случае выполнение сценария будет приостановлено на 1 О секунд.

12.5. Измерение времени
выполнения фрагментов кода
Модуль tirneit содержит очень полезные функции, которые можно исполь­
зовать для оптимизации работы программы, а именно для определения,
сколько времени выполняется тот или иной фрагмент кода.
a:t----------------·-·····--···········-···-····-············-······-···-····-···-·-'

·• python______________________

Глава 12. Да1а и вµемя

Используя модуль timeit, вы можете определить "узкие" места в произво­
дительности вашей программы.
Измерение производительности производятся с помощью класса Timer.
Конструктор класса Timer выглядит так:
Timer([stmt='кoд'] [, sеtuр='код_настройки'] [, timеr= ])

Первый параметр - это код, время работы которого нужно измерить. Вто­
рой параметр - это код, который должен быть выполнен до измеряемого
кода. Здесь, например, можно загрузить необходимые для выполнения ука­
занного в первом параметре кода модули. Третий параметр задает функцию
таймера.
Метод timeit объекта Timer позволяет измерить время выполнение кода.
Параметр number задает количество выполнений кода, например:
timeit(numЬer= lOOO)

По умолчанию код setup выполняется один раз, а код stmt - один миллион
раз, если иного не задано параметром number.
Метод repeat() позволяет вызвать метод timeit() несколько раз. Его параме­
тры по умолчанию выглядят так:
repeat(repeat= З, numЬer= lOOOOOO)

Первый параметр задает, сколько раз будет вызван timeit(), второй - значе­
ние number, которое будет передано в timeit. Метод возвращает результат
для каждого выполнения timeit().
Типичное использование всего этого выглядит так:
t = Timer(...)
try:
t. timeit(...)
except:
t.print_exc()



# за пределами try/except
# или t.repeat(...)

. . - . --- . --. - . -.· -... -.. - . - . -.... - . -... -........ -........ - . - . -...... - . -... -...... -..

-

Pythoп. Полное руководсrr.о

Пример:
>>> import timeit
>>> t = timeit.Timer('char in text', setup='text

string"; char = "g"')
>>> t.timeit()
0.14950477718537059
>>> t. repeat()
[О .11337469360387331, О .11169325663377094,
0.11340548288711716]

"sample

>>>

Зачем все усложнять? Ведь теоретически, можно написать такой код:
from time import *
tl = time()
# Код, время выполнения которого нужно измерить
t2 = time()
t = t2 - tl

В результате в t будет содержаться время выполнения кода в секундах. Но
такой способ не даст таких точных измерений, как метод timeit, хотя бы по­
тому что он считает, сколько времени прошло в общем, а ни сколько заняло
выполнение кода. Рассмотрим пример:
>>> from time import *
>>> tl = time()
>>> print('Привет')

Привет

>>> t2 = time()
>>> t2 - tl

27.691545009613037

>>>

Неужели на вывод строки Привет было потрачено 27 секунд? Конечно же
нет. В среде IDLE этот метод вообще использовать нельзя, поскольку он
учитывает не только время выполнения кода, но и время набора этого кода
программистом. В реальных программах данный метод сгодится разве что
для грубого оценивания больших фрагментов кода, где сотые секунды - не
важны.

C!t---------------------------------------------------------------------------------'

·



python-----------·----------

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

12.6. Модуль datetime
Для выполнения преобразований и вычислений, вовлекающих единицы
времени, в Python используется модуль datetime. Например, чтобы пред­
ставить интервал времени, создайте экземпляр timedelta:
>>>
>>>
>>>
>>>
>>>
3

from datetime import timedelta
а
timedelta(days= 3, hours=4)
Ь = timedelta(hours=6.5)
с = а + Ь
c.days

>>> c.seconds
37800
>>> c.seconds / 3600
10.5

Обратите внимание: свойства у hours нет, вместо него нужно разделить
свойство seconds на 3600.
Если вам нужно представить специфические даты и время, создайте экзем­
пляр datetime и используйте стандартные математические операции для
манипуляции с ним. Например:
>>> from datetime import datetime
>>> from datetime import timedelta
>>> а = datetime(2021, 3, 20)
>>> print(a + timedelta(days= S))
2021-03-25 00:00:00
>>> Ь = datetime(2021, 7, 27)
>>> d = Ь - а
>>> d.days
129
>>> now = datetime.today()
>>> print(now)
2021-03-19 19:00:26.413132
>>> print(now + timedelta(days=2))
2021-03-21 19:00:26.413132
>>>



·----------------------------------------------------------------------------------

Python. Полное руководство

_________________________• python

>>> fг,)1n datetime impcгt: datetime
>>> fгorn datetime iюpcart timedelta
>>>а= datetime(2021, З, 20)
>>> print(a + timedelta(days=S))
2021-03-25 00:00:00
>>> Ь = datetime(2021, 7, 27)
>>> d = Ь - а
>>> d.days
129
>>> now = datetime.today()
>>> print{now)
2021-03-19 19:00:26.413132
�>> print(now + timedelta(days=2))
2021-03-21 19:00:26.413132
>>>

Рис. 12.2. Практическое использование модуля datetime



-----------------------------------------------------------------------------------·

ГЛАВА 13.

МОДУЛИ И ПАКЕТЫ

Python. Полное руководство

. ______________________• python

13. 1. Понятие модуля
Модулем в Python называется любой файл с программой. Каждый модуль
может импортировать в себя другой модуль, в результате чего он получает
доступ к идентификаторам, находящимся в другом модуле.

Получить имя модуля можно с помощью специального атрибута _nаmе_.
Ранее у нашей программы был только один модуль (к которому мы, воз­
можно, подключали другие модули) - _rnain_. Проверить, что мы нахо­
димся в rnain можно так:
if

== " main
name




13.2. Инструкция import
Для подклю.чения другого модуля используется инструкция impott, в кото­
рой мы уже все знакомы:
import



----------------------------------------------------------------------------------·

Apy,thon
ll!iii

--------------------

Глава 13. Модули и пакеты

Например:
import itertools
Затем обратиться к идентификатору, находящемуся в модуле можно так:
itertools.count()
Однако имена некоторых модулей слишком длинные, и чтобы сделать код
компактнее, вы можете использовать псевдонимы модулей. Псевдоним за­
дается с помощью конструкции:
import as
Например:
import itertools as it
После этого все идентификаторы модуля itertools будут доступны через
nсевдоним it:
it. count()
Вот еще пример:
import math as m
а = m.pi * 10
Мы подключили модуль math, создали псевдоним m и доступ к числу Пи
теперь осуществляется через идентификатор m.pi.
Теперь немного практики. Создайте два файла - main.py и module.py. Во
второй файл поместите указанный в листинге 13.1 код.
Листинг 13.1. Файл module.py
# -*- coding: utf-8 -*а = О
В модуле module.py мы оnределили только один идентификатор - а. Теперь
создайте файл main.py (лист. 13.2) .



·----------------------------------------------------------------------------------at

Pytho11. Полное руководство

........................+

python

Листинг 13.2. Файл main.py

# -*- coding: utf-8 -*import module as m
а = 2
print(a)
print(m.a)
input()

Сначала вы увидите число 2 - это значение идентификатора а в основной
программе. А затем вы увидите число О - это значение идентификатора с
таким же именем в модуле. Думаю, принцип понятен.
Примечание. Обратите внимание на содержимое папки с фай­
лами после подключения модуля module.py. Внутри папки ав­
томатически был создан каталог _pycache_ с файлом module.
cpython-32.pyc. Этот файл содержит скомпилированный байт­
код одноименного модуля. Байт-код создается при первом им­
портировании модуля и изменяется только после изменения
кода внутри модуля.

13.3. Инструкция from
Инструкцию from удобно использо:13ать для импорта только определенных
идентификаторов. Синтаксис следующий:
from import [as ]

Пример:
from math import pi

После этого к идентификатору pi можно ссылаться напрямую, без указания
имени модуля и псевдонима:
а

=

pi

*

10

Для импорта всех идентификаторов из модуля можно использовать звез·
дочку*:

•···························-······················································

ф py1:hon____________________

Гп;:�;а 13 Модули и п1кет!>•

from import *

Посмотрим, что произойдет с нашей программой, если использовать
инструкцию from. Измените main.py, чтобы он выглядел, как показано в ли­
стинге 13.3.

Листинг 13.3. Файл main.py
# -*- coding: utf-8 -*­
from moduie import *
а = 2
print(a)
input()

Из модуля импортируется идентификатор а и в программе есть идентифи­
катор а. Какое :будет выведено значение? Здесь происходит слияние про­
странств и все идентификатор попадают в одно общее пространство. Если
программа испqльзует идентификатор с таким же именем, то онапереопре­
делит его значение, поэтому будет выведено значение 2.
При желании можно импортировать несколько идентификаторов. Как пра­
вило, это делается, если нужно импортировать только интересующие иден­
тификаторы. Пример:
from math import (pi, floor, sin, cos)

13.4. Путь поиска модулей
В каких каталогах Python будет искать модули? В самом простом случае
модули размещаются в одном каталоге с вашей программой. В этом случае
нет необходимости настраивать пути поиска, поскольку текущий каталог
автоматически включается в путь поиска.
Просмотреть текущий путь поиска можно с помощью следующих команд:
>>> import sys
>>> sys.path
['', 'Е: \\Python\\Lib\\idlelib', 'Е: \\Python\\python392. zip', 'Е: \ \
Python\\DLLs', 'Е: \\Python\\lib', 'Е: \ \Python', 'Е: \\Python\\lib\\
site-packages']



>>>

·----------------------------------------------------------------------------------

Pyttю,1. Полное руководстес

_____ ..•. ..•. _________ • python

Путь поиска программ состоит из текущего каталога, пути поиска стандарт­
ных модулей, переменной окружения PYTHONPATH и содержимого рth­
файлов (они должны находиться в каталоге Lib\site-packages, имя файла
может быть любым, но расширение должно быть .pth). Один из самых про­
стых способов добавить нужные каталоги в путь поиска - это создать рth­
файл. Перейдите в каталог Lib\site-packages, создайте файл, скажем, paths.
pth и добавьте в него нужные пути поиска - по одному в каждой строке:
C:\projects\my_libs
C:\python\my_py

Второй часто используемый способ пути поиска модулей - изменение переменной окружения PYTHONPATH.

13.5. Повторная загрузка модулей
Модуль загружается только один раз - при первой операции импорта. Все
последующие вызовы import будут возвращать уже загруженный объект мо0
дуля, даже если сам модуль был изменен. Допустим, вы изменили модуль,
как его загрузить заново?
Для этого нужно использовать функцию reload() из модуля imp:
from imp import reload
rеlоаd()

13.6. ЕGG-файлы
Сложные расширения (вроде Pytz) состоят из множества модулей, поэто­
му для простоты распространения таких пакеты принято распространять
в виде ЕGG-файлов. Такие пакеты можно скачать на сайтах разработчи­
ков пакетов-расширений. Разберемся, как их можно установить:
1. Скачайте еgg-файл и поместите его в кaтaлorc:\Python\Lib\site-packages\
2. Выполните из этого каталога команду:
- •••• - •••••••••• - ••• - •• - ••• - •.••? •••• - ••••• - •• - • - • - ••• - •••• - •••••• - • - • - ••••••••••• '

· .,py,thon_........... __ ._____

Глава 13. Модули и пакеты

python easy_install.py -Z
. Например,
python easy�install.py -z pytz.egg
Команда должна выглядеть именно так:
python easy_install.py -Z
А не так (как показано в некоторых руководствах):
python easy_install -Z
Ввод этой команды приведет к ошибке.
3. Просмотрите вывод команды. В случае успеха вы должны увидеть строку
Finished processing dependencies for

13.7. Разделение модуля на
несколько файлов
Пусть у нас есть модуль, который нам нужно разделить на несколько фай­
лов. Необходимо это сделать, не повредив существующий код, сохраняя
отдельные файлы объединенными как единственный логический модуль.
Программный модуль можно разделить на отдельные файлы, превратить
его в пакет. Рассмотрим следующий простой модуль:
# mymodule.py

class А:
def test(self):
print('А. test')
class В(А):
def bar(self):
print('B.bar')

Предположим, что вы хотите разбить mymodule.py на два файла - в каждом
будет собственное определение класса. Чтобы сделать это, начните с заме­
ны файла mymodule.py каталогом mymodule. В этот каталог поместите два

,tайла:

··················································································-fD

Pyttюri. Полное руководстве

•...__.._______________• python

mymodule/
init .ру
а.ру
Ь.ру

В файл а.ру поместите этот код:
# а.ру
class А:
def test(self):
print( 'А.test')

В файл Ь.ру поместите этот код:
# Ь.ру
from .а import А
class В(А):
def bar(self):
print('B.bar')

Наконец, в фaйл _init_.py поместите код, соединяющий все это вместе:
init .ру
#
from .а import А
from .Ь import В

Если вы сделаете эти действия, в результате будет пакет mymodule, который
будет представлен как единственный логический модуль:
>>> import m.ymodule
>>> а = m.ymodule.A()
>>> a.test()
A.test
>>> Ь = m.ymodule.B()
>>> b.bar()
B.bar
>>>



----------------------------------------------------------------------------------·

• python___________________ �

Глава 13. Модули и пакет;,�

Нужно определиться, хотите ли вы, чтобы пользователи работали с боль­
шим количеством маленьких модулей или с одним большим модулем? На­
пример, в большой базе кода, можно просто разбить большой модуль на
несколько меньших, но в результате пользователи должны будут использо­
вать много операторов импорта, например:
from mymodule.a import А

:fro� -�dule. Ь

import В

Это работает, но создает определенные неудобства для пользователя, кото­
рый должен знать, где расположены различные части. Часто проще объеди­
нить все в один модуль и использовать единственный оператор импорта:
from mymodule import А, В

Для нашего последнего примера нужно думать о mymodule как об одном
большом исходном файле. Однако этот раздел показывает, как объединить
несколько файлов в единственное логическое пространство имен. Ключ к
достижению этого результата - создание каталога пакета и использование
фaйлa _init_.py для объединения частей.
Когда модуль будет разделен, вы должны обратить особое внимание на пе­
рекрестные ссылки. Например, в этом разделе, класс В должен получить
доступ к классу А как к базовому. Чтобы получить такой доступ, использу­
ется относительный импорт .а import А.
Всюду по разделу используется относительный импорт, чтобы не указывать
жестко имя модуля верхнего уровня в исходном коде. В результате будет
проще переименовать модуль или переместить его в другое место.

13.8. Создание отдельных
каталогов импорта кода под общим
пространством имен
На этот раз у нас есть много кода, разделенного на части и, возможно, об­
служиваемого и распределяемого разными людьми. Каждая часть кода ор­
ганизована как каталог файлов, подобно пакету. Однако вместо того, чтобы



·------------------------------------------·-········-·-·-··-·-·-·-·-·-··-·-·-·-·--

Python. Полное руководство

;,
6python
----------------------·.

установить каждую часть как отдельный именованный пакет, вы хотите,
чтобы все части были объединены под одним общим префиксом пакета.
По существу, задача заключается в том, что нужно определить пакет Python
верхнего уровня, который служит пространством имен для большого ко­
личества отдельно сохраняемых подпакетов. Эта проблема часто возникает
в больших фреймворках, где разработчики хотят поощрить пользователей
распределять плагины или дополнительные пакеты.
Чтобы объединить отдельные каталоги под общим пространством имен,
нужно организовать код в виде обычного пакета, но опустить файлы _
init_.py в каталогах, где будут объединяться компоненты. Рассмотрим не­
большой пример. Предположим, что у вас есть два разных каталога кода:
foo-package/
test/
id.py
bar-package/
test/
run.py

В этих каталогах имя test используется как общее пространство имен. Обра­
тите внимание, что здесь нет файлов _init_.py - ни в одном из каталогов.

Теперь посмотрим, что произойдет, если в�1 добавите оба каталога foo-package и bar-package - в модуль Python и попытаетесь вызвать некото­
рые операторы импорта:
>>>
>>>
>>>
>>>
>>>

import sys
sys.path.extend(['foo-package', 'bar-package'])
import test.id
import test.run

Вы заметите, что, словно по волшебству, два разных каталога объединятся
вместе, и вы сможете импортировать test.id и test.run. Это работает просто.
Здесь используется особенность, известная как "пакет пространства и.мен"
(namespace package). По существу, пакет пространства имен - специаль­
ный вид пакета, который используется для слияния различных каталогов
кода под общим пространством имен, как показано в решении. Это может
быть полезно для больших платформ, поскольку позволяет разбивать части



&!t··················································································

О python....................

Глава 13. Модули и пакеты

платформы в отдельные загрузки. Также это позволяет легко создавать сто­
ронние дополнения и другие расширения для таких платформ.
Ключ к созданию пакета пространства имен - убедиться, что в каталоге
верхнего уровня нет файлов _init_.py. Благодаря отсутствию файлов
_init_.py при импорте пакета происходит интереснейшая вещь. Вместо
ошибки интерпретатор начинает создавать список всех каталогов, которые,
оказывается, содержат соответствующее имя пакета. Потом создается спе­
циальный модуль пакета пространства имен, а копия (доступная только для
чтения) списка каталогов сохраняется в переменной _path_. Например:
>>> import test
>>> test._yath_
_NamespacePath(['foo-package/test', 'bar-package/test'])
>>>

Каталоги в _path_ используются при определении местоположения даль­
нейших субкомпонентов пакета (например, при импорте test.run или test.id).
Важная функция пакетов пространства имен - то, что любой может расши­
рить пространство имен с помощью своего собственного кода. Например,
представим, что вы создали собственный каталог кода:
my-package/
test/
custom.py

Если вы добавите ваш каталог с кодом в sys.path вместе с другими пакетами,
то он будет беспрепятственно объединен с другими каталогами пакета test:
>>> import test.custom
>>> import test.run
>>> import test.id
>>>

Проверить, является ли пакет пакетом пространства имен, можно посред­
ством анализа его атрибута _file_. Если этот атрибут отсутствует, значит,
пакет является пакетом пространства имен. Такж� строка представления
будет содержать слово "namespace":



. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - --...... -... -... -..... .......... -........ -- - . - . ·

...

Pythoп. Полное руководство

.......................+

>>> test. file
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'module' object has no attribute '

python

file

>>> test

>>>

13.9. Перезагрузка модулей
Для перезагрузки ранее загруженного модуля используйте irnp.reload().
Например:
>>> import test
>>> import imp
>>> imp.reload(test)

>>>

.Перезагрузка модуля очень полезна при отладке и разработке, но не очень
безопасна в производственном коде, поскольку не всегда работает так, как
вы ожидаете.
За сценой операция reload() стирает содержимое базового словаря модуля и
обновляет его, заново выполнив код модуля. Идентификационные данные
самого объекта модуля остаются неизменными. Таким образом, эта опера­
ция обновляет модуль везде, где он был импортирован в программу.
Однако операция reload() не обновляет определения, которые были импор­
тированы с использованием операторов, таких как from module import пате.
В качестве примера рассмотрим следующий код:
# test.py
def bar():
print('bar')
def run():
print( 'run')

Теперь запустим интерактивный сеанс:
>>> import test



---------------------------------------·----·---·-···-·-··----------·-------------·

• python................... .

Глава 13. Модули и пакеты

>>> from test import run
>>> test.bar О
bar
>>> run()
run
>>>

Не выходя из Python, отредактируйте исходный код test.py, так чтобы
функция run() была такой:
def run():
print( 'New run' )
.

.

Теперь вернемся в наш интерактивный сеанс, выполним перезагрузку модуля и повторим этот эксперимент:
>>> import imp
>>> imp.reload(test)

>>> test.bar()'
bar
>>> run()
# С'.l'арый вЪIВод
run
>>> test.run()
# Новый ВЪIВОД
New run
>>>

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



···-························································-·····················-

_______________________tlJ

Python. Полное руководство

python

13. 1 О. Создание каталога или ziр­
архива, выполняемого как главный
сценарий
Иногда программы разрастаются и состоят из множества файлов. А Вы бы
хотели получить простой способ выполнения этой программы?
Если ваша программа состоит из множества файлов , вы можете поместить
ее в отдельный каталог и создать файл _mаin_.ру. Например, вы можете
создать подобный каталог:
myapplication/
test.py
bar.py
run.py
main .ру

Если в каталоге присутствует файл _main_.py, вы можете запустить ин­
терпретатор просто так:
$ pythonЗ myapplication

Интерпретатор автоматически запустит файл _main_.py как основной
файл программы. Данная техника также работает, если вы запакуете весь
ваш код в Ziр-архив. Например:
$ 1s
test.py bar.py run.py
main
$ zip -r myapp.zip *.ру
$ pythonЗ mya�p.zip
main .ру
... вывод из

.ру

Создание каталога или ziр-архива и добавление файла _mаin_.ру - один
из возможных способов упаковки большого Руthоn-приложения. Данный
способ отличается от упаковки кода в пакет, поскольку код не предназна­
чен для использования в качестве модуля стандартной библиотеки. Вме­
сто этого данный способ позволяет создавать пакеты Python-кoдa, которые
могут быть легко выполнены кем-то еще.



CD-···············································································-··

ф python................... .

Глава 13. Модули и пакеты

Так как каталоги и ziр-архивы отличаются от обычных файлов, вы можете
также добавить сценарий оболочки, чтобы упростить выполнение вашего
кода. Например, если ваш код находится в архиве myapp.zip, вы можете соз­
дать следующий сценарий оболочки:
#!/usr/bin/env pythonЗ /usr/local/bin/rnyapp.zip

13. 11. Добавление каталогов в sys.

path

Иногда приходится работать с кодом, который не может быть импортиро­
ван Python, потому что расположен в каталоге, не перечисленным в sys.path.
Вам нужно добавить новые каталоги в sys.path, но вам не хочется делать это
в своем коде. Что делать?
Есть два общих способа добавить новые каталоги в sys.path. Первый заклю­
чается в использовании переменной окружения PYTHONPATH. Например:
$ env PYТHONPATH=/some/dir:/other/dir pythonЗ
Туре "copyright", "credits" Qr "license()" for rnore inforrnation.
>>> import sys
>>> sys.path
[", '/sorne/dir', '/other/dir', ... ]
>>>

В пользовательском приложении эту переменную окружения можно уста­
новить при запуске программы или через сценарий оболочки.
Второй способ - создать файл .pth, который перечисляет необходимые ка­
талоги, например:
# rnyapplication.pth
/sorne/dir
/other/dir

Этот .pth файл нужно поместить в один из каталогов site-packages, который
обычно находится в /usr/local/lib/pythonЗ.9/site-packages или -/.local/
lib/pythonЗ.9/sitepackages.
При запуске интерпретатора каталоги, перечисленные в .рth-файле, будут
добавлены в sys.path (если они существуют в файловой системе). Уста-

•.

-- . - -... - - ...-�-... - ........ -........ - . -... -.. -..... - ... - .... - ................... ..

Python. Полное руководство

.......................О python

новка .рth-файла может потребовать прав администратора, если он добав­
ляется в общесистемный каталог (/usr/local/lib/pythonЗ.9/site-packages).
Столкнувшись с подобной проблемой, первое, что приходит в голову ,..,...
написать код, вручную корректирующий значение sуs.раth,·например:
import sys
sys.path.insert(O, '/some/dir')

sys.path.insert(O, '/other/dir')

Хотя это работает, такой подход чрезвычайно хрупкий и рекомендуется его
избегать. Проблема в том, что вы явно указываете в своем коде имена ка­
талогов. В результате рано или поздно это приведет к проблеме - когда
код будет перемещен в какое-то другое расположение. Лучше сконфигури­
ровать путь в другом месте и так, чтобы его можно было изменить, не
редактируя исходный код вашей программы.
Иногда вы можете использовать подобный способ, но только в случае,
если вы надлежащим образом создаете абсолютный путь, например,
используя переменные уровня модуля, такие как _file_. Например:
import sys
from os.path import aЬspath, join, dirname
sys . раth. insert ( О , аЬsраth ( dirname ( '_file_' ) , ' src' ) )

В результате каталог src будет добавлен в путь корректным образом: вы не
указываете абсолютный путь, а формируете его на основании переменной
file
Каталоги site-packages содержат сторонние модули и пакеты, установлен­
ные в вашей системе. Как правило, все сторонние модули и пакеты уста­
навливаются в эти пакеты. Не смотря на то, что .рth-файлы находятся в
этих каталогах, они могут ссылаться на любые ката.логи в системе. Таким
образом, реально ваш код может находиться за пределами каталогов· site­
packages, пока его местоположение указано в .рth-файле.

13.12. Распространение пакетов
Вы написали полезную библиотеку, и вы хотите сделать ее доступной дру­
гим. Первым делом нужно придумать вашей библиотеке уникальное имя и

•················································································'

Глава 13. Модули и пакеты

• python____ � _______________

очистить структуру каталогов. Например, типичный пакет библиотеки мо­
жет выглядеть примерно так:
projectname/
READМE.txt
Doc/
documentation.txt
projectname/
_init_.py
foo.py
bar.py
utils/
_init_.py
test.py
run.py
examples/
helloworld.py

Чтобы сделать ваш пакет пригодным к распространению, напишите файл
setup.py, который выглядит примерно так:
# setup.py
from distutils.core import setup
setup(name='projectname',
ve:rsion='l.0',
author='Baшe икя',
author_email='you@example.com',
url='http://www.you.com/projectname',
packages=[ 'projectname', · 'projectname.utils'],

Далее нужно ,::оздать файл MANIFEST.in, в котором перечислены различ­
ные файлы, не имеющие отношения к исходному коду:
# МANIFEST.in
include *.txt
recursive-include examples
recursive-include Doc *



*

·············-····································································-fJD

Python. Полное руководство

tl:Jpython

----.------------------- @i,

Убедитесь, что поместили файлы setup.py и MANIFEST.in в каталог верх­
него уровня для вашего пакета. Далее, чтобы создать дистрибутив пакета,
введите команду:
$ pythonЗ setup.py sdlst

В результате будет создан файл вроде projectname-1.0.zip или pro;ectname1.0.tar.gz - в зависимости от платформы. Если все работает, вы можете
отправить этот файл кому-то или же загрузить в индекс пакетов Python
( https:j/pypi.python.org/pypi).
Для чистого кода Python написание файла setup.py - несложное занятие.
Один тонкий момент в том, что вы должны вручную перечислить каждый
подкаталог, хранящий файлы с исходным кодом. Часто программисты ука­
зывают только каталог верхнего уровня пакета и забывают описать под­
компоненты пакета. Это неправильно! Это - то, почему спецификация· для
пакетов в setup.py содержит список packages = ['projectnarne', 'projectnarne.
utils').
Большинство программистов Python знает, что есть множество других
( сторонних) средств создания дистрибутива пакета, в том числе setuptools.
Некоторые из· них являются заменой библиотеки distutils и могут быть
найдены в стандартной библиотеке. Знайте, что если вы полагаетесь на эти
пакеты, то пользователи не смогут установить ваше программное обеспече­
ние, если они также не установили требуемый диспетчер пакетов. Старай­
тесь сохранять вещи максимально простыми. Как минимум, убедитесь, что
ваш код может быть установлен, используя стандартную установку Python
3. Дополнительные функции могут поддерживаться опционально, если
доступны дополнительные пакеты.

са-...-...�-...............-...............................----......---.--...-..._,

ГЛАВА 14.
ОБРАБОТКА"
ИСКЛЮЧЕНИИ

Pytlюn. Полное руководство

�_______________________• python

14. 1 . Что такое исключение?
Исключение - это извещение интерпретатора об ошибке в программном
коде или о каком-то другом событии. Если в коде программы вы не предус­
мотрите обработку исключений, то выполнение программы будет прервано
и будет выведено сообщение об ошибке.
Существует три типа ошибок: синтаксические, логические и ошибки времени
выполнения. Первый тип ошибок - самый простой. Это ошибки в синтак­
сисе языка, как правило, интерпретатор предупреждает о наличии таких
ошибок, а выполнение программы будет прервано. Простота этих оши­
бок в том, что интерпретатор сообщает в какой строке ошибка и вам остает­
ся лишь исправить ее.
Логические ошибки - более коварны. С точки зрения синтаксиса все нор­
мально, но вот результаты работы программы не соответствуют ожидае­
мым. Понять, что в программе ошибка можно только после анализа работы
программы.
Ошибки времени выполнения возникают во время выполнения програм­
мы. Причина таких ошибок - события, не предусмотренные программистом.
Например, вы создали функцию деления двух чисел и не предусмотрели,
что на О делить нельзя. В результате, если передать в качестве делителя О.

----------------···········-················-·-··-----------------------------------·

ф py1;hon_____ : ____________

Глава 14. Обработка исключений

то возникнет ошибка деления на ноль - это классическая ошибка времени
выполнения:
>>> а = 10 / О
Traceback (most recent call last):
File "", line 1, in
а = 10 / О
ZeroDivisionError: division Ьу zero
>>>

Если вы не предусмотрите обработку исключения ZeroDivisionError, то вы­
полнение вашей программы будет пре_рвано.
Второе частое событие - это ValueError. Оно может возникнуть, если под­
строка не найдена:
>>> "Hello".index('hi')
Traceback (most recent call last):
File "", line 1, in
"Hello". index( 'hi')
ValueError: substring not found

При работе со списками, кортежами могут возникнуть ошибки IndexError когда вы пытаетесь получить доступ к несуществующему элементу списка:
>>> а = [1, 2, 3]
>>> а[б] = 1
Traceback (most recent call last):
File "", line 1, in
а[б) = 1
IndexError: list assignment index out of range
>>>

14.2. Типы исключений
Разные типы ошибок генерируют разные типы исключений. Как было пока­



зано ранее, при попытке открыть несуществующий файл, было сгенериро-

·---------------------------------------------------------------------------------CD

Python. Полное руководство

i6python

-----------··-·-········ gjjj

вано исключение FileNotFoundError, а при попытке преобразовать число
в строку- ValueError.
Разные типы исключений представлены в таблице 14.1. Всего существует
более 20 исключений, мы же рассмотрим только самые популярные.
Таблица 14. 1. Самые распространенные исключения

Исключение

Описание

IOError

Генерируется, если невозможно вьiполнить операцию ввода/вывода

IndexError

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

KeyError

Если в словаре не найден указанный ключ

NameError

Если не найдено имя (переменной или функции)

SyntaxError

Если в коде обнаружена синтаксическая ошибка

TypeError

Если стандартная операция применяется к объекту
неподходящего типа

ValueError

Если операция или функция принимает аргумент с
неподходящим значением

ZeroDivisionError

Если есть деление на О

В Python вы можете использовать следующие встроенные классы исклю­
чений:
• BaseException - начиная с Python 2.5, является классом самого верхне­
го уровня
• Exception - именно этот класс, а не BaseException, необходимо наследо­
вать при создании пользовательских исключений

...

• AssertionError - возбуждается инструкцией assert



..................................................................................

ф python..................

Глава 14. Обработка исключений

• AttributeError - попытка обращения к несуществующему атрибуту объ·
екта
• EOFError - возбуждается функция input() и raw_input() при достиже·
нии конца файла
• IOError - ошибка доступа к файлу (ошибка ввода/вывода)
• ImportError - невозможно подключить модуль или пакет
• IndentationError - неправильно расставлены отступы в проrрамме
• IndexError - указанный индекс не существует в последовательности
• KeyError - указанный ключ не существует в словаре
• Keyboardlnterrupt - нажата комбинация клавиш Ctrl+C
• NameError - попытка обращения к идентификатору до его определения
• Stoplnteration - возбуждается метод next() как сигнал об окончании
итерации
• SyntaxError - синтаксическая ошибка
• TypeError - тип объекта не соответствует ожидаемому
• UnboundLocalError - внутри функции переменной присваивается зна­
чение после обращения к одноименной глобальной переменной
• UnicodeDecodeError - ошибка преобразования обычной строки в
Unicode строку
• UnicodeEncodeError - ошибка преобразования Unicode строки в обыч­
ную строку
• ValueError - переданный параметр не соответствует ожидаемому значе­
нию
• ZeroDivisionError - попытка деления на ноль
Иерархия классов исключений выглядит так:
BaseException
GeneratorExit (в Python 2.6 и вЬШiе)
Keyboardinterrupt
SystemExit
Exception
GeneratorExit (в Python 2.5)
Stopiteration



... - - ... -... - . -...... - . -.. -..- . -........ - . -................... : ....... -..... _·......

-

Python. Полное руководство

_. __ .,,___________________• python

Warnin_g
BytesWarning (в Python 2.6 и выше)
DeprecationWarning, FutureWarning, ImportWarning
PendingDeprecationWarning, RuntimeWarning,
SyntaxWarning
UnicodeWarning, UserWarning
StandardError
ArithmeticError
FloatingPointError, OverflowError,
ZeroDivisionError
AssertionError
AttributeError
BufferError (в Python 2.6)
EnvironmentError
IOError
OSError
WindowsError
EOFError
ImportError
LookupError
IndexError, KeyError
MemoryError
NameError
UnboundLocalError
ReferenceError
RuntimeError
NotimplementedError
SyntaxError
IndentationError
TabError
SystemError
TypeError
ValueError
·unicodeError
UnicodeDecodeError, UnicodeEncodeError
UnicodeTranslateError

Мы можем заставить интерпретатор реагировать не на все исключения, а
только на определенные, например:
Листинг 14.1. Пример обработки ValueError
try:

k = int(input("Bвeдитe целое число: "))
print("Bы ввели: ", k)
except ValueError:




fJD-··································-···············································

+python__________________

Глава 14. Обработка исключении

print("Hyжнo ввести целое!!!")

Здесь мы обрабатываем исключение определенного типа. При необходимо­
сти можно обработать несколько исключений:

Листинг 14.2. Пример обработки нескольких исключений
try:
а = int(input("Bвeдитe целое а: "))
Ь = int(input("Bвeдитe целое Ь: "))
print("a/b = ", а/Ь)
except ValueError:
print("Hyжнo ввести целое!!!")
except ZeroDivisionError:
рrint("Деление на О!")

Да, можно обрабатывать все исключения подряд, не указывая тип исклю­
чения в except. Но если вы будете обрабатывать только определенные ис­
ключения, то можете указать разные сообщения пользователю - так вы сде­
лаете свою программу информативнее.
Гибкость Python в том, что он может передать в вашу программу свое ру­
гательство, то есть сообщение об ошибки, но при этом программа не будет
прервана. Это называется передачей аргумента исключения (лист. 14.3).

Листинг 14.3. Передача аргумента исключения
try:

а = int(input("Bвeдитe целое а: "))
Ь = int(input("Bвeдитe целое Ь: "))
print("a/b = ", а/Ь)
except ValueError:
print("Hyжнo ввести целое!!!")
except ZeroDivisionError _as е:
рrint("Деление на О!")
рrint("Сообщение Python:")
print(e)

На рис. 14.1 показано, что кроме наших сообщений самим Python будет вы­
ведено сообщение division Ьу zero .

•.

--. -·.-. -.... -. - . -. -.... -. -. -............ - .. - .............. -... - -... -..... -.. -

... ...

Python. Полное ру1, )
Пример:
import shutil
shutil. copyfile ("filel. txt", "file2. txt")
Если нужно скопировать также права доступа, то нужно использовать
функцию сору(). Поведение функции такое же, как и у copyfile(). Синтаксис
тоже такой же:
сору(, )
Функция сору2() копирует не только права доступа, но и остальные мета­
данные:



•··················································································

ilfA-rpython
%&i;'J

---------------------

Глава 15. Файловый ввод/вывод

сору2(, )

Для перемещения файлов используется функция move():
mоvе(, )

Копирует файл в , а затем удаляет его. Если файл существу­
ет, то он будет перезаписан. В случае ошибки возбуждается исключение
IOError. Если файл нельзя удалить именно в Windows, то генерируется ис­
ключение WindowsError (это исключение генерируется только в Windows, в
других ОС генерируется только одно исключение - WindowsError).
Пример:
import shutil
рrint("Перемещение файла...")
try:
shutil.move("filel.txt", "file2.txt")
except WindowsError:
рrint("Ошибка при перемещении файла!")
else:
print("OK")

Далее мы будем рассматривать функции из модуля os. Для переименования
файла используется функция rename():
rename(, )

Если исходный файл отсутствует или новое имя уже существует, то в
Windows будет возбуждено исключение WindowsError. Но поскольку
WindowsError наследует OSError, то правильнее обрабатывать иск,люче­
ние OSError.
import os
try:
os.rename ('fl.doc', 'f2. doc')
except OSError:
print('Ошибка! ')
else:

'

.... ·-· .......................................... : .............................. ..

Python. Полное руковоррво

_________________________• pytthon

print( 'ОК')

Для удаления файла используются функции remove() и unlink():
rеmоvе()
un link()

В случае ошибки обе функции генерируют исключение OSError.
В модуле os.patl1 находятся следующие полезные функции:
• exists( ) - проверяет существование файла и возвращает True,
если файл существует
• getsize( ) - возвращает размер файла. Перед вызовом этой функ­
ции желательно проверить файл на существование
• getatime( ) - возвращает время последнего доступа файла. Воз­
вращается так называемая метка времени (timestamp) - количество се­
кунд, прошедших с 1 января 1970 года
• getmtime( ) - возвращает время последнего изменения
• getctime( ) - возвращает время создания файла
Пример:
irnport os.path
irnport tirne as t
rnod_tirne = os.path.getrntirne("hello.py")
print(t.strftirne("%d.%rn.%Y %H:%M:%S", t.localtirne(rnod_tirne))

Ранее вы уже были знакомы с одной функцией из модуля os.path - abspath().
Кроме этой функции в этом модуле есть и другие функции преобразова­
ния пути к файлу. Например, функция isabs() возвращает True, если
указанный путь является абсолютным. А функция basename() воз­
вращает базовое имя файла, то есть имя файла без пути к нему. А функция
dirname(), наоборот, возвращает путь без базового имени. Иденти­
фикатор sep содержит разделитель элементов пути, используемый в вашей
операционной системе. Примеры:
>>> frorn os.path irnport *
>>> sep

-----·-··················································------------·-············

+

python_______ •. _________ � __

Глава 15. Файловый ввод;в"шод

'\\'
>>> basename("c:\temp\hello.py")
'hello.py'
>>> dirname("C:\temp\hello.py")
'C:\temp'

Обратите внимание: даже не смотря на то, что я неправильно указал раз­
делитель пути ( поскольку я использую Windows, то я должен был исполь­
зовать разделитель\\ , как показано выше), функции os.path справились со
своей задачей без ошибок и правильно определили базовое имя и название
каталога, в котором находится файл.

15.2. Работа с каталогами
В модуле os находятся также и функции для работы с каталогами. Начнем с
функции getcwd(), которая возвращает текущий рабочий каталог:
>>> from os import *
>>> getcwd()
'C:\\Python39\\Lib\\idlelib'
>>>

Функция chdir() изменяет текущий рабочий каталог:
>>> chdir('c:\\test')
>>> getcwd()
'c.:\\test'
>>>

Для создания каталога нужf{О использов�ть функцию mkdir( [, ]). Второй параметр не обязательный, он задает права
доступа в UNIX/Linux. Если вы хотите его использовать, то нужно пере­
дать трехзначное число в восьмеричной системе, например, 00777. О
правах доступа мы поговорим в следующем разделе. Пример:
>>> mkdir('dirl')



···---·---·-·---------------------················································-

Python. Полное руководство

.

.&•python

v •••••••••••••••••••••••• �

Удалить пустой каталог можно с помощью функции rmdir( ). Обратите внимание: каталог должен быть пустым, иначе вы полу­
чите исключение OSError.
Вывести содержимое каталога позволяет функция listdir():
>>> listdir(getcwd())
['dirl', 'log2']
>>>

Обойти дерево каталогов можно с помощью функции walk(). Такая функ­
ция стандартна для Python и вам не придется изобретать велосипед заново.
Формат функции:
wаlk([, topdown= True]
[, onerror=None] [, followlinks= False])

В качестве значения функция walk() возвращает объект, на каждой итера­
ции которого доступен кортеж из трех элементов - текущий каталог, список
файлов и список каталогов. Параметр topdown задает порядок обхода ката­
лога - прямой или обратный.
Пример:
>>> for (а, Ь, с) in walk('c:\\test'): print(a)
c:\test
c:\test\dirl
>>> for (а, Ь, с) in walk('c:\\test', False): print(a)
c:\test\dirl
c:\test
>>>

При обходе дерева каталогов полезно знать, какой перед нами элемент файл или каталог. Функция isdir() возвращает True, если обрабатываемый
элемент - каталог. Аналогично, функция isfile() возвращает True, если об­
рабатываемый элемент - файл:



----------------------------------------------------------------------------·······

О python___.. _______________ _

Глава 15. Файловый ввод/вывод

>>> isdirt'c:\\test\\dirl')
True
>>> isfile('c:\\test\\dirl')
False
>>>

Обе функции находятся в модуле os.path.
Удалить дерево каталогов можно функцией rmtree() из shutil:
import shutil
rmtree("c:\\test")

15.3. Работа с файлами в разных
форматах
15.3.1. РАБОТА С CSV
Данная глава концентрируется на использовании Python для обработки
данных, представленных в разных форматах, например, в CSV, JSON, XML
и двоичных упакованных записях. Мы не будем рассматривать различные
алгоритмы по обработке данных, но вместо этого мы рассмотрим способы
ввода и вывода данных в программу и из нее.
Формат CSV (Comma-Separated Values) используется для хранения
электронных таблиц. Например, вы можете экспортировать электронную
таблицу Excel в этот формат. Для большинства видов СSV-данных исполь­
зуйте библиотеку csv.
Представим, что у нас есть некоторая электронная таблица в файле tаЫе.
csv. Вот код, который может обработать этот файл:
import csv
with open('taЫe.csv') as f:
f csv = csv.reader(f)
headers = next(f csv)
for row in f csv:
# Обрабатываем строку

'----,-----------------------------------------------------------------------------at

Python.

Полное руководство

.·------------------------·
6python
WD

В предыдущем коде ряд будет кортежем. Поэтому для доступа к опреде­
ленным полям вам нужно использовать индексирование, например, row[O]
(поле Symbol) и row[4] (поле Change).
Поскольку индексирование часто запутывает программиста, рассмотрите
использование именованных кортежей. Например:
from collections import namedtuple
with open('taЫe.csv') as f:
f_csv =csv.reader(f)
headings = next(f csv)
Row = namedtuple('Row', headings)
for r in f csv:
row = Row(*r)
# Обрабатываем строку

В итоге вы можете использовать заголовки колонок вроде row.FirstName и
row.LastName вместо индексов. Конечно, это только в том случае, если за­
головки колонок являются допустимыми идент�фикаторами Python. Если
это не так, обращаться к данным по заголовкам будет не очень удобно.
Другая альтернатива - чтение данных как последовательности словарей.
Чтобы сделать это, используйте этот код:
import csv
with open('taЫe.csv') as f:
f csv = csv.DictReader(f)
for row in f csv:
# обрабатываем строку

В этой версии получить доступ к элементам каждого ряда тоже можно с ис­
пользованием заголовков. Например: row['FirstName'] или row['LastName'].
Чтобы записать СSV-данные, вы также можете использовать модуль csv, но
создайте объект writer. Например:

....

headers = ['UserID', 'FirstName', 'LastName']
rows = [ ( 'userl ', 'John', 'Doe' ) ,

- - -. -. -. -. -. - - -- -....... - - - - -...... � ......................... -. -. -.... -. - --.....

'

ф python_____________________

Глава 15. Файловый ввод/вывод

('user2' , 'Jane', 'Doe')
with open('users.-csv', 'w') as f:
f_csv = csv.writer(f)
f_csv.writerow(headers)
f_csv.writerows(rows)
По умолчанию библиотека csv запрограммирована, чтобы понимать пра­
вила кодирования CSV, используемые Microsoft Excel. Это наиболее рас­
пространенный вариант и предоставит вам лучшую совместимость. Однако
если вы обратитесь к документации по csv, вы обнаружите, что есть воз­
можность подстроить кодирование под разные форматы (например, изме­
нить символ разделителя). Например, если вместо разделителя нужно ис­
пользовать символ табуляции, используйте следующий код:
with open('users.csv') as f:
f_tsv = csv.reader(f, delimiter='\t')
for row in f tsv:
# Обрабатываем строку

15.3.2. ЧТЕНИЕ И ЗАПИСЬ JSON-ДAHHЫX
Модульjsоn предоставляет простой способ кодировать и декодировать дан­
ные вJSON. Две основные функции -json.dumps() иjson.loads(), их работа
похожа на других функций сериализации в других библиотеках, например,
в pickle.
JSОN-кодирование поддерживает базовые типы данных - None, bool, int,
float и str, а также списки, кортежи и слоnари, состоящие из тех базовых ти­
пов. Для словарей считается, что ключами будут строки (любые нестроко­
вые ключи в словаре конвертируются в строки при кодировании). Чтобы
быть совместимыми со спецификациейJSОN, вы должны кодировать толь­
ко списки и словари. Кроме того, в неб-приложениях принято, что объект
верхнего уровня является словарем.
Формат кодирования JSON почти идентичен синтаксису Python за ис­
ключением нескольких незначительных изменений. Например, True ото­
бражается в true, False - в false, а None - в null.
Вот как преобразовать структуру данных Python в JSON:



import json

··-···············································································a:t

Python. Полное руководство

_________________________ • python

data = {
'firstname' : 'John',
'lastname' : 'Doe',
1979
'year'
json str

json.dumps(data)

А вот как вы можете преобразоватьJSОN-закодированную строку обратно
в структуру данных Python:
data

j�on.loads(json_str)

Если вы работаете с файлами, а не строками, вы можете использовать
функцииjsоп.dитр() иjson.load() для кодирования и декодированияJSОN­
данных. Например:
J Записываем JSОN-данные
with open('data.json', 'w') as f:
json.dump(data, f)
# Читаем данные обратно
with open('data.json', 'r') as f:
data = json.load(f)

15.З.З. ПАРСИНГ ХМL-ФАЙЛОВ
Для извлечения данных из простого ХМL-документа можно использовать
модуль xшl.etree.EleшentTree. Чтобы проиллюстрировать, предположим,
что вы хотите проанализировать и .сделать сводку произвольной RSS­
ленты, содержащей элементы title, pubDate, link. Вот сценарий, который
делает это:
from urllib.request import urlopen
from xml.etree.ElementTree import parse
# Загружаем RSS-ленту и парсим ее
и = urlopen('http://caйт/rss20.xml')
doc = parse(u)
# Извлекаем и выводим интересующие теги
for item in doc.iterfind('channel/item'):

.....

--

- . -- - - --- -- - . -- -� - - -- - - . - ..... - --- . - . - . ------- -� -- - -- - -- . - - - ... -- - - ... - --- ..

'

6python
••.....•.............


Глава 15. Файловый ввод/вывод

title = item.findtext('title')
item.findtext('pubDate')
date
link = item.findtext('link')
print(title)
print(date)
print(link)
print()

Очевидно, если вы хотите произвести допощштельную обработку, вам нуж­
но заменить операторы print() на что-то более интересное.
Работа с данными, представленными в виде XML, осуществляется во мно­
гих приложениях. Мало того, что XML широко используется в качестве
формата для обмена данными в Интернете, также он является стандартным
форматом для хранения данных приложения (например, при обработке
текста, в музыкальных библиотеках и т.д.). Все сказанное далее предполага­
ет, что читатель уже знаком с основами XML.
Во многих случаях, когда XML используется просто для хранения данных,
структура документа компактна и понятна. Например, вот типичная RSS­
лента:



RSS feed
http://caйт/
ru
Oпиcaниe

Заголовок 1
Ccылкa 1
Инфo 1
Дaтa 1


Заголовок 2
Ccылкa 2
Инфo 2
Дaтa 2


•.. --.

- - . -...... - -- . -

-- - -.................... -...-........ -.... -- ............... -

---

Python. Полное руководство

__ .._____________________ф python




Функция xml.etree.ElementTree.parse() парсит весь ХМL-документ в объ­
ект документа. Вы можете использовать методы вроде find(), iteifi,nd() и
findtext() для поиска определенных ХМL-документов. Аргументы к этим
функциям - имена определенных тегов, вроде channel/item или title.
При определении тегов вы должны принять полную структуру докумен­
та во внимание. Каждая операция find работает относительно начального
элемента. Аналогично, имя тега, которое вы передаете каждой операции,
тоже указывается относительно начального элемента. В примере вызов к
doc.iterfind('chann:el/item') находит все элементы "item", которые находят­
ся внутри элемента "chaцnel". "doc" представляет верхнюю часть документа
(элемент "rss"). Более поздние вызовы item.findtext() будут иметь место от­
носительно найденных элементов "item".
У каждого элемента, представленного модулем ElementTree, есть несколь�
ко существенных атрибутов и методов, которые полезны при парсинге.
Атрибут tag содержит имя тега, атрибут text содержит текст, а метод get()
может быть использоваться для извлечения атрибутов.
Нужно отметить, что xml.etree.ElementTree - не единствещюе средство
для парсинга XML. Для более сложных приложений вы можете рассмо­
треть использование lxml 1 . Эта библиотека использует тот же API, что и
ElementTree, таким образом, пример, показанный только что будет работать
и с lxml. Просто нужно заменить первый оператор импорта на from lxml.etree
import parse.lxml. Библиотека lxml лучше совместима с ХМL-стандартами.
Также она быстрее и предоставляет дополнительные функции· вроде вали­
дации, XSLT и XPath.

15.3.4. ПРЕОБРАЗОВАНИЕ СЛОВАРЯ В XML
Иногда нужно сохранить содержимое словаря в ХМL-формаrе. Хотя би­
блиотека xml.etree.ElementTree обычно используется для парсинга, она так­
же может быть использована для создания ХМL-документов. Например,
рассмотрим эту функцию:
from xml.etree.ElementTree import Element
1

https://pypi.python.org/pypi/lxml


al-------------------------------------------------------------------------········-·

• python_____________________

Глава 15. Файловый ввод/вывод

def dict_to_xml(tag, d):

'''

Преобразуем простой словарь из пар

'' ,,

ключей/значений в ХМL

elem = Element(tag)
for key, val in d.items():
child = Element (key)
child.text = str(val)
elem.append(child)
return elem

Вот пример использования:
>>> s = { 'name': 'Mark', 'shares': 70, 'price':590.l}
>>> е
dict to_xml('test', s)
>>> е..

>>>

Результат этого преобразования - экземпляр Element. Для ввода/вывода
проще конвертировать это в байтовую строку, используя функцию tostring()
в xml.etree.ElementTree. Например:
>>> from xml.etree.ElementTree import tostring
>>> tostring(e)
b'590.170Mark'
>>>

Если вы хотите присоединить атрибуты к элементу, используйте метод set():
>>> e.set('_id','1234')
>>> tostring(e)
b'590.170Mark
'
>>>

При создании XML программисты обычно просто создают ХМL-строку.
Например:

·------------------------------------------------------------------------------------

Python. Полное руководство

.--·-. ----------------------6python
m

def dict_to_xml str(tag, d):
1 1 1

Аналог нашей функции, но здесь просто создается ХМL-строка
1 1 1

parts = [''.format(tag)]
for key, val in d.items():
parts.append('{l}'.format(key,val))
parts.append(''.format(tag))
return ''.join(parts)

Проблема заключается в том, что вы можете запутаться, если попытаетесь
заняться обработкой ХМL-файла вручную. Например, что произойдет, если
значения словаря содержат специальные символы?
•>>> ·d = { 'name' : ''
>>>#Создание строки
>>> dict_to_xml str('item',d)
''
>>>#Правильное создание XML
>>> е = dict_to xml('item',d)
>>> tostring(e)
b'&lt;spam&gt;'
>>>

Обратите внимание, как в последнем примере заменяются символы < и >
на &lt; и &gt;.
Для справки: если вы даже вам захочется вручную обрабатывать такие спе­
циальные символы, вы можете использовать функции escape() и unescape()
в xml.sax.saxutils. Например:
>>> from xml.sax.saxutils import escape, unescape
>>> escape ( '')
'&lt;spam&gt;'
>>> unescape ( )
''
>>>

Кроме создания корректного вывода есть и другая причина, почему лучше
создавать экземпляры Element вместо строк - они могут быть л:егко объеди­
нены вместе, чтобы создать большой документ. Результирующие экземпля­
ры Element также могут быть обработаны разными способами и при этом не

C!!t------------. --------------------------.------------------------..-. ------------. '

ф python.....................

Глава 15. Файловый ввод/вывод

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

15.З.5. МОДИФИКАЦИЯ И ПЕРЕЗАПИСЬ ХМL-КОДА
Другая часто распространенная задача - нужно прочитать ХМL·документ,
внести в него изменения и записать обратно как XML.
Модуль xml.etree.ElementTree упрощает выполнение таких задач. По су·
ществу, вы начинаете парсинг документа обычным способом. Например,
предположим, что у вас есть документ, который называется pred.xml и по·
хож на это:


l4791
Moя компания

22
Улица
Индекс

22

.5000
Test
l378
22


lOOOO
Test
l867
22



Далее приведен пример использования ElementTree для чтения этого файла
и внесения изменения в его структуру:
>> from xml.etree.ElementTree import parse, Element
.

···································································-··············fI!D

Python. Полное руководство

:. ; ______ _.________________.python

>>> doc = parse('pred.xml')
>>> root = doc.getroot()
>>> root

>>>#Удаляем несколько элементов
>>> root.remove(root.find('sri'))
>>> root.remove(root.find('cr'))
>>>#Вставляем новый элемент пocлe-.i]
>>> root.getchildren().index(root.find('nm'))
1

>>> е = Element('spam')
>>> e.text = 'Test'
>>> root.insert(2, е)
>>>#Записываем результат обратно в файл
>>> doc.write('newpred.xml', xml_declaration=True)
>>>

В результате этих операций будет создан ·новый ХМL-файл, который вы­
глядит так:


14791
Moя компания
Tecт
SOOO
Tecт
1378
22


lOOO
Tecт
1867
22



Изменение структуры ХМL-документа достаточно простое, но вы должны
помнить, ч:rо все модификации обычно делаются в родительском элементе,
как будто это список. Например, если вы удаляете элемент, то он удаляет­
ся из его непосредственного родителя, используя метод remove() родителя.
Если вы вставляете или добавляете новые элементы, то вы также использу•



.....................................................................................

.• python_____________________

Глава 15. Файловый ввод/вывод

ете методы insert() или append() родителя. Элементами можно также управ­
лять, используя индексы и слайсы, например, element[i] или element[i:j].
Если вам нужно создать новые элементы, используйте класс Element, как
было показано.

15.3.6. ДЕКОДИРОВАНИЕ И КОДИРОВАНИЕ
ШЕСТНАДЦАТЕРИЧНЫХ ЧИСЕЛ
Вам нужно декодировать строку шестнадцатеричных чисел в байтовую
строку, или же кодировать байтовую строку как шестнадцатеричную. Для
этого можно использовать модуль Ьinascii. Например:
>>>#Начальная байтовая строка
>>> s = b'hello'
>>>#Кодируем в шестнадцатеричном виде
>>> import binascii
>>> h = binascii.b2a_hex(s)
>>> h
Ь'68656сбсбf'
>>>#Декодируем в байты
>>> binascii.a2b_hex(h)
b'hello'
>>>

Подобная функциональность также может быть найдена в модуле base64.
Например:
>>> import base64
>>> h = base64.Ыбencode(s)
>>> h
b'68656C6C6F'
>>> base64.Ыбdecode(h)
b'hello'
>>>

По большей части преобразование в шестнадцатеричную форму и обрат­
но с использованием описанных функций очень простое. Основное разли­
чие между этими двумя методами в обработке регистра. Функции base64.



·-----·--------·················································-·-····-··········-

Python. Полное руководство

.........................• python

Ь16decode() и base64.Ь16encode() работают только с прописными шестнад­
цатеричными буквами, тогда как функции в blnascii с любым регистром.
Важно отметить, что вывод, произведенный функциями кодирования, всег­
да является байтовой строкой. Чтобы привести его к Unicode, нужно доба­
вить дополнительный шаг декодирования. Например:
>>> h = base64.Ыбencode(s)
>>> print(h)
b'68656C6C6F'
>>> print(h. decode('ascii'))
68656C6C6F
>>>

При декодировании шестнадцатеричных чисел функции Ь16decode() и
a2b_hex() принимают или байты или Unicode-cтpoки. Однако эти строки
должны содержать ASCII �кодированньrе шестнадцатеричные цифры.

15.3.7. КОДИРОВАНИЕ/ДЕКОДИРОВАНИЕ BASE64
Кодирование Base64 используется на байтовых данных, такие как байто­
вые строки и массивы байтов. Кроме того, результат кодирования всегда
является байтовой строкой. Если вы смешиваете данные, закодированные
в Base64, с текстом Unicode, вам придется выполнить дополнительный шаг
декодирования.
В модуле base64 есть две функции - b64encode() и b64decode(), которые
позволяют работать с Ваsе64-кодированием. Например:
>>>#Некоторые байтовые данные
>>> s = b'hello'
>>> import base64
>>>#Кодируем как Base64
>>> а = base64.b64encode(s)
>>> а
b'aGVsbG8='
>>>#Декодируем данные из Base64
>>> base64.b64decode(a)
b'hello'
>>>

..

-· --

--

. . - . - . - - - - - -- - - - - - - - - - - - - - - - - - - - -



--- - - - -- - -- - - -- - - - - -- - - -- - - -- - - - - - - - - - - ---.

ГЛАВА 16.
ООП И РУТНОN

Pyttюn. Полное руководство

...................... _ф python.

16.1. Основы объектно­
ориентированного
программирования
Объектно-ориентированное программирование (ООП) - это особый
подход к написанию программ. Чтобы понять, что такое ООП и зачем оно
нужно, необходимо вспомнить некоторые факты из истории развития вы­
числительной техники. Первые программы вносились в компьютер с по­
мощью переключателей на передней панели компьютера - в то время ком­
пьютеры занимали целые комнаты. Такой способ "написания" программы,
сами понимаете, был не очень эффективным - ведь большая часть времени
(несколько часов, иногда - целый рабочий день) занимало подключение
кабелей и у�тановка переключателей. А сами расчеты занимали считанные
минуты. Вы только пред�тавьте, что делать, если один из программистов
(такие компьютеры программировались, как правило, группами програм­
мистов) неправильно подключил кабель или установил переключатель?
Да, приходилось все перепроверять - по сути, все начинать заново.
Позже появились перфокарты. Программа, то есть последовательность
действий, которые должен был выполнен компьютер, наносилась на пер­
фокарту. Пользователь вычислительной машины (так правильно было на•





•···············································•·····················s···········•

+

python...... � ............... .

Глава 16. ООП и Python

зывать компьютеры в то время) писали программу, оператор "записывал"
программу на перфокарту, которая передавалась оператору вычислитель·
наго отдела. Через определенное время оператор возвращал пользователю
результат работы программы - рулон бумаги с результатами вычислений.
Мониторов тогда не было, а все, что выводил компьютер, печаталось на бу·
маге. Понятно, если в расчетах была допущена ошибка (со стороны польза·
ватеш�, компьютеры ведь не ошибаются - они делают с точностью то, что
заложено программой), то вся цепочка действий (программист, оператор
перфокарты, оператор вычислительной машины, проверка результатов) по·
вторялась заново.
Следующий этап в программировании - это появление языка Ассембле·
ра. Этот язык программирования позволял писать довольно длинные для
того времени программы. Но Ассемблер - это язык программирова·
ния низкого уровня, все операции проводятся на уровне "железа"_. Если вы
не знаете, то сейчас я вам поясню. Чтобы в РНР выполнить простейшее
действие, например, сложение, достаточно записать '$А= 2 + 2; '. На языке
Ассемблера вам для выполнения этого же действия нужно было выполнить
как минимум три действия - загрузить в один из регистров первое число
(команда MOV), загрузить в другой регистр второе число (опять команда
MOV), выполнить сложение регистров командой ADD. Результат сложе·
ния будет помещен в третий регистр. Названия регистров я специально
не указывал, поскольку они зависят от архитектуры процессора, а это еще
один недостаток Ассемблера. Если вам нужно перенести программу на ком·
пьютер с другой архитектурой, вам нужно переписать программу с учетом
особенностей целевой архитектуры.
Требования к программным продуктам и к срокам их разработки росли
(чем _быстрее будет написана программа, тем лучше), поэтому появились
языки программирования высокого уровня. Язык высокого уровня позво·
ляет писать программы, не задумываясь об архитектуре вашего процессора.
Нет, это не означает, что на любом языке высокого уровня можно написать
программу, которая в итоге станет работать на процессоре с любой архи·
тектурой. Просто при написании программы знать архитектуру процессора
совсем не обязательно. Вы пишете просто А = В + С и не задумываетесь, в
каком из регистров (или в какой ячейке оперативной памяти) сейчас хра·
нятся значения, присвоенные переменным В и С. Вы также не задумывае·
тесь, куда будет помещено значение переменной А. Вы просто знаете, что к
нему можно обратиться по имени А. Первым языком высокого уровня стал
FORTRAN (FORmula TRANslatoг).
Следующий шаг - это появление структурного программирования. Дело в
том, что программы на языке высокого уровня очень быстро стали расти в

,

................................................................................ ...

Python. Полное руководство

.......................«�

python

размерах, что сделало их нечитабельными из-за отсутствия какой-нибудь
четкой .структуры самой программы. Структурное программирование под­
разумевает наличие структуры программы и программных блоков, а также
отказ от инструкций безусловного перехода (GOTO,JMP).
После выделения структуры программы появилась необходимость в созда­
нии подnрограмм, которые существенно сокращали код программы. Намно­
го проще один раз написать код вычисления какой-то формулы и оформить
его в виде процедуры (функции) - затем для вычисления 1 О результатов по
этой формуле нужно будет 10 раз вызвать процедуру, а не повторять 10 раз
один и тот же код. Новый класс программирования стал называться про­
цедурным.
Со временем процедурное программирование постигла та же участь, что и
структурное программирование - программы стали настолько большими,
что их было неудобно читать. Нужен был новый подход к программиро­
ванию. Таким стало объектно-ориентированное программирование (далее
ООП).
ООП базируется на трех основных принципах - инкапсуляция, полимор­
физм, наследование. Разберемся, что есть что.
С помощью инкапсуляции вы можете объединить воедино данные и обра­
батывающий их код. Инкапсуляция защищает и код, и данные от вмеша­
тельства извне. Базовым понятием в ООП является класс. Грубо говоря,
класс - это своеобразный тип переменной. Экземпляр класса (переменная
типа класс) называется объектом. В свою очередь, объект - это совокуп­
ность данных (свойств) и функций (методов) для их обработки. Данные и
методы обработки называются членами класса. Свойства в Python называ­
ются атрибутами класса, но также и есть понятие свойства класса, которое
не нужно путать с классическим свойством.
Получается, что объект - это результат инкапсуляции, поскольку он вклю­
чает в себя и данные, и код их обработки. Чуть дальше вы поймете, как это
работает, пока представьте, что объект - это эдакий рюкзак, собранный по
принципу "все свое ношу с собой".
Теперь поговорим о полиморфизме. Если вы программировали на языке С
(на обычном С, не С++), то наверняка знакомы с функциями abs(), fabs(),
labs(). Все они вычисляют абсолютное значение числа, но каждая из функ­
ций используется для своего типа данных. Если бы С поддерживал поли­
морфизм, то можно было бы создать одну функцию abs(), но объявить ее
трижды - для каждого типа данных, а компилятор бы уже сам выбирал
нужный вариант функции, в зависимости от переданного ей типа данных.
Данная практика называется перезагрузкой функций. Перезагрузка

•················································································'

• python . ......................

Глава 16. ООП и Python

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

16.2. Определение класса и
создание объекта
Определить класс можно с помощью ключевого слова class:
class :


Пример определения класса:
class SampleClass:
init (self):
def
print("Constructor")
self.nm = "SampleClass"
def printName(self):
print(self.nm)
obj = SampleClass()
obj .printName()
# При желании вы можете самостоятельно вывести атрибут
print(obj.nm)

Если запустить этот код, то его вывод будет следующим:
>>>
Constructor
SampleClass
SampleClass



..... - . -...... - . - . ·.· .. -.. - . - ... - ... - - . - . - . - . - - . - . - . -...... - . - . - . -- -.. - . - . -- - . -. -..

-

Python. Полное руководство

_______________________• python

Строка Constuctor выводит только один раз - во время создания объекта
obj. Далее выводятся две строки SampleClass. Одна .:_ когда мы используем
мeтoдprintName(), вторая - когда мы выводим атрибут объекта.
Как видите, формат обращения к методам и атрибутам следующий:
. ( [Параметры] )
.

Также для доступа к атрибутам вы можете использовать следующие функ­
ции:
• getattr() - возвращает значенце атрибута по его названию, которое ука­
зывается в виде строки
• setattr() - устанавливает значение атрибута. Название атрибута задает­
ся в виде строки
• delattr() - удаляет атрибут, название, как обычно, задается в виде строки
• hasattr() - проверяет наличие указанного атрибута. Если атрибут суще­
ствует, возвращается Trne
Синтаксис данных функций следующий:
getattr(,
setattr(,
delattr(,
hasattr(,

[, ])
, )
)
)

16.3. Конструктор и деструктор
Конструктор - это метод, вызываемый интерпретатором автоматически
при инициализации класса. В Python этот метод называется _init_():
def
init (self [, [, ..., ]] :


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



-----------------------------------------------------------------------------------·

Глава 16. ООП и Pythoв

• python_____. _ .. __________. __.

Аналогично, перед уничтожением объекта вызывается деструк·тор, который

в Python называется _del_(). Учитывая, что интерпретатор сам заботит­

ся об освобождении занимаемых объектом ресурсов, особого смысла в де­
структоре нет.

16.4.Наследование
Наследование - это то, за что программисты любят 00 П. Наследование
позволяет создать класс, в котором будет доступ ко всем атрибутам и мето­
дам родительского (базового) класса, а также к некоторым новым методам
и атрибутам.
Рассмотрим небольшой пример:
class Parent:
def print_narne(self):
рrint("Родитель")

# Родительский класс

class Child(Parent):
# Наследование класса Parent
def print_child(self):
рrint("Потомок")
obj = Child()
obj .print_narne()
obj.print child()

Посмотрите, что у нас получилось. Класс Child унаследовал метод pri,nt_
пате(), который мы можем вызвать из объекта obj.
Примечание. Терминология разная. Поэтому в некоторых ис­
точниках родительский класс могут называть базовым, а таюке
суперклассом. Дочерний класс называют подклассом или произ­
водным классом.

А что, если в дочернем классе вам захочется определить такой же метод, как
и в родительском? Например:
class Parent:
def print_narne(self):
рrint("Родитель")

# Родительский класс

class Child(Parent):

# Наследование класса Parent

·--------------------·-··················--------·-·--·---·---·--·-··,············-

Pythoп. Полное руководство

.....•.......... _. _..... _. python

def print childTselП:
рrint("Потомок")
def print name(self):
рrint("Потомок")
obj = Child()
obj .pЦnt_name ()

Какой метод будет вызван? Будет вызван метод дочернего класса, поскольку он переопределит метод с таким же именем родительского класса. Если
нужно вызвать именно метод родительского класса, тогда нужно явно ука­
зать имя класса, например:
def print_name(self):
рrint("Потомок")
Parent.print_name()

Примечание. Конструктор родительского класса автоматически
не вызы�ается, если он переопределен в дочернем классе!

В Python также доступно и множественное наследование - когда один класс
наследует атрибуты и методы нескольких классов. Просто нужно указать
родительские классы в скобках через запятую:
class Child(Parentl, Parent2):


16.5. Специальные методы
Классы в Python поддерживают представленные в таблице 16.1 специальные методы.
Таблица 16. 1. Специальные методы

Описание

Метод

_call_( self[, Параметр!,... Обрабатывает вызов экземпляра класса как
вызов функции
,ПараметрN])

...

--

. - . -...... -............. . -.... -............ - . - . -...... - .

---

••.•



... -............ -·

• python...........__ •.•..... _.

_setitem_(self,
)

,

Глава 16. ООП и Pytlюn

Будет вызван при присваивании значения по
индексу или ключу

_getitem_(self, )

Вызывается при доступе к значению по индексу или ключу. Метод будет автоматически
вызван при использовании операций, применимых к последовательностям, например,
при использовании цикла for

delitem_(self, )

Вызывается при удалении элемента по индексу или ключу с помощью оператора del

�getattr_(self,
)

Вызывается при обращении к несуществующему атрибуту класса

�etattribute_(self,
)

Вызывается при обращении к любому атрибуту класса

_setattr_(self, , Вызывается при попытке присваивания значения атрибуту экземпляра класса
)
_delattr_(self,
бут>)



= len(self.massiv):
self.ind = О
# Сбрасываем индекс
raise Stopiteration
# Генериру.ем исключение
else:
item = self.massiv[self.ind]
self.ind += 1
return item
obj = IterClass([l, 2, 3])
for i in obj:
print(i, end=" ") # выведет 1 2 3

16.6. Статические методы
Внутри класса можно создать метод, который будет доступен без создания
экземпляра класса. Для определения статического метода используется де­
коратор @staticmethod. Вызывается статический метод так:
.()

Также статический метод можно вызвать и через объект класса:
.()

Пример:
class StaticSample:
@staticmethod
def ssum(x, у):
return х + у
def msum(self, х, у):
return х + у

'-------········-····--····-······--·-·····-·················-···········-····-···•

Python. Полное руководство
print(StaticSamle.ssum(2, 2))
объекта
obj = StaticSample()
print(obj.msum(2, 2))
print(obj.ssum(2, 2))
через объект

.......................



python

# Вызываем до объяв.пения
# Вызываем обычный метод
# Вызываем статический метод

16.7. Абстрактные методы
Абстрактные методы содержат только определение метода без реализации.
Это эдакие заглушки, которые нужно реализовать в дочернем классе. Часто
в абстрактных методах возбуждают исключение, чтобы напомнить о необ­
ходимости реализовать метод, например:
class Sample:
def func(self, х, у):
raise NotimplementedError("Not implemented")
def msum(self, х, у):
return х + у

В данном случае метод /ипс() является абстрактным. Как видите, никаких
декораторов не используется. Хотя в версии 2.6 появился модуль аЬс; со·
держащий декоратор @abstractrnethod. Что дает нам использование этого
декоратора? А то, что вам не нужно вызывать самому исключение, данный
декоратор сгенерирует ошибку TypeError при использовании не переопре­
деленного абстрактного метода. Лучше использовать первый способ, но не
привести пример с использованием @abstractrnethod просто невозможно:
from аЬс import

*

class Sample:
@abstractmethod
def func(self, х, у):
pass
def msum(self, х, у):
return х + у

16.8. Перегрузка операторов



Перегрузка обычных операторов позволяет экземплярам классов участво·
вать в обычных операциях вроде сложения или вычитания. Например,

IID-····-···---·······································--···················-··········

• python__________ . ____________

Глава 16. ООП и Python

если вы хотите сложить два объекта, то для их класса должен бьпь оr1реде­
лен метод x._add_(y). Ниже приведен пример перегрузки операторов ==
и in:
class ReloadClass:
init (self):
def
self.x = О
self.a = (1, 2, З]
def _eq�(self, х):
return self.x == у
contains (self, у):
def
return у in self.a
о = ReloadClass()
if о == 10:
print("True")
else:
print("False")
if З in о:
print("True")
else:
print("False")

# Выведет False
# Выведет True

Методы, используемые для перегрузки обычн�rх операторов, приведены в
таблице 16.2.
Таблица 16.2. Методы перезагрузки обычных операторов
Метод

х.

.

add_(у)

Оператор

х+у

х.-radd_(у)

у + х (экземпляр класса справа)

х.-iadd_(у)

х+=у

х.-sub_(у)

х-у

х.

rsub_(у)

у - х (экземпляр класса справа)

х.

isub_(у)

х-=у

х.-mul_(у)

х*у

х.

rmul_(у)
.

у*х (экземпляр класса справа)


·--------------------------------------------------------------�-------------------�

Python. Полное руководство

_______________________ф python

х.-irnul_(у)

х*=у

х.-truediv_(y)

х/у

х._. rtru�div_(у)

у /х
х/=у

х.-itruediv_(y)
х.-floordiv_(y)

·.

х//у

х.-rfloordiv_(y)

у //х

х.-iflordiv _(у)

х//= у

х.�· rnod_(у)

х%у

х.

rrnod_(у)

у %х

х.-irnod_(у)

х%=у

x._pow_(y)

х**у

x._rpow_(y)

у **х

x._ipow_(y)

х **=у

x._neg_()

-х(унарный минус)

x._pos_()

+х(унарный плюс)

х.-abs_()

abs(x)

x._contains_(y)

in

x._eq_(y)

х== у

x._ne_(y)

х!=у

x._lt_(y)

х у

х.-l e_(y)

х=у



--------------------------------------------·-····-·---·-·-·--------·-------------·

О python_______________________

Глава 16. ООП и Python

16.9. Свойства класса
Внутри класса может быть создан идентификатор, через который будут
производиться операции по получению и изменению значения атрибута, а
также операция удаления атрибута. Создать такой идентификатор моно с
помощью функции property():


=

рrореrtу([, [, from urllib.parse import *
>>> url = urlparse("http://nit.ee�ter:80/index.php;st?param=value#ankor")
>>> url
ParseResult(scheme='http', netloc='nit.center:80', path='/index.php',
params='st', query='param=value', fragment='ankor')
>>> t = tuple(url)
>>> t
('http', 'nit.center:80', '/index.php', 'st', 'param=value', 'ankor')
>>> url.scheme, url[O]
('http', 'http')
>>> url.netloc, url[l]



••..•.....•..................•.•.•.•.........................•.•..................•

Pytlюn. Полное руководство

.........._____________.О python

('www .nit.center:80', 'www.nit.center:80')
>>> url.hostname
'nit.center'
>>> url.port
80
>>> url.path
' /index.php'
>>> url.params
'st'
>>> url.query
'param = value'
>>> url.fragment
'ankor'
>>>

Для выполнения обратной операции, то есть для сбора URL-aдpeca из от­
дельных частей используется функция urlunsplit():
>>> t = ('http', 'nit.center', 'index.php', 'par=value', 'ankor')
>>> urlunsplit(t)
'http://nit.center/index.php?par=value#ankor'

17.2. Декодирование строки запроса
Ранее мы получили строку запросов, которая выглядит так:
параметрl=значениеl&...&параметрN=значениеN

Теперь нужно разобрать эту строку на составляющие. Сложность заключа­
ется в том, что если значение содержит символы национальных алфавитов,
то они будут закодированы - каждый символ будет кодироваться последо­
вательностью символов $nn, где nn - шестнадцатеричное число. Пример:
https.j/m.wikipedia.orglwiki/%D0%A8%D0%B8%D1%84%D1%80%D0%B
E%1J0%B2%D0%B0%D0%BD%D0%B8%D0%B5
Для разбора строки запроса на составляющие можно использовать следую­
щие функции из модуля urllib.parse:



t!D-------······--···-·-·-····-···-···-···-·--·--·-···-···-·········-··-····-·········

О python____________________

Глава 17. Работа с Интернетом

• parse_qs() - разбирает строку запроса и возвращает словарь с ключами,
которые содержат названия параметров, и список значений
• parse_qsl() - в отличие от предыдущей функции возвращает список
кортежей из двух элементов
Синтаксис функций:
urlparse.parse_qs(qs[, keep_Ыank_values[, strict_parsing]])
urlparse.parse_qsl(qs[, keep_Ыank_values[, strict_parsing]])

Пример:
>>> from urllib.parse import parse_qs
>>> qs = 's =%D0%A8%D0%B8%D1%84%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D
0%B8%D0%B5'
>>> parse_qs(qs, encoding='cp1251')
{ 's': [ 'РЁРёС"С'Р.,РsРIР 0 РSРёРµ']}
>>> parse_qs(qs, encoding='utf-8')
{ 's': ( 'Шифрование ']}
>>>

Как видно из примера, очень важно правильно указать кодировку. Как пра­
вило, используется utf-8, что и показано в примере.
Если цараметров несколько, то функции с легкостью справятся и с этой за­
дачей:
>>> qs = 'parl=vall&par2=val2'
>>> parse_qs(qs, encoding='utf-8')
{'parl': ['vall'], 'par2': ['val2']}
>>> parse_qsl(qs, encoding='utf-8')
[('parl', 'vall'), ('par2', 'val2')]
>>>

17.3. Разбор НТМL-эквивалентов

....

В НТМL-документах часто встречаются так называемые НТМL­

эквиваленты. Например, пщ:ледовательность &gt соответствует знаку >, а

'- --.................-...... ---.---......................................... - ----

Python. Полное руководство

••••••••••••••••••••••••

if;{,jpython
Т✓

последовательность &lt - знаку >> irnport xrnl.sax.saxutils as х
>>> x.escape('""&""')
'""&lt;&gt;&arnp;""'

17 .4" Преобразование относительных
ссылок
Иногда в НТМL-документах указывают относительные ссылки, а не абсо­
лютные. Преобразовать относительную ссылку в абсолютную можно с по­
мощью функции urljoin() из модуля urllib.parse:
urljoin(, [,
])



- .-- ...-. -.-.. - ...- . -.-. -.. -.....-...-..- . -...-. --.-.-.-· -- ----.............. ·с·.·•

О python................... .

Глава 17. Работа с Интернетом

Пример использования:
>>> from urllib.parse import urljoin
>>> urljoin('http://nit.center', 'index.php')
'http://nit.center/index.php'
>>> u�ljoin('http://nit.center/index.php', 'test.html')
'http://nit.center/test.html'

17.5. Определение кодировки
Документы в Интернете представлены в самых разных кодировках. В
последнее время наблюдается тенденция использования единой кодиров­
ки - UTF-8 и это очень и очень хорошо. Определить кодировку документа
можно по заголовку Content-Type в заголовках сервера:
Content-Type: text/html; charset=utf-8

Также кодировка указывается с помощью МЕТА-тега content:


К сожсµ�ению, далеко не все используют UTF-8 и довольно часто кодировка
из ответа сервера не совпадает с указанной в МЕТА-теге. Для определения
кодировки можно использовать библиотеку chardet, скачать которую мож­
но по адресу http.//chardet.feedparser.org/download.
Скачайте архив pythonЗ-chardet-2.0.1.tgz и распакуйте его в любую папку.
Представим, что вы ее распаковали в папку C:\Python39. Далее откройте
командую строку и введите команду:
cd C:\Python39\python3-chardet-2.0.l

После этого нужно запустить программу установки:
cd c:\Python39\python.exe setup:py install
······································-···············-···························-tD

Pythoп. Полное руководство

.... ........................ python

После установки библиотеки ее можно использовать:
>>> import chardet

Для
определения
кодировки
используется
функция
detect( ). В качестве результата функция
возвращает словарь с элементами - encoding и confidence. Первый - это
определенная кодировка, второй - вещественное число, задающее коэффи­
циент точности определения кодировки.
Пример:
>>> import chardet
>>> chardet.detect(bytes("Teкcт", "ср1251"))
{'confidence':0.99, 'encoding': 'windows-1251'}

Как видите, мы передали функции строку байтов в кодировке Windows-1251,
и она правильно ее определила с точностью 99%.

17 .6. Реализация НПР-клиента
Представим, что есть сценарий http:j/nit.center/script.php. Ему нужно пере­
дать параметры var1 и var2, а потом считать результат его работы.
В простых случаях вполне достаточно будет модуля urllib.request. Напри­
мер, для отправки простого запроса НТТР GET удаленному сервису доста­
точно сделать следующее:
from urllib import request, parse
# URL, к которому производится доступ
url = 'http://nit.center/script.php'
# Словарь с параметрами запроса (если есть)
parms = {
'varl'
'valuel',
'var2' : 'value2'
}

# Кодируем строку запроса
querystring = parse.urlencode1parms)

•················································································'

• python....................

Глава 17. Работа с Интернетом

# Производим GЕТ-запр?,� и читаем ответ
u = request.urlopen(url+'?' + querystring)
resp = u.read()

Если вам нужно отправить параметры запроса в теле РОSТ·запроса, снача­
ла закодируйте их и отпра�ьте в виде опционального аргумента urlopen():
from urllib import request, parse
# URL, к которому производится доступ
url = 'http://nit.center/script.php'
# Словарь с параметрами запроса (если есть)
parms = {
'namel'
'valuel',
'name2'
'value2'
}

# Кодируем строку запроса,
querystring = parse.urlencode(parms)
# Производим РОSТ-запрос и читаем ответ
и = request.urlopen(url, querystring.encode('ascii'))
resp = u.read()

Если вам нужно предоставить пользовательские НТТР-заголовки в исхо­
дящем запросе, например, изменить поле U ser-Agent ( обычно используется
для указания браузера), создайте словарь, содержащий их значения, соз­
дайте экземпляр Request и передайте его в urlopen(). Например:
from urllib import request,.parse
# Дополнительные заголовки
headers = {
'User-agent' : 'Му Python Browser'
req = request.Request(url, querystring.encode('ascii'),
headers=headers)
# Производим запрос и читаем ответ
u = request.urlopen(req)
resp = u.read()



··················································································СВ

ГЛАВА 18.
ИТЕРАТОРЬIИ
ГЕНЕРАТОРЬI

Глава 18. Итераторы и генераторы

Итерация - одна из самых сильных функций Python. На высоком уровне вы
можете просто рассматривать итерацию как способ обработки элементов в
последовательности. Однако вам доступно намного больше, например, соз­
дание собственных объектов iterator (итераторов), применение полезных
ит·еративных образцов в модуле itertools, создание функций-генераторов
и т.д. Эта гла�а стремится лишь показать типичные задачи, вовлекающие
итерацию.

18. 1 . Ручное использование
итератора
Начнем с самого простого примера: вам нужно обработать элементы в ите­
рируемом, но по некоторым причинам вы не можете или не хотите исполь­
зовать цикл for.
Используйте функцию next() и напишите код для перехвата исключения
Stoplteration. Например, этот пример читает все строки из файла:
with open( ' cron . log' ) as f :
try:
while True:
line = next(f)
print(line, end=' ')
except Stopiteration:
pass

_______________________ • python

Обычно исключение Stoplteration используется для уведомления об окон­
чании итерации. Однако, если вы будете использовать next() вручную (как
и показано), вы можете также возвращать какое-то завершающееся значе­
ние вроде None. Например:
vith open( 'cron. log') as f:
vhile True:
line = next(f, None)
if line is None:
break
print (line, end=' ')

В большинстве случаев для перебора итерируемого используется оператор
for. Однако время от времени задачи будут требовать более точного управ­
ления итеративным механизмом. Таким образом, полезно знать, что факти­
чески происходит.
Следующий интерактивный пример иллюстрирует основную механику
того, что происходит во время итерации:
>>> items = [1, 2, З]
>>>#Получаем итератор
>>> it = iter(items) # Вызываем iterns.
>>>#Запускаем итератор
>>> next(it) # Вызываем it. next ()
1
>>> next(it)
2
>>> next(it)
3
>>> next(it)
Traceback (most recent call last):
File "", line 1, in
Stopiteration
>>>

iter

()

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



Ell-··················································································

• python.....•..............

Глава 18. Итераторы и генераторы

18.2. Делегирование итерации
Представим, что у нас есть пользовательский объект контейнера, который
внутри содержит список, кортеж или какое-то другое итерируемое. Нужно
проделать итерацию с вашим новым контейнером.
Как правило, все, что вам нужно сделать - это определить метод _iter()_,
который делегирует итерацию к внутреннему контейнеру. Например:
class Node:
def _init_(self, value):
self. value = value
self._children = []
def _repr_(self):
return 'Node({!r}) '.format(self._value)
def add_child(self, node):
self._children.append(node)
def _.iter_(self):
return iter(self._children)
# Пример
if _name_ = '_main_' ·
root = Node(O)
childl = Node(l)
child2 = Node(2)
root.add_child(childl)
root.add_child(child2)
for ch in root:
print(ch)
·#Выводит Node(l), Node(2)

В этом коде метод _iter_() просто перенаправляет запрос итерации к
внутреннему атрибуту_children.
Протокол итератора в Python требует _iter_() для возврата специального
объекта итератора, который реализует метод _next_() для выполнения
фактической итерации.
Если все, что вы делаете, это просто итерация по содержимому другого
контейнера, вам не нужно волноваться о том, как это работает. Все, что вы
должны сделать, это перенаправить запрос итерации вперед.

,

................................................................................ ..

Pytlюn. Полное руководство

........................• python

Использование функции iter() здесь что-то вроде ярлыка, который делает
код чище. Функция просто возвращает базовый итератор, вызывая метод
s._iter_().

18.3. Создание нового шаблона
итерации с помощью генераторов
В этом примере мы попытаемся реализовать пользовательский шаблон
итерации, который отличается от обычных встроенных функций (например, range(), reversed() и т.д.).
Если вам нужно реализовать новый вид шаблона итерации, определите его
как функцию-генератор. Здесь приведен генератор, который создает диа­
пазон чисел с плавающей точкой:
def my_range(start, stop, increment):
х = start
while х < stop:
yield х
х += increment ·

Чтобы использовать эту функцию, вам нужно итерировать по ней в цикле
for или использовать ее с другой функцией, работающей с итерируемым
(например, sum(), list() и т.д.). Например:
>>> for n in my_range(0, 4, 0.5):
print(n)
о
0.5
1.0
1.5
2.0
2.5
3.0
3.5
>>> list(my_range(0, 1, 0.125))
[О, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875]
>>>



-----------------------------------·-·-·-····-·--------·-·-·····-·--···-·-······-··

6python-------------------.'%'

Глава 18. Итераторы и генераторы

Простое присутствие оператора yield в функции превращает ее в генератор.
В отличие от обычной функции, генератор работает только в ответ на итера­
цию. Ради эксперимента рассмотрим, как работает такая функция:
>>> def countdown(n):
print('Нача.льная позиция отсчета:
while n > О:
yield n
n -= 1
print('Готово! ')

n)

>>>#Создаем генератор, обратите, что не будет никакого
вывода
>>> с =countdown(З)
>>> с

>>>#Запускаем генератор до первого yield и получаем значение
>>> next(c)
Начальная позиция отсчета 3
3
>>>#Запуск до следующего yield
>>> next(c)
2

>>>#Запуск до следующего yield
>>> next(c)
1

>>>##Запуск до следующего yield (итерация останавливается)
>>> next(c)
Готово!
Traceback (most recent call last):
File "", line 1, in
Stopiteration
>>>

Главная особенность - то, что функция генератора только работает в ответ
на "следующие" операции, выполненные в итерации. Как только функция
возвращается, итерация останавливается. Однако обычно цикл for забо­
титься обо всех деталях, поэтому вас это не должно волновать.

•·-------.--.........-.....·-...-..................................................tD

Python. Полное руководство

·-----------------------6python


18.4. Реализация протокола
итератора
При создании пользовательских объектов, поддерживающих итерацию, по­
лезно реализовать и протокол итератора.
Безусловно, самый простой способ реализовать итерацию на объекте за­
ключается в использовании функции-генератора.
Ранее был разработан класс Node для представления структур деревьев.
Возможно, вы хотите реализовать итератор, который делает обход узлов в
глубину (метод будет называться depth_first). Вот как это можно сделать
class Node:
def _init_(self, value):
self. value = value
self. children = []
def ·_repr_(self):
return 'Node({!r}) '.format(self._value)
def add_child(self, node):
self._children.append(node)
def

iter (self):
return iter(self._children)

def depth_first(self):
yield self
for с in self:
yield from c.depth_first()
# Пример
if
name
== ' main '·
root = Node(O)
childl = Node(l)
child2 = Node(2)
root.add_child(childl)
root.add_child(child2)
childl.add_child(Node(З))
childl.add_child(Node(4))
child2.add_child(Node(5))

-

-

-

for ch in root.depth_first():
print(ch)
tD---·---································-···········.··············-············-····

• python..................•.

Глава 18. Итераторы и генераторы

# Выведет Node(O), Node(l), Node(З), Node(4), Node(2),
Node(5)

Метод depth_first() прост. Сначала он выполняет оператор yield self, а за­
тем итерирует по каждому дочернему элементу, отправляя с помощью yield
кажд1;,1й дочерний элемент, произведенный методом depth_first() дочернего
элемента (используя yield from).
Протокол итератора Python требует метод _iter_() для возврата специ­
ального объекта итератора, который реализует операцию _next__() и
использует исключение Stoplteration для уведомления о завершении ите­
рации. Однако реализация таких объектов может часто быть " грязным" де­
лом. Например, следующий код показывает альтернативную реализацию
метода depth_first(), используя связанный класс iterator:
class Node:
def _init_(self, value):
self. value = value
self. children = []
def _repr_(self):
return 'Node({!r}) '.forшat(self._value)
def add_child{self, other_node):
self._children.append(other_node)
def _iter_(self):
return iter(self._children)
def depth_:first(self):
return �•pthFirstiterator(self)
class DepthFirstiterator(object):

,,,
Обход в глубину

,,,



def _init_(self, start_node):
self._node = start_node
self. children iter = None

-

-

·················································································--

Python. Полное руководство

-

-

____________... _ ..__.__.О python

self. child iter = None
def
iter (self):
return self
def
next (self):
# Возвращэ.ет себя, если только запущен. Создает итератор дпя дочерних
# объектов
if self._children_iter is None:
self. children iter = iter(self,_node)
return self. node
# Если обрабатывается дочерний объект, возвращэ.ет его следукщ,1Й элемент
elif self. child iter:
try:
nextchild = next(self._child_iter)
return nextchild
except Stopiteration:
self. child iter = None
return next(self)
# Переход к следующему пот омку и з апуск е г о итерации
else:
self._child_iter = next(self._children_iter).depth_first()
return next(self)

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

18.5. Итерация в обратном
направлении
Для итерации в обратном направлении используйте встроенную функцию
reversed(). Например:



crt-··················································································

• python__________________ -.-

Глава 18. Итераторы и генераторы

>>> а = [1, 2, З, 4]
>>> for х in reversed(a):
print(x)
4
3
2
1

Обратная итерация работает только, если объект имеет размер, который
может быть определен или же у объекта реализован специальный метод _
reversed_(). Если для вашего объекта не выполняется ни то, ни другое, вам
нужно сначала конвертировать ваш объект в список. Например:
# Выводим файл в обратном порядке
f = open( 'file.txt' )
for line in reversed(list(f)):
print(line, end=' ')

Знайте, что превращение итерируемого в список, как показано выше, может
требовать огромного количества памяти, если итерируемое большое.
Многие программисты не понимают, что обратная итерация может быть
настроена с помощью определяемьJх пользователем классов, если они реа­
лизуют метод _reversed_(). Например:
class Countdown:
def
init (self, start):
self.start = start
# Итератор вперед
iter (self):
def
n = self.start
while n > О:
yield n
n -= 1



# Итератор назад
def
reversed (self):
n = 1
while n >> f = open( 'file. txt')
>>> lines = linehistory(f�
>>> next(lines)
Traceback (most recent call last):
File "", line 1, �n
TypeError: 'linehistory' object is not an iterator
>>>#Сначала вызываем iter(), затем запускаем итерацию
>>> it = iter(lines)
>>> next(it)
'line 1\n'
>>> next(it)
'line 2\n'
>>>

18�7. Пропуск первой части
итерируемого
Модуль itertools содержит несколько функций, которые могут исполь­
зоваться для решения этой задачи. Первой является функция itertools.
dropwhile(), которой нужно передать функцию и итерируемое. Возвращен­
ный итератор отбрасывает первые элементы в последовательности, пока
предоставленная функция не вернет true.
Чтобы проиллюстрировать использование этой функции, представим, что у
нас есть файл, который начинается серией комментариев. Например:



··································································-····--·-·······-

Python. Полное рукозодство

fi6,python

··----------------------- >>

'Ь', 'с')
'с'' 'Ь')
'а', 'с')
�с', 'а')
'а'' 'Ь')
'.Ь', 'а')
о

1_i IDLE Shell 3.92

х

file fdit She!I Q,Ьug Qptions Jl,lindow tlelp
Python 3.9.2 (taqs/v3.9.2:la79785, FеЬ 19 2021, 13:44:55) {МSС v.1928 64 Ьit {АМ
Dб4)} on win32
Туре •ье1р•, •copyriqht•, "credits" or "license ()" for more information.
>>> items = ['>> fr.om itertools 'J•.r,,f,ю.rt; permutations
>>> for р :н-; permutations {item.s) :
print{p)
('а',
{'а',
{'Ь',
{'Ь',
('с',
('с',
»>



'Ь',
'с',
'а.',
'с',
'а',
'Ь',

'с')
'Ь')
'с'}
'а'}
'Ь ')
'а_')

Генерирование перестановок

·······················································.···························-tD

Pythoп. Полное руководство

6python

• ··-· ••• ----•- •• -• -- ----• IOii

Если вы хотите получить все перестановки меньшей длины, вы можете за­
дать необязательный параметр длины. Например:
>>> for р in permutations(items, 2):
print(p)
('а', 'Ь')
('а', 'с')
('Ь'.,
('Ь''
('с''
('с''
>>>

'а')
'с')
'а')
'Ь')

Функция itertools.comhinations() используется для создания последователь­
ности комбинаций элементов, взятых при вводе. Например:
>>> from itertools import comЬinations
>>> for с in comЬinations(items, 3):
print(c)
('а', 'Ь', 'с')
>>> for с in comЬinations(items, 2):
... print(c)
('а', 'Ь')
('а', 'с')
('Ь', 'с')
>>> for с in comЬinations(items, 1):
... print(с)
('а'')

('Ь'')
('с'')

>>>

Для comhinations() не рассматривается актуальный порядок элементов.
Поэтому комбинация ('а', 'Ь') рассматривается как аналогичная ('Ь', 'а') и
не выводится.
При создании комбинаций выбранные элементы удаляются из коллекции
возможных кандидатов (то есть, если 'а' уже выбрана, то она больше не рас­
сматривается).



------------·······································································

• python_____ • ______ • ____ •••

Глава 18. Итераторы и генераторы

Функция itertools.comhinations_with_replacement() позволяет одному и тому
же элементу выбираться несколько раз. Например:
>>> for с in comЬinations_with_replacement(items, 3):

print(с)

('а''
('а''
('а''
('а''
('а''
('а''

'а'' 'а')
'а'' 'Ь')
'а', 'с')

'Ь' 'Ь')
i
'Ь' 'с')
'

'с',

'с')

с',

'с')

('Ь'' 'Ь'' 'Ь')
('Ь'' 'Ь'' 'с')
('Ь'' 'с', 'с')
('с''

>>>

1

Этот пример демонстрирует только часть всей мощи, которую вы можете
обнаружить в модуле itertools. Несмотря на то, что вы можете, конечно, за­
писать код, чтобы произвести перестановки сами, это потребует дополни­
тельных затрат времени. Да и зачем изобретать колесо заново? Когда вы
сталкиваетесь с итеративными задачами, в первую очередь обратитесь к
модулю itertools. Если ваша задача распространенная, вполне возможно,
что ее решение уже есть в itertools.



······························"·········································"·········-

ГЛАВА 19.
ДОКУМЕНТИРОВАНИЕ
ПРОЕКТА

,l4python--------------------



Глава 19. Документирование проекта

Очень часто документированию проекта уделяется мало внимания или же
вообще этим никто не занимается. Делается это по самым разным причи­
нам, среди которых можно выделить нехватку времени, финансирования,
отсутствие технических писателей в штате. Почему-то так повелось, что ни­
кто не закладывает в проект время/финансы, необходимое на разработку
документации - ни разработчик, ни заказчик.
Заказчик часто не хочет тратить дополнительные средства в надежде, что
. в случае чего есть разработчик, который все поправит. Но в жизни все ме­
няется - компании, которая разрабатывала проект, может уже и не быть на
момент необходимости внесения изменений, отношения с этой компанией
могут быть испорчены и т.д. Заказчик почему-то думает, что сторонним раз­
работчикам будет просто разобраться в коде, который был написан кем-то
другим. На практике бывает так, что даже самому разработчику несколько
месяцев спустя сложно разобраться в собственном коде и нужно время, что­
бы все вспомнить. Наличие документации позволит "вспомнить все" гораз­
до быстрее, а также разобраться в коде сторонним разработчикам, если в
этом возникнет необходимость.



···································································· -·-···· ··-·-··

...

Pyttюn. Полное руководство

·........................• python

19.1· .· Рекомендации относительно,
написания технической
документации
t·:

Поддерживать хорошую документацию довольно несложно, даже проще,
чем писать код, хотя.многие думают иначе. Документировать проект станет
проще, если придерживаться некоторых рекомендаций относительно тех­
нического письма.
Если вы никогда раньше ничего не писали, не отчаивайтесь - ведь вам нуж­
но написать не роман, а всего лишь описать, как работает ваши API
(Application Programminglnterface, АР/ - описание способов (набор клас·
сов, процедур, функций, структур или констант), которыми одна компью­
терная программа может взаимодействовать с другой программой). Грубо
говоря, составить список функций, описать назначение каждой функции и
назначение каждого передаваемого в функцию параметра.
Вкратце рекомендации выглядят так:
• Сформулируйте направления, а затем развивайте их. Первым делом
опишите список того, о чем вы хотите написать, если хотите - оглавле­
ние. Дальше уже подробно описывайте каждый из разделов.
• Помните о читателе. При написании документации помните, что ее кто­
то будет читать. Если вы пишите руководство по панели управления
(dashboard), тогда нужно ориентироваться на обычного пользователя и,
возможно, некоторые моменты нужно описать подробнее. Если вы опи­
сываете API, тогда нужно ориентироваться на технического специади­
ста.
• Краткость - сестра таланта. Не нужно писать книгу, а тем более книгу в
нескольких томах. Пишите кратко и по делу.
• Используйте шаблоны. Шаблоны помогают читателю привыкнуть к об­
щей структуре документов.
• Используйте реальные примеры кода. Вместо того, чтобы фантазиро­
вать и высказывать предположения, демонстрируйте реальные примеры
кода и объясняйте, как он работает.





-------------------------------·---·-----·----·-----·································

• python....................

Глава 19. Документирование проекта

19.1.1. СФОРМУЛИРУЙТЕ НАПРАВЛЕНИЯ, А ЗАТЕМ
РАЗВИВАЙТЕ ИХ
Невозможно написать хороший текст за один проход. Не нужно пытаться
придумать сразу же какой·то идеальный и всеобъемлющий текст. Вместо
этого набросайте идеи, а затем развивайте их. Написав пару предложений,
обязательно перечитайте их. Возможно, придется внести корректировки.
План текста обязательно должен быть. В голове все удержать невозможно кто-то позвонил, написал, вы отвлеклись и уже забыли, о чем думали.
Есть и второй подход. Излагайте все идеи на бумаге, начинайте записывать
непрерывный поток мыслей и не останавливайтесь, не обращайте внимания
ни на стиль подачи, ни на грамматически ошибки/опечатки, просто пиши­
те. Не останавливайтесь, пока не изложите все идеи. А уже после всего это
займитесь структурированием текста, его улучшением и т.д.

19.1.2. ПОМНИТЕ О ЧИТАТЕЛЕ
При написании документа или его части вы должны понимать, кто будет
вашей целевой аудиторией. Это очень важно.
Не нужно стараться написать техническую документацию так, чтобы она
была понятной даже школьнику. Так не должно быть. С техническим тек­
стом все должно быть проще: он должен предназначаться определенному
типу читателя. Придерживаясь этого правила, вы сэкономите кучу време­
ни.
Если вы пишете документацию по API, то ваш единственный тип читате­
ля - прогрс1;ммист. Ориентируйтесь на него. Если пишете документацию
по панели управления - здесь вашим читателем будет обычный средне­
статистический пользователь, об этом тоже нужно думать. Не нужно бес­
покоиться о том, что этот текст может быть прочитан начальством со сто­
роны заказчика. Ведь начальство - это такой же обычный пользов'атель,
как и все остальные.
Создайте два разных документа - один для пользователя (например,
dashboard.pdf), второй - для разработчика (api.pdf). В каждом документе
придерживайтесь собственного стиля подачи материала - так чтобы текст
был понятен целевой аудитории. Понятно, что можно написать api.pdf так,
что он станет понятен даже пользователю, но зачем? Пользователь читать



···································-·············································--

Python. Полное руководство

------------------------,ltpython


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

19.1.3. КРАТКОСТЬ-СЕСТРА ТАЛАНТА
Упрощайте все по возможности. Простой текст воспринимается проще. Пи­
шите простые и короткие предложения. Даже если вы не обладаете талан­
том писателя, ваше творчество будет проще понять другим людям. Вы ведь
пишите не роман и не сказку, а руководство по использованию API вашего
программного продукта. Не нужно, чтобы это руководство (без крайней на
то необходимости) занимало объем в 1ООО страниц.
Придерживайтесь следующих рекомендаций:
1. Пишите короткие предложения. Максимальная длина предложения 150 символов с пробелами. В среднем в одной строке листа формата А4
помещается 80-85 символов с пробелами. Так что старайтесь, чтобы ваше
предложение не занимало более двух строк.
2. Каждый абзац должен выражать свою идею и состоять из 3-4 предложе­
ний. Не нужно писать абзацы, состоящие из более чем 1О предложений.
10 предложений по 150 символов каждое - более чем достаточно для
выражения законченной идеи.
3. Не используйте разные формы времени, при написании технической до­
кументации достаточно настоящего времени.
4. Избегайте шуток, вы пишите не роман и даже не книгу по программиро­
ванию. Вы просто пишите техническую документацию. Шутки и прочий
юмор в ней смотрится глупо и неуместно.
5. Не повторяйте одни и те же слова множество раз. Используйте аббреви­
атуры. Например, вместо слова "программное обеспечение" пишите ПО.
Как правило, в первый раз аббревиатура расшифровывается (ПО (про­
граммное обеспечение) или программное обеспече�,ше (далее - ПО)),
далее используется без расшифровки. Можно также составить список
используемых аббревиатур, если их слишком много.
Плохая документация имеет одну отличительную черту - в ней невозмож­
но найти нужную информацию, даже если она там есть. Продумайте струк­
туру документа, разбивайте текст на разделы, используйте осмысленные

са-----------·...........-..-------...-...-.. ---....................................•.

ifшpython-------------------€@

Глава 19. Документирование проекта

названия заголовков. Эти tiростые правила помогут создать понятный до­
кумент, который будет просто читать. Если же всего этого вы не будете при­
держиваться, читатели будут использовать средства поиска, чтобы найти
нужную информацию. Но когда документация находится в распечатанном
виде, сделать это будет очень проблематично.

19.1.4. ИСПОЛЬЗУЙТЕ ШАБЛОНЫ
В качестве примера использования шаблонов можно привести всем извест­
ную "Википедию". Все страницы "Википедии" выглядят одинаково. Справа
находятся блоки, обобщающие информацию из документов, которые при­
надлежат к одной и той же области. Первая часть статьи обычно представ­
ляет собой оглавление со ссылками, которые ведут на якоря в этом же тек­
сте. А в конце странички всегда есть справочная информация.
Шаблоны - штука полезная. Во-первых, к ним привыкают читатели. Они
знают, что в каждом документе/разделе есть оглавление, позволяющее бы­
стро найти нужную информацию. Также они знают, что внизу документа
обязательно будет справочная информация со ссылками на полезные темы.
Пользователи привыкают к общей структуре информации и учатся читать
быстрее.
Во-вторых, шаблоны полезны и для самого технического писателя - это
быстрый старт для нового документа. Вам не нужно ничего выдумывать, вы
просто используете уже готовый шаблон.

19.1.5. ИСПОЛЬЗУЙТЕ РЕАЛЬНЫЕ ПРИМЕРЫ КОДА
Помните, что вы пишите не учебник, объясняющий, как программировать
на том языке, на котором написан ваш программный продукт, а техниче­
скую документацию ло программному продукту. Следовательно, нереали­
стичные примеры кода усложняют понимание такой документации.
Проще всего взять реальный фрагмент кода и пояснить все на нем. Рас­
смотрим небольшой пример:

•..

# Получаем e-mail пользователя из объекта user
. . . •.· ....................................................................... - ...

-

Python. Полное руководство

. _ ... __________________ .О python

uemail � user.email;
# запрашиваем количество доступных уроков из СRМ
response = requests.get("http://exarnple.crrn/alfa/alfa.
php?ernail=" + uernail + "&op=getlessons")
# Текст ответа - это и есть количеств9 µоступных уроков
lessons = response.text
# Получаем дату регистрации пользователя
join_date = user.date_joined
# Задаем граничную дату регистрации, начиная с этой даты
# все зарегистрированные по_льзователи будут считаться новыми
border_date = dateutil.parser.parse('2021-09-23
00:00:00+00:00')
# если граничная дата "старше" даты регистрации, то
пользователь· - старый
# иначе - пользователь новый и переменную new user нужно
установить в 1
if border date > user.date joined:
new user
О
else:
1
new user
# Флаг сокрытия курсов
# Если пользователь новый и количество уроков
меню выбора курса
hide courses = О
"0":
if new user == 1 and lessons
1
hide courses

О, то скрываем

Мы только что не просто привели фрагмент кода, но и в комментариях под­
р9бно объяснили, для чего он используется.

19.2. Строки документации в Python
Лучший способ поддерживать документацию в актуальном состоянии относиться к ней, как к коду. Храните в репозитарии, отслеживайте вноси­
мые изменения - так легко поддерживать актуальность документации.



----------------····--------------------,-----------------------------------------·

• python................... .

Глава 19. Документирование проекта

Используя систему управления версиями (тот же Git), вь1 с легкостью
сможете отследить все изменения, внесенные в документацию - не только
вами, но и другими членами команды.
Существуют инструменты, позволяющие создавать документацию API
прямо из комментариев, которые включены в исходный код. Это считает­
ся лучшим способом создания технической документации для библиотеки
или любого другого компонента, подразумевающего многократное исполь­
зование кода.
Что касается Python, то у него есть уникальные качества, облегчающие про­
цесс документирования. Вы можете создавать красивую и полезную доку­
ментацию прямо из кода Python. Основной для этих инструментов служат
строки документации (docstnngs).
Строки документации представляют собой специальные строковые лите­
ралы Python, предназначенные для документирования функций, методов,
классов и моделей Python. Если первое определение функции, метода,
класса или модуля является строковым литералом, то он автоматически
станет строкой документации и превратится в значение атрибута _doc_
для этой функции, метода, класса или модуля.
Строки документации ДОЛЖНЫ иметь все модули, все функции и классы,
экспортируемые модулем. Также должны иметь строки документации пу­
бличные методы (в том числе _init_).
Для описания строк документации всегда используются три двойных ка­
вычки. Например: """строка документации""". Существует две формы строк
документации: однострочная и многострочная.
Однострочные строки документации должны помещаться на одной строке,
и использоваться для очевидных случаев:
def get_user_info () :
"""Возвращает массив с информацией о пользователе""'1
global user
if user: return user

Используйте тройные кавычки, даже если документация умещается на од­
ной строке. Закрывающие кавычки размещайте на той же строке. Это
смотрится лучше.
Многострочные строки документации состоят из однострочной строки до­
кументации с последующей uустой строкой, а затем более подробным опи-



········�·-···························--··············�·-·························-

Python. Полное руководство

санием. Первая строка может быть использована автоматическими сред­
ствами индексации, поэтому важно, чтобы она находилась на одной строке,
и была отделена от остальной документации пустой строкой. Первая строка
может быть на той же строке, где и открывающие кавычки, или на следую­
щей строке. Вся документация должна иметь такой же отступ, как кавычки
на первой строке. Рассмотрим пример:
def surn(a = O.O, Ь=О.0):
"""Вычисляет сумму двух чисел
Аргументы
а - первое число (по умолчанию О.О)
Ь - второе число (по умолчанию О.О)
return а+Ь

19.3. S1зыки разметки для
документации
Внутри строк документации можно писать все, что угодно. Но желательно
использовать какой-то язык разметки для документации, что поможет вам в
дальнейшем. Неплохими вариантами являются языки разметки Markdown
и AsciiDoc. Первый очень популярен в сообществе GitHub и является наи­
более распространенным языком разметки. Также он поддерживается раз­
личными инструментами для самодокументирующихся API.
Вообще, вы можете выбрать любой язык разметки, на котором вам легко
писать и какой удобно читать в сыром виде за пределами сгенерирован­
ной документации. Неплохим вариантом также является язык разметки
reStructured Text, который используется системой документации Sphinx.

19.4. Популярные генераторы
документации

-

Наиболее популярными инструментами для создания документации в со­
обществе Python являются Sphinx и MkDoks. Сначала мы рассмотрим
Sphinx, а затем - MkDocs.

................................................................................

'

• python__________________ -�

r лава 19. ДокументированиР. проекта

19.4.1. ИСПОЛЬЗОВАНИЕ SPHINX
Sphinx (англ. SQL Phrase Index) - система полнотекстового поиска, отличи­

тельной особенностью которого является высокая скорость индексации и
поиска, а также интеграция с существующими СУБД (MySQL, PostgreSQL)
и API для распространенных языков веб-программирования.
Для установки Sphinx введите команду:
pip install sphinx

Примечание. Если команда pip недоступна, ее установить мож­
но следующей командой sudo apt install pythonЗ-pip

В Ubuntu его можно также установить командой:
sudo apt-get install pythonЗ-sphinx

Использовать инструмент не очень сложно. Первым делом, нужно создать
каталог для нового проекта и перейти в него:
mkdir prj
cd prj

Для инициализаци:и проекта необходимо выполнить команду sphinx-quick­
start.
sphinx-quickstart

Программа задаст ряд вопросов. Все настройки можно будет позже изме­

нить в файле conf.py.

> Корневой каталог документации. По умолчанию текущий каталог.
> Root path for the documentation [.]:

,

> Сделать ли раздельные папки исх. кода и готовых страниц - Да
> Separate source and build directories (y/N) [n]: у
_

-- - - ......... - ....... -.. -....... - . -.. - . - - - . - . - . - - . - . - . - .. - . •.•

... - -.. - . -. -. - . .

-

Pytho11. Полное руководство

,·•••____ •• _____________ •.• python

> Префикс для директорий с шаблонами и статическими файлами.
> Name prefix for templates and static dir [ ]:
> Название проекта. Для начала лучше вводить на лат�нице.
> Project name:
> Имя автора/авторов. Для начала лучше вводить на латинице.
> Author name(s):
> Версия проекта
> Project version:
> Номер релиза проекта
> Project release [1]:
> Расширение исходного.Файла. По умолчанию .rst.

> Source file suffix [. rst]:

> Имя мастер-документа. По умолчанию index.rst.
> Name of your master document (without suffix) [index]:
> Генерировать ePub версию документации?
> Do you want to use the epub builder (y/n) [n]:
> Автоматически вставлять docstrings из модулей
> autodoc: automatically insert docstrings from modules (y/n) [n]:
>
> doctest: automatically test code snippets in doctest Ьlocks (y/n)

[n]:

>

> intersphinx: link between Sphinx documentation of different
projects (y/n) [n]:
>

> todo: write "todo" entries that сап Ье shown or hidden оп build
(y/n) [n]:
>

> coverage: checks for documentation coverage (y/n) [n.]:
> Использовать модуль pngmath для вставки формул в формате png
> pngmath: include math, rendered as PNG images (y/n) [n]:

-

> Использовать модуль mathjax для вставки формул в формате MathJax
> mathjax: include math, rendered in the browser Ьу MathJax (y/n)
[n]: у

. - -..... -. - - . - . -.. - - . -.. -. -.......... -... -....................... - . _,......... - ..

'

.python....................

Глава 19. Документирование проекта

>
> ifconfig: conditional inclusion of content based on config values
(y/n) [n]:
>
> viewcode: include links to the source code of documented Python
objects (y/n) [n]:
> Создать Mak efile - да
> Create Makefile? (y/n) [у]:
> Сделать ли файл .bat, - нет, если у вас Linux
> Create Windows command file? (Y/n) [у]: n ()

После ответа на вопросы будут созданы файлы index.rst, conf.py, Makefile,
_build, _static, _templates:
• Makefile - содержит инструкции для генерации результирующего доку­
мента командой make.
• _build - директория, в которую будут помещены файлы в определенном
формате после того, как будет запущен процесс их генерации.
• _static - в эту директорию помещаются все файлы, не являющиеся ис­
ходным кодом (например, изображения). Позже создаются связи этих
файлов с директорией build.
• conf.py - содержит конфигурационные параметры Sphinx, включая те,
которые были выбраны при запуске sphinx-quickstart в окне терминала.
• index.rst - это корень проекта. Он соединяет документацию воедино,
если она разделена на несколько файлов.
Файл index.rst используется для объедицения всех файлов в один проект.
По сути, это простой текстовый файл. Если открыть его, то можно будет
увидеть примерно следующее:
.. 3 documentation master file , created Ьу
sphinx-quickstart on Fri Sep 24 19:44:30 2021.
You can adapt this file completely to your l_iking, but i t
should at least
contain the root ·toctree· directive,



:····························-···-················ ····-···············--·-·······-

Python. Полное руководство
Welcome to З's documentation!

__.._____________________ +

python

Contents:
toctree::
:maxdepth: 2
Indices and taЬles
* :ref:'genindex'
* :ref:'modindex'
* :ref:'search'

Содержимое index.rst не должно включать много информации и в нем обя­
зательно должна присутствовать директива .. toctree::. Чтобы включить
в проект другие файлы, необходимо прописать названия этих файлов в
toctree::. Пример:
toctree::
:maxdepth: 2
examplel
example2

Здесь мы включили в проект два файла - example1.rst и example2.rst. Обра­
тите внимание, что название файла пишется без расширения:. Также важен
отступ и пустая строка.
Для генерации документации в HTML формат необходимо выполнить в
командной строке команду make html. Аналогичным образом можно выпол­
нить генерацию в другие форматы, например, make ериЬ.
cd prj
make html

Произойдет сборка HTML, выходные файлы будут помещены в директо­
рию _build/htmlj. Перейдите в нее и откройте файл index.html в браузере .



----------------------------------------------------------------------------------·

• pyth&n . ••••. . ___. __ ••______

Глава 19. Документирование проекта

Получив совсем немного исходных данных, Sphinx сумел создать нечто
большее. Вы получите несложную компоновку, содержащую информацию
о документации проекта, раздел поиска, содержание, заметки об авторских
правах, включая имя и дату, а также нумерацию страниц. И все это сделано
автоматически!

19.4.2. ИСПОЛЬЗОВАНИЕ MKDOCS
Оба генератора документации, и Sphinx, и MkDocs написаны на Python.
Sphinx известен тем, что именно на нем сгенерирована документация для
языка Python. MkDocs - это бесплатный статический генератор сайтов,
предназначенный для создания проектной документации. Его можно ис­
пользовать для создания автономного сайта с документацией по программ­
ному продукту.
Так как MkDocs создает статические файлы, ваша документация является
легкой и простой в размещении - с использованием бесплатных сервисов,
таких как GitHub Pages и Read Тhе Docs, - или, конечно, на вап:iем соб­
ственном сервере. Все, что вам нужно - это просто скопировать получив­
шиеся файлы в каталог на веб-сервере.
Для установки инструмента введите команду:
pip install mkdocs

Рис. 19. 1. Установка mkdocs
.......·••.•··· ............ -......... - . - -...... -................................... ...



Python. Полное руководство

_,� ______________________ • python

В Ubuntu mkdocs можно установить командой:
sudo apt install mkdocs
После этого нужно создать новый проект и перейти в его каталог:
cd ~
mkdocs new prj
cd prj
Вывод второй команды будет таким:
INFO - Creating project directory: prj
INFO - Writing config file: prj /mkdocs. yml
INFO - Writing initial docs: prj/docs/index.md
После этого в каталоге prj будут созданы следующие элементы:
• файл конфигурации mkdocs.yml
• одна страница документации docs/index.md
Далее запустим dev-cepвep для просмотра вашей документации. Убедитесь,
что вы находитесь в том же каталоге, что и файл mkdocs.yml, и введите сле­
дующую команду:

...

Рис. 19.2. Запуск веб-сервера



. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . •.•

ф python____________________

Глава 19. Документирование проекта

$ mkdocs serve
INFO
Building documentation...
INFO
Cleaning site directory
[I 270921 03:50:43 server:271] Serving on
http://127.0.0.l:8000
[I 270921 03:50:43 handlers:58] Start watching changes
[I 270921 03:50:43 handlers:60) Start detecting changes

Откройте браузер и введите URL http:j/127.0.0.1:8000. Вы увидите сгенери­
рованный шаблон для вашей будущей документации.
Начало положено. Далее нужно открыть rnkdocs.yrnl и указать название ва­
шего проекта и адрес сайта проекта:
site�name: MyPrj
site_url: https://example.com/

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

• Кll!Кl·r.e.o {di••-J .Q-eawa��• IWNКs terV'I! •SQ!llhel!w-l�uu,;ssei"ff.
•-,lxllld •&ild�oocurntf1Ult\OIJS:le.

• IIIWO(' •ll'•Plirt\w№1Nswg&11Пt111d.
Project layout

Ultlo,•..i """'�!н;_.,._,,,.,,,;;.
,f'!"AI,..,;...:;..,.f>.�•· .......,;.•.;;.
docs/about.md

Для изменения темы оформления документации используется параметр
theme в файле конфигурации:
site_name: MyPrj
site_url: https://example.com/
nav:
- Home: index.md
- AЬout: about.md
theme: readthedocs

В данном случае мы выбрали тему оформления readthedocs. Получить до­
полнительные темы можно на сайте:

https:j/jamstackthemes.dev/ssg/mkdocs/
После того, как вы добавили дополнительные страницы, изменили параме­
тры проекта и выбрали новую тему оформления, нужно изменить favicon
вашего проекта. Для этого внутри каталога docs создайте каталог img и до­
бавьте в него файл favicon.ico. Mkdocs автоматически будет использовать
этот файл в качестве favicon.
Когда результат в браузере по адресу 127.0.0.1:8000 вас будет устраивать,
настанет время для сборки сайта с документацией. Перейдите в каталог prj
и введите команду:
mkdocs build



--------·-·-·-·-··---·-·-·------------·--------··-·······-······-·-···--···-···-···

• python____________________

Глава 19. Документирование проекта

Внутри каталога проекта будет создан подкаталог site, в котором будет на­
ходиться НТМL-версия вашей документации. Содержимое этого каталога
можно опубликовать в Интернете , или поставлять вместе с вашим
программным продуктом.
Нужно отметить, что представленные генераторы документации, которые
хоть и рассмотрены в контексте Python, можно использовать для создания
документации по любому программному продукту, независимо от языка
программирования, на котором он написан.

https.j/www.mkdocs.org/getting-started/



·----------------------------------------------------·--------·-···--···-·-·-···---tD




ГЛАВА 20.
МЕТА­
ПРОГРАММИРОВАНИЕ

О python___________________ _

Глава 20. Метапрограммирование

Метаклассы - это магия, о которой 99% пользователей не стоит
даже задумываться. Если вам интересно, нужны ли они вам - тогда
точно нет. Люди, которым они на самом деле нужны, знают, зачем, и
что с ними делать.
- Гуру Python Тiт Peters

20. 1 . Введение в
метапрограммирование
Метапроrраммирование - подход к написанию программ, которые можно
обрабатывать как данные, что позволяет им просматривать, �оздавать/из­
менять себя во время работы. Другими словами, мы можем написать про­
грамму, которая будет изменять сама себя.
В основном, метаклассы используются для создания API. Типичным при­
мером является Django ORM. Можно написать что-то вроде этого:
class Person(models.Model):
name = models.CharField(max_length=ЗO)
age = models.IntegerField()



·--------------····--···----··-··-·-····-·-·-·--·---·-·-·-·--·-·-·-·-·-··-···-·-··-

Python. Полное руководство

Apythc»n

----------------· ------- 15

Но если написать так:
user = Person(name='den', аgе = 'ЗВ')
print(user.age)

Он не вернет объект IntegerField. Он вернет код int и даже может взять его
непосредственно из базы данных.
Существует два основных подхода к метапрограммированию:
• Первый концентрируется на возможности языка анализировать соб­
ственные элементы - функции, классы, типы, а также на возможности
создавать или изменять их динамически, то есть в процессе выполнения
программы. В Python есть множество инструментов для этого. Эти ин­
струменты используются различными IDE для анализа кода в режиме
реального времени. Также существуют специальные методы юiассов, по­
зволяющие вмешиваться в процедуру создания экземпляра класса '- ме­
таклассы. Благодаря метаклассам программист может полностью пере­
делать реализацию ООП в Python.
• Второй подход заключается в возможности программиста работать не­
посредственно с кодом - как в простом виде (обычный текст), так и в
форме абстрактного синтаксического дерева (AST, Abstract Syntax,
Tree). Такой подход более сложный в реализации, зато дает возможность
делать очень сложные и эффектные штуки, такие как расширение син­
таксиса самого Python или даже создание собственного языка програм­
мирования.
Далее мы рассмотрим декораторы в контексте метапрограммирования.

20.2. Декораторы
Декораторы - это синтаксический сахар, работающий по простой схеме:
def decorated_functioin():
pass
decorated function
some decorator(decorated_function)

---

Данная форма показывает, что именно делает декоратор: он принимает объ­
ект функции и изменяет его во время выполнения. Другими словами, новая

- -- -.. - . . - - . ...... -....... - . - - -- - . -..... -.. -........... - . -........- . -- -... -- . - -.
•.•

• python___________________ _

Глава 20. Метапрограммирование

функция создается на основе предыдущей версии функции с тем же име­
нем. Такое декорирование может быть очень сложной операцией, которая
выполняет некоторый самоанализ кода (а не такой простой случай, как мы
рассмотрели). Однако, все это позволяет использовать декораторы в каче­
стве инструмента метапрограммирования.
Основные принципы декораторов довольно просты, и это очень хорошо, по­
скольку остальные способы метапрограммирования в Python гораздо слож­
нее и приводят к существенному усложнению кода.
В Python программист может использовать декораторы класса. По синтак­
сису и реализации они аналогичны декораторам функций.
Декоратор класса призван модифицировать поведение и содержание клас­
са, не изменяя его исходный код. Похоже на наследование, но есть отличия:
1. Декоратор класса имеет более глубокие возможности по влиянию на
класс, он может удалять, добавлять, менять, переименовывать атрибуты
и методы класса. Он может возвращать совершенно другой класс.
2. Старый класс "затирается" и не может быть использован, как базовый .
класс при полиморфизме
3. Декорировать можно любой класс одним и тем же универсальным де­
коратором, а при наследовании - мы ограничены иерархией классов и
должны считаться с интерфейсами базовых классов.
4. Презираются все принципы и ограничения ООП (из-за пунктов 1-3).
Декораторы классов полезны, чтобы внедриться в класс и массово воздей­
ствовать на его методы и атрибуты. Далее мы создадим декоратор, который
будет измерять время выполнения каждого метода класса. При этом сам
кл>> ClassWithLittleBitLongerLongName(). class

>>> ClassWithLittleBitLongerLongName(). doc
'Подкласс, представляющий поведение декоратора'

20.3. Метаклассы
20.3.1. ВВЕДЕНИЕ В МЕТАКЛАССЫ
Метаклассы - одна из самых трудных концепций в l'ython, поэтому многие
программисты избегают ее использования. Давайте разберемся, что такое
метакласс и как его можно использовать для метапрограммирования.
Метакласс - это тип (класс), который определяет другие типы (классы).
Грубо говоря, это "фича", создающая новые классы. Классы, определяющие
экземпляры объектов, также являются объектами. А поэтому у них есть со­
ответствующий класс. Основным типом каждого класса по умолчанию яв­
ляется встроенный класс type.

-

Рассмотрим общий синтаксис метаклассов. Мы можем использовать вызов
встроенного класса type() в качестве динамического объявления класса.
Например, мы можем определить класс вызовом type():

--------- . -. ---.. -. - . -- - - . -. -. - ................... - ................--..--...-...

'

О python_____ ... _.. _ .. _.... _

Глава 20. Метапрограммирование

def method(self):
return 1
MyCls

=

type('MyCls', (object,), {'method': method})

Все это· эквивалентно обычному определению класса ключевым словом

Class:

class MyCls:
def method(self):
return 1

У каждого класса, создаваемого таким образом, есть метакласс type. Такое
поведение по умолчанию можно изменить, если добавить именованный ар­
гумент metaclass:
class ClMeta(metaclass=type):
pas.s

Здесь значение, предоставляемое в качестве атрибута metaclass, -:- еще один
объект класса, но может быть любым другим вызываемым объектом, кото­
рый принимает те же аргументы, что и класс type и возвращает другой объ­
ект класса.
Рассмотрим аргументы type:
• пате - имя класса, которое будет храниться в атрибуте _name _;
• bases - список родительских классов, которые станут атрибутом
base_,
• dict - словарь, который будет являться пространством имен для тела
класса (становится атрибутом _
' dict_').
Почему имя type() пишется cu строчной буквы? Скорее всего, это вопрос
соответствия со str - классом, который отвечает за создание строк, и int классом, создающим целочисленные объекты. type - это просто класс, соз­
дающий объекты класса. Проверить можно с помощью атрибута_сlаss_.
Все, что вы видите в Python - объекты. В том числе и строки, числа, классы
и функции. Все это объекты, и все они были созданы из класса:

·············�····································································•

Pyttюn. Полное руководство

---------------- ------- .

tfl.ipython
Jt''

>>> name = 'den'
>>> name. class

>>> age = 38
>>> age. class

>>> def foo(): pass
>>> foo. class

>>> class Cls(object): pass
>>> с = cls()
>>> с. class


А теперь самое интересное - что в атрибуте _class_ у самого _cla�s_?
>>> name. class

>>> age. class

>>> foo. class

>>> с. class


class
class
class
class

Итак, метакласс создает объекты класса. Это можно назвать "фабрикой
классов". type - встроенный метакласс, который использует Python.
Программист может также создать свой собственный метакласс,
Вернемся к атрибуту _metaclass_. При определении класса вы можете
указать этот атрибут:
class Foo(object):
metaclass
[
]

...

something...

После этого Python будет использовать метакласс для создания класса Foo.

Если написать class Foo(object), объект класса Foo не сразу создастся в па­
мяти - Python будет искать _metaclass_. Как только атрибут будет най­
ден, он будет использоваться для создания класса Foo. В том случае, если
этого не произойдет, Python будет использовать type для создания класса.

...

.. -.........--........ -� ....... -... -........--...........--..... -..-- . - . -....-- -

•..

• python____________________

Глава 20. Метапрограммирование

Рассмотрим пример:
class Foo(Bar):
pass

При таком объявлении Python проверит, есть ли атрибут _metaclass_ у
класса Foo? Если он есть, создаст в памяти объект класса с именем Foo с
использованием того, что находится в _metaclass_. Если _metaclass_
не найден, то Python будет искать его на уровне модуля и после этого по­
вторит процедуру. В случае если он вообще не может найти какой-либо_
metaclass_, Python использует собственный метакласс type, чтобы создать
объект класса.
Наверное, вас интересует вопрос: что можно добавить в_metaclass_. Да
практически все, что может создавать классы. Как минимум type или его
подклассы, а также все, что использует type.

20.3.2. ПОЛЬЗОВАТЕЛЬСКИЕ МЕТАКЛАССЫ
Основная цель метакласса - автоматическое изменение класса во время его
создания. Обычно это делается для API, когда нужно создать классы, соот­
ветствующие текущему контексту. Например, нам нужно, чтобы все клас­
сы в модуле должны иметь свои атрибуты, и они должны быть записаны в
верхнем регистре. Чтобы решить эту задачу, можно задать_metaclass_ на
уровне модуля.
Просто нужно сообщитьметаклассу, что все атрибуты должны быть в верх­
нем регистре. _metaclass_ действительно может быть любым вызывае­
мым объектом, он не обязательно должен быть формальным классом.

Начнем с очень простого примера с использованием функции:
# метаклассу автоматически передается тот же аргумент,
# который вы обычно передаете в 'type'
def upper_attr(future_class_name, future class_parents,
future_class_attr):

"""



Возвращает объект класса со списком его атрибутов
в верхнем регистре

·------------------------------------------------------------------------------------

Python. Полное руководство

________________________ф python

# выбирает любой атрибу т, который не начинается с " " и
переводит его
# в верх ний регистр
uppercase_attr = {}
for name, val in future class attr.items():
i f not name.startswi th(' '):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
# type создаст класс
return type(future class_name, future class_parents,
uppercase attr)
metaclass
модуле

upper_attr # это повлияет на всеклассы в

# Глобальный metaclass не будет работать с "объектом", но мы можем
# определить здесь _metaclass_, чтобы воздействовать1только на этот
# класс, и он будет работать с дочерними элементами "объекта"
class Foo():
bar = 'Ьiр'
print(hasattr(Foo, 'bar'))
# Выводит: False
print(hasattr(Foo, 'BAR'))
# Выводит: True
f = Foo()
print(f .BAR)
# Выводит: 'Ьiр'

Теперь проделаем то же самое, но с использованием метакласса:
# помните, что 'type' - это такой же класс, как 'str' и 'int'
# поэтому вы можете наследовать его
class UpperAttrMetaclass(type):
new
- это метод, который вызывается ДО
init
#
# он создает объект и возвращает его
# а метод init просто инициализирует объект, переданный как параметр
# вам нужно редКо использовать _new_, кроме случаев, когда вы хотите
# контролировать создание объекта
# В этом примере создаваемым объектом является класс и мы хотим
new
# кастомизировать его, поэтому мы переопределяем



.....................................................................................

• python__________ , _________

Глава 20. Метапроrраммирование

# Вы мож ете сделать некотору работу в
i nit
если вам это нужно
# Некоторые особе продвинутые программисты переопределяюr метод call
# но мы не будем этого делать
def _ new_(�pperattr_metaclass, future_class_name,
future class_parents, future_class_attr):
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith(' '):
uppercase attr[name.upper()] = val
else:uppercase_attr[name] = val
return type(future_class_name, future_class_parents, uppercase_attr)

Это нельзя назвать объектно-ориентированным программированием,
поскольку мы type не переопределяем, а вызываем напрямую. Давайте из­
меним это:
class UpperAttrMetaclass(type):
def

new

(upperattr_metaclass, future_class_name,
future class_parents, future class attr):

uppercase_attr = {)
for name, val in future_class attr.items():
if not name.startswith(' '):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
# Повторное использование метода type. new
# Никакой магии, это базовое ООП
return type. new (upperattr_пetaclass, future_class_папе,
future class_parents, uppercase_attr)

Наверное, вы заметили аргумент upperattr_metaclass. Этот метод первым
аргументом получает текущий экземпляр. Точно так же, как и self для
обычных методов. Имена аргументов такие длинные для наглядности, но
для self все имена имеют названия обычной длины. Поэтому реальный ме­
такласс будет выглядеть так:
class UpperAttrMetaclass(type):



. - - . -.... - . -............................................. -.......·- ................ ...

Python. Полное руководство
def

new

. �_________ ..__.._.__...О

python

(cls, clsname, bases, dct):

uppercase_attr = {}
for name, val in dct.�tems():
if not name.startswith(' '):
uppercase�attr[name.upper()]
else:
uppercase_attr[name] = val
return type.

val

new (cls, clsname, bases, uppercase_attr)

Используя метод super, можно сделать код более "чистым":
class UpperAttrMetaclass(type):
def

new

(cls, clsname, bases, dct):

uppercase_attr � {}
for name, val in dct.items():
if not name.startswith(' '):
uppercase_attr[name.upper()]
else:
uppercase_attr[name] = val
return super(UpperAttrMetaclass, cls).
clsname, bases, uppercase_attr)

val

new

(cls,

Собственно, это все, что вам нужно знать о метаклассах. Причина слож­
ности кода, который использует метаклассы, даже не в самих метаклассах.
Код становится сложным, поскольку обычно метаклассы используют для
сложных задач, основанных на наследовании и манипуляции такими пере­
менными, как _dict_. Также посредством метаклассов вы можете:
• перехватить создание класса
• изменить класс
• вернуть измененный класс

20.3.3. ИСПОЛЬЗОВАНИЕ МЕТАКЛАССОВ ВМЕСТО
ФУНКЦИЙ
Спрашивается, зачем использовать классы метаклассов вместо функций? А
тому есть несколько причин:



tD-···········································-························�·············

• python____________________

Глава 20. Метапроr раммирование

• Более понятные идентификаторы. Например, когда вы читаете
UpperAttrMetaclass(type), вы понимаете, что будет дальше.
• Можно использовать ООП. Метакласс может наследоваться от мета­
класса, переопределять родительские методы.
• Можно лучше структурировать свой код. Вряд ли вы будете использо­
вать метаклассы для чего-то простого. Обычно это более сложные зада­
чи. Возможность создавать несколько методов и группировать их в од­
ном классе очень полезна, чтобы сделать код более удобным для чтения.
• Можете использовать _new_, _init_·_ и _call_. Это открывает про­
стор для творчества. Обычно все это можно сделать в _new_, но неко­
торым программистам просто удобнее использовать _init_.

20.4. Генерация кода
Как уже было отмечено в начале этой главы, динамическая генерация кода
- самый сложный способ метапрограммирования. Python предоставля­
ет инструменты, позволяющие создавать и выполнять код, а также вносить
изменения в уже откомпилированные части кода. Все это открывает прак­
тически безграничные возможности метапрограммирования. К сожалению,
все это настолько сложные материи, что данному подходу можно посвятить
отдельную книгу. Можете считать данный раздел как указатель направле­
ния. Мы укажем путь, по которому вы сможете дальше развиваться само­
стоятельно, если это вам нужно.
Python содержит три встроенные функции, позволяющие вручную выпол­
нить, вычислить и откомпилировать произвольный код Python - ехес, eval,
compile.
Сигнатура функции ехес() выглядит так:
exec(object, global, locals)
Данная функция позволяет выполнить код Python. Элемент object должен
быть объектом кода (см. функцию compile) или же строкой, представляю­
щим один оператор или последовательность нескольких. Аргументы global
и local - это глобальные и локальные пространства имен для исполняемо­
го кода, которые являются необязательными. Если они не указаны, то код
будет выполнен в текущем пространстве. Если указаны, то global должен



·--··:····················-·····················-·-···-··-··················-·····-tD

Python. Полное руководство

........................Apyth�n
""

быть словарем, а local может быть любым объектом отображения, он всегда
возвращает None.
Сигнатура функции eval выглядит так:
eval(expression, global, locals)

Данная функция используется для вычисления выражения и возвращения
его значения. Она похожа на ехес(), но expression - это одно выражение, а
не большой кусок кода. Функция возвращает значение вычисленного вы­
ражения.
Сигнатура функции compile такая:
compile(source, filename, mode)

Компилирует источник в объект кода или АSТ. Исходный код предостав­
ляется в качестве строкового значения в аргументе source. Здесь filename это файл, из которого читается код. Если связанного файла нет (например,
если он был создан динамически), обычно используется значение .
Режим - ехес ( последовательность операторов), eval ( одно выражение) или
single (один интерактивный оператор, например, в интерактивной сессии
Python).
Дегче всего начать работу с функциями ехес() и eval(), поскольку функции
работают со строками. Если вы уже программировали на Python, то навер­
няка сможете сформировать рабочий исходный код из программы.
Наиболее полезная функция в контексте метапрограммирования - это
функция ехес(), поскольку она позволяет выполнить любую последова­
тельность операторов Python. Однако при работе с этой функцией нужно
быть осторожным. Вас должно беспокоить словосочетание "любую после­
довательность". Не нужно бояться сбоя Python - это не самое страшное.
Самое страшное - это возможные проблемы с безопасностью вашего при­
ложения, что может вам очень дорого стоить. Другими словами, функции
ехес() и eval() могут сделать ваше приложение уязвимым, поэтому нужно
с особой осторожностью относиться к тому, что вы передаете на вход этим
функциями. Одно дело, если код генерируете вы, другое дело, если его вво­
дит пользователь или он поступает откуда-то извне (база данных, внешний
файл и т.д.).




Cilt----------------------······------------------------------------------------;·····

О python....................

Глава 20. Метапрограммирование

Даже если вы 100% доверяете входным данным (например, пишете при·
ложение исключительно для себя, и входные данные будете формировать
исключительно вы), вы должны понимать, что результаты работы вашей
программы могут быть весьма неожиданными.
Первое, с чем вам придется столкнуться - это производительность. Перед
тем, как поговорить о ней, давайте разберемся, что делает Python, если вы
импортируете модуль (importfoo):
1. Он выполняет поиск модуля. Это происходит при просмотре информа­
ции из sys.path разными способами. Есть встроенная логика импорта,
есть ловушки импорта и, в целом, в этот процесс задействовано довольно
много магии, о которой мы сейчас не будем говорить.
2. После того, как модуль найден, Python, в зависимости от того, найден
откомпилированный код (.рус) или исходный (.ру), он производит неко­
торую работу. Если доступен байт-код и контрольная сумма соответству­
ет текущей версии интерпретатора, временная метка файла байт-кода
новее или равна исходной версии, он его загружает. Если байт-код от­
сутствует (есть только .ру-файл) или же устарел, он загрузит исходный
файл и откомпилирует его в байт-код. Для этого он проверяет магиче­
. ские комментарии в заголовке файла на предмет настроек кодирования
и декодирует в соответствии с этими настройками. Он также проверит,
существует ли специальный комментарий ширины табуляции для обра­
ботки табуляции как чего-то другого, кроме 8 символов, если это необ­
ходимо. Некоторые хуки импорта затем будут генерировать файлы .рус
или сохранять байт-код в другом месте (__pycache_) в зависимости от
версии и реализации Python.
3. Интерпретатор Python создает новый объект модуля (вы можете сделать
это самостоятельно, вызвав imp.new_module или создав экземпляр types.
ModuleType - это эквивалентно) с собственным именем.
4. Если модуль был загружен из файла, устанавливается ключ _file_.
Система импорта также будет следить за тем, чтобы __package_ и _
path_ были установлены правильно, если пакеты задействованы до
выполнения кода. Хуки импорта также устанавливают переменную _
loader
5. Интерпретатор Python выполняет байт-код в контексте словаря модуля.
Таким образом, локальные и глобальные фреймы для исполняемого кода
являются атрибутом _dict_ этого модуля.
6. Модуль вставляется в sys.modules.



•······•··•·-···•-•с•·••··································-·-·····················-

Python. Полное руководство

________________________• python

Ни один из в:ышеперечисленных шагов никогда не передавал строку клю­
чевому слову или функции ехес. Это, очевидно, правда, потому что все эти
действия происходят глубоко внутри интерпретатора Python, если вы не
используете ловушку импорта, написанную на Python. Но даже если интер­
претатор Python был написан на Python; он никогда не п�редал бы строку в
функцию ехес. Итак, что бы вы хотели сделать, если хотите сами преобразо­
вать эту строку в байт-код? Вы бы использовали встроенную компиляцию:
>>> code = compile('a
>>> ехес code
>>> print а
3

1 + 2', '', 'ехес')

Как видите, ехес тоже успешно выполняет байт-код (не обязательно переда­
вать строку). Поскольку переменная кода на самом деле является объектом
типа code, а не строкой. Второй аргумент для компиляции - подсказка име­
ни файла. Если мы компилируем из реальной строки, мы должны указать
значение, заключенное в угловые скобки, потому что это то, что будет делать
Python. и - общие значения. Если у вас есть файл, укажи­
те здесь фактическое имя файла. Последний параметр может быть одним
из "ехес", "eval" или "single". Первый - это то, что использует ехес, второй
- то, что использует функция eval. Разница в том, что первый может содер­
жать операторы, второй - только выражения. 'single' - это разновидность
гибридного режима, который бесполезен ни для чего, кроме интерактивных
оболочек. Он существует исключительно для реализации таких вещей, как
интерактивная оболочка Python, и очень ограничен в использовании.
Однако здесь мы уже использовали функцию, которую вы никогда и никог­
да не должны использовать: выполнение кода в пространстве имен вызыва­
ющего кода. Что делать вместо этого? Выполнить в новой среде:
>>>
>>>
>>>
>>>
3

code = compile('a
ns = {}
ехес code in ns
print ns['a']



1 + 2', '', 'ехес')



------------------------------------------------------------------------------------·

• python •...................

Глава 20. Метапрограммирование

Зачем так делать? Более понятное средство для начинающих, поскольку
ехес без словаря должен обойти некоторые детали реализации в интерпре­
таторе. Мы поговорим об этом позже.
Примечание. На данный момент: если вы хотите использовать
ехес и планируете выполнять этот код более одного раза, убе­
дитесь, что вы сначала скомпилировали его в байт-код, а затем
выполняете только этот байт-код, и только в новом словаре в
качестве пространства имен.

Однако в Python 3 оператор ехес ... in исчез, и вместо этого вы можете ис­
пользовать новую функцию ехес, которая принимает глобальные и локаль­
ные словари в качестве параметров.
А вот теперь мы можем поговорить о производительности. Насколько бы стрее выполняется байт-код по сравнению с созданием байт-кода и его вы­
полнением:
$ python -mtimeit -s 'code = "а = 2; Ь = 3; с
code'
10000 loops, .best of 3: 22.7 usec per loop

=

$ python -mtimeit -s 'code = compile("a = 2; Ь
Ь",
"", "ехес")' 'ехес code'
10000�0 loops, best of 3: 0.765 usec per loop

а * Ь"' 'ехес

3; с

а *

В 32 (!) раза быстрее даже на таком простом примере. И чем больше у вас
кода, тем хуже становится ситуация. Почему так происходит? Поскольку
синтаксический анализ кода Python и преобразование его в байт-код
дорогостоящая операция по сравнению с оценкой байт-кода.
Хорошо, урок усвоен. Сначала компиляция, а затем выполнение уже отком­
пилированного кода. Но что еще нужно учитывать при использовании ехес?
Следующее, что вы должны помнить, это огромная разница между глобаль­
ной и локальной областью видимости. Хотя и глобальная, и локальная об­
ласть видимости используют словари в качестве хранилища данных,
последняя на самом деле нет. Локальные переменные в Python просто бе­
рутся из локального словаря фрейма и помещаются туда по мере необхо-



··················································································-

Python. Полное руководство

,,.......................• python

димости. Для всех вычислений, которые происходят между ними, словарь
никогда не используется. Вы можете быстро убедиться в этом сами.
Выполните в интерпретаторе Python следующее:
>>> а = 42
>>> locals () ['а']
>>> а
23

23

Работает как положено. Почему? Потому что интерактивная оболочка
Python выполняет код как часть глобального пространства имен, как и лю­
бой код вне функций·или объявлений классов. Локальная область видимо­
сти - это глобальная область:
>>> globals() is locals()
True

Что произойдет, если мы попытаемся сделать то же самое, но на уровне
функции:
>>> def foo () :
а = 42
locals() ['а']
return а

23

>>> foo ()
42

Совсем не то, что мы ожидали, правда? Но это еще раз красочно демонстри­
рует, что локальные переменные - это не совсем локальные, по крайней
мере, в контексте программы, а не функции. Об этом нужно помнить просто
при программиро�ании, не говоря уже об использовании exec/eval.
А что можно сказать о производительности кода, выполняемого в глобаль­
ной области по сравнению с кодом, который выполняется в локальной об­
ласти? Это намного сложнее измерить, потому что модуль timeit по умол­
чанию не позволяет нам выполнять код в глобальной области видимости.
Поэтому нам нужно будет написать небольшой вспомогательный модуль,
который эмулирует это:
'

.

.....................................................................................

Глава 20. Метапрограммирование

• python____________________

code_global = compile( 1
sum = О
for х in xrange(S00·000):
sum += х
1
' 1 1
1, 1 ехес 1)
,
code_local = compile( 1 ' 1
def f():
sum = О
for х in xrange(S00000):
sum += х
1
1 11
1 , 1 ехес 1 )
,
' 1

def test_global():
ехес code_global in { }
def test local():
ns = {}
ехес code local in ns
ns [ 1f 1 ] ()

Здесь мы дважды компилируем один и тот же алгоритм в строку. Один раз
напрямую глобально, один раз - завернутый в функцию, Получается, что у
нас есть д1,3е функции. Первая выполняет этот код в пустом словаре, вторая
выполняет код в новом словаре; а затем вызывает объявленную функцию.
А теперь мы можем использовать timeit для вычисления нашей скорости:
$ python -mtimeit -s 1from execcompile import test_global as t'
10 loops, best of 3: 67.7 msec per loop

1

t()

1

$ python -mtimeit -s 'from execcompile import test local as t 1 't() 1
100 loops, best of 3: 23.3 msec per loop

Снова мы получили прирост производительности, правда, не такой ощути­
мый, как в прошлый раз. Однако, мы используем всего 100 циклов, при этом
прирост составил почти 200%, то есть мы стали в три раза быстрее, и один
цикл у нас выполняется 23.3 секунды против 67.7 секунд.
Почему так получается? Это связано с тем, что быстрые локальные пере­
менные быстрее словарей. В локальной области видимости Python отсле­
живает имена переменных, о которых он знает. Каждой из этих переменных



········--·-······-·····························--·-······························-

Pytho11. Полное руководство

........................О python

присваивается номер (индекс). Этот индекс используется в массиве объ­
ектов Python вместо словаря. Он вервется к словарю только в том случае,
если это необходимо (в целях отладки, в случае использования ехес и т.д.).
Несмотря на то, что ехес все еще существует в Python 3 (как функция), вы
больше не можете переопределять переменные в локальной области. Ком­
пилятор Python не проверяет, используется ли встроенная функция ехес, и
из-за этого не будет неоптимизировать область видимости.
Все вышеперечисленные знания полезно знать, если вы планируете ис­
пользовать интерпретатор Python для интерпретации вашего собственного
языка путем генерации кода Python и компиляции его в байт-код. Так, на­
пример, работают механизмы шаблонов, такие как Mako, Jinja2 или Genshi.
Однако, большинство людей используют оператор ехес для выполнения
реального кода Python из разных мест. Очень популярный кейс - выпол­
нение файлов конфигурации как кода Python. Так, например, делает Flask.
Обычно это вполне нормально, потому что вы не ожидаете, что ваш файл
конфигурации будет местом, будет реализован реальный код. Однако есть
люди, которые используют ехес для загрузки реального кода Python, объ­
являющего функции и классы. Это очень популярный подход в некоторых
системах плагинов и фреймворке web2py.
Почему это не очень хорошая идея? Да потому что она ломает некоторые
негласные соглашения относительно кода Python, а именно:
1. Классы и функции принадлежат модулю. Это основное правило спра­
ведливо для всех функций и классов, импортированных из обычных моду­
лей:
>>> from xml.sax.saxutils import quoteattr
>>> quoteattr. module
'xml.sax.saxutils'

Почему это важно? Посмотрим, как работает Pickle:
>>> pickle.loads(pickle.dumps(quoteattr))

>>> quoteattr._module
= 'fake'
>>> pickle.loads(pickle.dumps(quoteattr))
Traceback (most recent call last):



................. -·--................................................................ .


+

python____________________

Глава 20. Метапроrраммирование

pickle.PicklingError: Can't pickle quoteattr: it's not found
as fake.quoteattr

Если вы используете ехес для выполнения кода Python, будьте готовы

к

тому, что некоторые модули, такие как pickle, inspect, pkgutil, pydoc и, воз­
можно, некоторые другие, которые зависят от них, не будут работать долж­
ным образом.
2. В Python встроен циклический сборщик мусора, классы могут иметь де­
структоры, а завершение работы интерпретатора приводит к прерыванию
циклов. Что это значит? CPython внутренне использует рефсчет. Один из
многих недостатков подсчета ссылок заключается в том, что он не может
обнаруживать циклические зависимости между объектами. Таким образом,
в какой-то момент Python представил циклический сборщик мусора.
Однако Python также позволяет использовать деструкторы для объектов.
Однако деструкторы означают, что циклический сборщик мусора пропу­
стит эти объекты, потому что он не знает, в каком порядке он должен уда­
лить эти объекты.
Теперь давайте посмотрим на невинный пример:
class Foo(object):
del (self):
def
print 'Deleted'
foo = Foo()

Давайте выполним этот файл:
$ python test.py
Deleted

Выглядит нормально. Теперь выполним его же через ехес:
>>>
>>>
>>>
>>>
>>>
27



execfile('test.py', {})
execfile('test.py', {})
execfile('test.py', {})
import gc
gc. collect()

·····················-·········-··················································-

'.-....•.... � ..•...........• python

Python. Полное руководство

Он что-то почистил, но он никогда бы не очистил наш экземпляр Foo. Что
же происходит? Происходит неявный цикл между foo и самой функцией
_del_. Функция знает область видимости, в которой она была создана, и
из экземпляра _del_ ->global scope -> foo, у нее есть хороший цикл.
Теперь, когда мы знаем_ причину, почему этого не происходит, если у вас
есть модуль? Причина в том, что Python выполняет некоторый трюк при
закрытии модуля. Он переопределит все глобальные значения, которые не
начинаются с подчеркивания с None. Мы можем легко убедиться в этом,
если введем значение foo вместо Deleted:
class Foo(object):
def
del (self):
print foo
foo = Foo()

Что получим в итоге? Конечно, None:
$ python test.py
None

Поэтому при использовании ехес() и компании нужно быть предельно
осторожным, поскольку это может стать причиной утечек памяти.
3. Срок службы объектов. Глобальное пространство имен сохраняется
с момента его импорта до момента завершения работы интерпретатора. С
ехес вы, как пользователь, больше не знаете, когда это произойдет. Это мог­
ло случиться раньше в случайном месте. wеЫру здесь является частым на­
рушителем. В wеЫру волшебно исполняемое пространство имен приходит
и уходит с каждым запросом, что является очень неожиданным поведением
для .любого опытного разработчика Python.
Наконец, в завершении этой главы поговорим о РНР и Python. Многие
опытные неб-разработчики знакомы с обоими этими языками. Помните,
что Python - это не РНР. Не пытайтесь обойти идиомы Python, потому что
какой-то другой язык (наш любимый РНР) делает это иначе. Пространства
имен находятся в Python по какьй-то причине, и то, что он дает вам ин­
струмент ехес, не означает, что вы должны использовать этот инструмент.
Язык С дает вам setjmp и longjmp, но вы будете очень осторожны при их
использовании. Комбинация ехес и compile - мощный инструмент для всех,
кто хочет реализовать специфичный для предметной области язык поверх




------------------------------------------------------·-----·················-·-·-·

.python................... .

Глава 20. Метапрограммирование

Python, или для разработчиков, заинтересованных в расширении (а не об­
ходе) системы импорта Python.
Однако web2py и его использование execfile() - не единственные наруши­
тели в веб-сообществе Python. Werkzeug также изрядно злоупотребляет
соглащениями Python. Система импорта по требованию вызывает больше
проблем, чем решает, и в настоящее время разработчики Python от нее от­
казываются (несмотря на все свое нежелание этого делать).
Django также злоупотреблял внутренними компонентами Python. Она ге­
нерировала код Python на лету и полностью меняла семантику (до такой
степени, что импорт исчезал без предупреждения!). Разработчики Django
тоже усвоили урок и исправили эту проблему. То же самое касается и web.
ру, который злоупотреблял оператором print для· записи во внутренний
локальный буфер потока, который затем был отправлен в качестве ответа
браузеру. Также кое-что, что оказалось плохой идеей, было впоследствии
удалено.
Несмотря на все возможности, открываемые функцией ехес(), мы призыва­
ем отказаться от ее использования в пользу обычных модулей Python. Если
разработчик Python начнет свое путешествие в запутанном мире непра­
вильно выполненных модулей Python, он будет, как минимум, сбит с толку,
когда продолжит свое путешествие в другой среде Python. Наличие разной
семантики в разных фреймворках/модулях/библиотеках очень вредно для
Python как среды выполнения, и как языка.

,. ....--------------.-------... --_. ---.... ------------. -. ---...-................... tD

ГЛАВА 21.

КОНТРОЛЬ КОДА

О python.. ..................

Глава 21. Контроль кода

В современном мире невозможно обойтись без системы управления верси­
ями (она же система контроля версий). Программные продукты становятся
все сложнее и сложнее, над ними работает целая команда и управлять та­
ким проектом без системь1 контроля версий практически невозможно. Даже
если вы - программист-одиночка, система управления версиями так же бу­
дет полезна, ведь порой очень сложно вспомнить, какие изменения вноси­
лись в код неделю назад, не говоря уже про более длительные периоды. А
в случае с командной работой система контроля кода подскажет не только,
что и когда изменялось, но и кто вносил те или иные изменения.

21.1. Введение в системы контроля
версиями
Системы управления версиями ( Version Control Systems, VCS) предостав­
ляют возможность общей работы над любыми файлами, но все их преиму­
щества можно получить только при работе с текстовыми файлами. И при
этом совершенно не важно, на каком языке вы программируете, вообще не

•. ...... .... .......... .. .------.-...------.--.... ---.. -... -.-.-. -........ -........--tD

Python. Полное руководство

·········---------------

il,ipython
%6

важно, программируете ли вы или пишете роман. Главное, чтобы основным
типом обрабатываемой информации был текст.
Системы контроля версиями (СКБ) бывают централизованными и рас­
пределенными. Централизованная СКБ представляет собой один сервер
с файлами, позволяющий пользователям вносить свои и видеть чужие из­
менения в контролируемый набор файлов, называемый проектом. Прин­
цип очень и очень прост - каждый может скопировать нужные файлы на
свой компьютер, внести в них изменения, а после - загрузить изменения
на сервер. После применения изменений генерируется номер версии. Дру­
гие пользователи могут получить измененные файлы путем синхронизации
их копий проекта через механизм обновления. Другими словами, если вы
и Вася работаете над проектом, а Вася вечером внес изменения в какие-то
файлы, то спустя некоторое время, например, утром, когда вы включите
компьютер и приступите к работе, вы:
• Получите самую новую версию проекта.
• Увидите, кто и какие изменения вносил в предыдущую версию.
• Сможете сравнить версии файлов.
На вашем компьютере будет установлено специальное· программное обе­
спечение, которое "видит", что проект изменен и загрузит измененные фай­
лы на ваш компьютер. Аналогично, если вы внесете изменения в проект,
они будут загружены на сервер и другие члены команды увидят, какие из­
менения вы вносили. Это очень удобно, а главное позволяет с точностью до
последнего символа контролировать код.
Такой процесс прекрасно работает в проектах, над которыми трудятся
несколько разработчиков и где относительно небольшое количество фай­
лов. Но все становится сложнее, когда вырастает количество разработчиков
и размер кода. Сложные изменения часто затрагивают множество файлов,
разные разработчики практически в одно и то же время вносят изменения,
что порождает цепочку не совсем логичных версий, а проект разрастается
до таких размеров, что некоторые программисты просто не в состоянии хра­
нить у себя локальную копию всего проекта.
Назревают две проблемы:
1. Программист может долго работать только в своей локальной копии без
резервного копирования.



CD---------------------------------------------------------------------·········----·

О python____________________

Глава 21. Контроль кода

2. Сложно делиться своей работой с другими, пока она не отправлена в
хранилище, а отправлять ее без проверки и тестирования означает по­
ставить под угрозу работу всего проекта.
В централизованной СКБ подобные проблемы решаются посредством от­
ветвлений и с 7Iияний. От основного потока изменения могут ответвляться
ветви (форк:и), которые снова сливаются в основной поток. Но в этом слу­
чае возникает другая проблема. Представьте себе, что есть основной поток,
над которым работает множество программистов. Программист Вася соз­
дает свой форк и вносит в него изменения, работает над проектом неделю
или даже больше и затем хочет выполнить слияние своей ветки с основным
проектом. В процессе слияния выясняется, что пользователь Марк также
вносил изменения в файлы, которые правил Василий - уже после того, как
Василий создал свой форк. Чьи изменения считать правильными - Марка
или Василия?
Вот в этом и заключаются основные недостатки централизованной СКБ:
1. Ветвление и слияния сложно организовать. В сложных проектах такая
организация управления кодом может превратиться в настоящий ноч­
ной кошмар.
2. Поскольку система централизована, нельзя зафиксировать изменения в
автономном режиме. То есть вы не можете зафиксировать результаты
своей работы за вчера, чтобы сравнить их с сегодняшними. Вам нужно
только отправлять изменения на сервер, чтобы выполнить централи­
зованный commit, что в свою очередь породит создание новой версии.
Когда над проектом работает с десяток программистов (а это немного)
и каждый будет ежедневно делать коммит, то накопиться очень много
версий, в которых очень сложно будет разобраться.
3. Такая схема практически не работает для проектов, где у компаний есть
собственный филиал программного обеспечения и нет центрального
хранилища, в котором бы у каждого была своя учетная запись.



Внимание! Еще нужно помнить о следующем недостатке цен­
трализованной СКВ. У каждого клиента есть копия всего исход­
ного кода и внесенных изменений. В этом случае, если один из
серверов выйдет из строя, любой клиентский репозиторий мо­
жет быть скопирован на другой сервер для продолжения работы.

·------------------------------------------·····-------·-····-······-----·-····---,tD

Python. Полное руководство

.........................6python


Несмотря на эти недостатки, централизованные СКБ все еще популярны в
небольших проектах и даже в крупных компаниях за счет инертности кор­
поративной среды. Другими словами, когда-то, лет 10 назад, решили, что
будут использовать систему А и все эти 10 лет она используется, потому что
так принято.
В качестве примера централизованных СКБ можно привести Subversion
(SVN) и System Concurrent Version (CVS).
Многие крупные проекты из-за недостатков централизованных СКБ пе­
решли на распределенную архитектуру. Распределенные СКБ решают не­
достатки централизованных коллег. Они размещаются не на основном сер­
вере, с которым работают программисты, а на равноправных (peer-to-peer)
узлах. У каждого программиста есть свой независимый репозиторий для
проекта, и он синхронизирует его с другими репозиториями.
Здесь больше свободы. Например, Марк может загрузить файлы из какого­
то основного хранилища (пусть оно называется PRJ). Марк вносит измене·
ния в некоторые файлы. Василий загружает файлы из хранилища Марка и
тоже вносит изменения. Затем Василий отправляет изменения в PRJ Де­
нис загружает файлы из PRJ и вносит в них изменения, а затем загружает
их в PRJ.
Другими словами, здесь есть какойато один центральный репозиторий, но
вам не обязательно с ним работать. Вы можете использовать репозитории
других программистов, в зависимости от того, как организована работа
людей и управление проектом. Здесь больше нет какого-то центрального
сервера, на который пользователи вносят изменения, а менеджер проекта
определяет стратегию управления кодом - загрузки, выгрузки, внесения
изменений.
Недостаток распределенной системы в том, что программисты вынуждены
больше думать. В распределенной системе нет больше какого-то глобально­
го. номера, с которым можно сверяться. Таким образом, нужно задейство­
вать дополнительные текстовые метки (теги), которьrе могут быть присо­
единены к версии.
При работе с распределенной системой получается, что каждый может вно­
сить какие-либо изменения, загруженные у кого-либо. Может получиться
бардак. Поэтому при работе с распределенной системой также создают цен­
тральный сервер, но его назначение совсем иное, чем у централизованных
СКБ. Этот сервер представляет собой хаб, позволяющий всем программи­
стам одновременно использовать свои изменения в одном месте, а не зани·
маться загрузкой и выгрузкой друг у друга. Такой репозиторий часто пазы·

........................................ с •••••••••••••••••••••·••••••••••••••••••••• ,

ф python___________________ �

Глава 21. Контроль кода

вается вышестоящим и используется также в качестве резервного для всех
изменений, выполняемых в отдельных репозиториях всех членов команды.
Для объединения доступа к коду в распределенной СКБ используются раз­
личные подходы. Самый простой заключается в создании сервера, который
будет выполнять функцию обычного централизованного сервера, и каждый
программист может вносить изменения в общий поток. Но такой подход не
позволит вам получить все преимущества распределенной СКБ, поскольку
работа будет организована так же, как и в случае с централизованной СКБ,
а поменяется просто название системы СКБ. Будете использовать Git, гор­
до заявлять об этом, а работа будет построена так же, как в случае с SVN.
Для небольших проектов такой вариант использования распределенной
СКБ допускается - чтобы программисты сразу привыкали к использова­
нию нормальной системы контроля версий. Б сложных проектах принято
использовать другой подход, при котором на сервере создаются несколько
репозиториев с разными уровнями доступа:
• Нестабильный репозиторий - в него может вносить изменения каждый
программист.
• Стабильный репозиторий - с него может выгружать файлы любой про­
граммист, а вносить изменения могут только менеджеры проекта - они
решают, что нужно изменять, какие изменения нужно вносить из неста­
бильного репозитория.
• Релизный репозиторий - используется для релизов, доступен только для
чтения.
При таком подходе каждый участник может вносить изменения, а менед­
жеры могут решать, чьи изменения включить в стабильный репозиторий, а
какие-нет.
Какую систему выбрать ..:_ централизованную или распределенную? Сейчас
мы сэкономим вам кучу времени, и вы перестанете ломать голову над этим
вопросом. Забудьте о централизованных системах-это пережиток прошло­
го и если вы только выбираете, какую систему СКБ использовать, ни в коем
случае не выбирайте централизованные системы. Только распределенная.
Хорошо, следующий вопрос - какую распределенную систему выбрать?
Здесь тоже все просто -используйте Git. Git -это самая популярная рас­
пределенная система управления версиями, знаменитая тем, что была соз­
дана Линусом Торвальдсом (создатель Linux) для работы над кодом ядра

•·- --

------

-- - -- - -- - -- - - -- - - - -... -- ............. - -· ...................... -- .... ---

....

"�--______________________О

Python. Полное руководство

,

python
.

Linux, когда потребовалось отказаться от патентованного ПО ВitКеерег,
которое использовалось ранее для работы над кодом ядра.
Git - лучшая, но не идеальная система. В том, что она лучшая -' никто не
сомневается, ведь она используется для управления кодом ядра Linux - это
очень сложный проект, содержащий тонны кода (когда количество строк
кода в ядре Linux превысило 15 миллионов, эти строки перестали считать!).
Но Git также имеет свои недостатки. В первую очередь - это довольно
сложная система, особенно для новичков. В последнее время эта проблема
частично решается наличием специального ПО, облегчающего работу с Git,
например, GitHub Desktop (рис. 21.3). Приложение позволяет создавать ко­
пию репозитория, выгружать изменения в репозиторий, просматривать эти
изменения и т.д. Все же удобнее, чем командная строка.
Да, вы будете привязаны к сервису GitHub. GutHub - это крупнейший веб­
сервис для хостинга ИТ-проектов и их совместной разработки, основан на
системе Git. С одной стороны, привязка � GitHub, с другой - этот хостинг
бесплатный, за него не нужно платить, не нужно разворачивать отдельный
сервер для Git, можно создать репозиторий, скачать GitHub Desktop и при­
ступить к работе.

Рис. 21. 1. Приложение GitHub Desktop






................................................. -.................................. .

,r,,python-------------------Wii

Глава 21. Контроль кода

Здесь важно понимать разницу между Git и GitHub. GitHub - сервис он­
лайн-хостинга репозиториев, обладающий всеми функциями распределен­
ного контроля версий и функциональностью управления исходным кодом
- все, что поддерживает Git и даже больше. Также GitHub может похва­
статьс:я контролем доступа, багтрекингом, управлением задачами и вики
для каждого проекта.
Git-репозиторий, загруженный на GitHub, доступен с помощью интерфей­
са командной строки Git и Git-команд. Также есть и другие функции: доку­
ментация, запросы на принятие изменений (pull requests), история комми­
тов, интеграция со множеством популярных сервисов, еmаil-уведомления,
эмодзи, графики, вложенные списки задач, система @упоминаний, похожая
на ту, что в Twitter, и т.д.
Другими словами, Git - это инструмент, а GitHub - это сервис, основанный
на этом инструменте. Git - это как собственный автомобиль, а GitHub - это
как автомобиль напрокат, только в отличие от прокатного автосервиса, он
является бесплатным.

21.2. Знакомство с Git
Git - это распределенная система контроля версий, созданная, как уже от­
мечалось, создателем ядра Linux Линусом Торвальдсом для работы над ис­
ходным кодом ядра. До появления Git для разработки ядра Linux исполь­
зовался коммерческий продукт - BitKeeper VCS, который обеспечивал
сложные операции, недоступные в бесплатных системах контроля версий
того времени, таких как RCS и CVS ( Concurrent Version System ). Однако
компания, владеющая BitKeeper, установила дополнительные ограничения
на его "свободную" версию, выпущенную в 2005 году, и сообщество Linux
поняло, что BitKeeper больше не может использоваться.
Тогда Линус начал искать альтернативные решения. Сторонясь коммерче­
ских решений, он изучил пакеты бесплатного программного обеспечения,
но нашел те же ограничения и недостатки, которые были и раньше. Что же
не устраивало его в существующих СКБ? Каковы были неуловимые недо­
стающие возможности или характеристики, которые Линус хотел и не мог
найти?

РАСПРЕДЕЛЕННАЯ РАЗРАБОТКА

Есть много граней распределенной разработки и Линус хотел новую VCS,
которая бы покроет большинство из них. Она должна поддерживать парал-



·---------------------------------------------------------------------------------CD

Python. Полное руководство

,r,1f№python

.••••••••••••••••••••••• щj,

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

МАСШТАБИРУЕМОСТЬ, СПОСОБНАЯ
ПОДДЕРЖИВАТЬ ТЫСЯЧИ РАЗРАБОТЧИКОВ
Недостаточно иметь только распределенную модель разработки. Линус
знал, что над выпуском каждой версии Linux трудятся тысячи разработ­
чиков. Таким образом, любая новая СКБ должен был поддерживать очень
большое количество разработчиков, независимо от того, работают они над
одними и теми же или разными частями проекта. Новая СКБ должна быть
в состоянии надежно интегрировать всю их работу.

БЫСТРАЯ И ЭФФЕКТИВНАЯ РАБОТА
Нужно было гарантировать, что новая СКБ быстра и эффективна. Отдель­
ные операции обновления и операции передачи по сети должны быть очень
быстрыми. А чтобы сэкономить дисковое пространство и сократить время
передачи данных по сети, нужно использовать сжатие и методы "дельты".
Использование распределенной модели вместо централизованной также
гарантирует, что сетевая задержка не будет препятствовать ежедневной раз­
работке.

ПОДДЕРЖКА ЦЕЛОСТНОСТИ И ДОВЕРИЕ
Поскольку Git - это распределенная система управления версиями, жиз­
ненно важно получить уверенность в том, что обеспечивается целостность
данных, и никто не может внести несанкционированные изменения. Как вы
узнаете, что данные не были изменены при переходе от одного разработчи­
ка к другому? Или от одного репозитория к следующему? Git использует
общую криптографическую хеш-функцию, названную Secure Hash Func­
tion (SHA1), для идентификации объектов в базе данных. Данная функция
гарантирует целостность и доверие для распределенных репозиториев Git.



.....................................................................................

,

6python
................................. ..
.g,;_r

Глава 21 . Контроль кода

ОТСЛЕЖИВАЕМ ОСТЬ
Один из ключевых аспектов системы управления позволяет узнать, кто из­
менил файлы, и, если это возможно, почему. Git ведет журнал изменений
на каждой фиксации, изменяющей файл. Какая именно информация будет
храниться в журнале, определяется до начала проекта в зависимости от тре­
бования к проекту, управлению, разных соглашений и т.д. Git гарантирует,
что в ваших файлах не будет загадочных изменений, поскольку он отслежи­
вает каждое изменение.

НЕИЗМЕННОСТЬ
База данных репозитория Git содержит объекты данных, которые являются
неизменными. После их создания и помещения в базу данных они больше
не могут быть изменены. Они могут быть воссозданы иначе, конечно, но ис­
ходньJе данные не могут быть изменены без последствий. Проект базы дан­
ных Git означает, что вся история, сохраненная в базе данных управления
версиями, также неизменная. Использование неизменных объектов имеет
несколько преимуществ, в том числе быстрое сравнение для равенства.

АТОМАРНЫЕ ТРАНЗАКЦИИ
При использовании атомарных транзакций много разных, но связанных
изменений, выполняются вместе или не выполняются вообще. Это гаран­
тирует, что базу данных управления версиями не оставят в частично изме­
ненном или поврежденном состоянии при обновлении или фиксации. Git
реализует атомарные транзакции, записывая полные, дискретные состоя­
ния репозитория, которые не могут быть разделены на отдельные или мень­
шие изменения состощшя.

ПОДДЕРЖКА И ПООЩРЕНИЕ РАЗВЕТВЛЕНИЯ
РАЗРАБОТКИ
Почти все СКБ могут прддерживать различные генеалогии разработки в
единственном проекте. Например, одну последовательность изменений

Python. Полное руководство

__ ................._____ • python

кода можно было вызвать "разработкой", а другую - "тестированием".
Каждая система управления версиями может также разделить одну линию
разработки на несколько линий, а затем объединить их в одно целое. Git
называет каждую линию разработки ветвью и назначает каждой ветви соб­
ственное имя.
Вместе с ветвлением приходит объединение. Линус хотел не только воз­
можностьветвления, но и простое объединение те ответвлений. Поскольку
слияние ответвлений часто было болезненной и трудной работой в систе­
мах управления версиями, важно поддерживать быстрое и простое слияние.

ПОЛНЫЕ РЕПОЗИТОРИИ
Чтобы отдельные разработчики не запрашивали журнал изменений у цен­
трализованного сервера репозитория, важно, чтобы у каждого репозитория
была полная копия всех изменений каждого файла.

21.3. Установка Git
Для установки Git в Linux нужно ввести команду:
sudo apt install git git-doc gitweb git-gui gitk git-email
git-svn

Данная команда установит Git в DеЬiаn-подобном дистрибутиве (Ubuntu,
Kubuntu, DeЬian и т.д.). Если у вас Fedora, то команду apt нужно заменить
на dnf:
sudo dnf install git git-doc gitweb git-gui gitk git-email
git-svn

Git для Windows можно получить по адресу:

https.j/gitforwindows.org/



CD-··················································································

4, python

_____ 0 ______________

Глава 21. Контроль кода

и устанавливается он как обычное Windоws-приложение. Подробно описы­
вать процесс установки нет смысла, так как любой среднестатистический
Руthоn-программист справится с этой задачей.

21.4. Основы работы с Git
Git управляет изменениями. Git - одна из систем управления версиями.
Множество принципов, например, понятие фиксации, журнала изменений,
репозитария, являются одинаковыми для всех инструментов подобного
рода. Но Git предлагает много новинок. Некоторые понятия и методы дру­
гих систем управления версиями могут работать по-другому в Git или не
работать вообще. Независимо от наличия вашего опыта работы с подобны­
ми системами, данная книга учит вас работать с Git.

21.4.1. КОМАНДНАЯ СТРОКА GIT
Git очень просто использовать. Просто введите git без всяких аргументов.
Git выведет свои параметры и наиболее общие подкоманды.
$ git
git [-:.version] [--exec-path[=GIT_EXEC_PATH]]
[-р 1--paginate 1--no-pager] [--bare] [--git-dir=GIT_DIR]
[--work-tree=GIT_WORK_TREE] [--help] СОММАND [ARGS]

Далее приведены наиболее часто используемые команды git:
• add - Добавляет содержимое файла в индекс
• bisect - Выполняет бинарный поиск ошибки по истории фиксаций
• branch - Выводит, создает или удаляет ветки
• checkout- Проверяет и переключает на ветку
• clone - Клонирует репозиторий в новый каталог



• commit- Записывает изменения в каталог (фиксация)

·-----------------------------------------------------------------------------------

Python. Полное руководство

tlipython

· ••••••••••••••••••••••• tФ

• diff - Показывает изменения между фиксациями, рабочими деревьями
и т.д.
• fetch - Загружает объекты и ссылки из другого репозитория
• grep - Выводит строки, соответствующие шаблону
• init - Создает пустой репозиторий git или переинициализирует существующий
• log - Показывает журналы фиксации
• merge - Объединяет две или больше истории разработки
• mv - Перемещает или переименовывает файл, каталог или символиче­
скую ссылку
• pull - Получает изменения из удаленного репозитория и объединяет :Их с
локальным репозиторием или локальной веткой
• push - Загружает изменения из вашего локального репозитория в удаленный
• rebase - Строит ровную линию фиксаций
• reset - Сбрасывает текущий HEAD в указанное состояние
• rm - Удаляет файлы из рабочего дерева и из индекса
• show - Показывает различные типы объектов
• status - Показывает состояние рабочего дерева
• tag - Создает, выводит, удаляет или проверяет тег объекта, подписанно­
го с GPG
Чтобы вывести полный список подкоманд git, введите:
git help --all

Как видно из подсказки использования, имеется множество полезных оп­
ций для git. Большинство опций, показанных как [ARGS], применяются к
определенным подкомандам.
Например, опция --version применяется к команде git и производит вывод
номера версии:



a!t........ -... -. ·.-...--.... -..........-.. -. -....... --. --..-.-. -....-...... -.---..--.

--------------------

6python
�·

.

Глава 21. Контроль кода

$ git --version ·
git version 1.8.3.msysgt.O

А опция --amend, наоборот, относится к подкоманде commit:
$ git commit --amend

В некоторых случаях придется указывать обе формы опция, например:
$ git --git-dir =project.git repack -d

Получить справку по каждой подкоманде git можно следующими способа­
ми:
git help subcommand, git --help subcommand или git subcommand
--help

Раньше у Git был набор простых, отличных, автономных команд, разрабо­
танных согласно философии "Unix toolkit: небольцrие и независимые ин­
струменты. Каждая команда начиналась со строки git и содержала дефис,
после которого следовало название выполнимого действия, например, git­
commit и git-log. Однако на данный момент считается, что должна быть одна
единственная исполнимая программа git, а выполняемые действия должны
передаваться в качестве параметров, например, git commit и git log. Нужно
отметить, что формы команд git commit и git-commit идентичны.
Примечание. Онлайн-документация по Git доступна по адресу
https://mirrors.edge.kernel.org/puЬ/software/scm/giUdocs/

Команды git понимают "короткие" и "длинные" команды. Например, следу­
ющие две команды git commit эквиваленты:
$ git commit -m "Fixed а typo."



$ git commit --message="Fixed а typo."

··················································································---

Python. Полное руководство

r/1,python

•••••••••••••••••••••••• 1:12

В короткой форме используется один дефис, а длинная форма использует
два. Некоторые опщщ существуют только в одной форме.
Наконец, вы можете разделить параметры из списка аргументов, используя
"пустое двойное тире". Например, используйте двойное тире, чтобы отде­
лить часть управления командной строки от списка операндов, например,
от имен файлов:
$ git diff -w master origin -- tools/Makefile

Двойное тире нужно использовать, чтобы разделить и явно идентифициро­
вать имена файлов, если иначе они могли бы быть приняты за другую часть
команды. Например, если у вас есть тег "main.c" и файл "main.c", вы полу­
чите различное поведение:
#
$
#
$

Проверка тега с именем "main.c"
git checkout main.c
Проверка файла с именем "main.c"
git checkout -- main.c

21.4.2. БЫСТРОЕ ВВЕДЕНИЕ В GIT
Чтобы увидеть git в действии, давайте создадим новый репозиторий и до­
бавим в него некоторое содержимое, а после чего сделаем несколько версий
этого содержимого.
Существует две фундаментальных техники установки репозитория Git.
Вы можете создать его с нуля, а потом заполнить его содержимым, или же
скопировать, или как говорят в мире Git, склонировать, существующий ре­
позиторий. Проще начать с пустого репозитория, поэтому давайте сделаем
это.

СОЗДАНИЕ НАЧАЛЬНОГО РЕПОЗИТОРИЯ
Чтобы смоделировать типичную ситуацию, давайте создадим репозиторий
для вашего персонального сайта в каталоге -/puЬlic_html.

св-----------.. -....--...-.-......-.... -·..··-...·-...-.---... --...--.·-·-.·-..·---..•

• python....................

Глава 21. Контроль кода

Еслиу вас нет вашего личного сайта в -/puЬlic_html, создайте этот каталог
и поместите в него файл index.html с любой строкой:
$ mkdir ~/puЫic_html
.$ cd ~/puЫic_html
$ echo 'Мой сайт жив!' > index.html
Чтобы превратить -/puЬlic_html или любой другой каталог в репозиторий
Git, просто запустите git init.
$ git init
Initialized empty Git repository in .git/
Программе git все равно, начинаете ли вы с полностью пустого каталога или
с каталога, щшного файлов. Процесс преобразования каталога в репозито­
рий Git будет одинаковым.
Чтобм отметить, что ваш каталог является репозиторием Git, команда git
init создает скрытый каталог с названием .git, находящийся на верхнем
уровне вашего проекта.
Все остальное в каталоге -/puЬlic_html останется нетронутым. Git считает
этот каталог рабочим каталогом вашего проекта, в котором вы изменяете
свои файлы. Git интересуют только файлы из скрытого каталога .git.

ДОБАВЛЕНИЕ ФАЙЛОВ В ВАШ РЕПОЗИТОРИЙ
Команда git init создает новый репозиторий Git. В самом начале каждый ре­
позиторий Git будет пустым. Чтобы управлять контентом, вы должны явно
добавить его в репозиторий. Такой осознанный файл позволяет разделить
рабочие файлы от важных файлов.
Команда gi t add используется для добавления файла в
репозиторий:
$ git add index.html



.....................................................................................

Python. Полное руководство

.

,.............................................

iJ.lJpython
"i№

Примечание. Если в вашем каталоге есть несколько файлов,
не нужно добавлять все их вручную, пусть за вас это сделает git.
Чтобы добавить в репозиторий все файльi в каталоге и во всех
подкаталогах, используйте команду git add . (одна точка в Unix
означает текущий каталог).

После добавления файла Git знает, что файл index.html принадлежит репо­
зиторию. Но Git просто подготовил файл, это еще не все. В Git разделены
операции добавления и фиксации. Это сделано для лучшей производитель­
ности - вы только представьте, сколько времени займет обновление репози­
тория при каждом добавлении или удалении файла. Вместо этого Git пред­
лагает раздельные операции, что особенно удобно в пакетном режиме.
Команда git status покажет промежуточное состояние файла index.html:
$
#
#
#
#
#
#
#
#

git status
On branch master
Initial commit
Changes to Ье committed:
(use "git rm --cached ..." to unstage)
new file: index.html

Данная команда сообщает, что при следующей фиксации в репозиторий бу­
дет добавлен новый файл index.html.
Помимо актуальных изменений в каталоге и его файлах при каждой фикса­
ции Git записывает различные метаданные, включая сообщение журнала и
автора изменения. Полная команда фиксации выглядит так:
$ git commit -m "Начальное содержимое puЫic_html"
--author= "Jon Loeliger "
Created initial commit 9da58ld: Initial contents of puЫic
html
1 files changed, 1 insertions(+), О deletions(-)
create mode 100644 index.html

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



tD------..--. -... -......-. -........-. -.......... -. -.. -. •.•.. -. -....-....... -..-... -..

• python....................

Глава 21. Контроль кода

тора. Чтобы Git открывал ваш любимый текстовый редактор при фиксации
изменений, установите переменную окружения GIТ_EDIТOR:
#
$
#
$

В tcsh
setenv GIT EDITOR emacs
В bash
export GIT EDITOR=vim

После фиксации добавления нового файла в репозиторий, командаgit status
покажет, что нет ничего, что требовало ли фиксации:
$ git status
# On branch master
nothing to commit (working directory clean)

Git также сообщает вам, что ваш рабочий каталог чист. А это означает, что в
вашем рабочем каталоге нет неизвестных или измененных файлов, которые
отличаются от тех, которые находятся в репозитории.

НЕЯСНЫЕ СООБЩЕНИЯ ОБ ОШИБКАХ
Git пытается определить автора каждого изменения. Если вы не сконфигу·
рировали свое имя и e·mail так, чтобы они были доступны в Git, это может
стать причиной некоторых предупреждений. Но не нужно паниковать, ког­
да вы видите примерно такие сообщения:
You don't exist. Go away!
Your parents must have hated you!
Your sysadmin must.hate you!

Они просто означают, что Git не может определить ваше реальное имя по
какой-то причине. Проблема может быть исправлена путем установки ва­
шего имени и e-mail, что будет показано в разделе "Настройка автора
фиксации".

•.�

.
.
.
•...•................. ..................... ........ ...•..••••................•

Python. Полное руководство

.

i/la python

------------------------ $%J

НАСТРОЙКА АВТОРА ФИКСАЦИИ
Перед фиксацией вам нужно установить некоторые параметры конфигура­
ции и переменные окружения. Как минимум, Git должен знать ваше имя и
ваш e-mail. Вь� можете указывать эти данные при каждой команде фикса­
ции (git commit), как было показано ранее, но это очень неудобно. Вместо
этого лучше всего сохранить данную информацию в файле конфигурации,
используя команду git config:
$ git config user.name "Jon Loeliger"
$ git config user.email "jdl@example.com"

Вы также можете сообщить Git свое имя и свой e-mail, установив перемен­
ные окружения GIТ_AUTHOR_NAME и GIТ_AUTHOR_EMAIL. Если
эти переменные окружения установлены, они перезапишут все параметры,
установленные в файле конфигурации.

ВНОСИМ ДРУГУЮ ФИКСАЦИЮ
Чтобы продемонстрировать еще несколько функций Git давайте внесем не­
которые изменения и создадим сложную историю изменений внутри репо­
зитория.
Откройте index.html, конвертируйте его в HTML, сохраните изменения и
выполните фиксацию изменений:
$ cd ~/puЫic_html
# редактируем index.html
$ cat index.html


Мой сайт жив!



.... . .

$ git commit index.html

-· ................. - . -..-.--.-------- .. -·. -- -.............................

'

• python____________________

Глава 21. Контроль кода

Когда откроется редактор, введите описание фиксации, например, "Файл
конвертирован в HTML". После чего в репозитории появится две версии
файла index.html.

ПРОСМОТР ВАШИХ ФИКСАЦИЙ
Как только в вашем репозитории появилось несколько фиксаций, вы мо­
жете исследовать их различными способами. Некоторые команды Git по­
казывают последовательность отдельных фиксаций, другие - сводку об от­
дельной фиксации, а остальные показывают полную информацию о любой
фиксации в каталоге.
Команда git log показывает историю отдельных фиксаций в репозитории:
$ git log
commit ec232cddfb94e0dfd5b5855af8ded7f5eb5c90d6
Author: Jon Loeliger
Date: Wed Apr 2 16:47:42 2008 -0500
Файл конвертирован в HTML
commit 9da58ld910c9c4ac93557ca4859e767f5caf5169
Author: Jon Loeliger
Date: Thu Mar 13 22:38:13 2008 -0500
Начальное содержимое puЫic_html

Записи выводятся в порядке от самой последней до самой старой. Каждая
запись показывает имя и e-inail автора фиксации, дату фиксации и сообще­
ние, добавленное в журнал при фиксации.
Для получения более подробной информации о конкретной фиксации, ис­
пользуйте команду git show:

.

$ git show 9da58ld910c9c4ac93557ca4859e767f5caf5169
commit 9da58ld910c9c4ac93557ca4859e767f5caf5169
Author: Jon Loeliger
Date: Thu Mar 13 22:38:13 2008 -0500
Initial contents of puЫic html

··-·-··-·-·-·-····································································CD

Python. Полное руководство

diff --git a/index.html Ь/index.html
new file mode 100644
index ООООООО..34217е9
--- /dev/null
+++ Ь/index.html
@@ -0,0 +1 @@
+Мой сайт жив!

Если вы запустите git show без указания идентификатора фиксации, вы по­
лучите информацию о последней фиксации.
Другая команда, show-branch, выводит краткую сводку для текущей ветки
разработки:
$ �it show-branch --more = l0
[master] Файл конвертирован в HTML
[master л ] Начальное содержимое puЫic_html

Параметр --more= 10 указывает, что нужно вывести не более 10 версий, но в
нашем случае пока существуют только две версии. Имя master - это имя по
умолчанию для ветки.

ПРОСМОТР РАЗНИЦЫ МЕЖДУ ФИКСАЦИЯМИ
Чтобы увидеть разницу между двумя версиями index.html, вам понадобятся
идентификаторы этих двух фиксаций, которые нужно передать команде git

dijf.

$ git diff 9da581d910c9c4ac93557ca4859e767f5caf5169 \
ec232cddfb94e0dfd5b5855af8ded7f5eb5c90d6
diff --git a/index.html Ь/index.html
index 34217е9..8638631 100644
--- a/index.html
+++ Ыindex.html
@@ -1 +1,5 @@
+
+
Мой сайт жив!
+



CD-----............·.................... -... -.. -. -...--...-... -.... -... -.-.-.. -. -... -.

• python.....•..............

Глава 21 . Контроль кода

+

Этот вывод должен выглядеть знакомым. Он напоминает вывод команды
diff. Первая .версия, 9da581d910c9c4ac93557ca4859e767f5caf5169, является
более ранней версией содержимого, а версия с именем ec232cddfЬ94e0dfdS
b585Saf8ded7f5eb5c90d6 является более новой. Таким образом, знак "плюс"
( +) предшествует каждой строке нового содержимого.
Вы испугались, глядя на эти шестнадцатеричные идентификаторы? Не
волнуйтесь, Git предлагает более лаконичные способы идентифицировать
фиксации без необходимости указания таких сложных чисел.

УДАЛЕНИЕ И ПЕРЕИМЕНОВАНИЕ ФАЙЛОВ В ВАШЕМ
РЕПОЗИТОРИИ
Удаление файла из репозитории аналогично добавлению файла, но вместо
команды git add используется команда git rm. Представим, что в нашем ката­
логе веб-сайта есть файл poem.html, который больше не нужен.
$ cd ~/puЫic_html
$ 1s
index.html poem.html
$ git rm poem.html
rm 'poem.html'
$ git commit -m "Удаляем поэму"
Created commit 364а708: Удаляем поэму
О files changed, О insertions(+), О deletions(-)
delete mode 100644 poem.html

Как и добавление, удаление состоит из двух этапов. Сначала git rm удаляет
файл из репозитория, а затем git commit фиксирует изменения в репозито­
рии. Qrщия -m позволяет указать описание фиксации, например, "Удаляем
поэму". Вы можете опустить эту опцию, чтобы задать сообщение в вашем
любимом текстовом редакторе.
Для переименования файла можно использовать комбинацию git rm и git
add или же, что намного быстрее, использовать команду git mv:



··-···············································································-

Python. Полное руководство

·.
••••••••••••••••••••••••

8'i1python
•,.,.
l

$ mv foo.html bar.html
$ git rm foo.html
rm 'foo.html'
$ git add bar.html

В данном случае мы сначала переименовываем файл foo.html в bar.html и
удаляем файл foo.html, как из репозитория, так и с файловой системы, по­
сле чего добавляем файл bar.html в репозиторий.
Эту же операцию можно выполнить с помощью одной команды:
$ git mv foo.html bar.html

После всего этого нам нужно фиксировать изменения:
$ git commit -m "Переименование foo в bar"
Created commit 8805821: Переименование foo в bar
1 files changed, О insertions(+), О deletions(-)
rename foo.html => bar.html (100%)

Git обрабатывает операции перемещения файла иначе, чем другие систе­
мы, используя механизм, основанный на базе подобия содержания между
двумя версиями файлов.

КОПИРОВАНИЕ ВАШЕГО РЕПОЗИТОРИЯ
Ранее мы создали наш начальный репозиторий в каталоге -/puЬlic_html.
Теперь мы можем создать полную копию ( или клон) этого репозитория ко­
мандой git clone.
Сейчас мы создадим копию нашего репозитория в нашем домашнем катало­
ге, она будет называться my_website:
$ cd ~
$ git clone puЫic_html my_website



са---------------------------------------"-----------------------------------------·

6python
-------------------�

Глава 21. Контроль кода

Хотя эти два репозитория Git теперь содержат те же объекты, файлы и ка­
талоги, есть некоторые тонкие различия. Исследовать эти различия можно
командами:
$ ls -lsa puЫic_html my_website
$ diff -r puЫic_html my�website

На локальной файловой системе, подобно этой, использование git clone соз­
даст копию репозитория подобно командам ер -а или rsync. Однако Git под­
держивает богатый набор любых других источников, в том числе сетевые
репозитории.
Как только вы клонируете репозиторий, вы сможете изменять, делать но­
вые фиксации, исследовать журналы и историю и т.д. Это полностью новый
репозиторий со своей полной историей.

ФАЙЛЫ КОНФИГУРАЦИИ
Конфигурационные файлы Git - это простые текстовые файлы в стиле ini­
файлов. В них записываются различные установки, используемые многими
Git-командами. Некоторые установки представляю собой сугубо личные
предпочтения (например, color.pager), друrие жизненно важны для пра­
вильного функционирования репозитория (core.repositoryformatversion), а
остальные управляют поведением команд (например, gc.auto).
. Подобно многим другим утилитам Git поддерживает иерархию конфигура­
ционных файлов. В порядке уменьшения приоритета приведены его конфи­
гурационные файлы:
• .git/config - специфичные для репозитория параметры конфигурации,
которыми управляет опция --file. У этих настроек наивысший приоритет.
• -/gitconfig- специфичные для пользователя параметры конфигурации,
которыми управляет опция --global.
• /etc/gitconfig- системные параметры конфигурации, изменить которые
можно опцией --system, если у вас есть надлежащие права доступа Unix
(нужно право на запись этого файла). Данные настройки обладают наи­
меньшим приоритетом.



·---·-·--------·-·-·----·-·-·-·--·-·-·-·-·--·-·-·-·-·-·----·-·-·-··-·-·-·-·--------св

Python. Полное руководство

________ _______________ф python

В зависимости от вашей инсталляции, системные настройки могут быть
где-то еще, возможно, в /usr/local/etc/config, или могут полностью отсут­
ствовать.
Например, для установки имени и e-mail пользователя, произведшего фик­
сацию, нужно указать значения параметров user.name и user.email в вашем
файле $HOME/gitconfig. Для этого используется команда git config --global:
$ git config --global user.name "Jon Loeliger"
$ git config --global user.email "jdl@example.com"

Можно установить специфичные для репозитория имя пользователя и
адрес электронной почты, которые переопределят глобальные установки,
для этого просто опустите флаг --global:
$ git config user.name "Jon Loeliger"
$ git config user.email "jdl@special-project.example.org"

Используйте git config -1 для вывода всех переменных, найденных во всем
наборе файлов конфигурации:
#
$
$
$

Создаем пустой репозиторий
mkdir /tmp/new
cd /tmp/new
git init

#
$
$
$

Устанавливаем некоторые переменные
git config --global user.narne "Jon Loeliger"
git config --global user.email "jdl@exarnple.com"
git config user.ernail "jdl@special-project.example.org"

$ git config -1
user.narne=Jon Loeliger
user.ernail =jdl@example.com
core.repositoryformatversion= O
core.filemode= true
core.bare=false
core.logallrefupdates= true
user.ernail= jdl@special-project.exarnple.org



CID----------------------------------······························-··············-···

О python____________________

Глава 21. Контроль кода

Поскольку файлы конфигурации - это обычные текстовые файлы, вы мо­
жете просмотреть их содержимое командой cat и отредактировать в люби­
мом текстовом редакторе:
# Посмотрим на параметры репозитария
$ cat .git/config
[core]
О
repositoryforrnatversion
filernode = true
bare = false
logallrefupdates = true
[user]
ernail = jdl@special-project.exarnple.org

Или, если вы используете ОС от Microsoft, у вас будут некоторые измене­
ния. Скорее всего, ваш конфигурационный файл будет выглядеть примерно
так:
[core]
repositoryforrnatversion
О
filernode = true
bare = true
logallrefupdates
true
syrnlinks = false
ignorecase = true
hideDotFiles = dotGitOnly

Большинство из этих изменений - из-за разницы в характеристиках файло­
вой системы. Отключены символические ссылки, включено игнорирование
регистра символов, другие настройки для скрытых файлов.
Используйте опцию --unset для удаления настройки:
$ git config --unset --global user.ernail

Поведение команды git config изменено между версиями 1.6.2 и 1.6.3. Бо­
лее ранние версии потребовали, чтобы после опции --unset следовала опция
--global. В новых версиях допустим любой порядок.

•. ------. --". --" --. "... -" -...---....-.-...-.---.-..-.--..---. --..... ·-· ---.-.. ---"-tD

Python. Полное руководство

••...............•......• python

Для одной и той же цели существуют несколько параметров конфигурации
и переменных окружения. Например, редактор, который будет вызван при
создании сообщения журнала фиксации, можно задать с помощью:
• Переменной окружения GIТ_EDIТOR
• Опции конфигурации core.editor
• Переменной окружения VISUAL
• Переменной окружения EDIТC
• Команды vi
Есть несколько сотен параметров конфигурации. По мере чтения книги я
буду обращать ваше внимание на самые важные из них. На странице руко­
водства ( man) по команде git config вы найдете более подробный ( и все же не
полный) список параметров конфигурации.

НАСТРОЙКА ПСЕВДОНИМОВ
Новичкам пригодится совет по настройке псевдонимов командной строки.
Часто команды Git довольно сложны, поэтому для самых часто использу­
емых команд вы можете создать псевдонимы:
$ git config --global alias.show-graph \
'log --graph --abbrev-commit --pretty=oneline'

В этом примере я создал псевдоним show-graph и сделал его доступным в
любом созданном мной репозитории. Теперь, когда я использую команду
git show-graph будет выполнена длинная команда git log со всеми заданными
опциями.

21.5. Основные понятия Git
В предыдущем разделе было рассмотрено типичное применение Git. Ско­
рее всего, после ее прочтения у вас появилось гораздо больше вопросов,
чем было до того. Какие данные Git хранит для каждой фиксации? Каково



CID-···································"··············································

• python____________________

Глава 21. Контроль кода

назначение кат.iлога .git? Почему 1D фиксации напоминает мусор? Нужно
ли мне обращать внимание на него?
Если вы использовали другую СКБ, например, SVN или CVS, команды,
представленные ранее, наверняка покажутся вам знакомыми. На самом
деле, Git служит для той же цели и предоставляет все операции, которые вы
ожидаете увидеть в современной СКБ. Однако, Git некоторые понятия Git
отличаются, о чем мы и поговорим.
Сейчас мы выясним, чем отличается Git от других СКБ, исследуя ключевые
компоненты его архитектуры и некоторы� важные понятия.

21.5.1. РЕПОЗИТОРИИ
Репозиторий Git - просто база данных, содержащая всю информацию,
необходимую для управления версиями и историей проекта. В Git, как и
в большинстве других систем управления версиями, репозиторий хранит
полную копию всего проекта на протяжении всей его жизни. Однако, в от­
личие от большинства других VCS, репозиторий Git не только предостав­
ляет полную рабочую копию всех файлов � репозитории, но также и копию
самого репозитория, с которым работает.
В каждом репозитории Git обслуживает ряд значений конфигурации. Вы
уже видели некоторые из них (имя пользователя и его e-mail) ранее. В отли­
чие от данных файла и других метаданных репозитория, параметры конфи­
гурации не переходят из одного репозитория в другой при клонировании.
Вместо этого Git исследует конфигурацию и информацию об установке на
основе пользователя, сайта, репозитория.
В репозитории Git управляет двумя основными структурами данных - хра­
нилищем объектов и индексом. Все эти данные репозитория хранятся в
корне вашего рабочеrо каталога в скрытом подкаталоге с именем .git.
Хранилище объектов разработано для эффективного копирования при опе­
рации клонирования как часть механизма, полностью поддерживающего
распределенный VCS. Индекс - это переходная информация, персональная
для репозитория. Индекс может быть создан или изменен по требованию в
случае необходимости.
В следующих двух разделах подробно рассмотрены хранилище объектов и
индекс



·----------------------·····-------------------------------------------------------t:ID

____________............О

Python. Полное руководство

python

21.5.2. ТИПЫ ОБЪЕКТОВ GIT
Сердце репозитория Git .,- это хранилище объектов. Оно содержит ваши
исходные файлы и все сообщения журналов, информацию об авторе, даты
и другую информацию, необходимые для сборки любой версии или ветки
проекта.
В хранилище объектов Git помещает объекты четырех типов: блобы (от
англ. BLOB, Binary Large Object), деревья, фиксации и теги.

БЛОБЫ
Каждая версия файла представлена как БLОБ. BLOB - это аббревиатура
для "Binary Large Object ", то есть большой двоичный объект. Этот термин
часто используется в информатике для обозначения некоторой переменной
или файла, которая (который) может содержать любые данные и внутрен­
няя структура которой (которого) игнорируется программой. Блоб содер­
жит данные файлы, но не содержит какие-либо метаданные о файле, даже
его имя.

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

21.5.3. ФИКСАЦИИ
Объекты фиксации хранят метаданные для каждого изменения, произо­
шедшего в репозитории, включая имя автора, дату фиксации и сообщение



--------------------------------·-······-·-·····------·-·----·-·-·-·---·--·-·-·---·

Глава 21. Контроль кода

• python_________________ .. _

журнала. Каждая фиксация указывает на объект дерева, который захваты­
вает в одном полном снимке состояние репозцтория на момент осущест­
вления фиксации. У начальной (корневой) фиксации нет родителя. У
большинства обычных фиксаций есть одна родительская фиксация, хотя
фиксация может ссылаться на несколько родительских фиксаций.

ТЕГИ
Объект теrа назначает человекочитаемое имя определенному объекту,
обычно фиксации. Хотя 9da581d910c9c4ac93557ca4859e767f5caf5169 по­
зволяет точно идентифицировать фиксацию, для человека более удобным
идентификатором является что-то вроде Ver-1.0-Alpha.
Со временем вся информация в хранилище объе.ктов изменяется и растет,
отслеживая и моделируя ваши правки проекта, добавления и удаления фай­
лов. Чтобы эффективно использовать дисковое пространство и пропускную
способность, Git сжимает объекты в расk-файлы, которые также помещают­
ся в хранилище объектоw.

21.5.4. ИНДЕКС
Индекс - временный и динамический двоичный файл, который описывает
структуру каталогов всего репозитория. В частности, индекс получает вер­
сию полной структуры проекта в некоторый момент времени. Состояние
проекта может быть представлено фиксацией и деревом.
Одна из ключевых отличительных функций Git - то, что он позволяет вам
изменить содержание индекса четко определенными действиями. Индекс
позволяет разделение между шагами поэтапной разработки и поддержкой
тех изменений.
Вот как это работает. Как разработчик, вы выполняете команды Git, чтобы
подготовить изменения в индексе. Изменения обычно включают добавле­
ние, удаление или редактирование файлов. Индекс записывает эти измене­
ния и хранит _их, пока вы не зафиксируете их. Вы можете также удалить или
заменить изменения в индексе. Таким образом, индекс - это как мост ме�ду
двумя сложными состояниями репозитория.

21.5.5. АССОЦИАТИВНЫЕ ИМЕНА
Хранилище объектов Git организовано и реализовано как ассоциативная
система хранения. В частности, каждому объекту в хранилище присваи­
вается уникальное имя, применяя алгоритм SHA1 к содержанию объекта,



··················································································-

Python. Полное руководство

.
i/Ьpython
· ------------------.·
------- @;

что приводит к значению хэш-функции SHA1. Поскольку значение хэш­
функции вычисляется на основании содержания объекта, полагают, что оно
уникально для определенного содержания. Следовательно, у каждого объ­
екта будет уникальное имя в базе объектов. Крошечное изменение в файле
приведет к изменению хэша SHA1, в итоге новая версия файла будет индек­
сироваться отдельно.
Значения SHA1 - 160-разрядные значения, которые обычно представляют­
ся как 40-разрядное шестнадцатеричное число, такое как 9da581d910c9c4ac
93557ca4859e767f5caf5169. Иногда во время отображения значения SHA1
сокращаются до меньшего уникального префикса. Пользователи Git назы­
вают эти числа SHA1, хэш-код и идентификатор объекта - все эти понятия
взаимозаменяемы.

21.5.6. ГЛОБАЛЬНО УНИКАЛЬНЫЕ
ИДЕНТИФИКАТОРЫ
Важная характеристика вычисления хеша SHA1 - то, что он всегда вычис­
ляет тот же ID для идентичного содержания, независимо от того, где это
содержание находится. Другими словами, одно и то же содержание файла
в разных каталогах и даже на разных машинах даст одно и то же значение
хэша SHA1. Таким образом, идентификатор SHA1 - это глобально уни­
кальный идентификатор.
Благодаря глобально уникальным идентификаторам мы можем через Ин­
тернет сравнивать блобы или файлы произвольного размера путем про­
стого сравнения их идентификаторов. Нам не нужно пересылать файлы по
сети, чтобы определить, идентичны ли они.

21.5.7. GIT ОТСЛЕЖИВАЕТ КОНТЕНТ
Git - это нечто больше, чем просто СКБ. Git - это система отслеживания
1щн,тента. Отслеживания контента в Git реализовано двумя способами,
которые существенно отличаются от почти всех других систем управления
версиями.
Во-первых, хранилище объектов Git основано на вычислении хэша содер­
жимого объекта, а не на имени файла или каталога. Таким образом, когда
Git помещает файл в хранилище, он это делает на основании данных хэша, а
не на основании имени файла. Фактически, Git вообще не отслеживает имес

-



................................................................................. .

• python. ................ ,;�.

Глава 21. Контроль кода

на файлов и каталогов, которые связаны с файлами вторичными способами.
Git вместо имен файлов отслеживает их содержимое.
Если у двух отдельных файлов есть одинаковое содержание, независимо от
того, хранятся ли они в одном каталоге или разным, Git хранит только одну
копию этого содержанirя в виде блоба в хранилище объекта. Git вычисляет
хэш-код каждого файла исключительно по его содержанию. Оба файла в
проекте с одинаковым содержанием, независимо от того, где они располо·
жены, используют один и тот же объект содержания (блоб).
Если один из этих двух файлов будет изменен, Git вычислит новый хэш
SHA1 для него и добавит новый блоб в хранилище объектов. Исходный
блоб останется в хранилище и будет использован для файла, который
остался без изменений.
Во·вторых, внутренняя база данных Git хранит каждую версию каждого
файла, а не разницу между ними. Поскольку Git использует хеш полного
содержания файла как имя для этого файла, он должен оперировать с пол­
ной копией файла. Он не может основывать свою работу на части содержа­
ния файла или на разнице между версиями файла.
Типичное пользовательское представление файла в виде версий и изме­
нений между версиями является обычным артефактом. Git вычисляет эту
историю как ряд изменений между различными блобами вместо того, чтобы
хранить имя файла и набор различий между версиями.

21.5.8. ПУТЬ ПО СРАВНЕНИЮ С СОДЕРЖАНИЕМ
Как и в случае с другими СКБ, Git должен вести явный список файлов, ко­
торые формируют содержание репозитория. Однако это не требует, чтобы
внутренне Git основывался на именах файлов. Действительно, Git обраба­
тывает имя файла только как часть данные, которые отличны от содержа,
ния этого файла. В результате индекс отделяется от данных в традицион­
ном смысле базы данных. Посмотрите на таблицу 21.1, которая сравнивает
Git с другими известными системами.



··················································································-

Python. Полное руководство

------------------------6python


Таблица 21.1. Сравнение баз данных
Система

Механизм индекса

Хранилище данных

Индексно-ПоследоваТрадиционная база
тельный Метод Доступа Записи данных
данных
(ISAM)
Файловая система Каталоги
UNIX
лу)
Git

(/путь/к/фай-

Блоки данных

.git/objects/hash, содержи- Объекты блоб, объекмое объекта дерева
ты дерева

Названия файлов и каталогов происходят от базовой файловой системы, но
Git действительно не заботится об именах. Он просто записывает каждый
путь и удостоверяется, что он может точно воспроизвести файлы и катало­
ги из содержимого, которое индексировано значением хэша.
Физический формат данных не моделируется после пользовательской
структуры каталога. Вместо этого есть абсолютно другая структура, кото­
рая может, тем не менее, воспроизвести оригинальную разметку пользова­
теля. Внутренняя структура Git - более эффективная структура данных
для своих собственных внутренних операций и средств хранения.
Когда Git нужно создать рабочий каталог, он говорит файловой системе:
"Привет! У меня есть большой блоб данных, которые я хочу поместить в
файл с именем /каталог/файл. Сделай это как посчитаешь нужным". На что
файловая система отвечает: "Я распознала строку, являющуюся набором
имен подкаталогов, и я знаю, куда поместить твои блоб- данные! Спасибо!".

21.5.9. РАСК-ФАЙЛЫ
Проницательный читатель может заметить: "Очень неэффективно хранить
полное содержимое каждой версии каждого файла. Даже если содержимое
сжато, все равно неэффективно хранить содержимое разных версий каждо­
го файла. Почему, если добавляется всего одна строка в файл, Git сохраняет
полное содержимое файла?".

са---------------------------------------------.----.-.--. -. -.. --.-.-.. ---. -. -. -----.•

+

python....................

Глава 21 . Контроль кода

Давайте попробуем разобраться. Git использует очень эффективный ме·
ханизм хра:цения, называемый расk·файлом. Для создания упакованного
файла Git сначала определяет местоположение файлов, содержание кото·
рых очень подобно и хранит полное содержание для одного из них. Затем он
вычисляет различия или дельты между подобными файлы и хранит просто
различия. Например, если вам нужно просто изменить или добавить одну
строку в файл, Git может сохранить полную; более новую версия файла и
затем записать изменение одной строки как дельта и тоже сохранить ее в
пакете.
Хранение полной версии файла и дельт, необходимых для создания друrи)i:
версий подобных файлов - далеко не новый прием. Это, по существу, тот же
механизм, которые другие VCS, такие как RCS, использовали на протяже·
нии многих десятилетий.
Git очень умно упаковыщ�ет файл. Поскольку Git основывается на контен·
те файла, ему действительно все равно, принадлежат ли дельты, которые
он вычисляет между двумя файлами двум версиям одного и того же фай·
ла или нет. То есть Git может взять два любых файла и вычислить дельты
между ними, если он ечитает, что они могли бы быть достаточно подобны·
ми, ,чщбы быть хорошо сжатыми. Таким образом, Git обладает тщательно
продуманным алгоритмом, чтобы найти потенциальных кандидатов дельты
по всему репозиторию.
Упакованные файлы хранятся в хранилище объектов вместе с другими
объектами. Они также используются для передачи данных репозитория по
сети.

21.5.1О. ПОНЯТИЯ GIT В ДЕЙСТВИИ
Теперь, когда вы знакомы с некоторыми понятиями, давайте посмотрим,
как они работают в самом репозитории. Давайте создадим новый репозито·
рий и посмотрим на него внутренние файлы.

ВНУТРИ КАТАЛОГА .GIT
Для начала инициализируем пустой репозиторий, используя git init, а затем
запустим командуfind, чтобы посмотреть, что же было.создано.



·························································-·····-··················-СВ

Python. Полное руководство

,f6python

•••••••••••••••••••••••• !iШ,

$ mkdir /tmp/hello
$ cd /tmp/hello
$ git init
Initialized empty Git repository in /tmp/hello/.git/
# Выводим все файлы в текущем каталоге
$ find .
. /.git
./.git/hooks
./.git/hooks/commit-msg.sample
./.git/hooks/applypatch-msg.sample
./.git/hooks/pre-applypatch.sample
./.git/hooks/post-commit.sample
./.git/hooks/pre-rebase.sample
./.git/hooks/post-receive.sample
./.git/hooks/prepare-commit-msg.sample
./.git/hooks/post-update.sample
./.git/hooks/pre-commit.sample
./.git/hooks/update.sample
./.git/refs
./.git/refs/heads
./.git/refs/tags
./. git/config
./.git/objects
./.git/objects/pack
./.git/objects/info
./.git/description
./.git/HEAD
./.git/branches
./.git/info
./.git/info/exclude

Как видите, в каталоге .git много чего есть. Файлы, выведенные на экран,
основаны на каталоге шаблона, который вы можете при желании изменить.
В зависимости от используемой версии Git содержимое этого каталога мо­
жет немного изменяться. Например, более старые версии Git добавляют
"расширение" .sample к файлам в каталоге .git/hooks.
В целом, вам не нужно ни просматривать, ни управлять файлами из ка­
талога .git. Эти "скрытые" файлы считаются частью инфраструктуры или
конфигурации Git. Конечно, в Git есть набор команд по управлению этими
скрытыми файлами, но вы будете редко их использовать.



•···············································�··································

• python____________________

Глава 21. Контроль кода

Изначально каталог .git/objects (каталог для всех объектов Git) пуст, за
исключением нескольких заполнителей:
$ find .git/objects
.git/objects
.git/objects/pack
.git/objects/info
Теперь давайте осторожно создадим простой объект:
$ echo "hello world" > hello.txt
$ git add hello.txt
Если вы введете "hello world" точно так же (без изменений в регистре сим­
волов или интервале между ними), ваш каталог объектов будет выглядеть
примерно так:
$ find .git/objects
.git/objects
.git/objects/pack
.git/objects/Зb
.git/objects/3Ь/18e512dЬa79e4c8300dd08aeb37f8e728b8dad
.git/objects/info
Все это выглядит довольно таинственным. Но на самом деле все просто и в
следующем разделе вы поймете почему.

ОБЪЕКТЫ, ХЭШИ, &ЛО&Ы
Когда Git создает объект для hello.txt, он не беспокоится об имени файла
hello.txt. Его интересует только то, что в файле: последовательность из 12
байтов - строка "hello world" и символ новой строки. Git выполняет не­
сколько операций на этом блобе, вычисляет его хеш SHA1 и помещает в
хранилище объектов как файл, в качестве имени файла используется вы­
численное значение хэша.
Хэш в этом случае - ЗЬ18e512dba79e4c8300dd08aeb37f8e728b8dad. 160 би­
тов хэша SHA1 соответствуют 20 байтам, а при отображении на экране в
шестнадцатеричном виде - 40 байтов. Итак, наш контент сохранен как .git/
objects/3b/18e512dba79e4c8300dd08aeb37f8e728b8dad.

--

Git вставляет / после первых двух разрядов, чтобы повысить эффектив­
ность файловой сцстемы. Производительность некоторых файловых си-

•. --

.. ................... -.... - . - . - . - . -. . - . - . - . - . -.... -... -........ - . -........ - . - .

Python. Полное руководство

-✓ •••••••••••••••••••••••

python

стем заметно падает, если поместить в один каталог слишком много файлов.
А если превратит первый SHA1 байт в каталог, то это самый простой способ
создать фиксированное разделение пространства имен для всех возможных
объектов с равным распределением.
Чтобы показать, что Git действительно ничего не сделал с содержанием
файла (это все еще строка "hello world"), вы можете использовать хэш фай�
ла, чтобы получить доступ к содержимому в любой момент:
$ git cat-file -р ЗЫ8e512dЬa79e4c8300dd08aeb37f8e728b8dad
hello world

Примечание. Git также знает, что 40 символов немного риско­
ванно, чтобы их вводить вручную, поэтому он предоставил ко­
манду для поиска объектов по префиксу хэша:
$ git rev-parse ЗЫ8е512d
ЗЫ8e512dba79e4c8300dd08aeb37f8e728b8dad

ФАЙЛЫ И ДЕРЕВЬЯ
Теперь, когда блоб "hello world" безопасно помещен в хранилище объектов,
что произошло с его именем файла? Git был бы не очень полезен, если он не
мог бы найти файлы по имени.
Как было упомянуто выше, Git отслеживает пути файлов через другой вид
объектов - деревья. Когда вы используете git add, Git создает объект для
содержания каждого добавленного вами файла, но он не создает объект для
вашего дерева сразу же. Вместо этого он обновляет индекс. Индекс хранит­
ся в .git/index и используется для отслеживания пути файла и соответству­
ющих блобов. Каждый раз, когда вы выполняете команды вроде git add, git
rm или git mv, Git обновляет индекс, устанавливая новую информацию бло·
ба и пути файла.
Создать объект дерева из вашего текущего индекса можно с помощью низ­
коуровневой команды git write-tree.
Для просмотра содержимого индекса введите следующую команду (на мо­
мент ее ввода индекс содержал только один файл - hello.txt):
$ git ls-files -s
100644 ЗЫ8e512dЬa79e4c8300dd08aeb37f8e728b8dad О hello.txt

CD-------------------------·-------------------------------------------------------'

• python____________________

Глава 21. Контроль кода

Здесь мы видим, что фацл hello.txt соответствует блобу ЗЫ8е5.... Далее, да­
вайте получим состояние индекса и сохраним его как объект дерева:
$ git write-tree
68aba62e560cOebc3396e8ae9335232cd93a3f60
$ find .git/objects
.git/objects
.git/objects/68
.git/objects/68/aba62e560c0ebc3396e8ae9335232cd93a3f60
.git/objects/pack
.git/objects/Зb
.git/objects/3Ь /18e512dba79e4c8300dd08aeb37f8e728b8dad
.git/objects/info

Теперь у нас есть два объекта: объект "hello world" с ID ЗЬ18е5 и новый объ­
ект, объект дерева, с ID 68аЬа6. Как видите, имя объекта соответствует ка­
талоrу и файлу в каталоге .git/objects.
Но как выглядит само дерево? Поскольку дерево - это тоже объект, подоб­
но блобу, вы можете использовать ту же команду для его просмотра:
$ git cat-file -р 68аЬаб
100644 ЫоЬ ЗЫ8e512dЬa79e4c8300dd08aeb37f8e728b8dad hello.txt

Содержимое объекта просто интерпретировать. Первое число - 100644 представляет атрибуты файла в восьмеричной системе, если вы работали с
Unix, то вы с ними знакомы. Далее идет имя (ЗЫ8е5) объекта, а hello.txt
имя файла, связанное с блобом. ·

ПРИМЕЧАНИЕОТНОСИТЕЛЬНО ИСПОЛЬЗОВАНИЯ
SHA1
ПеJ?ед более подробным рассмотрением содержимого объекта дерева, да­
вайте посмотрим на очень важную функцию SНА1-хэшей:
$ git write-tree
68aba62e560c0ebc3396e8ae9335232cd93a3f60
$ git write-tree
68aba62e560c0ebc3396e8ae9335232cd93a3f60

'---------------------------------------------------------------------------------CD

Python. Полное руководство

....____________________О

python

$ git write-tree
68aba62e560c0ebc3396e8ae9335232cd93a3f60

Каждый раз, когда Вы вычисляете другой объект дерева для того же индек­
са, хэш SHA1 остается неизменным. Git не должен воссоздать новый объект
дерева. Если вы вводите эти команды на своем компьютере, то вы должны
увидеть те же хэши, что и приведенные в этой книге.
Хэш-функция - истинная функция в математическом смысле: для заданно­
го ввода она всегда производит один и тот же вывод.
Это чрезвычайно важно. Например, если вы создаете то же самое содержа­
ние как другой разработчик, независимо от того, гд� или когда или как вы
оба работаете, идентичный хэш - доказательство полной идентичности со­
держимого.
Но задержитесь на секунду. Разве SHA1 хэши не являются уникальными?
Что произошло с триллионами людей с триллионами блобов в секунду, ко­
торые никогда не произведут одну единственную коллизию? Это частый
источник недоразумений среди новых пользователей Git. Внимательно
продолжайте читать далее, поскольку если вы сможете понять эту разницу,
тогда все остальное в этой главе - просто.
Идентичные хэши SHA1 в этом случае не считаются коллизией. Коллизия
- это если два разных объекта производят один и тот же хэш. Здесь же мы
создали два отдельных экземпляра одного и того же содержимого, поэто­
му хэш одинаковый (у одного и того же контента всегда будет одинаковый
хэш).
Git зависит от другого последствия хеш-функции SHA1: не имеет значения,
как вы получили дерево, названное 68aba62e560c0ebc3396e8ae9335232cd93
a3f60. Если оно есть у вас, вы можете быть полностью уверены, что это - тот
же древовидный объект, который, скажем, есть у другого читателя этой кни­
ги. Боб, возможно, создал дерево, комбинируя фиксации А и В от Дженни и
фиксации С от Сергея, тогда как вы получили фиксацию А от Сью и обнов­
ление от Лакшми, которое комбинирует фиксации В и С. Результат - тот же,
и это существенно упрощает распределенную разработку.
Если вас попросили найти объект 68aba62e560c0ebc3396e8ae9335232cd93a
Зf60 и вы можете найти именно этот объект, поскольку SHA1 - криптогра­
фический хэш и вы можете быть уверены, что нашли именно те данные, по
которым был создан хэш.



CD------······-············-··-············-·········-············--··--··········-··

.python..... :� .......... : ..

Глава 21. Контроль кода

Также истинно: если вы нашли объект с определенным хэшем в вашем хра­
нилище объектов, вы можете быть уверены, что у вас нет копии этого объек­
та. Хэш таким образом является надежной меткой или именем для объекта.
. Но Git также полагаепщ на что-то более важное, чем просто заключение.
Рассмотрим самую последнюю фиксацию ( или связанный с ней объект де­
рева). Поскольку она содержит, как часть ее контента, хэш ее родительский
фиксаций и ее дерево содержит хэш всех его поддеревьев и блобов. А это
означает, что хэш исходной фиксации однозначно определяет состояние це­
лой структуры данных, которая основана на той фиксации.
Наконец, импликации нашего требования в предыдущем абзаце приводят
к мощному использованию хеш-функции: она предоставляет эффективный
способ сравнения двух объектов, даже очень больших и сложных структур
данных без передачи этих объектов полностью.

ИЕРАРХИЯ ДЕРЕВЬЕВ
Хорошо иметь информацию относительно одного файла, как было показано
в предыдущем разделе, но проекты обычно более сложны и содержат глу­
боко вложенные каталоги, которые со временем перестраиваются и переме­
щаются. Давайте посмотрим, как Git обработает создание нового каталога,
в который мы поместим полную копию файла hello.txt:
$ pwd
/tmp/hello
$ mkdir subdir
$ ер hello.txt sцbdir/
$ git add subdir/hello.txt
$ git write-tree
492413269336d21fac079d4a4672e55d5d2147ac
$ git cat-file -р 4924132693
100644 ЫоЬ 3Ы8e512dЬa79e4c8300dd08aeb37f8e728b8dad hello.txt
040000 tree 68aba62e560c0ebc3396e8ae9335232cd93a3f60 sub�ir

В новом корневом дереве теперь есть два элемента: исходный файл hello.txt
и новый подкаталог subdir, который выводится как tree, а не как ЫоЬ.
Что же тут необычного? Посмотрите на имя объекта subdir. Это наш старый
друг - 68aba62e560c0ebc3396e8ae9335232cd93a3f60!



··················································································tD

.. ············-·······ф python

Python. Полное руковоцство

Новое дерево для subdir содержит только один файл, hello.txt и этот файл
содержит старый контенt - строку "Ъello world". Поэтому дерево subdir в
глазах Git выглядит так же, как и корневой каталог.
Теперь давайте посмотрим на каталог .git/objects и посмотрим, на что
повлияло это новое изменение:
$ find .git/objects
.git/objects
.git/objects/49
.git/objects/49/2413269336d21fac079d4a4672e55d5d2147ac
.git/objects/68
.git/objects/68/aba62e560c0ebc3396e8ae9335232cd93a3f60
.git/objects/pack
.git/objects/Зb
.git/objects/3Ь/18e512dba79e4c8300dd08aeb37f8e728b8dad
.git/objects/info

У нас все еще есть три уникальных объекта: блоб со строкой "hello world";
дерево, содержащее hello.txt, в котором есть текст "hello world" плюс новая
· строка; второе дерево, которое содержит другую ссылку на hello.txt в первом
дереве.

ФИКСАЦИИ
Следующий объект для обсуждения - фиксации. Теперь, когда hello.txt был
добавлен с помощью git add и был произведен объект дерева командой git
writer·tree, вы можете создать объект фиксации, используя следующие ко·
манды:
$ echo -n "Commit а file that says hello\n" \
1 git coппnit-tree 492413269336d21fac079d4a4672e55d5d2147ac
3ede4622cc241bcb09683af36360e7413b9ddfбc

Результат будет примерно таким:
$ git cat-file -р Зеdе462
tree 492413269336d21fac079d4a4672e55d5d2147ac
author Jon Loeliger 1220233277 -0500
committer Jon Loeliger 1220233277 -0500
Commit а file that says hello



CD -------------------------------------------------------------. -----------. -. -----.

• python_.._________________

Глава 21. Контроль кода

Если вы выполняете примеры из этой книге на своем компьютере, вы, веро­
ятно, обнаружили, что у объекта фиксации, сгенерированного вами, будет
другое имя, которое отличается от приведенного в книге. Если вы до сих
пор понимали все написанное, причина должна быть очевидной: это не та
фиксация. Ведь фиксация содержит ваше имя и время создания фиксации,
а эти данные в вашем случае будут отличаться. С другой стороны, у вашей
фиксации есть то же дерево, ЧТ!) и в примерах.
Это то, почему объекты фиксации отделяются от их объектов дерева: раз­
ные фиксации часто относятся к одному и тому же дереву. Когда это проис­
ходит, Git достаточно умен, чтобы передать только новый объект фиксации,
который обычно крошечный, вместо копирования всего дерева и всех блоб­
объектов, которые, наверняка, гораздо больше.
В реальной жизни вы можете (и должны) вызвать команды git write-tree и
git commit-tree, и потом просто использовать команду git commit. Вам не нуж­
но помнить все команды, чтобы быть счастливым пользователем Git.
Основной объект фиксации довольно прост и содержит следующее:
• Имя объекта дерева, который фактически идентифицирует связанные
файлы.
• Имя человека, создавшего новую версию (author) и время создания этой
версии.
• Имя человека, который поместил новую версию (commiter) в репозиторий и время фиксации.
• Описание версии (сообщение о фиксации).
По умолчанию author и commiter - это один и тот же человек. Но существу­
ют ситуации, когда это разные люди.
Примечание. Вы можете использовать команду git show
-pretty=fuller для просмотра дополнительной информации о за­
данной фиксации.
Объекты фиксации также хранятся в виде структуры графа, хотя эта струк­
тура полностью отличается от структур, которые используются объектами
дерева. Когда вы производите новую фиксацию, вы можете указать одну
или больше родительских фиксаций.

•.

-.. -.... - . - ........ - . -. - . -............... -- - . -.. -... - . -. - . - . -- . - . - . - - -- · .. - - - . - - .

-

Python. Полное руководство

ТЕГИ
Наконец, мы добрались до последнего объекта, которым управляет Git
тег. Хотя в Git реализован тег только одного вида, на самом деле существует
два вида тегов - легковесный и аннотированный.
Легковесный тег - это просто ссылка на объект фиксации и обычно он част­
ный дл.я репозитория. Эти теги не создают постоянный объект в хранилище
объектов. Аннотированный тег более существенный и создает объект, со­
держащий сообщение, предоставленное вами и может содержать цифровую
подпись, созданную с помощью GРG-ключа согласно RFC4880.
Git обрабатывает, как легковесные, так и аннотированные теги, но по умол­
чанию большинство тегов Git работают только с аннотированными тегами,
поскольку считают их "постоянными" объектами.
Создать аннотированный тег с сообщением можно командой git tag.
$ git tag -m "Tag version 1.0" Vl.0 Зеdе462

Увидеть объект тега можно командой git cat-file -р, но как узнать хэш SHA1
объекта тега? Чтобы найти его,используйте совет из "Объекты, хэши и бло­
бы":
$ git rev-parse Vl.O
бЬ608с1093943939ае78348117dd18ЫЬа151сба
$ git cat-file -р бЬб08с
object 3ede4622cc24lbcb09683af36360e7413b9ddfбc
type commit
tag Vl.0
tagger Jon Loeliger Sun Oct 26 17:07:15 2008
-0500
Tag version 1.0

В дополнение к сообщению журнала и информации об авторе тег ссылается
на объект фиксации (Зеdе462).
Git обычно тегирует объект фиксации, указывающий на объект дерева, ох­
ватывающее общее состояние всей иерархии файлов и каталогов в вашем
репозитории.
Поведение Git не похоже на другие СКБ, где тег применяется отдельно к
каждому файлу, а затем на основании коллекции этих теггированных фай-

------------------------------------·--·�----·--·-············-···-··------·-···-'

• python....................

Глава 21. Контроль кода

лов воссоздается целая теггированная версия. Другие СКБ позволяют вам
перемещать теги отдельных файлов, а Git требует создания новой фикса­
ции, которая охватывает изменение состояние файла, тег которого был пе­
ремещен.

21.6. Управление файлами
Когда ваш проект на попечении системы контроля версий, вы редактируете
файлы проекта в своем рабочем каталоге, а потом передаете свои изменения
в ваш репозиторий для сохранности. Git работает также, но в нем есть про­
межуточный уровень между рабочим каталогом и репозиторием - индекс.
Индекс используется для подготовки, или организации, изменений. Когда
вы управляете своим кодом с помощью Git, вы редактируете изменения в
своем рабочем каталоге, накапливаете их в своем индексе, а затем фиксиру­
е,те то, что накопилось в индексе как один набор изменений.
Вы можете думать об индексе, как о ряде намеченных или предполагаемых
модификаций. Вы добавляете, удаляете, перемещаете или редактируете
файлы до точки фиксации, которая реализовывает накопленные файлы
в репозитории. Большая часть важной работы фактически предшествует
шагу фиксации.
Примечание. Помните, что фиксация - двухступенчатый про­
цесс. Сначала вы подготавливаете изменения, а потом вы фик­
сируете изменения. Изменение, найденное в рабочем каталоге,
но не в индексе, не подготовлено и не может фиксироваться.
Для удобства Git позволяет вам комбинировать эти два шага,
когда вам нужно добавить или изменить файл:
$ git commit index.html

Но если вы переместили или удалили файл, у вас не будет такой
роскоши. Вам нужно будет выполнить два действия:
$ git rm index.html
$ git commit

Сейчас мы поговорим, как управлять индексом. Будет показано, как добав­
-11ять и удалять файлы из вашего репозитория, как переименовать файл и
т.д.



··················································································-

Python. Полное руководство

___________________ . ____• python

21.6.1. ВСЕ ОБ ИНДЕКСЕ
Линус Торвальдс утверждал в списке рассылки Git, что вы не можете осоз­
нать всю мощь Git без понимания назначения индекса.
Индекс Git не содержит названий файла, он просто отслеживает то, что вы
хотите фиксировать. Когда вы выполняете фиксацию, Git проверяет ин­
декс, а не ваш рабочий каталог.
Несмотря на то, что многие высокоуровневые команды Git разработаны,
чтобы скрыть детали индекса, все еще важно помнить об индексе и его со­
стоянии.
Вы можете запросить состояние индекса в любое время командой git status.
Она явно отображает файлы, подготовленные для фиксации (находящиеся
в индексе). Также вы можете узнать о внутреннем состоянии Git командами
вроде git ls-files.
Вероятно, вам пригодится команда git diff. Эта команда может вывести на
экран два различных набора изменений: git diffвыводит изменения, которые
есть в рабочем каталоге, но которых нет в индексе; git diff -cached выводит
изменения, которые уже подготовлены (находятся в индексе) и будут по­
мещены в репозиторий при следующей фиксации.

21.6.2. КЛАССИФИКАЦИЯ ФАЙЛОВ В GIT
Git классифицирует ваши файлы на три группы: отслеживаемые, игнориру­
емые и неотслеживаемые.

ОТСЛЕЖИВАЕМЫЕ
Отслеживаемым называется файл, уже находящийся в репозитории или в
индексе. Чтобы добавить файл somefile в эту группу, выполните команду git
add somefile.

ИГНОРИРУЕМЫЕ
Игнорируемый файл должен быть явно объявлен невидимым или игнори­
руемым в репозитории (даже при том, что он может присутствовать в ва­
шем рабочем каталоге). В вашем проекте может быть много игнорируемых
файлов: ваши личные заметки, сообщения, временные файлы, вывод ком­
пилятора и большинство файлов, сгенерированных автоматически во вре-



CD ... --. --·· .... ----. -----. -. --. -. ----------· ---·-. --. -·-. -. ------. -·-. ---. ----. ---.

• python....................

Глава 21. Контроль кода

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

НЕОТСЛЕЖИВАЕМЫЕ
Неотслеживаемым является любой файл, не относящийся к любой из пре­
дыдущих двух категорий. Git просматривает весь набор файлов в вашем ра­
бочем каталоге и вычитает из него от�леживаемые и игнорируемые файлы,
в результате получается список неотслеживаемых файлов.
Давайте исследуем разные категории файлов, создав совершенно новый ра­
бочий каталог и репозиторий:
$ cd /uлp/шy_stufJ.
$ git init
$ git status
# On branch master
#
# Initial commit
#
nothing to commit (create/copy files and use "git add" to
track)
$ eclio "Новuе даННl�lе" > data
$ git status
# On branch master
#
# Initial commit
#
# Untracked files:
# (use "git add ..." to include in what will Ье
committed)
#
# data
nothing added to commit but. untracked files present (use "git
add" to track)



··················································································-

Pythoп. Полное руководство

6PYthon

------------ --- ------- ? .gitignore
$ git status
# Оп branch rnaster
#
# Initial cornrnit
#
# Untracked files:
# (use "git add ... " to include in what will Ье
cornrnitted)
#

# .gitignore
# data

Как видите, файл main.o был проигнорирован, но git status показал новый
неотслеживаемый файл .gitignore. Хотя этот файл имеет специальное на­
значение в Git, он обрабатывается как любой другой обычный файл в пре­
делах вашего репозитория. Пока .gitignore не будет добавлен, Git будет рас­
сматривать его как неотслеживаемый.



....---------- •.--- ----- - -- .... -......................··- - .. - .- ....... -..- ·-·-·о. -- - ..

.python....................

Глава 21 . Контроль кода

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

21.6.3. ИСПОЛЬЗОВАНИЕ GIT ADD
Команда git add организует файл, после следующей фиксации (git commit)
такой файл будет добавлен в репозиторий. В терминологии Git файл будет
неотслеживаемым, пока он не будет добавлен командой git add, что изменит
его статус на отслеживаемый. Если команде git add передать имя каталога,
все файлы и подкаталоги этого каталога будут добавлены рекурсивно.
Давайте продолжить пример из предыдущего раздела:
$ git status
# On branch master
#
# Initial commit
#
# Untracked files:
t (use "git add ... " to include in what will Ье
committed)
#
# .gitignore
# data
# Track both new files.
$ git add data .gitignore
$
#
#
#
#
#
#
#
#
#
#

git status
On branch master
Initial commit
Changes to Ье committed:
(use "git rm --cached ... " to unstage)
new file: ;gitignore
new file: data

Первая команда·git status показывает, что у нас есть два неотслеживаемых
айла и напоминает, что для того, чтобы сделать файл отслеживаемым,
/

····························"·····················································-

Python. Полное рукvводство

________________________• python

нужно просто использовать команду git add. После выполнения команды git
add оба файла ( data и .ignore) будут отслеживаться и будут подготовлены
для помещения в репозиторий при следующей фиксации.

В терминах объектной модели Git добавление файла командой git add оз­
начает копирование файла в хранилище объектов и его индексирование.
Организацию файла (git add) так же называют "кэшированием файла" или
"помещением файла в индекс".
Вы можете использовать команду git ls-files для определения хэш-кодов для
организованных файлов:
$ git 1s-files --stage
100644 0487f44090ad950f61955271cf0a2d6c6a83ad9a О .gitignore
100644 534469f67ae5ce72a7a274faf30dee3c2ea1746d О data

Большинство ежедневных изменений в вашем репозитории, вероятно, бу­
дут простыми правками. После редактирования и перед фиксацией изме­
нений нужно запустить git add для обновления индекса, чтобы занести
в него последнюю и самую высокую версию вашего файла. Если вы это не
сделаете, у вас будут две разные версии файла: одна из хранилища объек­
тов, на которую ссылается индекс, и другая - в вашем рабочем каталоге.
Чтобы продолжить пример, давайте изменим данные файлы так, чтоб-1>1 они
отличались от тех, которые есть в индексе. После сего, командой git hash­
object вычислим и отобразим хэш новой версии файла.
$ git 1s-files --stage
100644 0487f44090ad950f61955271cf0a2d6c6a83ad9a О .gitignore
100644 534469f67ae5ce72a7a274faf30dee3c2ea1746d О data
# отредактируйте файл "data" ...
$ cat data
Новые данные
Еще немного данных
$ git hash-object data
e476983f39f6e4f453f0fe4a859410f63b58b500
After the file is amended, the previous version of the file in
the object store and index
has SHAl 534469f67ae5ce72a7a274faf30dee3c2ea1746d. However,
the updated version
of the file has SHAl e476983f39f6e4f453f0fe4a859410f63b58b500.
Let's update the
index to contain the new version of the file:

...................................................................................

,

Глава 21 . Контроль кода

• python_________ • __________

$ git add data
$ git ls-files �-stage
100644 0487f44090ad950f61955271cf0a2d6c6a83ad9a О .gitignore
100644 e476983f39f6e4f453f0fe4a859410f63b58b500 О data

Теперь индекс содержит обновленную версию файла. Снова, "данные фай­
ла были подготовлены (для фиксации)" или, проще говоря, "данные файла
были помещены в индекс". Последняя фраза не очень точная, но зато более
понятная.
Опция --interactive для команд git add или git commit может быть полезна,
чтобы узнать, какие файлы вы бы хотели подготовить для фиксации.

21.6.3. НЕКОТОРЫЕ ЗАМЕЧАНИЯ ОТНОСИТЕЛЬНО
ИСПОЛЬЗОВАНИЕ GIT COMMIT
ИСПОЛЬЗОВАНИЕ GIT COMMIT -ALL
Опция -а или --all командь1 git commit заставляет Git автоматически под­
готавливать все неподготовленные, прослеженные изменения файла, в том
числе удаление отслеживаемых файлов из рабочей копии, перед осущест­
влением фиксации.
Давайте посмотрим, как это работает, установив несколько файлов с разны­
ми характеристиками подготовки:
# Ус�анавливаем тестовый репозитарий
$ mkdir /tmp/commit-all-example
$ cd /tmp/commit-all-example
$ git init
Initialized empty Git repository in /tmp/commit-all-example/.
git/
$ echo something >> ready
$ echo somthing else >> notyet
$ git add ready notyet
$ git commit -m "Setup"
[master (root-commit) 71774al] Setup
2 files changed, 2 insertions(+), О deletions(-)
create mode 100644 notyet
create mode 100644 ready
# Изменяем файл "ready" и добавляем командой "git add" его в
индекс
$ git add ready

•·- --



- - . -..... , ................................. ' ...................... - ... - ... - - .

-•

Python. ПолноР. руководпво

________________________О python

# Изменяем файл "notyet"
# редактируем "notyet"
# Добавляем новый файл в подкаталог, но не добавляем его в
репозиторий
$ mkdir subdir
$ echo Nope >> subdir/new

Используем git status, чтобы просмотреть изменения, требующие фиксации:
$ git status
# On branch master
# Changes to Ье committed:
# (use "git reset HEAD ..." to unstage)
#
# modified: ready
#
# Changed but not updated:
# (use "git add ... " to update what will Ье committed)
#
# modified: notyet
#
# Untracked files:
# (use "git add ..." to include in what will Ье
committed)
#
# subdir/

Здесь индекс подготовлен для фиксации только одного файла - с именем
ready, поскольку только этот файл был подготовлен (добавлен).
Однако, если вы запустите команду git commit -all, Git рекурсивно обойдет
весь репозиторий, организует все известные, измененные файлы. В этом
случае, когда ваш редактор представит шаблон сообщения фиксации, он
должен указать, что измененный и известный файл notyet тоже будет фик­
сироваться:
#
#
#
#
#
#

Please enter the commit message for your changes.
(Comment lines starting with '#' will not Ье included)
On branch master
Changes to Ье committed:
(use "git reset HEAD ..." to unstage)



CD---.......-............----. -................. -............---.......---....... -.. .


.

: python___________________ ;

ГлаЕ:[• 21. Контроль кода

# modified: notyet
# modified: ready
#
# Untracked files:
# (use rtgit add ... " to include in what will Ье
committed)
#
# subdir/

Наконец, поскольку каталог с именем subdir/ новый и в нем не находится
ни один из файлов, даже опция --all не заставит его фиксироваться:
Created commit db7de5f: Some --all thing.
2 files changed, 2 insertions(+) 1 О deletions(-)

Git рекурсивно обойдет репозиторий в поисках измененных и файлов. Пол­
ностью новый каталог subdir/ и всего его файлы не станут частью фиксации.

НАПИСАНИЯ СООБЩЕНИЙ ЖУРНАЛА ФИКСАЦИИ
Если в командной строке вы явно не указываете сообщение журнала, Git
запустит редактор и предложит вам написать его. Будет запущен установ­
ленный в вашей конфигурации редактор.
Если щ,1 выйдете из редакторе без сохранения, Git будет использовать пу­
стое сообщение журнала. Если вы уже сохранили сообщение, но еще не
вышли из редактора, вы можете удалить сообщение и опять сохранить
тогда Git тоже сохранит пустое сообщение фиксации.

21.6.4. ИСПОЛЬЗОВАНИЕ GIT RM
Команда git rm, как и ожидается, обратна для git add. Она удаляет файл из
репозитория и рабочего каталога. Однако удаление файла может быть бо­
лее проблематичным (если что-то пойдет не так), чем добавление файла.
Поэтому Git относится к удалению файла с большей осторожностью.
Git может удалить файл только из индекса или одновременно из индекса и
рабочего каталога. Git не может удалить файл только из рабочего каталога,
для этого используется команда операционной системы rm.



··-····-·--·----······-·----------·-···················-·-···-····-·-·-·-·········-

Python. Полное руководе� во

,l.1python

�--••-•·--···-··--··--·· �v

Удаление файла из каталога и из индекса не удаляет историю файла из ре­
позитория. Любая версия файла до момента удаления хранится в хранили­
ще объектов и не будет оттуда удалена.
Давайте представим, что у нас есть некоторый файл, который мы еще не
добавили в репозиторий (не выполнили команду git add), и мы пытаемся
удалить его командой git rm:
$ echo "Random stuff" > oops
# Не можем удалить файл, не добавленный в репозиторий
# Нужно использовать команду "rm oops"
$ git rm oops
fatal: pathspec 'oops' did not match any files

Теперь давайте добавим файл командой git add, а затем выполним команду
get status:
#
$
$
#
#
#
#
#
#
#
#
#
#
#

Добавляем файл
git add oops
git status
On branch master
Initial commit
Changes to Ье committed:
(use "git rm --cached ..." to unstage)
new file: .gitignore
new file: data
new file: oops

Чтобы конвертировать файл из подготовленного в неподготовленный, ис­
пользуйте команду git rm - -cached:
$ git ls-files --stage
100644 0487f44090ad950f61955271cf0a2d6c6a83ad9a О .gitignore
100644 e476983f39f6e4f453f0fe4a859410f63b58b500 О data
100644 fcd87b055f261557434fa9956e6ce29433a5cdlc О oops
$ git rm --cached oops
rm 'oops'
$ git ls-files --stage

.........





- . -..·.................................... -................................ .

О python___________ �-______ _

Главt1 21. Контроль кода

100644 0487f44090ad950f6195527lcf0a2d6c6a83ad9a О .gitignore
100644 e476983f39f6e4f453f0fe4a859410f63b58b500 О data

Обратите внимание: git rm --cached удаляет файл только из индекса, но
оставляет его в рабочем каталоге, в то время как команда git rm удаляет
файл, как из индекса, так и с рабочего каталога.
Примечание. Использование команды git rm --cached делает
файл неотслеживаемым, в то время как его копия остается в ра­
бочем каталоге. Это довольно опасно, так как вы можете забыть
о нем, и файл больше никогда не будет отслеживаемым. Будьте
осторожны!

Если вы хотите удалить файл, как только он был зафиксирован, просто
отправьте запрос через команду git rrn :
$ git commit -m "Add some files"
Created initial cornmit 5Ь22108: Add some files
2 files changed, 3 insertions(+), О deletions(-)
create mode 100644 .gitignore
create mode 100644 data
$ gitrm data
rm 'data'
$ git status
# On branch master
# Chan9es to Ье cornmitted:
# (use "git reset HEAD ... " to unstage)
#
# deleted: data
#

Перед удалением файла Git проверяет, соответствует ли версия файла в
рабочем каталоге последней версии в текущем ответвлении (в версии, ко­
торую команды Git называют HEAD). Эта проверка устраняет случайную
потерю любых изменений, которые, возможно, были сделаны в файле.
Примечание. Используйте команду git rm -f для принудительно­
го удаления вашего файла. В этом случае файл будет удален,
даже если он был изменен с момента последней фиксации.



·----·-···--·-···-···------·-·-·----·------·----·-·····························-·--

Python. Полное руv.аводиво

6python

------------------------

�$)

Если вам нужно сохранить файл, который вы случайно удалили, просто
добавьте его снова:
$ qit add data
fatal: pathspec 'data' did not match any files

Ошибочка вышла! Git ведь удалил и рабочую копию тоже! Но не волнуй­
тесь, VCS содержит отличный механизм восстановления старых версий
файлов:
$ qit checkout НЕАD -- data
$ cat data
Новые данные
Еще немного новых данных
$ qit status
# On branch master
nothing to commit (working directory clean)

21.6.5. ИСПОЛЬЗОВАНИЕ GIT MV
Предположим, что вам нужно удалить или переименовать файл. Вы можете
использовать комбинацию командgitrт (для удаления старого файла) иgit
add (для добавления нового файла). Или же вы можете использовать не­
посредственно команду git mv. Представим, что в нашем репозитории есть
файл с именем stuff и вы хотите переименовать его в newstuff. Следующие
две последовательности действий являются эквивалентными:
$ mv stuff newstuff
$ git rm stuff
$ git add newstuff

или
$ git mv stuff newstuff

В обоих случаях Git удалит путь stuff из индекса, добавит новый путь
newstujf, сохраняя оригинальное содержимое stuff в хранилище объектов и
реассоциирует это содержимое с путем newstuff.



CID-----------------------------------------------------··-···-······-·-·-··········-·



python___________________ �

Глава 21. Контроль кода

Файл data мы уже восстановили, теперь давайте его переименуем в mydata:
$
$
#
#
#
#
#
#

git mv data mydata
git status
On branch master
Changes to Ье committed:
(use �git reset HEAD ... " to unstage)
renamed: data -> mydata

$ git commit -m "Moved data to mydata"
Created commit ec7d888: Moved data to mydata
1 files changed, О insertions(+), О deletions(-)
rename data => mydata (100%)

Если вы проверите историю файла, вы можете быть немного удивлены, ког­
да увидите, что Git, очевидно, специально потерял историю исходного фай­
ла и помнит, только, что данные были переименованы:
$ git log mydata
commit.ec7d888b6492370a8ef43f56162a2a4686aea3b4
Author: Jon Loeliger
Date: Sun Nov 2 19:01:20 2008 -0600
Moved data to mydata

Git все еще помнит всю историю, но отображает только то, что касается
определенного имени файла, указанного в команде. Опция --follow просит
Git отследить журнал и найти всю историю, связанную с контентом:
$ git log --follow mydata
commit ec7d888b6492370a8ef43f56162a2a4686aea3b4
Author: Jon Loeliger
Date: Sun Nov 2 19:01:20 2008. -0600
Moved data to mydata
commit Sb22108820b6638a86bf57145a136f3a7ab71818
Author: Jon Loeliger
Date: Sun Nov 2 18:38:28 2008 -0600



·----------------------------- ----------------------------------------------------

Python. Полное руководство

.

fl:№python

------------------------ fry

Add some files

Одна из классических проблем многих VCS заключается в том, что после
переименования файла невозможно отследить его историю. В Git эта
проблема решена.

21.6.7. ЗАМЕЧАНИЕ ОТНОСИТЕЛЬНО
ОТСЛЕЖИВАНИЯ ПЕРЕИМЕНОВАНИЙ
Давайте немного подробнее рассмотрим отслеживание переименований
файла.
SVN, как пример традиционного управления версиями, производит боль­
шую работу по отслеживанию во время переименования/перемещения
файла, поскольку она отслеживает только разницу между файлами. Если
вы перемещаете файл, то это, по сути, то же, что и удаление всех строк из
старого файла и их добавление в новый файл. Но передавать все содержа­
ние файла каждый раз, когда вы делаете простое переименование, очень не­
эффективно. Подумайте о том, что будет, если нужно переименовать целый
подкаталог с тысячами файлов.
Чтобы облегчить эту ситуацию, SVN явно отслеживает каждое переимено­
вание. Если вы хотите переименовать hello.txt в subdir/hello.txt, вы должны
использовать команду svn mv вместо команды svn rm и svd add. Иначе SVN
никак не поймет, что это переименование и ему придется пойти по неэф­
фективному пути удаления/добавления, что и было описано выше.
Затем, учитывая
эту исключительную функцию отслеживания переиме­
V
нования, S N нуждается в специальном протоколе, чтобы сказать его кли­
ентам: "переместите файл hello.txt в subdir/hello.txt". Кроме того, каждый
клиент SVN должен убедиться, что выполнил эту работу правильно.
Git, с другой стороны, не отслеживает переименование. Вы можете пере­
местить или скопировать hello.txt куда угодно, но это влияет только на
объекты дерева. Помните, что объекты дерева хранят отношения между
содержимым, тогда как само содержимое хранится в блобах. Посмотрев на
разницу между двумя деревьями, становится очевидным, что блоб с именем
ЗЬ18е5 переместился в новое места.
В этой ситуации, как и во многих других, система хранения Git, основанная
на хэше, упрощает много вещей по сравнению с другой RCS.

а:а-.... -..... -............--..----..........-......................................•,

• python____________________

Глава 21. Контроль кода

ПРОБЛЕМЫ С ОТСЛЕЖИВАНИЕМ
ПЕРЕИМЕНОВАНИЯ
Отслеживание переименования файла порождает постоянные дебаты среди
разработчиков VCS.
Простье переименование - объект разногласия. Аргументы становятся еще
более весомыми, когда изменяется и имя, и содержимое файла. Тогда сце­
нарий переговоров переходит от практического к философскому. Что это:
переименование или новый файл (раз у него другое содержимое и другое
имя)? Насколько новый файл подобен старому? Если вы применяете чей­
то патч, который удаляет файл и воссоздает подобный в другом месте, как
это обрабатывать? Что произойдет, если файл переименован двумя различ­
ными способами на двух разных ветках? Какая тактика менее подвержена
ошибкам: используемая в Git или в SVN?
В реальной жизни, похоже, что система отслеживания переименований, ис­
пользуемая в Git, очень хороша, поскольку есть много способов переиме­
новать файл и человек может просто забыть уведомить SVN об этом. Но
помните, что нет идеальной системы для обработки переименований, к со­
жалению.

21.6.8. ФАЙЛ .GITINGNORE
Ранее в этой главе было показано, как использовать файл .gitignore для иг­
норирования файла main.o. Чтобы проигнорировать любой файл, просто
добавьте его имя в файл .gitignore, который находится в этом же катало­
ге. Вы -также можете игнорировать файлы где угодно, добавив его в файл
.gitignore, который находится в корневом каталоге вашего репозитория.
Но Git предоставляет более богатый механизм. Файл .gitignore может со­
держать список шаблонов имен файлов, указывающий, какие файлы нужно
игнорировать. Формат .gitignore следующий:
• Пустые строки игнорируются, как и строки, начинающиеся с решетки
(#).Такие строки можно использовать для комментариев, однако символ
# не представляет комментарий, если он не является первым в строке.
• Обычные имена файлов соответствуют файлу в любом каталоге с ука­
занным именем.

•··-·-........·:.... -........ --.......... -...-.-.-...-· -·..·-..... -·...-...-. -.....at

Python. Полное ру�.•�водс во
0

•••• ____________________• python

• Имя катала отмечается с помощью слеша(/). Это правило соответствует
любому каталогу или любому подкаталогу, но не соответствует файлу
или символической ссылке.
• Шаблон может содержать маски оболочки, такие как звездочка(*). Звез­
дочка может соответствовать единственному имени файла или каталога.
Также звездочка может быть частью шаблона, включающего наклонные
черты для обозначения имен каталогов, например, debug/32Ьit/*.о.
• Восклицательный знак(!) инвертирует смысл шаблона оставшейся ча­
сти строки. Дополнительно, любой файл, исключенный предшеству­
ющим образцом, но соответствующий этому правилу инверсии, будет
включен. У инверсии более высокий приоритет.
Кроме того, Git позволяет вам создавать файл .gitignore в любом каталоге
вашего репозитория. Каждый такой файл влияет на свой каталог и все под­
каталоги. Правила .gitignore каскадные: вы можете переопределить прави­
ла в каталоге более высокого уровня, включив инвертированный шаблон(с
использованием ! в начале правила) в одном из подкаталогов.
Приоритет игнорирования следующий:
• Шаблоны, определенные в командной строке
• Шаблоны, прочитанные из файла .gitignore, находящего в том же ката­
логе
• Шаблоны в родительских каталогах. Шаблоны, находящиеся в текущем
каталоге, будут перезаписывать шаблоны родительского каталога, сле­
довательно, шаблоны более близкого родителя перезапишут шаблоны
родителя более высокого уровня.
• Шаблоны из файла .git/info/exclude
• Шаблоны из файла, указанного переменной конфигурации core.
excludefile.
Поскольку .gitignore обрабатывается как обычный файл в вашем репози­
тории, он копируется во время операций клонирования и применяется ко
всем копиям вашего репозитория.
Если образец исключения, так или иначе определенный для одного вашего
репозитория, не должен применяться к какому-нибудь клону этого репози­
тория, то шаблоны должны находиться в файле .git/info/exclude, поскольку



------------------------------------------------------------------------------------·

• python_____________-_______

Глава 21. Контроль кода

он не копируется во время операций клонирования. Формат его шаблонов
полностью совпадает с форматом файла .gitignore.
Рассмотрим другой сценарий. Нам нужно исключить файлы * .о, сгенериро­
ванные компилятором из исходного кода. Для игнорирования этих файлов
поместите шаблон * .о в файл .gitignore самого верхнего уровня. Но что если
в определенном каталоге у вас есть определенный .о файл, который нужно
отследить? Тогда у вас может быть примерно эта конфигурация:
$ cd my_package
$ cat .gitignore
*.о
$ cd my_package/vendor_files
$ cat .gitignore
!driver.o

Данная конфигурация игнорирует все .о файлы в репозитории, но Git будет
отслеживать одно исключение - файл driver.o в подкаталоге vendor_files.



·---------·-·-·-·-··-·-·-· ···--··--·---·-·------·-·-·-··-···-·-·-·--·-·-·-·-·-----

ГЛАВА 22.
ОПТИМИЗАЦИЯ КОДА
РУТНОN

ф python____________________

Глава 22. Оптимизация кода Python

Профилирование используется для поиска узких мест в коде программы.
Посредством профилирования разработчик или тестировщик может найти
части кода, выполняющиеся дольше остальных. Далее, разработчик может
оптимизировать эти части так, чтобы они выполнялись быстрее. В Python
имеется три встроенных профайлера: cProfile, profile и hotshot. Последний
использовать не рекомендуется - он устарел и уже не поддерживается. Mo­
дyльprofile это в корне своем модуль Python, но добавляет много чего сверху
в профилированные программы. Поэтому лучше использовать cProfile, ко­
торый содержит интерфейс, имитирующий модуль profile.

22. 1. Профилирование кода с
помощью cProfile
Профилировать код с помощью cProfile достаточно просто. Вам нужно толь­
ко импортировать модуль и вызвать команду run. Сразу рассмотрим
простенький пример:
import hashlib
import cProfile



. . •.... - .... -..................... -................ -.. -- - -- -.......................

-

Python. Пошюе руководство

,
6python
........... ............

·

cProfile. ru·n ( "hashlib .mdS (Ь'hello') . digest () ")

Результат профилирования показан на рис. 22.1.


� IDLE Shell 3.92

х

Eile fdit She!I Qebug Qptions Window 1:!elp

Python 3.9.2 (taqs/v3.9.2:la79785, Feb 19 2021, 13:44:55) [MSC v.1928 64 Ьit
D64) ] on win32
Туре "help", "copyright", "credits� or "license()" for Шоrе information.

>>> impoz.'t hashlib
>>> in1p;:,rt. cProfile
>>> cProfile � run (' hashl.ib .:mdS (tэ' tte.lJ.c'} �c.li g-,:;�st О 1t)
5 function calls in 0�001 seconds
1

Ordered Ьу: sta·ndard name

ncalls
mdS}

1
1

tottime

О.ООО
О.ООО

percall cumtime percall filename: lineno·(function)
0.001
0.001 :l()
О.ООО
О.ООО {built-in method _hash].ib.openssl_
О.ООО
О.ООО

О.ООО
О.ООО

О.ООО
О.ООО

0.001
О.ООО

0.001 {Ьuilt-in method builtins.exec}
О.ООО {method 'diqest' of '_hashlil>.НASR

1
О.ООО
iler' objects}

О.ООО

О.ООО

О.ООО {method 'disaЬle' of

1

objects)

»> 1

Рис. 22. 1. Результат профилирования

Здесь мы импортировали модуль hashlib и использовали cProfile для про­
филирования того, что создал хеш MDS. Первая строка показывает, что в
ней 5 вызовов функций. Следующая строка говорит нам, в каком порядке
результаты выдачи. Здесь есть несколько столбцов.
• ncalls - это количество совершенных вызовов;
• tottime - все время, потраченное в данной функции;
• percall - ссылается на коэффициент tottime, деленный на ncalls;
• cumtime - совокупное время, потраченное как в данной функции, так и
наследуемых функциях. Работает также и с рекурсивными функциями.



--···-·············································································

Глава 22. Оптимизация кода Python

• python____________________

• Второй столбец percall - это коэффициент cumtime, деленный на при­
митивные вызовы;
• filename:lineno(junction) предоставляет соответствующие данные о каж­
дой функции.
Примитивный вызов - это вызов, который был совершен без рекурсии. Это
очень интересный пример, так как здесь 1нет очевидных узких мест. Давайте
создадим часть кода с узкими местами, и посмотрим, обнаружит ли их про­
файлер.
# -*- coding: utf-8 -*­
import time
def fast():
рrint("Быстрая функция")
def slow():
time.sleep(4)
рrint("Медленная функция")
def medium():
time.sleep(0.5)
рrint("Средняя функция... ")
def main():
fast()
slow()
medium()
if

name
main()

main

1 .

Сохраните программу как proftest.py. В этом примере нами создано четы­
ре функции. Первые 1:ри работают с разными темпами. Быстрая функция
запустится с нормальной скоростью, средняя функция потратит примерно
. полсекунды на запуск, медленная функция потратит примерно 5 секунд
для запуска. Главная функция вызывает остальные три. Давайте запустим
cProfile в этой простой программе:



----------------------------------------------------

Python. Полное руководство
import cProfile
import proftest
cProfile.run ( 'proftest.main() ')
Рис. 22.2. Результат профилирования

На этот раз мы видим, что у программы ушло 4.536 секунды на запуск. Если
вы изучите результаты, то увидите, что cProfile выявил медленную функ­
цию, которая тратит 4 секунды на запуск. Это и есть самая "слабая" часть
основной функции. Обычно, когда вы обнаруживаете такие места, есть два
варианта развития ситуации:
1. Вы приходите к заключению, что такая задержка приемлема.
2. Переписываете алгоритм (код), если это возможно.
В нашем простом примере, мы знаем, что лучший способ ускорить функцию,
то убрать вызов time.sleep, или, до крайней мере, снизить продолжитель­
ность сна. На практике бывает сложно определить, как можно оптимизиро­
вать программу и часто оптимизация заключается в изменении алгоритма
ее работы. При необходимости можно также вызвать cProfile в командной
строке, вместо применения в интерпретаторе. Вот как это можно сделать:
python -m cProfile ptest.py

В этом случае cProfile будет запущен в вашем скрипте аналогично тому, как
мы делали это ранее. Вам нужно сохранить выдачу профайлера? cProfile
также позволяет это сделать. Все что вам нужно, это передать ему параметр
-о, за которым следует название (или путь) файла. Пример:
python -m cProfile -о output.txt proftest.py

К сожалению, файл результата едва ли можно назвать читаемым. Для его
чтения вам пригодится модуль Python pstats. Вы можете использовать
pstats для форматирования выдачи разными способами. Разберем пример,
демонстрирующий, как получить выдачу, по аналогии с тем, как мы делали
это раньше:
import pstats
р = pstats.Stats("output.txt")

CD-------------....--...·."· .......................-.-.. ----. ---..-.. -. ----.-... -.. '

• python__________ • _________

Глава 22. Оптимизация кода Python

p.strip_dirs().sort_stats(-1).print_stats()

Вызов strip_dirs вырезает все пути к модулям из вывода, а вызов sort_stats
делает сортировку, которая нужна нам для виденья картины. В документа­
ции по cProfile вы найдете множество интересных примеров, которые
наглядно демонстрируют различные пути извлечения информации с ис­
пользованием модуля pstats.
Конечно, можно использовать средства перенаправления ввода/вывода
операционной системы:
python -rn cProfile proftest.py > out.txt

В этом случае вы сразу получите читаемый тексrовый файл.

22.2. Практический пример:
вычисление скорости загрузки сайта
В данном примере мы пройдем небольшой марафон и узнаем, какой сайт
быстрее всех откроется из нашей программы.
# -*� coding: utf-8
irnport requests
irnport cProfile

-*-

def facebook():
requests.get('https://facebook.corn')
def google():
requests.get('https://google.corn')
def twitter():
requests.get('https://twitter.corn')
def lenta():
requests.get('https://lenta.ru')
def rnain():
facebook()
google()
twitter()

,

................................................................................ ...

Pytho11. Полное руководство

tl,python
--------------.----------

,,@%

lenta()
cProfile. run ( 'main () ')

Далее мы сократили вывод профайлера, удалив все лишнее:
48556 function calls (48476 primitive cal ls) in 2.158 seconds
Ordered Ьу: standard name
ncalls tottime percal l cumtime percal l filename: lineno(function)
1
1
1
1
1

О.ООО
О.ООО
О.ООО
О.ООО
О.ООО

О.ООО
О.ООО
О.ООО
О.ООО
О.ООО

1.023
0.462
0.216
2.158
0.457

1.023
0.462
0.216
2.158
0.457

speedtest.py:l0(google)
speedte�t.py:12(twitter)
speedte�t.py:14{lenta)
speedtest.py:18(main)
speedtest.py:6(facebook)

Самым медленным стал Google, затем - Twitter, чуть быстрее (хотя пример­
но такой же) - Facebook, а самый быстрый - Lenta.ru.

22"3" Событийные профайлеры
Событийные (event-based) профайлеры привязаны к событиям, как не­
сложно догадаться из их названия. Статистические профайлеры срабаты­
вают каждые N раз. А событийные профайлеры - только в определенных
случаях, например, после вызова функции или завершения работы функ­
ции (смотря, к какому событию они привязаны).
Событийные профилировщики заметно точнее статистических, но точ­
ность здесь достигается в ущерб скорости работы. Из-за этого ними редко
пользуются в продакшене. На Python мы можем написать свой профайлер
- при желании:
i mport sys
d ef profiler(frame, event, args):
print(frame.f_lineno, event, args)

•·············�·······································-··························'

О python____________________

Глава 22. Оптимизация кода Python

sys.setprofile(profiler)
def main(name):
print('Hello, %s!' % name)
if

name
== ' main
main('world')



Самое интересное здесь происходит после вызова функции sys.setprofile,
которая говорит интерпретатору, что теперь у нас есть профайлер. После
этого интерпретатор на каждое событие будет вызывать функцию profiler. В
Python таких событий не так уж и много: вызов функции (call), возврат из
функции (retum) и обработка uс1СЛючения (exception).
Функция-профайлер принимает три параметра:
• frame - представляет собой текущий стековый фрейм выполнения на­
шей программы (sys._current_frarnes);
• event - строка, имя события;
• args - специальные аргументы, которые отличаются в зависимости от
типа события.
Классическим примером такого профайлера в Pythonслужит cProfile, ко­
торый я:�щяется частью стандартной библиотеки Python и написан в виде
С-расширения. В документации достаточно подробно написано о cProfile,
поэтому не будем повторяться:
https.//docs.python.org/3/library/profile.html

22.4. Ручно� профилирование
Не стоит забывать также и о возможности ручного профилирования кода.
Если мы уже нашли узкое место в коде, можно использовать так называемое
ручное профилирование - способ профилирования, при котором мы рука­
ми вставляем код, измеряющий скорость работы. Все очень и очень просто
- мы ''засекаем" время до вызова функции, затем - после и таким нехитрым
способом узнаем, сколько она выполнялась. Рассмотрим простой пример:

•·-----··-···-··-················-·-----····---·-·-··-···-··--·······-··---·-·····--

Python. Полное руководство

- --- -- - -- -- - -- - -- .. - - ......



.J;!python
·.�---

import time
tl = time.time()
do_something ()
t2 = time.time
print(t2-tl)

Одной из часто встречающихся реализаций такого профилировщика мож­
но считать такой декоратор:
def profiler(func):
def wrapper(*args, **kwargs):
before = time.time()
retval = func(*args, **kwargs)
after = time.time() ·
LOG.debug("Function '%s': %s", func.

narne_, after-before)

return wrapper
@profiler
def hello(name):
print('Hello, %s' % name)

Решение настолько простое и эффективное, что в него даже нечего доба­
вить. Тем не менее, следует помнить, что такой "профайлер" будет снижать
скорость работы программы, и использовать его в продакшене крайне не
рекомендуется.
Скорее всего, это самый простой способ измерить скорость выполнения
какого-то фрагмента кода. Большой недостаток этого решения заключается
в следующем: необходимо не просто модифицирова:гь код - нужно. писать
его. А это означает, что с большой долей вероятности этот код будет нельзя
переносить между проектами. Ведь такой код пишется не просто под нужды
определенного проекта, а под нужды определенной функции или несколь­
ких функций.
Зато есть и преимущество: можно измерять только то, что нам надо, и тогда,
когда это необходимо. Например, включать профилирование только но­
чью, когда посетителей сайта мало и производительностью можно немного
пожертвовать. Также можно включать такой "профайлер", если сработает
какое-то условие ("пользователь нажал кнопку А"), и проверить скорость
работы нужной функции.

св-..............-.. --.. -...............-....... --....-............................ '

ГЛАВА 23.

МНОГОЗАДАЧНОСТЬ
В PVТHON

Pythor1. Полное руководство

. . ......................О python

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

23. 1. Есть ли необходимость в
многозадачности?
Зачем нужна многозадачность и что это вообще такое? Начнем со второго
вопроса, ответ на который может удивить тех разработчиков, которые счи­
тают, что многозадачность и параллельная обработка - это одно и то же.
Если попытаться вникнуть в теорию распределенных систем, то два собы­
тия считаются параллельными, если ни одно из них не влияет на другое.
Другими словами, мы можем сказать, что нечто выполняется одновремен­
но, если его можно полностью или частично разложить на составные ча­
сти, которые не зависят друг от друга. Такие части могут обрабатываться
CD-·······································································,········'

• python____________________

Глава 23. Многозадачность в Pyttюn

независимо друг от друга и порядок их обработки не влияет на конечный
результат.
Если мы обрабатываем информацию таким образом, то речь идет действи­
тельно о параллельной обработке. В качестве примера приведем програм­
мы цо загрузке файлов, где файл разбивается на части и каждая часть за­
гружается отдельно - в отдельном потоке, затем все эти части соединяются
воедино в один файл. В этом случае действительно несколько потоков вы­
полняются одновременно настолько, насколько это возможно (например,
если в системе есть 1 процессор с одним ядром, то о каком параллельном
выполнении может идти речь, если в определенный момент времени может
выполняться инструкции только одного "потока"). Каждый из потоков за­
гружает свою часть файла, а затем все это соединяется вместе, и нет разни­
цы, в каком порядке были загружены эти части - главное, что на выходе вы
получите целый и неповрежденный в результате такой загрузки файл.
По-настоящему выполнять задачи распределенным, способом мы можем
только при наличии многоядерных процессоров или вычислительных
кластеров. В одно время (начало и первая половина 2000-х) кластеры были
очень популярны для организации параллельных вычислений - тогда в
кластер объединялось несколько машин, на которых выполнялось специ­
альное ПО для парал.д,ельных вычислений. Сегодня, когда 8 ядерным (и бо­
лее) процессором никого не удивишь, настоящую многозадачность можно
реализовать не только на настольном компьютере, но и даже на смартфоне.
Теперь разберемся, зачем нам многозадачность. Естественно, для повыше·
ния эффективности работы приложения - обработка информации будет
происходить быстрее. При разработке программ мы привыкли к последо­
вательности выполнения шагов. Большинство компьютерных программ
используют синхронные алгоритмы, позволяющие выполнять по одному
действию за один раз. Такой способ обработки данных не очень подходит
для масштабных задач или кейсов. К многозадачному выполнению прихо­
дят в следующих случаях:
1. Когда масштаб задачи настолько велик, что единственный способ решить
ее за приемлемое время - это распределение ее выполнения на несколь­
ко бло:ков (потоков), которые могут обрабатывать задачу параллельно.
2. Когда есть необходимость принимать новые данные, даже если обработ­
ка старых еще не завершена.



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

·-------------·-··········-·············-···--·-·-·--·-·--·····-·-·-······-------·CD

Python. Полное руководство

- ----------------------",mpython
@#

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

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

23.2.1. ВВЕДЕНИЕ В МНОГОПОТОЧНОСТЬ
Первым делом нужно разобраться, что такое многопоточность. Разработ­
чик приложения может разделить свою работу на потоки, работающие
одновременно и использующие один и тот же контекст памяти. Если код
не зависит от сторонних ресурсов, то многопоточность никак не позволит
ускорить работу на одноядерном процессоре, а только лишь добавит лиш­
ние затраты ресурсов на управление потоками. Другими словами, для реа­
лизации многопоточности нужен как минимум двухядерный процессор, в
противном случае многопоточность навредит производительности.
На многоядерных или многопроцессорных машинах, где каждый поток мо­
жет выполняться на отдельном ядре процессора, многопоточность позволя-



at-····················································-···-··············-···········

О python____________________

r·лава 23. Многозадачность в Python

ет повысить производительность программы. В принципе, данная аксиома
характерна для всех языков программирования, а не только Python.
Примечание. Раньше, скажем лет 20-25 назад, все было пре­
дельно просто. Один процессор - одно ядро (речь идет о бюд­
жетных пользовательских компьютера, а не о высокопроизво­
дительных серверах). По-настоящему многозадачность можно
было реализовать на так называемых SМР-машинах, где уста­
навливалось как минимум два одинаковых процессора, которые
подключались к общей памяти и периферийным устройства. В
таких системах процессоры тесно связаны друг с другом посред­
етвом общей шины и имеют равный доступ ко всем ресурсам
вычислительной системы, а также управляются одной копией
операционной системы. Ранее SMP использовалась в основном
на серверах, высокопроизводительных графических станциях
- в общем, везде, где нужно было задействовать мощные вы­
числительные ресурсы. Для обычных "десктопных" целей SМР­
машины не покупались - дорого, да и необходимости особой в
них не было.

Итак, термином многопроцессорный обозначают компьютеры, имеющие не­
сколько физически раздельных процессоров (например, серверные мате­
ринские платы часто имеют 2 или 4 сокета для подключения нескольких
чипов), но управляемые одним экземпляром операционной системы (ОС).
Со временем технологии стали дешевле и стали появляться многоядерные
процессоры. Многоядерным считается центральный процессор (CPU), со­
держащий в одном корпусе два или более вычислительных ядра на одном
процессоре. С появлением многоядерности появилась некоторая путаница,
поскольку есть мультиядерные (англ. multi-core) и многоядерные (many­
core) процессоры. Термин мультиядерный обычно применяется к централь­
ным процессорам, содержащим два и более ядра общего назначения, одна­
ко иногда используется и для цифровых сигнальных процессоров (DSP)
и однокристальных систем (SoC, СнК). Под многоядерностью процессора
понимают, что несколько ядер являются интегрированными на одну инте­
гральную схему (изготовлены на одном кремниевом кристалле). Если же в
один корпус были объединены несколько полупроводниковых кристаллов,
то конструкцию называют многочиповый модуль (англ. multi-chip module,
МСМ)



·------------------------------------·-·-·-··---·-·-··-··----··-·--·-··-···-·-·---CD

Python. Полное руководе�во

________________________• python

Понятие многоядерный (англ. many-core) может использоваться для описа­
ния многоядерных систем, имеющих высокое количество ядер, от десятков
до сотен или более. Например, именно название "многоядерный" ("many­
core") использовалось Intel для вычислителей Intel MIC.
Еще большую путаницу внесла технология Hyper-Threading от Intel. В ней
одно физическое ядро процессора определяется операционной системой
как два логических ядра. Взять, например, процессор Intel Core i5-7200U. В
нем два ядра, но каждое из них разделено на два логических ядра - говорят
- на 2 потока (threads). Такая компоновка позволяет выполнять одновре­
менно (якобы) четыре потока. Не будем вдаваться в технические особен­
ности реализации этой технологии (об этом вы сможете прочитать в Сети),
но скажем, что прибавка к производительности на процессорах с Hyper­
Threading составила 30% по сравнению с процессором с таким же количеством ядер, но без НТ.
Но вернемся к обсуждению многопоточности. Поскольку у нас есть не­
сколько потоков, которые используют один и тот же контекст, нужно за­
щитить данные от неконтролируемого одновременного доступа. Если два
потока изменяют одни и те же данные, это может породить ситуацию, когда
малейшее изменение в одном из потоков самым неожиданным образом по­
влияет на конечный результат. Представим, что у нас есть два потока, уве­
личивающих значение общей переменной:
counter = shared counter
shared counter = counter + 1

Представим, что у переменной shared_counter есть начальное значение О.
Представим, что потоки обрабатываю один и тот же код параллельно, см.
табл. 23.1.

Таблица 23.1. Параллельная обработка кода потоками
Поток 1

Поток2

counter = shared-counter
[counter о]

...

shared-counter = counter
[shared counter = о + 1]

counter = shared-counter
counter о
+

1

shared-counter = counter
[shared_counter = о + 1]

+

1

- - - - - - - - - - - - - - - - -- - - - -- - - - - - -- - - -- - - - -- - - -- - - - -- -- - - - -- - -- -- -. - - - - -- - - - - - - - -- - - - -

•-.

f!rtpython••••••••••••••••••••
.;u,

Глава 23. Многозадачность в Python

В зависимости от моментов выполнения и доступности контекста может
получиться результат 1 или 2. Такая ситуация называется опасностью гон­
ки или состоянием zонки, и часто является причиной серьезных проблем в
работе программы. Если ничего не предпринимать, то результатам работы
такой программы мы не можем доверять.
С помощью механизмов блокировки мы можем защитить данные и про­
граммировать потоки, поскольку 'с их помощью мы можем убедиться, что
доступ к ресурсам дается безопасным образом. Однако, неосторожное ис­
пользование блокировок также может создать проблемы. Самая большая
проблема возникает тогда, когда из-за неправильной организации кода два
потока блокируют один и тот же ресурс и пытаются выполнить блокиров­
ку второго ресурса, который уже был заблокирован ранее. Тогда программа
"зависнет". Чтобы такого не повторилось, нужно отслеживать повторное
обращение, что частично решает проблему, поскольку ограничивает попыт­
ки заблокировать ресурс дважды.
Однако если потоки используются для изолированных задач (например,
одна задача получает файл 1, а вторая - файл 2, или один поток загружает
обновление программы, а второй - обрабатывает различные события при­
ложения), то и использование может существенно увеличить скорость ра­
боты программы.
Многопоточность, как правило, поддерживается на уровне ядра системы.
Если у машины один процессор с одним ядром, система использует технику
квантования времени, когда центральный процессор переключается с од­
ного потока на другой так быстро, что кажется, что потоки выполняются
одновременно, хотя на самом деле это не так - в один момент времени вы­
полняется какой-то один поток. В этом случае, разумеется, ни о каком при росте производительности речи идти не может.
Но все изменится, если в системе есть несколько процессоров или процес­
сор с несколькими ядрами - тогда процессы и потоки перераспределяются
между процессорами и программа выполняется быстрее.
В Python используется несколько потоков на уровне ядра и каждый из них
может запустить любой из потоков уровня интерпретатора. В реализации
CPython есть ограничение, делающее потоки менее пригодными для ис­
пользования в разных контекстах. Все потоки, которые обращаются к объ­
ектам Python, работают с одной глобальной блокировкой. Это делается
потому, что большая часть структур интерпретатора и сторонний код С не­
безопасны для потоков и нуждаются в защите.
Данный механизм называется GIL (zлобальная блокировка интерпретато­
а, Global Interpreter Lock). Периодически в сообществе разработчиков под.
··················································································СD

Pythoп. Полное руководство

�;.....................__• python

нимается тема об удалении GIL из Python, но'пока процесс не сдвинулся с
мертвой точки, поэтому вам придется работать с тем, что есть.
Разберемся, что такое многопоточность в Python. Если в потоках есть толь­
ко код Python, использовать потоки для ускорения программы не очень
правильно, поскольку GIL будет глобально сериализовать выполнение всех
потоков. Но GIP работает лишь с кодом Python. На практике GIL может
быть убран в расширениях С, в которых не применяются функции Python.
Другими словами, несколько потоков могут производить операции ввода/
вывода или выполнять код С сторонних расширений параллельно.

23.2.2. КЕЙСЫ, ПОДХОДЯЩИЕ ДЛЯ
ИСПОЛЬЗОВАНИЯ МНОГОПОТОЧНОСТИ
Использовать многопоточность следует в следующих случаях:
• Когда необходимы адаптивные интерфейсы
• Делегирование работы
• Создание многопользовательских приложений
Разберем все эти случаи подробно.

АДАПТИВНЫЕ ИНТЕРФЕЙСЫ
Представим, что вы пишете какую-то программу, которой нужно скопиро­
вать файлы из одного места в другое, например, программу для резервного
копирования файлов или же программу для резервного копирования базы
данных. У вашей программы есть пользовательский интерфейс, и вы хо­
тите, чтобы задача по копированию файлов/создания дампа базы данных
отошла на второй план (выполнялась в фоне), а интерфейс программы по­
казывал бы пользователю информацию о ходе выполнения задачи и давал
бы возможность прекратить задачу (кнопка Отмена).
Хороший интерфейс позволяет пользователям работать с несколькими за­
дачами одновременно. Достичь этого невозможно без использования пото­
ков.



CD------·-----··-··----····················-············-······-···-········-···-···

il,; python
'>{?;·

---- ------ ................. ..

Глава 23. Многозадачность в Python

ДЕЛЕГИРОВАНИЕ РАБОТЫ
Когда работа приложения зависит от нескольких внешних ресурсов, пото­
ки могут повысить производительность приложения . Например, у нас есть
функция индексирования содержимого файлов в папке. Для этого наша
программа использует какую-то внешнюю программу.
Мы можем обрабатывать файлы последовательно, выполняя нужную
программу, но мы также можем создать отдельный поток для каждого кон­
вертера и отправлять задачи по потокам. Общее время выполнения будет
примерно равно времени обработки самого медленного конвертера, но оно
не будет равно сумме работы всех конвертеров, как в случае с последова­
тельным выполнением. Другими словами, если нам нужно обработать фай­
лы размером 1, 5, 10 и 20 Мб, а время обработки равно 1 Мб/с, то при по­
следовательном выполнении мы выполним всю задачу за 36 секунд, а при
параллельном (при условии наличия четырех ядер у процессора), наша
программа обработает все файлы за примерно 20 секунд.
Именно поэтому использование потоков значительно увеличивает произ­
водительность приложения и сокращает общее время выполнения .
Другой достаточно популярный кейс использования потоков - выполнение
нескольких запросов к внешнему серверу. Например, вам нужно делать
несколько АРI-запросов к какому-то веб-сервису. Если сервер находится
далеко, последовательное выполнение запросов, может занять достаточно
много времени - ведь вам нужно дождаться ответа от предыдущего запроса.
При параллельном выполнении вы потратите гораздо меньше времени на
получения ответов.
При работе с http нужно помнить, что при выполнении запроса максималь­
ное время тратиться на чтение из ТСР-сокета. Данная операция блокирует
1/0, из-за этого Pythbn снимает GIL при выполнении С-функции recv(),
что позволяет существенно улучшить производительность приложения.

МНОГОПОЛЬЗОВАТЕЛЬСКИЕ ПРИЛОЖЕНИЯ
Рассмотрим обратный пример - мы разрабатываем приложение, не обра­
щающееся к внешнему http-cepвиcy, а которое само является веб-сервером.
Веб-сервер должен принять запрос пользователя, поставить его в новый
поток, а затем ожидать новых запросов. Отдельный поток на пользовате-



.... . . . ......... . .. . ... ... . .. .

.,

................................................... ...

Python. Полное руководство

;f41python

> ........................................ ·�ж:

ля существенно упрощает работу разработчика. Кщrечно, нужно помнить о
блокировках, если это запрос на запись. С запросами на чтение все гораздо
проще.

23� .. Практика: создание
приложения
23.3.1. МОДУЛЬ THREAD
Запуск нескольких потоков аналогичен одновременному запуску несколь­
ких разных программ, но со следующими преимуществами:
• Несколько потоков в рамках процесса используют одно и то же про­
странство данных с основным потоком и поэтому могут обмениваться
информацией или взаимодействовать друг с другом более легко, чем
если бы они были отдельными процессами.
• Потоки иногда называют легковесными процессами, и они не требуют
больших затрат памяти; они дешевле процессов.
У потока есть начало, последовательность выполнения и завершение. У
него есть указатель инструкции, который отслеживает, где в его контексте
он выполняется в данный момент. Поток может быть �егко прерван. Поток
можно временно приостановить, пока работают другие потоки - это назы­
вается уступкой (yield).
Разберемся, как начать новый поток. Для этого нам нужно вызвать следую­
щий метод, находящийся в модуле thread:
thread.start new_thread ( function, args[, kwargs] )

Данный метод позволяет создать потоки, как в Linux, так и в Windows. Вы­
зов метода немедленно возвращается, запуская дочерний поток. В качестве
кода потока будет использована функция, заданная первым аргументом.
Остальные аргументы передаются вызываемой функции. Когда функция
завершает работу, то завершает работу и поток.



---································································-···-·-�·-······

i/Ьpython
%W>

Глава 23. Многозадачность в Python

...........................................

Здесь mgs - это,набор аргументов, передаваемых функции. Если функции
не нужно передавать аргументы, используйте пустой кортеж. Последний
параметр - необязательный. Это словарь аргументов ключевых слов.
Рассмотрим полноценный пример (лист. 23.1).

Листинг 23.1. Запуск потока
#!/usr/bin/python
import thread
import time
# Функция для потока
def print_time( threadName, delay):
count = О
while count < 5:
time.sleep(delay)
count += l
print ''%s: %s'' %
threadName, time.ctime(time.time()) )
# Создаем 2 поток�
try:
thread.start new thread ( print time, ( "Thread-1", 2,
thread.start new_thread( print_time, ("Thread-2", 4,
except:
print "Ошибка: не могу создать поток"

while 1:
pass

Код из листинга 23.1 породит следующий вывод:
Thread-1:
Thread-1:
Thread-2:
Thread-1:
Thread-2:
Thread-1:
Thread-1:
Thread-2:
Thread-2:
Thread-2:

Mon
Mon
Mon
Mon
Mon
Mon
Mon
Mon
Mon
Mon

Oct
Oct
Oct
Oct
Oct
Oct
Oct
Oct
Oct
Oct

04
04
04
04
04
04
04
04
04
04

08:42:17
08:42:19
08:42:19
08:42:21
08:42:23
08:42:23
08:42:25
08:42:27
08:42:31
08:42:35

2021
2021
2021
2021
2021
2021
2021
2021
2021
2021

•..-.-.-....-.-.-.-....-. -.-.-..-. -.-. -..-.-..........-.....................-......-,СВ

Руttюп. Полное руковоi\'- ,но

-�

tl1python

,

................................

Примечание. Вывод будет у вас отличаться - в зависимости от
даты выполнения и настроек лакали.

Хотя модуль thread очень эффективен для низкоуровневой обработки по­
токов, он очень ограничен по сравнению с более новым модулем threading.

23.3.2. МОДУЛЬ THREADING
Более новый модуль threading, впервые. появившийся в в Python 2.4, обе­
спечивает гораздо более мощную и высокоуровневую поддержку потоков,
чем модуль thread, рассмотренный в предыдущем разделе.
Модуль threading предоставляет все.методы модущ1 thread и некоторые до­
полнительные методы:
• threading.activeCount() - возвращает количество активных объектов по­
тока;
• threading.currentThread() - возвращает количество объектов потока в
элементе управления потоком вызывающего объекта;
• threading.enumerate() - возвращает список всех активных в данный момент объектов потока.
В дополнение к методам, в модуле threading есть класс Thread, реализую­
щий потоки. Класс Thread предоставляет следующие методы:
• run() - является точкой входа для потока;
• start() - запускает поток, вызывая метод run();
• jоiп([время]) - ожидает завершения потоков;
• isAlive() - проверяет, выполняется ли еще поток;
• getName() - возвращает имя потока;
• setName() - устанавливает имя потока.
Рассмотрим, как можно создать поток, используя метод threading. Для этого
нам нужно сделать следующее:



CD--..... -.. -. -. -...... -. -... -.. -.-.-. -. -.... -. -. -...... -.-... -.................-... .

• python_ .. ••___________ .......

Гnавг 23. Многозадачность в Pytlюr1

1. Оnределить новый поhкласс класса Thread.
2. Переопределить метод _init(self[,args])_ для добавления дополн:и­
тельных аргументов
3. Переопределить метод run(self[,args]) для реализации того, что должен
сделать поток после запуска.
Создав новый подкласс Thread, вы можете создать его экземпляр, а затем
запустить новый поток, вызвав start(), который, в свою очередь, вызывает
метод run(). Полный пример показан в лист. 23.2.
Листинг 23.2. Пример создания потоков с использованием
модуля threading
#!/usr/bin/python
import threading
import_time
exitFlag = О
class miтhread (threading;Thread):
def
init (self, threadID, name, counter):
threading.Thread._init_(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self) :
print "Запуск"+ self.name
print..:.time(self.name, 5, self.counter)
print "Выход из"+ self.name
·def print_time(threadName, counter, delay):
while counter:
if exitpag:
threadName.exit()
time.sleep(delay)
print "%s: %s" % (threadName, time.ctime(time.time()))
counter.-= 1
# Создание новыхпотоков
threadl
myThread(l, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
к
• Запуспотоков

·-- ·..•..••..•.....••••••.•..•.•••••.......................................•....•••

_______________________ .О python

Python. Пол�юе руководr.тво
threadl.start()
thread2.start()
print "Выход из главного потока"

Вывод программы будет следующий:
Запуск Thread-1
Запуск Thread-2
Выход из главного
Thread-1: Mon Oct
Thread-1: Mon Oct
Thread-2: Mon Oct
Thread-1: Mon Oct
Thread-1: Mon Oct
Thread-2: Mon Oct
Thread-1: Mon Oct
Выход из Thread-1
Thread-2: Mon Oct
Thread-2: Mon Oct
Thread-2: Mon Oct
Выход из Thread-2

потока
04 09:10:03
04 09:10:04
04 09:10:04
04 09:10:05
04 09:10:06
04 09:10:06
04 09:10:07

2021
2021
2021
2021
2021
2021
2021

04 09:10:08 2021
04 09:10:10 2021
04 09:10:12 2021

23.3.3. СИНХРОНИЗАЦИЯ ПОТОКОВ
Модуль threading содержит простой в реализации механизм блокировки,
позволяющий синхронизировать потоки. Новая блокировка создается пу­
тем вызова метода Lock(), который возвращает новую блокировку.
Метод acquire(Ьlocking) нового объекта блокировки используется для при­
нудительного выполнения· потоков синхронно. Необязательный параметр
Ыocking позволяет вам контролировать, ожидает ли поток получения бло­
кировки.
Если Ыocking равен О, поток немедленно завершается со значением О, если
блокировка не может быть получена и 1, если блокировка была получена.
Если Ыocking равен 1, поток будет заблокирован в ожидании снятия блоки­
ровки.
Метод release() используется для снятия блокировки, когда она больше не
нужна. В листинге 23.3 приведен пример работы с блокировками.

...

- - - -- - - - -

".""

- - - - - . . - . ... - .. - .. - ".
"

"

"

""

"

- -.-

"."." ""

""

•.

.... - - .. - - -- -- - -- - - - - -- ""

"

"

"

• python____________________

Глава 23. Многозадачность в Py1hon

Листинг 23.3. Работаем с блокировками
#!/usr/bin/python
import threading
import time
class myThread (threading.Thread):
def
init (self, threadID, ·паmе, counter):
threading.Thread. init (self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print "Запуск " + self.name
# Получаем блокировку для синхронизации потока
threadLock.acquire()
print_time(self.name, self.counter, 3)
# Снимаем блокировку для освобождения нового потока
threadLock.release()
def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print "%s: %s" % (threadName, time.ctime(time.time()))
counter -= 1
threadLock = threading.Lock()
threads = []
# Со�даем новые потоки
threadl
myThread(l, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# Запускаем новые потоки
threadl.start()
thread2.start()
# Добавляем потоки в список потоков
threads.append(threadl)
threads.append(thread2)
# Ждем завершения всех потоков
for t in threads:
t.join()
print "Выход из главного потока"



Вывод будет следующим:

·--·---·-----------·--------------------··------------------------··-···--·-·-···--cD

Pytlюn. Поr1ное руководство
Запуск Thread-1
Запуск Thread-2
Thread-1: Mon Oct
Thread-1: Mon Oct
Thread-1: Mon Oct
Thread-2: Mon Oct
Thread-2: Mon Oct
Thread-2: Mon Oct
Выход из главного

04 09:11:28
04 09:11:29
04 09:11:30
04 09:11:32
04 09:11:34
04 09:11:36
потока

....................... ,. .....

,l,1Jpython
�д::;:

2021
2021
2021
2021
2021
2021

23.3.4. МНОГОПОТОЧНАЯ ПРИОРИТЕТНАЯ ОЧЕРЕДЬ
Модуль Queue позволяет вам создавать новые объект очереди, позволяю­
щий хранить определенное число элементов. Следующие методы использу­
ются для управления очередью:
• get() - удаляет и возвращает элемент из очереди;
• put() - добавляет элемент в очередь;
• qsize() - возвращает количество элементов, которые в настоящее время
находятся в очереди;
• empty() - возвращает True, если очередь пуста; в противном случае False;
• full() - возвращает True, если очередь заполнена; в противном случае False.
Рассмотрим пример (лист. 23.4).

Листинг 23.4. Работа с очередью
#!/usr/bin/python
import Queue
import threading
import time

.,

exitFlag = О
class myThread (threading.Thread):

........ - -. -...... -..... -.... - . - . -......... -....... - - - . - -...... -- - - . -.... - .. - . - .

'

• python....•...•.........•.

Глава 23. Мноrозада•tность в Python

def

init (self, threadID, name, q):
threading.Thread._init_,_(self)
self.threadID = threadID
self.name = name
self.q = q
def run(self):
·print "Запуск " + self.name
process_data(self.name, self.q)
print "Выход из " + self.name

def process_data(threadName, q):
while not exitFlag:
queueLock.acquire()
if not workQueue.empty():
data = q.get()
queueLock.release()
print "%s обрабатывает %s" % (threadName, data)
else:
queueLock.release()
time.sleep(1).
threadList = [ "Thread-1", "Threa,d-2", "Thread-3"]
nameList = ["Марк", "Денис", "Евгения'", "Валерия", "Андрей"]
queueLock = threading.Lock()
workQueue = Queue.Queue(lO)
threads = []
threadID = 1
# Create new threads
for tName in threadList:
thread = myThread(threadID, tName, workQueue)
thread.start()
threads.append(thread)
threadID += 1
# Заполняем очередь элементами из списка имен nameList
queueLock.acquire()
for word in nameList:
workQueue.put(word)
queueLock.release()
# Ждем пока освободится очередь
while not workQueue.empty():
pass
# Уведомляем потоки, что настало время завершения
exitFlag = 1



# Ждем пока завершат работу все потоки

······················.···························································-

Python. Полное руководство

6python

-------•••·············· �ю

for t in threads:
t.join()
print "Выход из основного потока"

Вывод скрипта будет следующим:
Запуск Thread-1
Запуск Thread-2
Запуск Thread-3
Thread-1 обрабатывает Марк
Thread-2 обрабатывает Денис
Thread-3 обрабатывает Евгения
Thread-1 обрабатывает Валерия
Thread-2 обрабатывает Андрей
Выход из Thread-3
Выход ИЗ Thread-1
Выход из Thread-2
Выход ИЗ основного потока

Как видно, три потока поочередно обрабатывают элементы из списка имен.

23.4. Практический пример:
мноrопотоковый сетевой сервер
В этом разделе будет показано, как создать сетевое приложение, обслужи­
вающее нескольких клиентов. Возможно, оно станет основой для вашего
собственного проекта. Начнем с разработки сервера - программа, которая
будет "слушать" сокет и принимать соединения от клиентов. Наш сервер
будет слушать порт с номером 8888. Порты с номерами 80, 448, 8080, 8000,
8081, 3128 использовать не рекомендуется, поскольку они могут быть за­
няты веб-сервером или прокси-сервером. Чтобы сервер слушал порт, нам
нужно выполнить два вызова:
server.bind((LOCALHOST, PORT))
server.listen(l)



CD·-····-·-·--······································································

• python____________________

Глава 23. Многозадачность в Python

Здесь LOCALHOST- это имя локального компьютера (с адресом 127.0.0.1),
а PORT - нужный нам порт. Метод bind() связывает наш сервер с опреде­
ленным портом. А вот метод listen() запускает прослушку этого порта.

Листинг 23.5. Классический сервер, обслуживающий одного
клие11та
irnport socket
LOCALHOST = "1�7.0.0.1"
РОRТ = 8888
server = socket.socket(socket.AF_INET, socket.SOCK_STREAМ)
server'.Ьind((LOCALHOST, PORT))
server.listen(l)
print("Ждeм запрос клиента ...")
while True:
clientConnection,clientAddress = server.accept()
рrint("Подключился клиент :" , clientAddress)
data = clientConnection.recv(l024)
рrint("Получено от клиента :" , data.decode())
clientConnection.send(bytes("Hello", 'UTF-8'))
clientConnection.close()

Наш сервер ожидает запрос клиента. Как только клиент подключится, сер­
вер примет соединение (метод accept()), получит данные от клиента (метод
recv())-и отправит ему в отчет строчку Hello (метод send()).
Программа-клиент приведена в лист. 23.6.

Лист�нг 23.6. Программа-клиент
irnport socket
SERVER = "127.0.0.1"
РОRТ = 8888
client = socket.socket(socket.AF INET, socket.SOCK_STREAМ)
client.connect( (SERVER, PORT))
client.sendall(bytes("Hello, server! ! !�, 'UTF-8'))
data = client.recv(1024)
print(data.decode())
client.close()

Клиент работает просто - он подключается к серверу и отправляет строчку
Hello, server!!!. Кодировка UTF-8 (вы можете использовать кириллицу).
Теперь усложним задачу - напишем многопотоковый сетевой сервер. Он
будет запускать отдельный поток для обработки каждого нового клиента
подобно тому, как работают "настоящие" серверы. Для реализации нашей
'-----------------············"··································-·-----·--··-------tD

Python. Полное руководство

........................• python

программ мы будем использовать модули socket и threading. Код програм·
мы·сервера приведен в лист. 23.7.
Листинг 23. 7. Код многопотокового сервера
import socket, threading
class ClientThread(threading.Thread):
def
init (self,clientAddress,clientsocket):
threading.Thread. init (self)
self.csocket = clientsocket
print ("Новое соединение: ", clientAddress)
def run(self):
print ("Адрес клиента : ", clientAddress)
fself.csocket.send(bytes("Hi, This is from
Server..",'utf-8'))
msg = ''
while True:
data = self.csocket.recv(2048)
msg = data.decode()
if msg=='bye':
break
print ("от клиента ", msg)
self.csocket.send(bytes(msg, 'UTF-8'))
print ("Клиент ", clientAddress , " отключился...")
LOCALHOST = "127.0.0.1"
РОRТ = 8888
server = socket.socket(socket.AF_INET, socket.SOCK_STREAМ)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((LOCALHOST, PORT))
print("Ждем запросы...")
while True:
server.listen(l)
clientsock, clientAddress = server.accept()
newthread = ClientThread(clientAddress, clientsock)
newthread.start()

Сначала мы определяем объект ClientThread класса threading.Thread. Мы
переопределяем метод _init_ - инициализация и вывод информации о
клиенте. Затем мы переопределяем метод run() - это и будет наш метод об­
работки соединения с клиентом. Мы просто принимаем сообщения от кли­
ента, пока не увидим сообщение Ьуе. Как только клиент передает там это
сообщение, мы прерываем обработку клиента.

-

Программу-клиент (лист. 23.8) также нужно немного переделать. Техни­
чески она мало чем отличается от первой версии, просто добавлена реакция
на ключевое слово Ьуе.




...........·...................................................................... .

• python -· ...................

Глава 23. Многозадачность в Pythori

Листинг 23.8. Новый. клиент
import socket
SERVER = "127.0.0.1"
РОRТ = 8888
client = socket.socket(socket.AF INET, socket.SOCK_STREAМ)
client.connect((SERVER, PORT))
client.sendall(bytes("This is from Client",'UTF-8'))
while True:
in data = client.recv(l024)
print("Oт сервера :" ,in data.decode())
out_data = input()
client.sendall(bytes(out_data,'UTF-�'))
if out_data=='bye':
break
client.close()

Как запускать все это? Первым делом откройте командную строку (или
терминал) и запустите сервер (python server.py), а затем откройте несколь­
ко терминалов и запустите в каждом из них по клиенту (python client.py).
А затем наблюдайте за волшебством! Вы увидите, что наш сервер обрабаты­
вает параллельно каждоr:о клиента .



··················································································CD

,,Издательство Наука и Техника,, рекомендует:

Python на примерах. Практический курс по программированию. 3-е изда­
ние. - СПб.: Наука и Техника. - 432 с., ил.
В этой книге решать будем две задачи, одна из которых приоритетная, а вторая, хотя
и вспомогательная, но достаточно важная. Наша основная задача, конечно же,
изучение синтаксиса языка программирования Python. Параллельно мы будем осва­
ивать программирование как -таковое, явно или неявно принимая во внимание, что
соответствующие алгоритмы предполагается реализовывать на языке Python.
Большинство авторов книг в своих трудах рассматривают теоретические основы
языка и уделяют основное внимание базовому синтаксису языка, не рассматривая
при этом практическую сторону его применения. Эта же книга старается воспол­
нить недостаток практического материала, содержит множество примеров с ком­
ментариями, которые вы сможете использовать в качестве основы своих программ­
ных решений, изучения Python.
Материал книги излагается последовательно и сопровождается большим коли­
чеством наглядных примеров, разноплановых практических задач и детальным раз­
бором их решений

,,Издательство Наука и Техника,, рекомендует:

Создаем
программы и игры
P11ap.jбofka {с nрнмер.1ми кода)

r�роtтейщ"х

ПJ)'НIJ()Ж:2НИ�

♦"

,,

,.�
N -�

--

1 - • Гр•фнк,,
-� _) объ�ктн&-орЖ;-t-tf\фООа�ное
nроrраммирщн.нше, -нортежи,
·.... ,:иджеl'ы и м,-ioroe друrЕ'

IBb!КI

Python: Создаем программы и игры. 2-е издание. - СПб.: Наука и Техника.

-400с.,ил.

Данная книга позволяет уже с первых шагов создавать свои программы на языке
Python. Акцент сделан на написании компьютерных игр и небольших приложений.
Есть краткий вводный курс в основы языка, который''поможет лучше ориентиро­
ваться на практике. По ходу изложения даются все необходимые пояснения, при­
водятся примеры, а все листинги (коды· программ) сопровождаются подробными
комментариями.
Отличный выбор для всех, кто хочет быстр0 и эффективно научиться писать
программы на Python.

Издательство «Наука и Техника»
КНИГИ ПО КОМПЬЮТЕРНЫМ ТЕХНОЛОГИЯМ,
МЕДИЦИНЕ, РАДИОЭЛЕКТРОНИКЕ

Уважаемые читатели!
Книги издательства «Наука и Техника» вы можете:
► заказать в нашем интернет-магазине БЕЗ ПРЕДОПЛАТЫ по ОПТОВЫМ ценам

www.nit.com.ru

• более 3000 пунктов выдачи на территории РФ, доставка 3-5 дней
• более 300 пун�тов выдачи в Санкт-Петербурге и Москве, доставка - на следующий день
Справки и заказ:
• на сайте www.nit.com.ru
• по тел. (812) 412-70-26
• по эл. почте nitmail@nit.com.ru
► приобрести в магазине издательства по адресу:

Санкт-Петербург, пр. Обуховской обороны, д.107
М. Елизаровская, 200 м за ДК им. Крупской
Ежедневно с 10.00 до 18.30
Справки и заказ: тел. (812) 412-70-26
► приобрести в Москве:

«Новый книжный» Сеть магазинов
ТД «БИБЛИО-ГЛОБУС»
Московский Дом Книги,
«ДК на Новом Арбате»
Московский Дом Книги,
«Дом технической книги»
Московский Дом Книги,
«Дом медицинской книги»
Дом книги «Молодая гвардия»

тел. (495) 937-85-81, (499) 177-22-11
ул. Мясницкая, д. 6/3, стр. 1, ст. М «Лубянка»
тел. (495) 781-19-00, 624-46-80
ул. Новый Арбат, 8, ст. М «Арбатская»,
тел. (495) 789-35-91
Ленинский пр., д.40, ст. М «Ленинский пр. »,
тел. (499) 137-60-19
Комсомольский пр., д. 25, ст. М «Фрунзенская»,
тел. (499) 245-39-27
ул. Б. Полянка, д. 28, стр. 1, ст. М «Полянка»
тел. (499) 238-50-01

► приобрести в Санкт-Петербурге:

Санкт-Петербургский Дом Книги
Буквоед. Сеть магазинов

Невский пр. 28, тел. (812) 448-23-57
тел. (812) 601-0-601

► приобрести в регионах России:

г. Воронеж, «Амиталь» Сеть магазинов
г. Екатеринбург, «Дом книги» Сеть магазинов
г. Нижний Новгород, «Дом книги» Сеть магазинов
г. Владивосток, «Дом книги» Сеть магазинов
г. Иркутск, «Продалить» Сеть магазинов
г. Омск, «Техническая книга» ул. Пушкина, д.101

тел. (473) 224-24-90
тел. (343) 289-40-45
тел.(831) 246-22-92
тел. (423) 263-10-54
тел. (395) 298-88-82
тел. (381) 230- 1 3-64

Мы рады сотрудничеству с Вами!

Кольцов Дмитрий Михайлович

РУТНОN

полное руководство

Группа ПОДГОТОВКИ издания:
Зав. редакцией компьютерной литературы: М В. Финков
Редактор: Е. В. Финков
Корректор: А. В. Громова

ООО "Издательство Наука и Техника"
ОГРН 1217800116247, ИНН 7811763020, КПП 781101001
192029, г. Санкт-Петербург, пр. Обуховской обороны, д. 107, лит. Б, пом. 1-Н
Подписано в печать 27.10.2021. Формат 70xl00 1/16.
Бумага газетная. Печать офсетная. Объем 30 п. л.
Тираж 1500. Заказ № 1031.
Отп ечатано в АО «Можайский п олиграфический комбинат"
143200, Россия, г. Можайск, ул. Мира, 93.

www.oaornpk.ru, тел.: (495) 748-04-67, (49638) 20-685