Uploaded by igor tsimbal

Знакомство с Python

advertisement
Python Basics: А Practical Introduction
to Python З
Real Python
~ &И&ЛИОТЕКА
Ttl ПРОГРАММИСТА
знакомство с
PYTHON
Дэн Бейдер, Дэвид Эймос
Джоанна Яблонски, Флетчер Хейслер
~nnTEP®
Санкт-Петербург
2023
• Москва • Минск
ББК
УДК
32.973.2-018.1
004.43
641
Бейдер Дэн, Эймос Дэвид, Яблонски Джоанна, Хейслер Флетчер
Б41
Знакомство с
Python. -
СПб.: Питер,
2023. -
512
с.: ил.
-
(Серия «Библио­
тека программиста»).
ISBN 978-5-4461-1924-0
Пытаетесь найти что-нибудь для начинающих о языке
Python в интернете? Не можете решить,
с чего начать? Как структурировать это море информации? В каком порядке изучать?
Если вы задаетесь подобными вопросами, потому что хотите заложить фундамент будущей
карьеры питониста,
-
эта книга для вас'
Вместо скучного перечисления возможностей языка авторы рассказывают, как сочетать разные
структурные элементы
Python,
чтобы сразу создавать скрипты и приложения.
Книга построена по принципу
80/20:
большую часть полезной информации можно усвоить,
изучив несколько критически важных концепций. Освоив самые популярные команды и приемы,
вы сразу сосредоточитесь на решении реальных повседневных задач.
16+ (В соответствии с Федеральным законом от 29 декабря 2010 r. № 436-ФЗ.)
ББК
УДК
Права на издание nолучены
no
соглашению с
DevAcademy Media lnc.
32.973.2-018.1
004.43
Все nрава защищены. Никакая часть
данной книги не может быть воспроизведена в какой бы то ни было форме без nисьменного разрешения
владельцев авторских nрав.
Информация, содержащаяся в данной книге, nолучена из источников, рассматриваемых издательством как
надежные. Тем не менее, имея в виду возможные человеческие или технические ошибки. издательство не
может гарантировать абсолютную точность и nолноту nриводимых сведений и не несет ответственности за
возможные ошибки, связанные с исnользованием книги. Издательство не несет ответственности за достуn­
ность материалов, ссылки на которые вы можете найти в этой книге. На момент nодготовки книги к изданию
все ссылки на интернет-ресурсы были действующими.
ISBN 978-1775093329 англ.
ISBN 978-5-4461-1924-0
© Real Python
(гealpython.com)
©Перевод на русский язык ООО «Прогресс книга»,
2022
©Издание на русском языке, оформление ООО «Прогресс книга»,
2022
©Серия «Библиотека nрограммиста»,
2022
Оглавление
Об авторах ........................................................................................................................
Предисповие
14
........................................................................................ 15
Python как язык полного спектра ............................................................................ 15
Глава
1.1.
1. Введение •..............•.•••.••..............•.•.•...........••.••••.•.....••••••.••••••• 21
Почему именно эта книга? ..................................................................................
22
1.2. О Real Python .......................................................................................................... 23
1.3.
Как пользоваться книгой ..................................................................................... 24
1.4. Дополнительный
Глава
2. Установка
материал и учебные ресурсы
и настройка
.......................................... 25
Python .............................................. 27
2.1. О версиях Python ................................................................................................... 27
2.2. Windows ..................................................................................................................... 28
2.3. macOS ......................................................................................................................... 31
2.4. Linux ........................................................................................................................... 34
Глава З. Первая проrрамма
Python .................................................... 38
3.1. Написание программы Python .......................................................................... 38
3.2. Ошибки ...................................................................................................................... 42
3.3.
Создание переменной ........................................................................................... 44
3.4.
Просмотр значений в интерактивном окне ................................................... 48
3.5.
Заметки на память
3.6.
Итоги и дополнительные ресурсы
................................................................................................. 50
................................................................... 52
6
Оглавление
Глава
4.
Строки и строковые методы
..•.•.••..•....•......•..•.....•.......•...•.... 54
4.1.
Что такое строка? ................................................................................................... 54
4.2.
Конкатенация, индексирование и срезы ........................................................ 60
4.3.
Манипуляции со строками с использованием методов ............................ 68
4.4.
Взаимодействие с пользовательским вводом ............................................... 73
4.5.
Задача: разбор пользовательского ввода ........................................................ 75
4.6.
Работа со строками и числами ........................................................................... 76
4.7.
Упрощение команд вывода
4.8.
Поиск подстроки в строке ................................................................................... 82
4.9.
Задача: преобразование текста .......................................................................... 84
4.10.
Глава
................................................................................. 80
Итоги и дополнительные ресурсы
5.
................................................................... 85
Числа и математические вычисления ..................................
87
5.1.
Целые числа и числа с плавающей точкой .................................................... 87
5.2.
Арифметические операторы и выражения .................................................... 91
5.3.
Задача: выполнение вычислений с пользовательским вводом
5.4.
Когда
5.5.
Математические функции и числовые методы
5.6.
Оформление чисел при выводе ....................................................................... 104
5.7.
Комплексные числа ............................................................................................. 107
5.8.
Итоги и дополнительные ресурсы
Глава
Python
6. Функции
.............. 98
говорит неправду ........................................................................ 98
и циклы
......................................... 100
................................................................. 109
•.•.......•..•................................................... 111
6.1.
Что же такое функция? ...................................................................................... 111
6.2.
Написание ваших собственных функций .................................................... 115
6.3.
Задача: конвертер температур
6.4.
Циклическое выполнение ................................................................................. 123
6.5.
Задача: отслеживание прибыли по вкладу .................................................. 130
......................................................................... 122
Оглавление
6.6.
Область видимости в
6.7.
Итоги и дополнительные ресурсы
Глава
7
Python ........................................................................... 131
................................................................. 136
7. Поиск и исправление ошибок в коде ...•.........•............•..•.•. 137
7.1.
Использование окна
7.2.
Исправление ошибок .......................................................................................... 143
7.3.
Итоги и дополнительные ресурсы
Глава
8. Условная лоrика
8.1.
Debug Control .............................................................. 137
................................................................. 149
и управление проrраммой .............•.•..•..
151
Сравнение значений ............................................................................................ 151
8.2. Добавим немного логики ................................................................................... 154
8.3.
Управление последовательностью выполнения программы ................ 161
8.4.
Задача: поиск множителей числа ................................................................... 170
8.5.
Управление циклами ........................................................................................... 171
8.6.
Восстановление после ошибок ........................................................................ 17 4
8. 7.
Моделирование событий и вычисление вероятностей ........................... 179
8.8.
Задача: моделирование эксперимента с броском монеты ...................... 183
8.9.
Задача: моделирование выборов ..................................................................... 184
8.10.
Глава
Итоги и дополнительные ресурсы
9. Кортежи.
списки и с.nовари
................................................................. 184
............................................ ..... 186
9.1.
Кортежи как неизменяемые последовательности .................................... 186
9.2.
Списки: изменяемые последовательности .................................................. 195
9.3.
Вложение, копирование и сортировка кортежей и списков ................. 206
9.4.
Задача: список списков
9.5.
Задача: приступ вдохновения .......................................................................... 212
9.6.
Храните отношения в словарях
9.7.
Задача: цикл по столицам .................................................................................. 222
9.8.
Как выбрать структуру данных ....................................................................... 223
...................................................................................... 210
...................................................................... 213
8
Оглавление
9.9.
Задача: коты в шляпах ........................................................................................ 223
9.10.
Итоги и дополнительные ресурсы ................................................................. 224
Гпава
1 О. Объектно-ориентированное проrраммирование
(ООП) ..•...•........•.........•........••........•..•................................................... 226
10.1. Определение класса .......................................................................................... 226
10.2.
Создание экземпляров (инстанцирование) .............................................. 230
10.3. Наследование от других классов .................................................................. 235
10.4. Задача:
10.5.
Глава
модель фермы ...................................................................................... 242
Итоги и дополнительные ресурсы ............................................................... 243
11. Модули
и пакеты .......•.•....•....•..•....•....•....•....•.•.•......•.•....•... 244
11.1.
Работа с модулями ............................................................................................. 244
11.2.
Работа с пакетами .............................................................................................. 253
11.3.
И тоги и дополнительные ресурсы ............................................................... 260
Гпава
12. Операции ввода и вывода с файпами •..•.•..•.•.•••.•..•.......... 261
12.1.
Файлы и файловая система ............................................................................ 261
12.2. Работа с путями к файлам в Python ............................................................ 265
12.3. Основные операции файловой системы .................................................... 272
12.4. Задача:
перемещение всех графических файлов
в новый каталог .................................................................................................. 285
12.5. Чтение и запись файлов .................................................................................. 286
12.6. Чтение и запись данных CSV ........................................................................ 298
12.7. Задача:
12.8.
Гпава
создание списка рекордов
Итоги и дополнительные ресурсы ............................................................... 307
1 З. Установка
пакетов с помощью
13.1. Установка сторонних пакетов с
13.2.
............................................................... 307
pip ....•.....•..••...•......•.•....... 309
помощью
pip .......................................... 309
Подводные камни сторонних пакетов ........................................................ 318
13.3. Итоги и дополнительные ресурсы ............................................................... 320
Оглавление
9
Глава 14. Создание и изменение файлов PDF """""""""""""""". 321
14.1.
Извлечение текста из файла
14.2.
Извлечение страниц из файлов
14.3.
Задача: класс
14.4.
Конкатенация и слияние файлов
14.5.
Поворот и обрезка страниц
14.6.
Шифрование и дешифрование файлов
14.7. Задача:
PDF ................................................................ 321
PDF .......................................................... 327
PdfFileSplitter .......................................................................... 332
PDF ....................................................... 333
PDF .................................................................. 339
PDF ........................................... 348
восстановление порядка страниц ................................................. 351
14.8.
Создание файла
14.9.
Итоги и дополнительные ресурсы ............................................................... 357
Глава
15. Базы
PDF с
нуля .......................................................................... 352
данных ......•....•.•.•..•.•..•.••.•.•..••........................••••••.••.•• 359
15.1.
Знакомство с
15.2.
Библиотеки для работы с другими базами данных
15.3.
Итоги и дополнительные ресурсы ............................................................... 370
SQLite ......................................................................................... 359
SQL ..................... 369
Глава 16. Веб-проrраммирование """""""""""""""""""""""""" 372
16.1.
Скрапинг и парсинг текста с веб-сайтов .................................................... 372
16.2.
Использование парсера
16.3.
Работа с НТМL-формами ............................................................................... 385
16.4.
Взаимодействие с веб-сайтами в реальном времени ............................. 390
16.5.
Итоги и дополнительные ресурсы ............................................................... 393
HTML для
извлечения веб-данных ............. 380
Глава 17. Научные вычисления и построение графиков"""""""" 395
17.1.
Использование
NumPy для
матричных вычислений ............................ 395
17.2.
Построение графиков с помощью
17.3.
Итоги и дополнительные ресурсы ............................................................... 422
Matplotlib .......................................... 404
Глава 18. Графические интерфейсы """""""""""""""""""""""". 423
18.1. Добавление элементов GUI
18.2.
с помощью
EasyGUI ................................. 423
Пример: программа для поворота страниц
PDF ..................................... 434
Оглавление
1О
18.3. Задача:
приложение для извлечения страницы
PDF ............................ 439
18.4. Знакомство с Tkinter ........................................................................................ 440
18.5.
Работа с виджетами ........................................................................................... 443
18.6.
Управление макетом при помощи менеджеров rеометрии ................. 463
18.7.
Интерактивность в приложениях ................................................................ 478
18.8.
Пример приложения: конвертер температур ........................................... 485
18.9.
Пример приложения: текстовый редактор
18.10.
Задача: возвращение поэта ............................................................................. 496
18.11. Итоги
Гпава
и дополнительные ресурсы ............................................................... 498
19. Мыспи
19.1.
............................................... 489
напоследок и следующие warи
.........•................. 500
Еженедельные бесплатные советы для питонистов .............................. 501
19.2. Книга
~чистый
Pythoni.> .................................................................................. 501
19.3.
Библиотека видеокурсов
19.4.
Благодарности ..................................................................................................... 503
Real Python ........................................................ 502
Что питонисты говорят о книге «Знакомство с
Python»
«Обожаю [эту книгу]! Книга написана доступным языком, материал понятен,
а последовательность изложения выглядит логично. Я никогда не терялся
в материале, а интенсивность изложения не слишком велика, что позволяет
мне снова и снова возвращаться к предыдущим главам.
Я просмотрел более
10 разных учебников/книг/сетевых курсов о Python.
Real Python 1 !»
По­
жалуй, больше всего я узнал именно из материалов
(Томас Вон)
Thomas Wong
Real Python, когда требу­
языка Python».
«Прошло три года, а я все еще возвращаюсь к книгам
ется быстро освежить в памяти важные команды
Роб Фаулер
(Rob Fowler)
«Я долгое время заставлял себя заняться самообучением. Я продирался через
десятки неполных сетевых учебников. Я засыпал при просмотре многочасовых
скучных видеороликов. Я разочаровался в бесчисленных заумных книгах от
именитых издателей. А потом я открыл для себя
Real Python.
Доступные пошаговые инструкции разделяют большие темы на легко усваива­
емые части, написанные простым языком. Авторы никогда не забывают о своих
читателях, их объяснения неизменно обстоятельны и подробны. Сейчас я уже
перешел к самостоятельной работе, но постоянно возвращаюсь к книге за на­
ставлениями».
Джаред Нильсен
(Jared Nielsen)
«Мне нравится эта книга, потому что каждый урок завершают реальные и ин­
тересные задачи. Я только что написал программу для подсчета сбережений,
которая учитывает состояние моего сберегательного счета,
-
класс!»
Дрю Прескотт
1
Real Python -
(Drew Prescott)
проект Дэна Бейдера, одного из авторов книги «Знакомство с
и автора бестселлера «Чистый
Подробнее об этом ресурсе
-
в
Python. Тонкости программирования
разделе «06 авторах». - Примеч. ред.
Python»
для профи».
12
Что питон исты говорят о книге «Знакомство с Python»
«Чтобы потренироваться в том, что я узнал, я начал строить простые сценарии,
упрощающие повседневную работу моей команды. Когда руководство заметило
это, мне предложили новую должность разработчика. Я знаю, что мне еще многое
предстоит узнать и проблем будет немало, но я наконец-то начал заниматься
тем, что мне действительно нравится.
Еще раз: ОГРОМНОЕ СПАСИБО!»
KaмWl
«В курсах
Real Python
(Kamil)
мне больше всего нравится то, что они объясняют все
на максимально простом уровне.
Для освоения многих учебных курсов
плине
-
- притом практически в любой дисци­
необходимо изучать массу специальных терминов, тогда как на самом
деле материал можно объяснить быстро и лаконично. Авторы
Real Python ста­
раются использовать интересные примеры, и у них это отлично получается».
Стивен Грэди
«После освоения первого курса
(Stephen Grady)
Real Python я написал программу для автома­
тизации моих повседневных операций на работе. То, на что раньше требовалось
от трех до пяти часов, теперь выполняется менее чем за
10 минут!»
Брэндон Янгдейл
(Brandon Youngdale)
«Честно говоря, осваивая материал книги, я усердно искал, что бы можно было
в ней добавить или улучшить, но учебник получился просто замечательный!
Вы прекрасно умеете объяснять и обучать
Python на уровне, доступном даже
для людей вроде меня, то есть полных новичков.
Последовательность изложения материала работает идеально. Упражнения очень
сильно помогают, и после освоения материала книги вы справедливо ощущаете
гордость. Мне кажется, у вас есть особый дар
- делать так, чтобы Python казался
более досягаемым для людей, не принадлежащих к миру программирования.
Я никогда не думал, что буду иметь дело с
Python или изучать его. Но теперь
- и вижу, что в будущем
с небольшой поддержкой с вашей стороны я изучаю его
мне это принесет только пользу!»
Ши Клусевич
(Shea Кlu.sewicz)
Что питон исты говорят о книге «Знакомство с Python»
1З
«Авторы курсов НЕ забыли о том, каково быть новичком (а об этом не помнят
многие авторы!). Они ничего не требуют от своих читателей, и поэтому их
книги так хорошо читаются. К курсам прилагаются превосходные видеоролики,
а также множество ссылок для дополнительного обучения и домашней работы
и примеры кода, с которыми вы можете экспериментировать.
Мне очень понравилось, что все уроки сопровождались полными примерами
кода и каждая строка прокомментирована, чтобы вы понимали, что происходит.
У меня много книг о
Python, но только книги Real Python я прочитал от корки
до корки: просто они однозначно лучшие на рынке. Если вы, как и я, не при­
надлежите к числу профессиональных программистов (я работаю в сетевом
маркетинге), эти книги станут для вас настоящим наставником благодаря до­
ступным объяснениям, освобожденным от всего лишнего! В высшей степени
рекомендую!»
Крейг Эддиман
(Craig Addyman)
Об авторах
14
ОБ АВТОРАХ
Ресурс
Real Python предназначен для
всех, кто хочет освоить навыки реального
программирования при поддержке сообщества профессиональных разработчи­
ков
Python со
Веб-сайт
всего мира.
realpython.com
был запущен в
году. В настоящее время он еже­
2012
месячно помогает более чем трем миллионам разработчиков
Python
своими
бесплатными учебными пособиями и курсами.
Все, кто работал над книгой «Знакомство с
Python», -
практики, имеющие
многолетний профессиональный опыт в программировании, члены препода­
вательской команды
Дэвид Эймос
-
Real Python.
технический директор по контенту сайта
ухода из образовательной системы в
2015
Real Python.
После
году Дэвид работал на различных
технических должностях как программист и специалист по обработке данных.
В
2019
году он перешел в штат
Real Python,
чтобы развить свое увлечение об­
разованием. Дэвид возглавил переработку и обновление материала книги для
Python 3.
Дэн Бейдер
-
владелец и старший редактор сайта
Real Python,
а также веду­
щий разработчик образовательной платформы гealpython.com. Дэн занимается
20 лет, он имеет степень магистра в области компью­
«Python Tricks» 1 - понулярную
Python.
продвинутых разработчиков
программированием более
терных технологий. А кроме того, Дэн нависал
книгу для
Джоанна Яблонски
- главный редактор сайта Real Pytlюn. Она любит есте­
ственные языки в той же степени, что и я:iыки программирования. Ее при­
страстие к загадкам, закономерностям и нудным мелочам привело к тому, что
она выбрала карьеру переводчика. Прошло совсем немного времени, и она
влюбилась в новый язык - Python! Джоанна присоединилась к проекту Real
Python в 2018 году и с тех пор помогает программистам Python повышать нро­
фессиональную квалификацию.
Флетчер Хейслер
-
основатель нроекта
Hunter2,
он обучает разработчиков
тонкостям программирования и построению безопасных современных веб­
приложений. Флетчер, один из основателей
первую версию учебного курса
1
Бейдер Д. Чистый
Python,
Real Python,
в
2012
году написал
на котором основана эта кни1·а.
Python. То11кости программирова11ия для 11рофи. - С\ 16.: Питер.
Предисловие
Добро пожаловать! Надеюсь, вы готовы узнать, почему
столь многих разработчиков
-
Python
привлекает
как профессионалов, так и любителей
-
и как
вы можете применять его в ваших проектах, мелких и крупных.
Эта книга написана для новичков, которые либо немного владеют навыками
программирования, но не языком и экосистемой
Python, либо начинают осваи­
вать язык с нуля без какого-либо предварительного опыта программирования.
Если у вас нет диплома в области компьютерных технологий, не огорчайтесь.
Дэвид, Дэн, Джоанна и Флетчер разъяснят вам основные концепции программи­
рования в процессе изложения основ
Python; и что не менее важно -
на первых
порах не будут отвлекаться на несущественные подробности.
РУТНОN КАК ЯЗЫК ПОЛНОГО СПЕКТРА
При изучении нового языка программирования у вас еще нет опыта для того,
чтобы судить, насколько хорошо он послужит вам в долгосрочной перспективе.
Если вы намерены изучать
Python, позвольте заверить, что вы сделали хороший
Python является
выбор. Одна из важнейших причин заключается в том, что
языком полноrо спектра.
Что мы имеем в виду? Некоторые языки очень хорошо подходят для новичков.
Они ведут их за руку, и программирование становится невероятно простым.
Этот подход доводится до крайности в таких визуальных языках, как
В
Scratch вы имеете дело с
Scratch.
блоками, представляющими концепции программи­
рования: переменные, циклы, вызовы методов и т. д. Эти блоки перетаскиваются
на визуальной поверхности. Возможно,
Scratch хорошо подходит для написания
первых, очень простых программ, но профессиональное приложение на нем не
построишь. Назовите хотя бы одну компанию из списка
реализует свою основную бизнес-логику на
Fortune 500,
которая
Scratch.
Не получилось? У меня тоже, потому что это было бы полным безумием.
Другие языки обладают невероятной мощью
-
но в руках опытных разработ­
чиков. Пожалуй, самым популярным в этой категории является С++ и его бли­
жайший родственник С. Любой веб-брауэер, которым вы пользовались сегодня,
16
Предисловие
с большой вероятностью был написан на С или С++. Операционная система,
в которой этот браузер работает, тоже, скорее всего, написана на С/С++. Ваша
любимая стрелялка или стратегическая видеоигра? Верно: С/С++.
На этих языках можно делать невероятные вещи, но они совершенно недру­
желюбны к новичкам, которые предпочитают спокойное знакомство с новой
областью.
Вы еще не видели код С++? Иногда от него начинают слезиться глаза. Приведу
пример (относительно сложный):
template <typename Т>
_Defer<void(*(PID<T>, void (T::*)(void)))
(const PID<T>&, void (T::*)(void))>
defer(const PID<T>& pid, void (Т: :*method)(void))
{
void (*dispatch)(const PID<T>&, void (T::*)(void))
&process::template dispatch<T>;
return std::trl::bind(dispatch, pid, method);
}
Пожалуйста, только не это ...
И
Scratch,
и С++ определенно не являются тем, что я наэываю языками пол­
ного спектра.
Scratch
упрощает начало программирования, но для построе­
ния реальных приложений придется переключиться на «настоящий» язык.
И, наоборот, на С++ можно строить реальные приложения, но на деликатное
введение в тему лучше не рассчитывать. Вы с головой погружаетесь во всю
сложность языка, который создавался для поддержки полнофункциональных
приложений.
С другой стороны,
Python отличается от обеих крайностей -
спектра. Мы часто судим о простоте языка по программе
это язык полного
Hello, World.
Иначе
говоря, какой синтаксис и действия необходимы, чтобы программа вывела
сообщение
Hello, World?
На яэыке
Python трудно
представить что-то проще:
print("Hello, World")
И все! Тем не менее вряд ли это можно назвать rюлноценным критерием.
Тест Hello, World полеэен, но его недостаточно для того, чтобы продемон­
стрировать всю мощь или сложность языка. Рассмотрим другой пример. Не
старайтесь понять все от начала до конца
-
просто следите за процессом,
чтобы уловить суть. Все эти (и многие другие) концепции будут рассмотрены
в книге. Следующий пример будет вам вполне по силам, когда вы доберетесь
до конца книги.
Python как язык полного спектра
17
Итак, новый критерий: насколько трудно написать программу, которая обра­
щается к внешнему веб-сайту, загружает контент в память вашего приложения,
а затем выводит часть этого контента для пользователя? Для этого эксперимен­
Python 3 с
главу 12):
та мы воспользуемся
установить
см.
-
пакетом
requests
(который вам необходимо
import requests
resp = requests.get("http://olympus.realpython.org")
html = resp.text
print(html[86:132])
Вы не поверите, но это все! При запуске программа выводит результат следу­
ющего вида:
<h2>Please log in to access Mount Olympus:</h2>
Это простая, дружелюбная к начинающим часть спектра возможностей
Python.
Всего несколько тривиальных строк раскрывают невероятную мощь. Посколь­
Python доступно много мощных, но удобных библиотек (таких,
requests ), часто говорят, что «батарейки входят в комплект Python~.
ку для
как
Итак, вы увидели простой, но мощный пример. В реальном мире многие за­
мечательные приложения были написаны на
На
Python написан YouTube,
Python.
самый популярный сайт потокового видео, обра­
сайты, такие
Instagram - еще один пример
Python. Примеры можно найти и ближе - realpython.com и мои
как talkpython.fm.
Тот факт, что
Python является языком полного спектра, означает, что вы можете
батывающий более миллиона запросов в секунду.
приложения на
начать с основ и осваивать расширенные возможности по мере роста потреб­
ностей вашего приложения.
Популярность Python
Вы наверняка слышали, что
Python
популярен. Может показаться, что по­
пулярность языка менее важна, чем то, что на нем можно построить нужное
вам приложение.
Как бы то ни было, популярность языка программирования многое говорит
о качестве доступных библиотек и о количестве опубликованных вакансий.
Короче говоря, лучше придерживаться более популярных технологий, потому
что в вашем распоряжении окажется больше вариантов применения получен­
ных навыков.
Предисловие
18
Действительно ли
Python
настолько популярен? Да, настолько. Без шу­
михи и преувеличений дело не обходится, но обширная статистика под­
крепляет это утверждение. Взгляните на аналитику, представленную на
stackoverflow.com Stack
известном сайте вопросов и ответов для программистов.
Oveгflow поддерживает сайт StackOveгflowTrends, на котором можно
искать информацию о трендах различных технологий. Если сравнить
Python
с другими возможными кандидатами для изучения программирования, вы
увидите, что
~
Python
выделяется на их фоне.
16.00% -,-~--,.---,---,----..--~---.,..-~---,.---.--~-~·
Язык
u
1 8 python
~
.- 14.00% -i-+---+---+---+---+---t---+--+---+---1--f---11-12 8 javascript
з
~
~
~
;:
8 java
48С
12.00% +-+---t--+--+--t---t---+-r-""1'~-'dt""""'~:---l'il/--'-t--1
о
'tQj
>
о
iJ
8.00% -i"'r+---l~,_.1'+--'F"--'P'---+---t---+--+--F+-\~· \~""\1----+--I
~
V1
~
6.00% ~--!-~Nd-'--+--+---l---+--,,J<""'-='-+--+-~--1----i--I
2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020
Год
Если вы захотите изучить эти тренды, воспользуйтесь данными
insights.stackoverflow.com/trends.
Обратите внимание на невероятный рост применения
с графиками других кандидатов
-
Python
по сравнению
горизонтальными и даже снижающимися!
Если ваше будущее зависит от успеха конкретной технологии, то какой язык
вы бы выбрали из этого списка?
Впрочем, это всего лишь один график
гой .
Stack Overflow
-
что он говорит нам? Рассмотрим дру­
проводит среди разработчиков ежегодный опрос, очень
исчерпывающий и очень хорошо продуманный. Полные результаты за
можно найти на
insights.stackoverflow.com/survey/2020.
2020 год
Pythoп как язык полного спектра
19
В этом описании обратите внимание на раздел «Самые любимые языки»
( «Most
Wanted» ).
В нем приведены данные о доле «разработчиков, которые не исполь­
зуют язык или технологию, но выразили интерес к разработке на этом языке».
И снова из диаграммы видно, что
Python стоит на первом месте со значительным
отрывом даже от второго места.
Pythoп
25.7%
JavaScript
17.8%
Go
15.Оо/о
TypeScript
14.6%
Kotliп
11.1%
Rust
9.5%
С++
9.1%
WebAssemЫy
8.9%
Если вы согласны с тем, что относительная популярность языка программиро­
вания важна, то
Python,
очевидно, является хорошим вариантом.
Вам не надо знать теорию
Еще одно обстоятельство, которое я хочу подчеркнуть в начале вашего путеше­
ствия в мир
Python:
вам не обязательно быть специалистом по информатике.
Если вы к этому стремитесь
-
прекрасно. Изучение
Python
станет большим
шагом в этом направлении. Однако приглашение к изучению программирования
часто преподносится в виде: «У нас столько свободных вакансий! Нам нужны
разработчики!»
Это может быть правдой, а может и не быть. Но что гораздо важнее, про­
граммирование (даже простейшие навыки) может стать вашей персональной
суперсилой.
Представьте, что вы
-
биолог. Стоит ли бросать биологию и поступать на долж­
ность программиста, специализирующегося на построении веб-интерфейсов?
Возможно, нет. Но навыки вроде того, который был показан в начале преди­
словия (с использованием requests для получения данных с веб-сайта), могут
оказаться невероятно полезными и для биолога.
20
Предисловие
Вам не придется вручную экспортировать и извлекать данные с веб-сайтов или
из электронных таблиц, вы просто используете
Python для обработки тысяч ис­
точников данных или электронных таблиц за время, необходимое для ручной
обработки всего одного источника. Владение языком
Python переформатирует
-
вашу повседневную работу в качестве биолога, выведет ее на другой уровень
значительно выше уровня ваших коллег, так что она станет вашей суперсилой.
Дэн и Real Python
Позвольте мне напоследок сказать пару слов об авторах. Дэн Бейдер и другие
авторы изо дня в день трудятся над тем, чтобы доступно и ярко объяснять
концепции
Python
на сайте realpython.coт.
Они обладают неповторимыми взглядами на экосистему
Python
и ориентиру­
ются на то, что должны знать начинающие программисты.
Я со спокойной душой доверяю вас им в путешествии по миру
Python.
Отправ­
ляйтесь в путь и изучайте этот замечательный язык по отличной книге. А самое
главное
-
получайте удовольствие от процесса!
Майкл Кеннеди,
основатель
Talk Python
(@тkennedy)
От издательства
Ваши замечания, предложения, вопросы отправляйте по адресу comp@piteг.
com (издательство
«Питер», компьютерная редакция).
Мы будем рады узнать ваше мнение!
На веб-сайте издательства
о наших книгах.
www.piter.com
вы найдете подробную информацию
ГЛАВА
1
Введение
Добро пожаловать! Встречайте новое издание книги «Знакомство с
Python»,
полностью обновленное для
Python 3.9.
мирования на языке
которые мы иллюстрируем интересными прак­
Python,
Книга посвящена методам програм­
тическими примерами. Кем бы вы ни были
-
начинающим программистом
или профессионалом, желающим освоить новый язык,
-
здесь вы найдете все
необходимое для того, чтобы начать самостоятельную работу на
Python.
Если ваша деятельность связана с компьютером, то, каковы бы ни были ваши
цели, вы откроете для себя огромное количество возможностей упростить свою
жизнь за счет автоматизации задач и решения проблем в написанных вами про­
граммах
Python. Достаточно
Но чем же так хорош
изучить материал этой книги.
Python
как язык программирования? Прежде всего он
свободно распространяется с открытым кодом, а это означает, что вы можете
бесплатно загрузить его и использовать для любых целей (в том числе ком­
мерческих).
Кроме того, приверженцы
Python
создали сообщество и разработали целый
ряд полезных инструментов, которыми вы можете пользоваться в своих про­
граммах. Понадобилось поработать с документами
PDF? Для
этого вам пред­
лагается многогранный инструментарий. Извлечь данные с веб-страниц? Вам
не придется начинать с нуля!
Python создавался
с таким расчетом, чтобы им было проще пользоваться, чем
любым другим языком программирования. Как правило, код на
Python намного
легче читается и намного быстрее пишется, чем на других языках.
Вот простейшая программа на С, другом популярном языке программирования:
#include <stdio.h>
int main(void)
{
printf("Hello, World\n");
ГЛАВА 1
22
Введение
Эта программа просто выводит текст
Hello, World на экран.
Многовато работы
для вывода одной фразы! А вот как выглядит та же программа, написанная на
Python:
print("Hello, World")
Совсем просто, верно? Теперь вы сами убедились, что код на
Python быстрее пи­
шется и проще читается. И еще он выглядит более дружественным и доступным.
В то же время
Python обладает всей функциональностью других языков -
и не
только. Вы не поверите, сколько профессиональных продуктов построено на
базе
Python: Instagram, YouTube, Reddit, Spotify...
список можно продолжать.
Python не только доступен и интересен для изучения. Этот язык также положен
в основу технологий некоторых компаний мирового уровня, что открывает
фантастические возможности карьерного роста для любого программиста,
который им хорошо владеет.
1.1.
ПОЧЕМУ ИМЕННО ЭТА КНИГА?
Будем откровенны: в интернете с лихвой хватает информации о
Python.
Но
многим новичкам, которые изучают язык самостоятельно, иногда трудно разо­
браться, что изучать и в какой последователыюсти.
Возможно, вас интересует вопрос: что следует в первую очередь узнать о
Python,
чтобы заложить надежную базу для дальнейшего обучения? В таком случае эта
книга для вас независимо от того, абсолютный ли вы новичок или у вас уже
есть опыт работы на
Python
или других языках.
Книга написана просто. Базовые концепции, которые вам необходимы, излагают­
ся доступно. Это означает, что вы быстро начнете добиваться успехов в
Python.
Вместо перечисления возможностей языка я рассказываю, как разные струк­
турные элементы сочетаются друг с другом и что необходимо для построения
реальных приложений и сценариев на языке
Python.
Шаг за шагом вы освоите фундаментальные концепции, которые помогут вам
сделать первые шаги в применении
Python.
Многие книги по программированию грешат описанием всех возможных ва­
риаций каждой команды, из-за чего читатели быстро теряются в лабиринте
подробностей. Такой подход отлично годится для справочников, но не для
изучения языка программирования. Мало того, что вы тратите большую часть
1.2. О Real Python
23
времени, пытаясь уложить в голове множество деталей, которые вам никогда
не понадобятся,
-
это попросту скучно!
Книга построена по принципу 80/20: большую часть нужной информации мож­
но усвоить, изучив несколько критически важных концепций. Мы рассмотрим
команды и приемы, используемые в большинстве ситуаций, и сосредоточимся
на решении реальных повседневных задач.
Тем самым я гарантирую, что вы:
•
быстро освоите полезные приемы программирования;
•
потратите меньше времени на борьбу с лишними сложностями;
•
начнете применять
•
получите больше удовольствия от процесса.
Python на практике;
Книга дает вам возможность получить базовые знания, и дальнейшие ваши
вылазки на более сложную территорию будут проходить намного проще.
За основу мы взяли материал первой части исходного курса «Real
Python Course»,
2012 году. За прошедшие годы этот курс опробовали тысячи
программистов на Python, экспертов по работе с данными и разработчиков,
трудящихся в компаниях разных уровней, включая Amazon, Red Hat и Microsoft.
выпущенного в
Для этой книги мы тщательно доработали, расширили и обновили материал,
чтобы вы могли быстро и эффективно развивать свои навыки работы на Python.
1.2. О REAL РУТНОN
Сайт
Real Python даст вам возможность освоить навыки реального программи­
рования в сообществе профессиональных питонистов.
Веб-сайт
realpython.com
был запущен в
2012
году. В настоящее время он еже­
месячно помогает более чем трем миллионам разработчиков на
Python, предо­
ставляя доступ к книгам, учебникам и другим учебным ресурсам.
Все, кто работал над этой книгой,
Real Python с
-
практикующие программисты из команды
многолетним профессиональным опытом.
Контактные данные
Real Python
•
realpython.com
•
@realpython
в
в интернете:
Twitter (https.j/twitter.com/realpython)
ГЛАВА
24
1
Введение
•
The Real Python Newsletter (https.j/twitter.com/newsletter)
•
The Real Python Podcast (https.j/twitter.com/podcast)
1.3. КАК ПОЛЬЗОВАТЬСЯ КНИГОЙ
- краткий, но разносторонний обзор всех фундамен­
Python. Никакой предшествующий опыт программиро­
вания вам для этого не понадобится. Вторая половина - практические решения
Первая половина книги
тальных возможностей
интересных реальных задач программирования.
Новичкам мы рекомендуем изучить первую половину книги от начала до конца.
Темы во второй половине книги в меньшей степени связаны друг с другом, так
что вам будет проще осваивать их по отдельности, однако имейте в виду: чем
дальше, тем материал сложнее.
Если у вас уже есть опыт программирования, возможно, вам стоит с ходу об­
ратиться ко второй части книги. Но все же подумайте над тем, чтобы сначала
разобраться в основах, а потом уж заполнять информационные пробелы в про­
цессе решения практических задач.
Большинство разделов каждой главы завершается упражнениями, которые
помогут вам убедиться в том, что вы хорошо усвоили учебный материал. Также
в книге предлагаются сложные задачи, для решения которых вам придется
воспользоваться знаниями, полученными из предыдущих глав.
В файлах, прилагаемых к книге, содержатся полные решения задач и самых
сложных упражнений. Но чтобы извлечь максимум пользы, постарайтесь решать
задачи самостоятельно, а не с ходу заглядывать в ответы.
Если у вас вообще нет опыта программирования, первые главы желательно под­
крепить дополнительной практикой. Мы рекомендуем проработать учебники
начального уровня, которые можно бесплатно загрузить с сайта
(https.j/realpython.com/python-basics), -
realpython.com
они помогут убедиться в том, что ма­
териал вы усвоили.
А если у вас появятся вопросы или вы захотите поделиться своим мнением,
то всегда можете обратиться к нам напрямую
(https.j/realpython.com/contact).
Обучение на практике
Принцип обучения на практике взят за основу в этой книге, поэтому обязательно
вводите вручную все фрагменты кода, которые вам встретятся. Для достижения
1.4. Дополнительный материал и учебные ресурсы
25
наилучших результатов мы рекомендуем избегать копирования/вставки при­
меров. Вы быстрее поймете концепции и усвоите синтаксис, если будете вводить
каждую строку самостоятельно. Кроме того, если вы совершите ошибку
что
-
абсолютно нормально и что частенько случается с любым разработчиком,
-
то
даже простое исправление опечаток поможет вам научиться отлаживать код.
Пробуйте выполнять упражнения и задачи самостоятельно, прежде чем обра­
щаться за помощью к внешним ресурсам. При достаточной практике вы усвоите
материал, а попутно хорошо проведете время!
Сколько времени потребуется для изучения
материала книги?
Если вы уже знакомы с любым другим языком программирования, вам доста­
точно каких-нибудь
35-40 часов. Если же у вас нет опыта программирования,
100 часов и более.
вам может потребоваться
Не торопитесь, вас никто не подгоняет. Программирование
дарное, но непростое. Удачи на вашем пути в мир
Python!
-
занятие благо­
Мы за вас болеем!
1.4. ДОПОЛНИТЕЛЬНЫЙ МАТЕРИАЛ
И УЧЕБНЫЕ РЕСУРСЫ
К книге прилагаются бесплатные дополнительные ресурсы и материалы, ко­
торые можно загрузить из интернета по приведенной ниже ссылке. Здесь же
опубликован и постоянно обновляется список опечаток с исправлениями:
realpython.com/python -basics/resources
Интерактивные тесты
Для многих глав книги были созданы бесплатные интерактивные тесты для
проверки того, как вы усвоили материал (на английском языке!). К ним можно
обратиться по ссылкам, приведенным в конце глав. Тесты размещаются на сайте
Real Python,
и их можно просматривать с телефона или с компьютера.
В каждом тесте вам предлагается ответить на серию вопросов, относящихся
к конкретной главе книги. Иногда требуется выбрать один вариант из предла­
гаемого авторами набора, в других случаях вам придется напечатать ответ или
ввести код
Python. Информация о том, на какие вопросы вы ответили правильно
в процессе тестирования, сохраняется.
26
ГЛАВА
1
Введение
В конце теста вам будет выставлена оценка, вычисленная по вашим результатам.
Если вы не набрали
100 процентов с первой попытки, не огорчайтесь!
Эти тесты
и должны быть сложными. Предполагается , что вы пройдете их несколько раз,
каждый раз улучшая свою оценку.
Репозиторий кода упражнений
У книги существует репозиторий кода в интернете . Он содержит примеры
исходного кода, а также ответы на упражнения и задачи . Репозиторий разбит
по главам, так что вы можете сравнить свой код с нашими решениями после
изучения каждой главы. Ссылка на репозиторий:
realpython.com/python -basics/exercises
ПРИМЕЧАНИЕ
'
Код, приведенный в книге, был протестирован с Pythoп
macOS
и
3.9 для Windows,
Linux.
Лицензия на примеры кода
Сценарии
Python, имеющие отношение к книге, распространяются на условиях
Commons PuЬ\ic Domain). Это означает, что вы можете
лицензии ССО (Cгeative
свободно использовать в своих программах любые части кода для любых целей.
Обратная связь и опечатки
Мы охотно примем ваши идеи, предложения и даже критику. Какая-то тема
показалась вам непонятной? Вы нашли ошибку в тексте или в коде? Мы про­
пустили тему, о которой вам хотелось бы узнать побольше? Мы всегда рады
возможностям улучшить свои учебные материалы. Вы можете поделиться
с нами вашим мнением на:
realpython.com/python-basics/feedback
ГЛАВА2
Установка и настройка Python
Эта книга посвящена программированию на языке
Python.
Вы можете прочи­
тать ее от корки до корки, ни разу не прикоснувшись к клавиатуре, но так вы
упустите самое интересное
программирование!
-
Чтобы получить максимум пользы от книги, вам понадобится компьютер с уста­
новленной версией
Python,
а также средства для создания, редактирования
и сохранения файлов с кодом, который вы будете создавать.
В этой главе вы узнаете, как:
•
установить последнюю версию
•
запустить
Python 3 на вашем
компьютере;
IDLE - интегрированную среду разработки и обучения
(Integratcd Development and Learning Environment ), встроенную в Python.
Итак, за дело!
2.1.
О ВЕРСИЯХ РУТНОN
Многие операционные системы, включая
установленной версией
Python.
macOS и Linux, поставляются с пред­
Она называется системной версией.
Системная версия используется вашей операционной системой, и обычно она
уже устаревшая. Чтобы вы могли успешно воспроизводить примеры из книги,
важно установить последнюю версию
Python.
Не пытайтесь удалять системную версию
Python!
На компьютере можно установить несколько версий этого языка. В этой главе
вы установите последнюю версию
Python 3,
не удаляя системную версию, ко­
торая уже может существовать на вашей машине.
ГЛАВА
28
2 Установка и настройка Python
ПРИМЕЧАНИЕ
Даже если у вас уже установлен
Python 3.9, все равно стоит бегло просмотреть
эту главу и лишний раз убедиться, что окружение правильно настроено для
повторения примеров книги .
Глава разбита на три раздела:
Windows, macOS и Ubuntu Linux. Найдите раздел,
посвященный вашей операционной системе, и выполните установку и настройку,
после чего можете перейти к следующей главе.
Если у вас установлена другая операционная система, обратитесь к разделу
Python 3 Installation & Setup Guide
на сайте
Real Python
и посмотрите, под­
держивается ли ваша ОС. Читатели, пользующиеся планшетами и мобильными
устройствами, могут заглянуть в раздел
Online Python Interpreters, чтобы полу­
чить информацию о некоторых настройках для браузеров.
2.2. WINDOWS
Здесь описана процедура установки
Python 3 и запуска IDLE в системе Windows.
ВАЖНО!
Код, приведенный в книге, тестировался только для копии
Python,
установ­
ленной так, как описано в этом разделе.
Python каким-то другим способом (например,
Anaconda Python), могут возникнуть проблемы при запуске не­
Учтите: если вы установили
средствами
которых примеров.
Установка Python
Системная версия
Python обычно не входит в поставку Windows. К счастью,
Python
установка сводится лишь к загрузке и запуску программы установки
с сайта
Шаг
Python.org.
1. Загрузите
программу установки
Запустите браузер и перейдите на
https.j/www.python.org/downloads/windows/
Python
З
2.2. Windows
29
Щелкните на ссылке
Latest Python 3 Release - Python З.х.х под заголовком Python
Releases for Windows
в верхней части страницы. На момент написания книги
новейшей версией была
Python 3.9.
Затем прокрутите страницу вниз и щелкните на ссылке
installer,
Windows х86-64 executaЫe
чтобы начать загрузку.
ПРИМЕЧАНИЕ
Если ваша система оснащена 32 - разрядным процессором, выберите 32-раз­
рядную программу установки . Если вы не уверены в том , является ли ваш
компьютер 32-разрядным или 64- разрядным, выбирайте 64-разрядную про ­
грамму установки, о которой мы говорили выше .
Шаг
2. Запустите
программу установки
Откройте папку Загрузки в Проводнике Windows и дважды щелкните на файле,
чтобы запустить программу установки. На экране появляется диалоговое окно,
которое выглядит примерно так:
~ Python 3.9.1 (64-blt) Setup
о
х
lnstall Python 3.9.1 (64-blt)
Select lnstall Now to install Python with default settings, or choose
Customize t o еnа Ые or disaьte features.
lпstall
Now
C:\Uшs\damos\AppData\Local\Programs\Python\PythonЗ9
lnclude:s IDLE, pip and documentation
Create:s shortcuts and file associations
~
Customize installation
Choose location and features
python
for
windows
Если номер версии
0
0
lnstall launcher for all users (recommended)
Add Python 3.9 to РАТН
Python
3.
чтобы он был не меньше
окажется больше
Cancel
3.9.1,
это нормально
-
главное,
ГЛАВА 2
30
Установка и настройка Python
.
ВАЖНО!
Обязательно включите флажок
.
.
.
'
.
Add Pythoп З.х to РАТН . Если вы установили
Pythoп, не выбрав этот флажок, снова запустите программу установки и вы­
берите его.
Щелкните на кнопке
Install Now, чтобы установить Python 3. Дождитесь завер­
IDLE.
шения установки и переходите к запуску
Запуск
IDLE
Чтобы запустить
IDLE,
выполните следующие действия.
1.
Откройте меню Пуск и найдите папку
2.
Откройте папку и выберите IDLE
Python 3.9.
(Python 3.9).
IDLE открывает командную оболочку (shell) Python в новом окне. Оболочка
Python - интерактивная среда, в которой можно ввести код Python и немед­
ленно выполнить его. Она отлично подходит для изучения Python!
- о
ПРИМЕЧАНИЕ
.
Хотя ничто не мешает вам использовать вместо
.
IDLE
другой редактор кода,
если он вам больше нравится, учтите, что в некоторых главах (особенно в гла­
ве
7 «Поиск и исправление ошибок в коде») работа построена исключительно
IDLE.
на использовании
Окно командной оболочки
Python выглядит
примерно так:
о
IDLE Shell 3.9.1
х
File Edit Shell Debug Options Window Help
Python 3.9.1 (tag 3/v3.9 . l
Dб4)] on win 32
Туре "help ", " copyright ",
: le5d3Зe ,
" creditэ "
Dec
or
7 2020, 17 : 08:21)
" licenэe() "
[MSC v . 1927 64
Ьit
(АМ
"
for more information .
>>>
Ln: 3 Col: 4
2.3. macOS
В верхней части окна выводится номер версии
31
Python и информация об опера­
ционной системе. Если номер меньше 3.9, возможно, вам стоит вернуться к ин­
струкциям по установке из предыдущего раздела и установить нужную версию.
Символы
>>> образуют так называемое приглашение (prompt).
Python ожидает от вас инструкций.
Когда вы видите
его, это означает, что
ИНТЕРАКТИВНЫЙ ТЕСТ
К этой главе прилагается бесплатный интерактивный тест для проверки усво­
енных вами знаний . Тест доступен на телефоне или компьютере:
realpython.com/quizzes!pybasics-setup
Итак,
Python установлен в вашей системе,
Python! Переходите к главе 3.
и мы можем написать первую про­
грамму
2.3. macOS
Ниже описана процедура установки
ВАЖНО!
Python 3 и запуска IDLE в macOS.
•
Код, приведенный в книге, тестировался только для копии
Python, установ­
ленной так, как описано в разделе .
Учтите : если вы установили
средствами
Python каким -то другим способом (например,
Anaconda Python), могут возникнуть проблемы при запуске не­
которых примеров .
Установка Python
Чтобы установить последнюю версию
программу установки
Шаг
Python в macOS, загрузите и запустите
Python с сайта Python.org.
1. Загрузите программу установки Python З
Запустите браузер и перейдите на страницу:
https.j/www.python.org/downloads/ mac-osx/
Щелкните на ссылке Latest
Releases for шасОS
Python 3 Release - Python З.х.х под заголовком Python
в верхней части страницы. На момент написания книги по­
следней версией была
Python 3.9.
ГЛАВА 2
32
Установка и настройка Python
Затем прокрутите страницу вниз и щелкните на ссылке macOS 64-Ьit installer,
чтобы начать загрузку.
Шаг
2. Запустите
программу установки
Откройте Findeг и дважды щелкните на файле, чтобы запустить программу
установки. На экране появится диалоговое окно, которое выглядит пример­
но так:
•••
8
lnstall Python
Welcome to the Python lnstaller
Тhis
package will install Python 3.9.1 for macOS 10.9 or later.
Python for macOS consists of the Моп programming language
interpreter and its batteries-included standard library to allow easy
access to macOS features. 11 also includes the Python integrated
development environment, IDLE. You сап also use the included plp to
download and install third-party packages from the fython Packa~
.1.шШх.
At the end of this install, cfick оп Install Certificates to install
set of current SSL гооt certificates.
Go Back
Несколько раз нажмите
а
Continue
Continue, пока появится предложение подтвердить
лицензионное соглашение. Затем нажмите кнопку
Agree.
На экране появится окно с информацией о том, в каком каталоге будет уста­
новлена копия
Python
и сколько места она займет. Скорее всего, изменять
каталог по умолчанию не понадобится; щелкните на кнопке Install, чтобы
начать установку.
Когда копирование файлов будет завершено, закройте окно программы уста­
новки кнопкой
Close.
2.3. macOS
Запуск
33
IDLE
Чтобы запустить
IDLE, выполните следующие действия.
1.
Откройте
Finder и
выберите категорию Приложения.
2.
Дважды щелкните на папке
3.
Дважды щелкните на значке
Python 3.9.
IDLE.
IDLE откроет командную оболочку (shell) Python в новом окне. Оболочка
Python - интерактивная среда, в которой можно ввести код Python и немед­
ленно выполнить его. Она отлично подходит для изучения Python!
ПРИМЕЧАНИЕ
Хотя ничто не мешает вам использовать вместо
IDLE
другой редактор кода,
если он вам больше нравится, учтите, что в некоторых главах (особенно в гла­
ве 7 «Поиск и исг1равление ошибок в коде») работа построена исключительно
на использовании
IDLE.
Окно командной оболочки
• •
Python выглядит примерно так:
IDLE Shell 3.9.1
Python 3.9.1 (v3.9.l:le5d33e9b9, Dec 7 2020, 12:10:52)
[ Clang 6.0 (clang-600.0.57)] оп darwin
Туре "help" , "copyright", 11 credits" or "license() " for more information .
>>>
Ln:4
В верхней части окна выводится номер версии
рационной системе. Если номер меньше
3.9,
Python
1
Со1 : 4
и информация об опе­
возможно, вам стоит вернуться
к инструкциям по установке из предыдущего раздела.
ГЛАВА
34
2
Установка и настройка Python
Символы»> образуют так называемое приглашение. Когда вы видите его, это
означает, что
Python
ожидает от вас инструкций.
ИНТЕРАКТИВНЫЙ ТЕСТ
К этой главе прилагается бесплатный интерактивный тест для проверки усво·
енных вами знаний. Тест доступен на телефоне или компьютере:
realpython.comlquizzes/pybasics-setup
Итак,
Python установлен в вашей системе,
Python! Переходите к главе 3.
и мы можем написать первую про­
грамму
2.4. LINUX
Ниже описана процедура установки
Python 3 и запуска IDLE
в
Ubuntu Linux.
ВАЖНО!
Код, приведенный в книге, тестировался только для копии Python, установ­
ленной так, как описано в разделе.
Учтите: если вы установили Python каким-то другим способом (например,
средствами Anaconda Python), могут возникнуть проблемы при запуске не­
которых примеров.
Установка Python
Ubuntu уже установлен Python, но, скорее всего,
новой - например, Python 2 вместо Python 3.
Весьма вероятно, что в вашем
версия окажется не самой
Чтобы определить номер вашей версии, откройте окно терминала и попробуйте
выполнить следующие команды:
$ python --version
$ pythonЗ ··version
Одна или обе команды могут вывести номер версии:
$ pythonЗ --version
Python 3.9.1
2.4. Linux
35
Если версия Python -- 2.х или меньше 3.9, то вам стоит заняться установкой
Python. Способ установки Python в Ubuntu зависит от версии Ubuntu на вашем
компьютере. Для проверки локальной версии Ubuntu можно воспользоваться
следующей командой:
$ lsb_release -а
No LSB modules are availaЫe.
Distributor ID: Ubuntu
Ubuntu 18.04.1 LTS
Description:
18.04
Release:
bionic
Codename:
Найдите номер версии в строке
Release и выполните инструкции, приведенные
ниже.
Ubuntu 18.04 и выше
Ubuntu
18.04 по умолчанию не включает Python 3.9, но пакет доступен
Universe. Чтобы установить его, выполните следующие команды
версии
в репозитории
в окне терминала:
$ sudo apt-get update
$ sudo apt-get install
pythonЗ.9 idle-pythonЗ.9 pythonЗ-pip
Учтите, что обновление репозитория
пуска
Python.
Universe
Возможно, загруженная версия
обычно отстает от графика вы­
Python 3.9 не будет
Python 3.9.
новейшей.
Тем не менее для этой книги годится любая версия
Ubuntu 17 и ниже
Python 3.9 недоступен в репозитории Universe.
(Personal Package Archive ). Чтобы уста­
новить Python из deadsnakes РРА (https//launchpad.net/-deadsnakes/+archive/
ubuntu/ppa), выполните следующие команды в окне терминала:
Для
Ubuntu
версий
17
и ниже
Его необходимо загрузить из архива РРА
$ sudo add-apt-repository ppa:deadsnakes/ppa
$ sudo apt-get update
$ sudo apt-get install pythonЗ.9 idle-pythonЗ.9
pythonЗ-pip
Python, введите коман­
3.9, возможно,
следует ввести команду pythonЗ.9 --version. Теперь вы можете запустить IDLE
и приготовиться к созданию вашей первой программы на языке Python.
Чтобы проверить, что установлена правильная версия
ду pythonЗ
--lJersion.
Если будет выведен номер версии меньше
36
ГЛАВА 2
Запуск
IDLE
Чтобы запустить
$
Установка и настройка Python
IDLE
из командной строки, введите следующую команду:
idle-pythonЗ.9
В некоторых установках
$
Linux можно запустить IDLE сокращенной
командой:
idleЗ
IDLE открывает командную оболочку (shell) Python в новом окне. Оболочка
Python - интерактивная среда, в которой можно ввести код Python и немед­
ленно выполнить его. Она отлично подходит для изучения Python!
ПРИМЕЧАНИЕ
Хотя ничто не мешает вам использовать вместо
IDLE
другой редактор кода,
если он вам больше нравится, учтите, что в некоторых главах (особенно в гла­
ве 7 «Поиск и исправление ошибок в коде») работа построена исключительно
на использовании
IDLE.
Окно командной оболочки
Python выглядит
IDLE
примерно так:
,-
Shell 3.9.1
file .§dit She!I QeЬug Qptions ~indow .1:telp
Python 3.9.1 (default, Dec 8 2828, 03:24:52)
[GCC 7.5.6] on linux
Туре "help • , •copyright", •credits" ог "license() • for more information.
~
>>>
-:;
Ln:
В верхней части окна выводится номер версии
рационной системе. Если номер меньше
3.9,
Python
4 Col:'!
и информация об опе­
возможно, вам стоит вернуться
к инструкциям по установке из предыдущего раздела .
2.4. Linux
37
ВАЖНО!
Если вы запустили IDLE командой idleЗ и в окне оболочки Python выводит­
ся номер версии меньше 3.9, значит, IDLE нужно запустить командой idlepythonЗ . 9
Символы
command.
>>> образуют так называемое приглашение. Когда вы видите его, это
означает, что
Python ожидает от вас
инструкций.
ИНТЕРАКТИВНЫЙ ТЕСТ
К этой главе прилагается бесплатный интерактивный тест для проверки усво­
енных вами знаний. Тест доступен на телефоне или компьютере:
realpython.com/quizzes/pybasics-setup
Python установлен в вашей системе,
грамму Python! Переходите к главе 3.
Итак,
и мы можем написать первую про­
ГЛАВАЗ
Первая программа
Итак, вы установили последнюю версию
Python
пора браться за програм­
Python -
мирование!
В этой главе мы:
•
напишем нашу первую программу на
•
запустим программу, содержащую ошибку, и посмотрим, что происходит;
•
объявим переменную и просмотрим ее значения;
•
напишем комментарии.
Готовы сделать первые шаги в мир
3.1.
Python?
Python;
Вперед!
НАПИСАНИЕ ПРОГРАММЫ РУТНОN
Если вы еще не открыли
IDLE, сделайте это сейчас. В IDLE вы будете работать
с двумя основными окнами: интерактивным, которое открывается при ;iа11уске
IDLE,
и окном редактора.
Код можно вводить в любом из этих окон. Но они отличаются тем, как в них
выполняется код. В этом разделе мы расскажем, как выполнять код
Python
в обоих окнах.
Интерактивное окно
Интерактивное окно
IDLE содержит командную оболочку Python - текстовый
интерфейс, иснользуемый для взаимодействия с языком Python. Если ввести
фрагмент кода Python в интерактивном окне и нажать клавишу Enter, вы не­
медленно увидите резул1,таты
-
отсюда и название «интерактивное окно».
Это окно открывается автоматически при запуске
IDI,E.
В верхней его части
появляется следующий текст (с нс:тачительными отличиями, которые :швисят
от конфигурации):
3.1. Написание программы Pythoп
39
Python 3.9.1 (tags/v3.9.l:lb293b6)
[MSC v.1916 32 bit (Intel)] оп win32
Туре "help", "copyright", "credits" or "license" for more information.
>»
В тексте указана версия
Python,
выполняемая в
IDLE.
Также приводится
информация об операционной системе и некоторые команды, которыми
можно воспользоваться для получения справки и просмотра информации
о
Python.
Символы»> образуют так называемое приглашение. Здесь вы вводите свой код.
Введите после приглашения
нажмите клавишу
1+1 и
Enter:
>» 1 + 1
2
»>
Python обрабатывает выражение, выводит результат (2),
а затем
-
следующее
приглашение. Каждый раз, когда вы выполняете код в интерактивном окне,
прямо под результатом появляется новое приглашение.
Выполнение кода
Python
в интерактивном окне можно описать циклом, со­
стоящим из трех этапов.
1. Python читает код,
введенный в приглашении.
2. Python вычисляет результат.
3. Python выводит результат и
ожидает нового ввода.
Этот цикл обычно называют циклом «чтение
evaluate - print loop,
или
-
вычисление
-
печать»
(read -
REPL).
Попробуем сделать что-то поинтереснее сложения чисел. У программистов есть
традиция начинать программирование на новом языке с программы, которая
выводит на экран фразу
«Hello,World».
Введите после приглашения в интерактивном окне слово
print,
за которым
следует пара круглых скобок с текстом "Hello, World":
»> print("Hello, World")
Hello, World
Функцией называется код, который выполняет некоторую операцию и может
вызываться по имени. Показанный код вызывает функцию
ей в качестве входных данных текст
"Hello, World".
print (), передавая
40
Первая программа Python
ГЛАВА З
Круглые скобки приказывают
Python
вызвать функцию print (). В них также
заключено все, что должно передаваться функции на вход. Кавычки означают,
что "Hello, World" - обычный текст, а не что-то иное.
ПРИМЕЧАНИЕ
В процессе ввода
IDLE выделяет части вашего кода разными цветами, чтобы
вам было проще их распознать. По умолчанию функции выделяются фиоле­
товым, а текст
-
зеленым цветом.
Интерактивное окно выполняет одну строку кода за раз. Такой режим вы­
полнения удобен для небольших примеров и знакомства с языком
Python,
но
у него есть серьезный недостаток: вам приходится вводить код по одной строке!
Также возможен другой вариант: сохранить код
Python
в текстовом файле,
а затем выполнить сразу весь код из файла.
Окно редактора
Python в окне редактора IDLE. Окно редакто­
File ~ New File меню, находящегося в верхней части
Вы будете создавать ваши файлы
ра можно открыть командой
интерактивного окна.
Когда открывается окно редактора, интерактивное окно остается открытым.
В нем отображается вывод, генерируемый кодом в окне редактора, поэтому окна
желательно расположить так, чтобы они были видны одновременно .
Введите в окне редактора тот же код, который использовался для вывода со­
общения "Hello, World" в интерактивном окне:
print("Hello, World")
IDLE выделяет
цветом код в окне редактора точно так же, как и в интерактив­
ном окне .
ВАЖНО!
Если вы пишете код в файле Pythoп, не нужно включать в код приглашение
>>>.
Прежде чем запускать программу, ее необходимо сохранить. Выберите в меню
команду
File ~ Save и
сохраните файл под именем
hello_world.py.
3.1 . Написание программы Pythoп
41
ПРИМЕЧАНИЕ
IDLE по умолчанию исполь­
Python. Не сохраняйте свои файлы в этом каталоге.
В некоторых системах для сохранения файлов в
зуется каталог установки
Сохраните их на рабочем столе или в папке, расположенной в домашнем
каталоге пользователя.
Расширение .ру означает, что файл содержит код
Python.
Более того, при сохра­
нении файла с любым другим расширением цветовая разметка кода исчезнет.
IDLE
применяет ее только в коде
Запуск программ
Python
внутри файлов .ру.
Python в окне редактора
Чтобы запустить программу, выберите команду
Run ~ Run Module в меню окна
редактора.
Нажатие клавиши
FS
также запускает программу из окна редактора.
Вывод программы всегда отображается в интерактивном окне.
Каждый раз, когда вы запускаете код из файла, в интерактивном окне появляется
сообщение, которое выглядит примерно так:
>>>
===================
RESTART
===================
IDLE перезапускает интерпретатор Python -
компьютерную программу, которая
фактически выполняет ваш код при каждом запуске файла. Это гарантирует,
что программы будут каждый раз выполняться в одинаковых условиях.
Открытие файлов
Python в окне редактора
Чтобы открыть существующий файл в
1D LE, выберите в меню команду File ~ Open,
IDLE открывает каждый
а затем выделите файл, который вы хотите открыть.
файл в новом окне редактора, так что вы можете открыть несколько файлов
одновременно.
Файл можно также открыть из файлового менеджера
водник
Windows
или
macOS Finder.
-
такого, как Про­
Щелкните правой кнопкой мыши на
значке файла и выберите команду Edit with IDLE, чтобы открыть файл в окне
редактора
IDLE.
ГЛАВА 3
42
Первая программа Python
Двойной щелчок на файле .ру в файловом менеджере запускает программу.
Однако файл обычно выполняется системной версией
мы исчезает сразу же после ее завершения
-
Python,
и окно програм­
нередко до того, как вы увидите
какой-либо вывод.
А пока, если вы хотите запустить программу
в редакторе
3.2.
Python, лучше всего сделать это
IDLE.
ОШИБКИ
Все совершают ошибки
- особенно в программировании! Даже если вы еще не
сделали ни одной, мы сыграем на опережение и нарочно испортим код, чтобы
посмотреть, что из этого выйдет.
Ошибки в программах делятся на два основных типа: синтаксические ошибки
и ошибки времени выполнения.
Синтаксические ошибки
Синтаксическая ошибка возникает, если вы написали код, недопустимый
в
Python.
Давайте намеренно создадим синтаксическую ошибку. Удалите последнюю
кавычку из кода в файле
hello _ world.py, созданного в предыдущем ра:щеле:
print("Hello, World)
Сохраните файл и запустите его нажатием клавиши
будет!
IDLE
FS.
Программа рuботать не
отображает окно сообщения со следующим текстом:
EOL while scanning string literal.
(EOL при
сканировании строкового литерuла.)
В этом тексте встречаются двu термина, которые могут быть вuм нез11uкомы.
1. Строковый литерал (string literal) - текст, заключенный в кавычки.
"Hello, World"
является строковым литералом.
2. EOL означает конец строки (End of Line).
Таким образом, IПLE сообщает, что Pythoп достиг конца строки во время чте­
ния строкового литерала. Строковые литералы должны эавершаться кавычкой,
предшествующей концу строки.
3.2. Ошибки
43
IDLE выделяет красным цветом строку с командой print( "Hello, World), чтобы
вы смогли быстро найти строку кода с синтаксической ошибкой. Без второй
кавычки все символы, следующие после первой, включая завершающую круглую
скобку, считаются частью строкового литерала.
Ошибки времени выполнения
IDLE обнаруживает синтаксические ошибки еще до того, как программа начнет
работу. Напротив, ошибки времени выполнения происходят только в работа­
ющих программах.
Чтобы сгенерировать ошибку времени выполнения, удалите обе кавычки
в файле
hello _ world.py:
print(Hello, World)
Вы заметили, что при удалении кавычек цвет кода меняется на черный?
уже не распознает
IDLE
Hello, World как текст. Как вы думаете, что произойдет при
запуске программы? Нажмите
FS,
чтобы узнать!
В интерактивном окне следующий текст выводится красным цветом:
Traceback (most recent call last):
File "/home/hello_world.py", line 1, in <module>
print(Hello, World)
NameError: name 'Hello' is not defined
Каждый раз, когда в программе происходит ошибка,
программу и выводит несколько строк текста
-
Python
останавливает
так называемую обратную
трассировку. Она содержит полезную информацию об ошибке.
Обратную трассировку лучше читать снизу вверх.
•
В последней строке обратной трассировки содержится имя ошибки и со­
общение об ошибке. В данном случае ошибка NameError возникла из-за
того, что имя
•
Hello
нигде не определено.
В предпоследней строке выводится код, который стал причиной ошибки.
Файл
hello_world.py состоит
всего из одной строки, так что понять, где
возникла проблема, несложно. Но такая информация пригодится для
больших файлов.
•
Третья строка с конца сообщает имя файла и номер строки, чтобы вы
могли перейти к строке кода, в которой произошла ошибка.
В следующем разделе вы узнаете, как определять имена для значений в коде.
Но прежде чем двигаться дальше, советую вам потренироваться в устранении
ГЛАВА З
44
Первая программа Python
синтаксических ошибок и ошибок времени выполнения
-
в обзорных упраж­
нениях.
Упражнения
1.
Напишите программу, которая не будет запускаться в
IDLE
из-за син­
таксической ошибки.
2.
Напишите программу, в которой ошибка происходит только во время вы­
полнения, потому что программа содержит ошибку времени выполнения.
3.3. СОЗДАНИЕ ПЕРЕМЕННОЙ
В языке
Python
переменной называется имя, с которым можно связать зна­
чение, чтобы в дальнейшем в коде программы ссылаться на значение по имени.
Переменные чрезвычайно важны для программирования по двум причинам.
1.
Переменные предоставляют быстрый доступ к значениям. Например,
результат некоторой продолжительной операции можно присвоить
переменной, чтобы вашей программе не приходилось заново выполнять
операцию каждый раз, когда потребуется использовать результат.
2.
Переменные наделяют значения контекстом. Число
28 может означать
что угодно: количество студентов в группе, количество обращений поль­
зователя к неб-сайту и т. д. Если связать значение
28 с именем (например,
num_students), его смысл становится более понятным.
В этом разделе вы научитесь использовать переменные, а также освоите неко­
торые соглашения, которые соблюдаются программистами
Python при
выборе
имен переменных.
Оператор присваивания
Оператор представляет собой знак (например, + ), выполняющий операцию
с одним или несколькими значениями. Например, оператор
+
получает два
числа (одно слева, а другое справа) и складывает их.
Для присваивания значений именам переменных используется специальный
знак, называемый оператором присваивания
( =).
Оператор= получает значе­
ние, указанное справа от оператора, и присваивает его имени, указанному слева.
Изменим файл hello_uюrld.py из предыдущего раздела, чтобы программа при­
сваивала текст переменной
file,
а затем выводила ее на экран:
3.3. Создание переменной
45
>» greeting = "Hello, World"
>>> print(greeting)
Hello, world
В первой строке создается переменная с именем
оператора= присваивается значение
greeting, которой при помощи
"Hello, World".
Команда
имя
print(greeting) выводит текст Hello, World, потому что Python ищет
greeting, узнает, что ему было присвоено значение "Hello, World", и перед
вызовом функции заменяет имя переменной значением.
Если не выполнить команду greetiпg
= "Hello, World"
команды priпt(greetiпg), вы получите ошибку
выполнения команды priпt(Hello,
World)
перед выполнением
NameError, как при попытке
в предыдущем разделе.
ПРИМЕЧАНИЕ
Хотя символ= известен нам из курса математики как знак равенства, в Pythoп
он имеет другой смысл . Это важный момент, который может сбить с толку
многих начинающих программистов.
Просто запомните: когда вы видите оператор=, значение в правой части при­
сваивается переменной в левой части.
В именах переменных важен регистр символов, так что greetiпg и Greetiпg
-
две разные переменные. Например, при попытке выполнения следующего кода
будет выдана ошибка
NameError:
>>> greeting = "Hello, World"
>>> print(Greeting)
Traceback (most recent call last):
File "<stdin>", line 1, in cmodule;
NameError: name 'Greeting' is not defined
Если у вас возникнут сложности при выполнении какого-либо примера из книги,
проверьте каждый символ в вашем коде, включая пробелы,
-
ваш код должен
абсолютно соответствовать примеру в книге. Компьютер точно выполняет то,
что вы прикажете, так что «почти правильно~> будет недостаточно!
Правила выбора имен переменных
Имена переменных могут быть сколь угодно длинными или короткими, но есть
несколько правил, которые необходимо соблюдать. Имена переменных могут
ГЛАВА З
46
Первая программа Pythoп
состоять из латинских букв верхнего и нижнего регистра
и символов подчеркивания
(_),
(A-Z, a-z), цифр (0-9)
но они не могут начинаться с цифры.
Например, каждое из следующих имен является действительным именем пере­
менной
Python:
•
stringl
•
_alp4a
•
list_of_names
А вот такие имена не являются действительными именами переменных, потому
что они начинаются с цифры:
•
9lives
•
99_balloons
•
2beOrNot2Be
Кроме латинских букв и цифр, имена переменных в
Python
могут содержать
многие символы Юникода .
Юникод
(Unicode) -
стандарт цифрового представления символов, исполь­
зуемых в большинстве мировых письменных систем . Это значит, что имена
переменных могут содержать буквы из других алфавитов, например буквы
с диакритическими знаками (е,
ti и т. д.), и даже знаки китайской, японской
и арабской письменности.
Тем не менее не каждая система нормально отображает расширенные символы,
поэтому лучше избегать их, если только вы не собираетесь обмениваться своим
кодом с жителями других стран .
'
ПРИМЕЧАНИЕ
'
.
.
.
'
.
О Юникоде мы более подробно поговорим в главе
.
·
.
·
12.
Также о поддержке Юникода в Python можно узнать в официальной докумен­
тации Pythoп (https://docs.python.org/3/howto/unicode.html#python-s-unico de-
support).
Даже если имя переменной допустимо, оно не всегда может оказаться хорошим.
Выбрать хорошее имя для переменной иногда на удивление нелегко. К счастью,
существуют рекомендации, которые помогут вам.
3.3. Создание переменной
47
Содержательные имена лучше коротких
Содержательные имена переменных очень важны, особенно в сложных про­
граммах. Часто для записи содержательных имен требуется несколько слов.
Не бойтесь использовать длинные имена переменных.
В следующем примере значение
s
=
присваивается переменной
3600
s:
3600
Имя
s
не несет никакой информации. Если вы используете полное слово, вам
будет намного проще понять смысл переменной в коде:
seconds
Имя
= 3600
seconds
лучше подходит для переменной, потому что оно предоставляет
больше информации. Но оно все еще не полностью передает смысл. Что такое
3600
секунд
время завершения некоторого процесса, продолжительность
-
фильма, еще что-нибудь? Непонятно.
А вот такое имя не оставляет ни малейших сомнений относительно того, что
хранится в переменной:
seconds_per_hour
= 3600
Читая этот код, вы точно знаете, что
3600 -
количество секунд в одном часе.
Ввод имени
или слова
seconds_per_hour занимает больше времени, чем ввод одной буквы s
seconds, но и выигрыш велик - полная ясность.
Хотя содержательные имена переменных обычно имеют большую длину, слиш­
ком длинных имен все же стоит избегать. Хорошее практическое правило
-
имена переменных должны содержать не более трех-четырех слов.
Соглашения о выборе имен в Python
Во многих языках программирования имена переменных обычно записывают
в смешанном регистре. То есть с прописной буквы начинается каждое слово,
кроме первого, а все остальные буквы остаются строчными. Например, имена
numStudents
В
Python
и
listOfNames
записаны в смешанном регистре.
имена переменных чаще записываются в нижнем регистре с под­
черкиваниями. Здесь все буквы являются строчными, а слова разделяются
символами подчеркивания. Например, имена
записаны именно по такому принципу.
num_students
и
list_of _names
ГЛАВА 3
48
Первая программа Python
Нет правил, которые бы требовали, чтобы имена переменных следовало запи­
сывать в нижнем регистре с подчеркиваниями. Однако эта практика закреплена
в документе, который называется РЕР
8,
и часто его считают официальным
руководством по стилю при написании кода
Python.
ПРИМЕЧАНИЕ
Сокращение РЕР означает «предложение по улучшению Pythoп» (Pythoп
Enhancement Proposal). РЕР - проектировочный документ, используемый со­
обществом Python для предложений о включении новых возможностей в язык.
Соблюдение стандартов, описанных в РЕР
8, гарантирует,
что ваш код
Python
будет понятен большинству Руthоn-программистов. Это упрощает коммуни­
кацию и сотрудничество при обмене кодом для всех участников разработки.
Упражнения
1.
В интерактивном окне выведите какой-либо текст вызовом функции
print().
2.
В интерактивном окне присвойте строковый литерал переменной. Затем
выведите содержимое переменной вызовом функции
3.
print().
Повторите первые два упражнения в окне редактора.
3.4. ПРОСМОТР ЗНАЧЕНИЙ
В ИНТЕРАКТИВНОМ ОКНЕ
Введите следующий фрагмент в интерактивном окне
IDLE:
»> greeting = "Hello, World"
»> greeting
'Hello, World'
Когда вы нажмете Enter после ввода второй строки с
строковый литерал, присвоенный
print ().
greeting, хотя вы
greeting, Python выведет
и не использовали функцию
Этот механизм называется проверкой переменных.
Теперь выведите строку, присвоенную
>>> print(greeting)
Hello, World
greeting,
вызовом функции
print( ):
3.4. Просмотр значений в интерактивном окне
Вы заметили отличия между выводом, полученным при использовании
print (),
и выводом, полученным при простом вводе имени переменной и нажатием
Когда вы вводите имя переменной greeting и нажимаете
49
Enter?
Enter, Python выводит
значение, присвоенное переменной, в том виде, в котором оно отображается
в коде. Вы присвоили
'Hello, Wor ld'
greeting
строковый литерал
поэтому
"Hello, World",
выводится в кавычках.
ПРИМЕЧАНИЕ
Строковые литералы
Python могут создаваться в одинарных или двойных ка ­
Real Python мы используем двойные кавычки там, где это воз ­
как вывод IDLE по умолчанию заключается в одинарные кавычки .
вычках . На сайте
можно, тогда
В Python «Hello, World» и 'Hello, World' означают одно и то же - здесь важнее
всего последовательно подходить к использованию кавычек. О строках более
подробно мы расскажем в главе
С другой стороны,
4.
print() выводит более удобочитаемое представление зна­
чения переменной, что для строковых литералов означает вывод текста без
кавычек.
Иногда вывод и проверка переменной выдают одинаковые результаты:
>>>
»>
х
= 2
х
2
»> print(x)
2
Здесь переменной х присваивается число
2.
Как при вызове
проверке х результат выводится без кавычек, потому что
print(x), так и при
2 является
числом,
а не текстом. В большинстве случаев проверка переменной дает более полезную
информацию, чем
print().
Предположим, имеются две переменные: х, которой присваивается значение
и у, которой присваивается строковый литерал
и
print(y)
выводят одно и то же:
»> х = 2
»> у = "2"
»> print(x)
2
»> print(y)
2
"2".
В этом случае
2,
print(x)
ГЛАВА З
50
Первая программа Python
Однако при проверке х и у проявляются различия между значениями пере­
менных:
»>
х
2
»>
у
'2'
Главный вывод таков:
print ()
выводит удобочитаемое представление значе­
ния переменной, тогда как проверка переменной выводит значение в том виде,
в котором оно встречается в коде.
Учтите, что проверка переменной работает только в интерактивном окне. На­
пример, попробуйте выполнить следующую программу из окна редактора:
greeting = "Hello, World"
greeting
Программа выполняется без ошибок, но ничего не выводит!
3.5. ЗАМЕТКИ
НА ПАМЯТЬ
Программисты нередко читают код, написанный ими же некоторое время назад,
и спрашивают себя: «Что он делает?» Если вы давно не обращались к своему
коду, вам трудно будет вспомнить, почему вы написали его именно так, а не
иначе!
Чтобы избежать этой проблемы, вы можете оставить комментарии в своем коде.
Комментарии
-
это текст, который никак не влияет на вьшолнение програм­
мы. Они только описывают, что делает код или почему программист 11ринял
конкретные решения.
Как пишутся комментарии
Самый простой способ вставить комментарий
-
в начале новой строки. Во время выполнения кода
начинающиеся с
это разместить символ
Python
#
игнорирует строки,
#.
Комментарии, начинающиеся с новой строки, называются блоковыми. Также
можно использовать встроенные комментарии, они размещаются в одной строке
с кодом, к которому относятся. Просто поставьте в конце строки символ#, а за
ним разместите текст комментария.
3.5.
Заметки на память
51
Пример программы, содержащей комментарии обоих типов:
#
Блоковый
greetiпg =
комментарий.
"Hello, World"
# Встроенный
priпt(greeting)
комментарий.
Символ# можно использовать и в строке. Например,
Python не примет символ#
в этой строке за начало комментария:
»> print("#l")
#1
В общем случае рекомендуется использовать как можно более короткие ком­
ментарии, но иногда требуется больше текста, чем помещается в одной строке.
В таком случае можно продолжить комментарий в новой строке, которую также
следует начать с символа#:
#
Моя
#Она
первая
программа.
выводит фразу
#Комментарии
в
"Hello, World".
ней длиннее
кода!
greeting = "Hello, World"
print(greeting)
Также комментарии используют для временного исключения кода в процессе
тестирования программы. Разместив символ
#
в начале строки, можно вы­
полнить программу так, словно эта строка не существует, но без фактического
удаления кода.
Чтобы закоммснтировать фрагмент кода в
IDLE, выделите этот фрагмент и
на­
жмите соответствующую комбинацию клавиш:
•
Windows: Alt+ З;
•
macOS: Ctrl+З;
•
Ubuntu Linux: Ctrl+D.
Чтобы убрать комментарии, выделите закомментированныс строки и нажмите:
•
Windows: Alt+4;
•
macOS: Ctrl+4;
•
Ubuntu Linux: Ctrl+Shift+D.
Рассмотрим некоторые распространенные соглашения, относящиеся к ком­
ментариям в коде.
Первая программа Pythoп
ГЛАВА З
52
Соглашения и вредные привычки
Согласно РЕР
8,
комментарии надо всегда записывать в виде завершенных
предложений, а символ# следует отделять от первого слова комментария од­
ним пробелом:
#
#А
Этот комментарий отформатирован по правилам РЕР
этот
8.
нет
Для встроенных комментариев РЕР
8 рекомендует
отделять код от символа#
как минимум двумя пробелами:
phrase = "Hello, World" # Этот
print(phrase)# А этот нет.
комментарий соответствует РЕР
8.
РЕР 8 рекомендует осмотрительно использовать комментарии. Главной вредной
привычкой программисты считают комментарии, описывающие то, что и так
очевидно из чтения кода.
Например, комментарий в следующем коде не нужен:
# Вывести "Hello, World"
print("Hello, World")
Этот комментарий лишний, потому что сам код наглядно показывает, что
он делает. Комментарии лучше использовать для пояснения трудного для
понимания кода или объяснения того, почему что-то вы делаете именно так,
а не иначе.
3.6.
ИТОГИ И ДОПОЛНИТЕЛЬНЫЕ РЕСУРСЫ
В этой главе вы написали и выполнили свою первую программу на языке
Python!
Это была небольшая программа, которая выводит текст "Hello, World"
при помощи функции
print ().
Далее вы узнали о синтаксических ошибках, которые возникают до выпол­
нения программы с недействительным кодом
Python,
и об ошибках времени
выполнения, происходящих только во время выполнения программы.
Вы научились присваивать значения переменным оператором присваивания
=,
а также проверять значения переменных в интерактивном окне. Наконец,
вы узнали, как включать в код полезные комментарии на случай, если вы или
кто-то другой будете просматривать его в впоследствии.
3.6. Итоги и дополнительные ресурсы
53
ИНТЕРАКТИВНЫЙ ТЕСТ
К этой главе прилагается бесплатный интерактивный тест для проверки усво­
енных вами знаний. Тест доступен на телефоне или компьютере:
realpython.com/quizz es!pybasics-first-prog ram
Дополнительные ресурсы
За дополнительной информацией обращайтесь к следующим ресурсам:
•
« 11 Beginner Tips for Learning Python Programming» (https.j/realpython.
com/python-beginner-tips/)
•
«Writing Comments in Python (Guide)» (https.j/ realpython.com/pythoncomments-guide/)
ГЛАВА4
Строки и строковые
методы
Многие программисты разных специализаций ежедневно имеют дело с текстом.
Например, веб-разработчики работают с текстовыми данными веб-форм. Экс­
перты по аналитическим данным обрабатывают текст для извлечения данных
и выполнения таких операций, как анализ эмоциональной окраски, для иден­
тификации и классификации мнений в блоке текста.
Текстовые блоки в
Python называются строками ( strings ). Для обработки строк
используются специальные функции, называемые строковыми методами. Су­
ществуют строковые методы для преобразования строки из нижнего регистра
в верхний, удаления пропусков в начале или конце строки, замены частей строки
другим текстом и многих других операций.
В этой главе вы научитесь:
•
обрабатывать строки с использованием строковых методов;
•
работать с пользовательским вводом;
•
работать с числовыми данными в строках;
•
форматировать строки для вывода.
Задело!
4.1.
ЧТО ТАКОЕ СТРОКА?
В главе 3 вы создали строку "Hello, World" и вывели ее в интерактивном окне
1D LE при помощи функции print (). В этом разделе мы более детально разбе­
ремся в том, что собой представляют строки, а также изучим различные способы
их создания в Pythoв.
4.1. Что такое строка?
55
Строковый тип данных
Строки принадлежат к числу фундаментальных типов данных
Python.
Тип
данных определяет, какого рода данные представляет значение. Строки ис­
пользуются для представления текста.
ПРИМЕЧАНИЕ
В Python существуют другие встроенные тиnы данных. Наnример, числовые
5, а логические типы данных -
типы данных будут рассматриваться в главе
в главе
8.
Строки называют фундаментальным типом данных, потому что они не могут
быть разбиты на меньшие значения другого типа. Не все типы данных являются
фундаментальными. В главе
9 я расскажу о составных типах данных, также на­
зываемых структурами данных.
Для строкового типа данных в
Python есть специальное сокращенное имя: str.
type( ), которая используется для
Его можно вывести при помощи функции
определения типа данных заданного значения.
Введите следующие команды в интерактивном окне
IDLE:
»> type("Hello, World")
<class 'str'>
Вывод
<class 'str' >означает, что значение "Hello, World" является экземпля­
str. Другими словами, "Hello, World" - строка.
ром типа данных
ПРИМЕЧАНИЕ
Пока слово
class
можно рассматривать как синоним для типа данных, хотя
на самом деле оно имеет более конкретный смысл. О том, что такое классы,
мы расскажем в главе
Функция
1О .
type() также работает для значений, которые были присвоены пере­
менной:
»> phrase = "Hello, World"
»> type(phrase)
<class 'str' >
56
ГЛАВА 4
Строки и строковые методы
Строки обладают тремя важными свойствами.
1.
Строки содержат отдельные буквы или знаки, которые называются
символами.
2.
У строк есть длина
3.
Символы в строке образуют последовательность; это означает, что каждый
-
количество символов, содержащихся в строке.
символ в строке занимает позицию с определенным номером.
Посмотрим, как создаются строки.
Строковые литералы
Как вы уже видели, строку можно создать, заключив текст в кавычки:
stringl
string2
'Hello, World'
"1234"
Для создания строки можно использовать как одинарные кавычки
так и двойные
(string2) -
(stringl),
важно лишь, чтобы в начале и в конце строки ис­
пользовались кавычки одного вида.
Любая строка, созданная заключением текста в кавычки, называется строковым
литералом. Все строки, которые встречались вам до настоящего момента, были
строковыми литералами.
ПРИМЕЧАНИЕ
Не каждая строка является строковым литералом. Некоторые строки вво­
дятся пользователем или читаются из файла. Так как в этом случае они
не заключаются в кавычки, такие строки не являются строковыми литера­
лами.
Кавычки, в которые заключена строка, называются ограничителями, потому
что они сообщают
Python, где начинается и где завершается строка.
Если одну
разновидность кавычек вы используете в качестве ограничителя, другую раз­
новидность можете использовать внутри строки:
stringЗ = "We're #1!"
string4 = 'I said, "Put it over
После того как
Python
Ьу
the llama."'
прочитает первый ограничитель, он будет считать все
последующие символы частью строки, пока не достигнет второго парного
4.1. Что такое строка?
57
ограничителя. Вот почему вы можете использовать одинарную кавычку в строке,
заключенной в двойные кавычки, и наоборот.
Если же вы попытаетесь использовать двойные кавычки в строке, заключенной
в двойные кавычки, произойдет ошибка:
>>> text = "She said, "What time is it?""
File "<stdin>", line 1
text = "She said, "What time is it?""
SyntaxError: invalid syntax
Python выдаст ошибку SyпtaxError, потому что решит, что строка завершается
после второй двойной кавычки ",и не будет знать, как интерпретировать оста­
ток строки. Если вам понадобится включить в строку кавычку, совпадающую
с ограничителем, вы можете экранировать символ обратной косой чертой:
>>> text = "She said, \"What time is it?\""
»> print(text)
She said, "What time is it?"
ПРИМЕЧАНИЕ
Когда вы работаете над проектом, рекомендуется использовать только одинар­
ные кавычки (или только двойные) в качестве ограничителей для всех строк.
Помните, что здесь нет однозначно правильного или ошибочного варианта!
Нужно только действовать последовательно, потому что это упростит чтение
и понимание вашего кода.
Строки могут содержать произвольные символы Юникода. Например, строка
"We' re #1 ! " содержит символ «решетка» ( # ), а строка "1234" содержит цифры .
"xPytnФrJ x " также является действительной строкой
Python!
Определение длины строки
Количество символов в строке, включая пробелы, называется длиной строки.
Например, длина строки "аЬс" -
В
Python
3, а длина строки
"Dоп 't Рапiс" равна
11.
существует встроенная функция lеп(), предназначенная для опре­
деления длины строки. Чтобы увидеть, как она работает, введите следующую
команду в интерактивном окне
»> len("abc")
з
1D LE:
58
Строки и строковые методы
ГЛАВА 4
Функция
len () также может использоваться для получения длины строки,
присвоенной переменной:
>>> letters = "аЬс"
>>> len(letters)
3
Сначала строка "аЬс" присваивается переменной
len()
возвращает длину
letters,
которая равна
letters. Затем вызов функции
3.
Многострочный текст
Стилевое руководство РЕР
содержала не более
ПРИМЕЧАНИЕ
Длина строки в
79
8
рекомендует, чтобы каждая строка кода
Python
символов, включая пробелы .
'
'
79 символов, указанная в РЕР 8, - рекомендация, а не правило.
Некоторые Руthоn-программисты предпочитают несколько большую длину
строки .
В этой книге четко соблюдается длина строки, рекомендованная в РЕР 8.
Независимо от того, следуете ли вы рекомендациям РЕР
8
или нет, иногда
возникает необходимость создать строку, количество символов в которой пре­
вышает указанное ограничение.
Чтобы работать с более длинными строками, можно разбить их на несколько
«логических строк~.. Допустим, необходимо включить следующий текст в стро­
ковый литерал:
This planet has - or rather had - а proЬlem, which was
this: most of the people living on it were unhappy for
pretty much of the time. Many solutions were suggested
for this proЬlem, but most of these were largely concerned with the movements of small green pieces of
paper, which is odd because он the whole it wasn't the
small green pieces of paper that were unhappy.
Дуглас Адамс. «Автостопом по галактике~.
79 символов, так что любая строка кода, содер­
жащая этот текст в виде строкового литерала, нарушает РЕР 8. Что же делать?
Абзац содержит намного более
4.1. Что такое строка?
59
Есть пара возможных решений. Вы можете разбить строку на несколько частей
и поставить обратную косую черту
\
Для выполнения рекомендаций РЕР
не должна превышать
79
в конце каждой части, кроме последней.
8 общая
длина каждой части, включая
\,
символов.
Пример записи абзаца в виде многострочного текста с использованием\:
paragraph : "This plaпet has-or rather had-a proЫem, which was \
this: most of the people liviпg оп it were uпhappy for pretty much \
of the time. Мапу solutioпs were suggested for this proЫem, but \
most of these were largely сопсеrпеd with the movemeпts of small \
greeп pieces of paper, which is odd because оп the whole it wasп't \
the small greeп pieces of paper that were uпhappy."
Обратите внимание: закрывать каждую строку кавычкой не нужно. В обычной
ситуации
Python
доберется до конца первой строки и пожалуется на то, что
строка не закрывается парной двойной кавычкой. Но если в конце стоит сим­
вол
\, Python понимает, что вы
просто продолжаете ту же строку в следующей
строке кода.
При выводе функцией print () многострочного текста, разбитого символами
\,
вывод отображается в одну строку:
»>
>>>
This
: "This
loпg_striпg
displayed
оп опе
multiliпe
is \
striпg
liпe"
priпt(loпg_striпg)
multiliпe striпg
is displayed
оп опе
liпe
Также можно создавать многострочный текст, используя в качестве ограни­
чителей утроенные кавычки
("""
или
' ' ').
Пример записи длинного абзаца
подобным способом:
paragraph : """This plaпet has-or rather had-a proЫem, which was
this: most of the people liviпg оп it were uпhappy for pretty much
of the time. Мапу solutioпs were suggested for this proЫem, but
most of these were largely сопсеrпеd with the movemeпts of small
greeп pieces of paper, which is odd because оп the whole it wasп't
the small greeп pieces of paper tt1at were uпhappy. """
Строки в утроенных кавычках сохраняют символы пропуска
(whitespace), ко­
торые включают пробелы и символы начала новой строки. Это означает, что
команда
print(paragraph) выведет строку с разбиением на части - в том виде,
в котором она определяется в строковом литерале. Иногда это именно то, что
нужно, иногда
-
нет, поэтому вам стоит обдумать, какой вывод вас устроит, до
того, как выберете способ записи многострочного текста.
ГЛАВА 4
60
Строки и строковые методы
Чтобы увидеть, как пробелы сохраняются внутри строки в утроенных кавычках,
введите следующий фрагмент в интерактивном окне
>>> print("""An example of
IDLE:
а
string that spans across multiple lines
and also preserves whitespace.""")
An example of а
string that spans across multiple lines
and also preserves whitespace.
Обратите внимание: вторая и третья строки вывода содержат точно такие же
отступы, как в строковом литерале.
Упражнения
1.
Выведите строку, внутри которой используется двойная кавычка.
2.
Выведите строку, внутри которой используется апостроф.
3.
Выведите многострочный текст с сохранением пробелов.
4.
Выведите строку, которая определяется в многострочном формате, но
выводится в одной строке.
4.2.
КОНКАТЕНАЦИЯ, ИНДЕКСИРОВАНИЕ
И СРЕЗЫ
Итак, теперь вы знаете, что такое строка и как объявляются строковые лите­
ралы в коде. Рассмотрим некоторые операции, которые часто выполняются со
строками.
В этом разделе рассматриваются три основные операции:
1) конкатенация, или объединение двух строк;
2)
индексирование, или получение одного символа из строки;
3)
срезы, или получение сразу нескольких символов из строки.
Конкатенация строк
Две строки можно объединить оператором+ (эта операция называется конка­
тенацией):
>>> stringl = "abra"
>>> string2 = "cadabra"
4.2. Конкатенация, индексирование и срезы
61
stringl + string2
>>> magic_string
>>> magic_string
'abracadabra'
В этом примере конкатенация выполняется в третьей строке. Для конкатена­
ции
stringl и string2 используется знак+, после чего результат присваивается
magic_string. Обратите внимание: при конкатенации строки не
переменной
разделяются пробелом.
Конкатенация часто используется для объединения двух взаимосвязанных
строк
-
например, отображения полного имени из имени и фамилии:
>>> first_name
»> last_name
>>> full_name
>» full_name
'Arthur Dent'
=
"Arthur"
"Dent"
first_name + " " + last_name
В этом примере конкатенация выполняется дважды в одной строке кода. Сначала
first_name объединяется с" ",чтобы в итоговой строке после имени следовал
пробел. В результате вы получаете строку "Arthur ",которая затем объединяется
с last_name для получения полного имени "Arthur Dent".
Индексация строк
Каждому символу в строке соответствует номер позиции, называемый индек­
сом. Чтобы обратиться к символу в n-й позиции, укажите число
скобках
сразу же после строки:
[]
>>> flavor
n в квадратных
= "fig pie"
»> flavor[l]
'i'
flavor[l]
возвращает символ в позиции
Стоп! Разве первый символ
В
Python -
"fig pie"
не
1 строки "fig pie", то
есть
i.
f?
как и в большинстве других языков программирования
-
отсчет
всегда начинается с нуля. Чтобы получить символ в начале строки, необходимо
задать позицию О:
»> flavor[0]
'f'
62
Строки и строковые методы
ГЛАВА 4
. ВАЖНО!
.
.
.
.
.
,
.
Если вы забудете, что нумерация индексов начинается с нуля, и попробуете
обратиться к первому символу в строке по индексу 1, произойдет ошибка
смещения на
1.
Ошибки смещения на 1 часто создают проблемы как начинающим, так и опыт­
ным программистам!
На следующей диаграмме показаны индексы всех символов строки
f
i
g
0
1
2
i
р
3
е
5
4
"fig pie".
6
При попытке обращения по индексу, значение которого больше, чем число
символов в строке,
Python
выдает ошибку IndexError:
»> flavor[9]
Traceback (most recent call last):
File "<pysl1ell#4>", line 1, in <module>
flavor[9]
IndexError: string index out of range
1 меньше длины строки. Так
"fig pie" равна 7, наибольший допустимый индекс равен 6.
Наибольший индекс символа в строке всегда на
как длина
В строках можно также использовать отрицательные индексы:
»> flavor[ -1]
'е'
Последнему символу в строке соответствует индекс
-1; лля
строки
"fig pie"
это
буква е. Предпоследнему символу i соответствует индекс -2 и т. д.
На следующей диаграмме представлены отрицательные индексы всех сим1юлов
строки
"fig pie" .
f
i
g
-7
-6
-5
р
-4
-3
i
-2
е
-1
4.2. Конкатенация, индексирование и срезы
63
Как и в случае с положительными индексами, при попытке обращения по от­
рицательному индексу, значение которого меньше значения индекса первого
символа в строке,
Python выдает ошибку IndexError:
»> flavor[ -10]
Traceback (most recent call last):
File "cpyshell#S>", line 1, in cmodule>
flavor[-10]
IndexError: string index out of range
(Индекс строки вне диапазона.)
На первый взгляд отрицательные индексы кажутся бесполезными, но иногда
с ними удобнее работать, чем с положительными.
Допустим, строка, введенная пользователем, присваивается переменной
input.
user _
Если вы захотите получить последний символ строки, как узнать, какой
индекс для этого использовать?
Один из способов получения последнего символа строки основан на вычислении
его индекса при помощи функции
len( ):
final_index = len(user_input) - 1
last_character = user_input[final_index]
Получение последнего символа с индексом
-1 сокращает объем кода и не требует
промежуточного шага для вычисления индекса последнего символа:
last_character
=
user_input[-1]
Срезы
Допустим, вам нужна строка, содержащая первые три буквы строки
"fig pie".
Можно обратиться к каждому символу по индексу и выполнить конкатенацию:
>>> first_three_letters = flavor[0] + flavor[l] + flavor[2]
>>> first_three_letters
'fig'
Если вам нужно больше символов, чем несколько первых букв строки, возврат
каждого символа по отдельности с последующей конкатенацией выглядит
громоздким и неудобным. К счастью,
компактно.
Python
позволяет решить эту задачу
ГЛАВА 4
64
Строки и строковые методы
Чтобы выделить часть строки, называемую подстрокой, поставьте двоеточие
между двумя индексами внутри квадратных скобок:
>>> flavor = "fig pie"
» > flavor [ 0: З]
'fig'
Выражение
flavor,
возвращает первые три символа строки, присвоенной
flavor [ 0: З]
начиная с символа с индексом О и заканчивая символом с индексом
3 (не
включая последний). Часть [ 0: з] выражения flavor [ 0: з] называется срезом.
В данном случае она возвращает срез строки
"fig pie".
Понятие срезов нередко усваивается с трудом, потому что подстрока, возвра­
щаемая срезом, включает символ с первым индексом, но не включает символ
со вторым индексом. Чтобы запомнить, как работают срезы, представьте себе
строку как последовательность квадратных ячеек. Левая и правая границы
каждой ячейки нумеруются последовательно от нуля до длины строки, и каждая
ячейка заполняется символом строки.
Вот как это выглядит для строки
0
Таким образом, для
возвращает строку"
g
i
f
1
"fig pie":
3
2
"fig pie"
pie".
i
р
4
5
е
6
срез (0: З] возвращает строку
Если опустить первый индекс в срезе,
Python
7
"fig", а срез [3:7]
считает, что вы хотите начать
с индекса О:
»>
flavor[:З]
'fig'
Срез
[ : з] эквивалентен [ 0: з],
"fig pie".
так что
flavor [ : з]
вернет первые три символа
строки
Аналогичным образом, если опустить второй индекс в срезе,
Python
считает,
что вы хотите получить подстроку, начинающуюся с первого индекса и завер­
шающуюся последним символом в строке:
»>
flavor[З:]
' pie'
4.2. Конкатенация, индексирование и срезы
Для строки
65
"fig pie" срез [З:] эквивалентен срезу [З: 7]. Так как символ
с индексом з является пробелом, flavor [ з: 7] возвращает подстроку, которая
начинается с пробела и завершается последней буквой:
" pie".
Если в срезе опущен как первый, так и второй индекс, вы получите строку, ко­
торая начинается с символа с индексом О и заканчивается последним символом.
Иначе говоря, в этом случае возвращается вся строка:
»> flavor[:]
'fig pie'
Важно заметить, что, в отличие от индексации строк,
чение
IndexError
Python не выдает исклю­
при попытке определения среза, выходящего за начальную
и/или конечную границу строки:
>» flavor[:14)
'fig pie'
>>> flavor[13:15)
В этом примере первая строка получает срез от начала строки до четырнадцатого
символа, не включая его. Длина строки, присвоенной
бы,
Python выдаст ошибку.
Вместо этого
индексы и возвращает всю строку
flavor, равна 7; казалось
Python игнорирует несуществующие
"fig pie".
Третья строка показывает, что происходит при попытке получения среза,
в котором весь диапазон лежит за границами строки;
flavor [ 13: 15]
получить 13-й и 14-й символы, которых нет. Вместо ошибки
пытается
Python возвращает
пустую строку("").
ПРИМЕЧАНИЕ
Пустая строка не содержит никаких символов. Чтобы создать пустую строку,
включите в программу две кавычки без каких-либо символов между ними:
empty_string
= ""
Строка с любыми символами - даже с пробелом - пустой не является. Все
следующие строки не пусты:
non_empty_stringl
non_empty_string2
non_empty_stringЗ
Хотя эти строки не содержат видимых символов, пустыми они не считаются,
потому что содержат пробелы.
66
ГЛАВА 4
Строки и строковые методы
В срезах можно использовать отрицательные числа. Срезы с отрицательными
индексами подчиняются тем же правилам, что и срезы с положительными
числами. Попробуйте наглядно представить строку в виде последовательности
ячеек, границы которой помечены отрицательными числами.
f
-7
g
i
-5
-6
Как и прежде, срез
[ х: у]
i
р
-4
-3
-2
е
-1
возвращает подстроку, которая начинается с индексах
и идет до индекса у (не включая последний). Например, срез
первые три буквы строки
[ - 7: -4]
возвращает
"fig pie":
>>> flavor[-7:-4]
'fig'
Однако следует учитывать, что крайняя правая граница строки не имеет от­
рицательного индекса. Логично предположить, что этой границе соответствует
число О, но такая запись не работает.
Вместо всей строки
[ - 7: 0]
возвращает пустую строку:
»> flavor[-7:0]
Это происходит из-за того, что второе число в срезе должно обозначать границу,
которая находится справа от границы, соответствующей первому числу в срезе.
Но в нашем случае
- 7,
и
0 относятся к крайней левой границе диаграммы.
Если вы хотите включить в срез последний символ строки, опустите второе
число:
»> flavor[-7:]
'fig pie'
Конечно, использование
немного странно
-
flavor [ - 7:]
для получения всей строки выглядит
ведь для этого достаточно применить переменную
flavor
без среза!
Тем не менее срезы с отрицательными индексами полезны для получения
нескольких последних символов строки. Например, выражение
возвращает
"pie".
flavor [ - З: ]
4.2. Конкатенация, индексирование и срезы
67
Неизменяемость строк
Прежде чем завершать этот раздел, обсудим важное свойство строк. Строки
являются неизменяемыми (immutaЬle); это означает, что после создания их
невозможно изменить. Например, посмотрите, что происходит при попытке
присвоить новую букву в определенной позиции строки:
>>> word
= "goal"
>>> word[0] = "f"
Traceback (most recent call last):
File "<pyshell#lб>", line 1, in cmodule>
word[0] = "f"
TypeError: 'str' object does not support i tem assignment
Python выдает ошибку TypeError и сообщает, что объекты str не поддерживают
присваивание элементов.
Если вы захотите изменить строку, придется создать новую строку. Чтобы
преобразовать строку "goal" в строку "foal", можно воспользоваться срезом
и объединить букву "f" со всеми буквами слова "goal", кроме первой:
>» word
>>> word
»> word
'foal'
"goal"
"f" + word[l:]
Сначала строка
"goal" присваивается переменной word. Затем срез word [ 1: ] ,
то есть строка "oal ",объединяется с буквой "f" для получения строки "foal ".
Если вы получили другой результат, проверьте, что вы не забыли включить
в срез двоеточие
( : ).
Упражнения
1.
Создайте строку и выведите ее длину при помощи функции
2.
Создайте две строки, выполните их конкатенацию и выведите получен­
len ().
ную строку.
3.
Создайте две строки, воспользуйтесь конкатенацией для добавления
пробела между ними и выведите результат.
4.
Выведите строку
"zing",
используя синтаксис срезов для определения
правильного диапазона символов в строке
"bazinga ".
Строки и строковые методы
ГЛАВА 4
68
4.3.
МАНИПУЛЯЦИИ СО СТРОКАМИ
С ИСПОЛЬЗОВАНИЕМ МЕТОДОВ
В
Python
существуют специальные функции для работы со строками; они
называются строковыми методами. Их множество, но мы ограничимся лишь
наиболее часто используемыми.
В этом разделе вы научитесь:
•
преобразовывать строку к верхнему или нижнему регистру;
•
удалять пропуски из строк;
•
определять, содержатся ли определенные символы в начале или конце
строки .
Преобразование регистра
Чтобы преобразовать все буквы строки к нижнему регистру, используйте метод
. lower(). Вызов метода . lower() добавляется непосредственно после преоб­
разуемой строки:
»> "Jean-Luc Picard" .lower()
'jean-luc picard'
Точка
(.)
метода
сообщает
Python,
что далее следует имя метода
-
в данном случае
lower() .
ПРИМЕЧАНИЕ
В тексте строковые методы будут обозначаться точкой в начале имен. Напри­
мер,.
lower()
записывается с начальной точкой вместо
lower().
При таких обозначениях вам будет проще отличать строковые методы от
встроенных функций
-
например, priпt () или
type ().
Строковые методы работают не только со строковыми литералами. Также можно
вызвать
. lower()
для строки, присвоенной переменной:
>>> пате = "Jean-Luc Picard"
» > name. lower ()
'jean-luc picard'
4.3. Манипуляции со строками с использованием методов
69
Для . lower() существует парный метод. upper( ), который преобразует каждый
символ строки к верхнему регистру:
>» name.upper()
'JEAN-LUC PICARD'
Сравните строковые методы
. upper ()
и
. lower ()
с функцией
len (),описанной
ранее. Кроме того, что различаются результаты этих функций, важно обратить
внимание на то, как они используются.
len() -
автономная функция. Если вы хотите определить длину строки
вы вызываете функцию
len ()
name,
напрямую:
»> len(name)
15
А методы
. upper()
и
. lower()
должны вызываться в соединении со строкой.
Они не существуют независимо.
Удаление пропусков из строки
Пропусками
( whitespace) называются любые символы, которые выводятся как
- специальный символ,
пустое место. К ним относятся пробел и новая строка
продолжающий вывод со следующей строки.
Иногда возникает необходимость в удалении пропусков в начале или конце строки.
Эта операция особенно полезна при работе со строками, полученными от пользо­
вателя, так как в такие строки могут быть случайно включены лишние пропуски.
Три строковых метода, которые могут использоваться для удаления пропусков
из строки:
1. . rstrip()
2. . lstrip()
3. . strip()
. rstrip()
удаляет пропуски в правой части строки:
>>> name = "Jean-Luc Picard
»> name
'Jean-Luc Picard
>>> name.rstrip()
'Jean-Luc Picard'
70
ГЛАВА 4
Строки и строковые методы
В этом примере строка
Метод
. rstrip()
"содержит пять пробелов в конце.
"Jean-Luc Picard
используется для удаления таких пробелов. В результате вы
получаете новую строку "Jean-Luc Picard" - без пробелов.
Метод
. lstrip()
работает точно так же, как и
. rstrip(),
не считая того, что
пропуски удаляются в левой части строки:
>>> name = " Jean-Luc Picard"
»> name
' Jean-Luc Picard'
>>> name.lstrip()
'Jean-Luc Picard'
Чтобы удалить пропуски и в левой, и в правой части строки, используйте метод
.strip():
>>> name = " Jean-Luc Picard "
>>> name
' Jean-Luc Picard
»> name.strip()
'Jean-Luc Picard'
Важно заметить, что ни один из методов
. rstrip( ), . lstrip()
или
. strip()
не
удаляет пропуски из середины строки. В каждом из приведенных выше при­
меров сохраняется пробел между "Jean-Luc" и "Picard".
Проверка наличия символов в начале
или в конце строки
При работе с текстом иногда требуется определить, начинается ли (заканчи­
вается ли) заданная строка некоторыми символами. Для решения этой задачи
используют два строковых метода:
Допустим, имеется строка
. startswith ()
"Enterprise".
и
. endswith ().
Используем
. startswith(),
чтобы
определить, начинается ли строка с букв «е~ и «п~:
>>> starship = "Enterprise"
>>> starship.startswith("en")
False
Чтобы сообщить .startswith(), какие символы следует искать, вы передаете
строку, содержащую эти символы. Таким образом, чтобы определить, начина­
ется ли "Enterprise" с букв «е~ и «n~. мы вызываем. startswith("en" ). Метод
возвращает
False.
Как вы думаете, почему?
4.3. Манипуляции со строками с использованием методов
71
Если вы предположили, что
.startswith("en") возвращает False, потому что
"Enterprise" начинается с буквы «Ei> в верхнем регистре - вы абсолютно правы!
Метод . startswi th () различает регистр символов. Чтобы вызов . startswi th ()
вернул True, необходимо передать ему строку "En":
>>> starship.startswith("En")
True
Также при помощи метода
.endswith()
можно определить, завершается ли
строка заданными символами:
>>> starship.endswith("rise")
True
Как и
. startswi th (),
метод
. endswi th ()
различает регистр символов:
>>> starship.endswith("risE")
False
ПРИМЕЧАНИЕ
Значения True и False не являются строками. Они принадлежат особому типу
данных - логическому. О логических значениях более подробно мы пого­
ворим в главе
8.
Строковые методы и неизменяемость
Вспомните, в предыдущем разделе мы говорили, что строки являются неиз­
меняемыми
-
после того, как они будут созданы, изменить их уже не удастся.
Многие строковые методы, которые модифицируют строки, например
и
. upper()
. lower( ), в действительности возвращают копии исходной строки с соответ­
ствующими изменениями.
Если действовать неосторожно, это может внести коварные ошибки в вашу
программу. Попробуйте выполнить следующий фрагмент в интерактивном
окне
IDLE:
>>> name = "Picard"
>» name.upper()
'PICARD'
»> name
'Picard'
Строки и строковые методы
ГЛАВА 4
72
При вызове
name. upper ()
в
name ничего не изменяется.
Если вы хотите сохранить
результат, его необходимо присвоить переменной:
"Picard"
name.upper()
>>> name
>>> name
»> name
'PICARD'
name. upper ()
возвращает новую строку
"PICARD", которая
присваивается пере­
менной name. Она заменяет исходную строку "Picard", которая была изначально
присвоена
name.
Эксперименты с другими строковыми методами
в IDLE
Со строками связано множество методов, но здесь мы затронем эту тему кратко.
IDLE
поможет вам изучить новые строковые методы. Чтобы увидеть, как это
делается, сначала присвойте строковый литерал переменной в интерактивном
окне:
>>> starship
= "Enterprise"
Теперь введите
starship и точку,
но не нажимайте Enter. В интерактивном окне
должен появиться следующий текст:
»> starship.
Подождите пару секунд.
IDLE
выводит список всех строковых методов; этот
список можно прокручивать клавишами управления курсором.
В
IDLE реализована еще одна похожая возможность: клавиша ТаЬ автоматически
заполняет текст без необходимости вводить длинные имена. Например, если
starship. u и нажмете ТаЬ, то IDLE автоматически дополнит
starship. upper, потому что starship содержит только один метод, имя
вы введете только
текст до
которого начинается с
u.
Этот прием работает даже с именами переменных. Попробуйте ввести не­
сколько первых букв имени starship и нажать ТаЬ. Если в программе не были
определены другие переменные, начинающиеся с тех же букв,
имя
starship за
вас.
IDLE завершит
4.4. Взаимодействие с пользовательским вводом
73
Упражнения
1.
Напишите программу, которая преобразует следующие строки к нижне­
му регистру:
"Animals", "Badger",
"Нопеу Вее",
"Honey Badger".
Каждый
результат преобразования должен выводиться с новой строки.
2.
Повторите упражнение
1,
но преобразуйте каждую строку к верхнему
регистру, а не к нижнему.
3.
Напишите программу, которая удаляет пропуски из следующих строк,
а затем выводит полученные результаты:
4.
Filet Mignon"
"Brisket
" Cheeseburger
stringl
string2
= "
stringЗ
=
Напишите программу, которая выводит результат вызова
. start-
swi th ( "Ье") для каждой из следующих строк:
stringl
string2
stringЗ
string4
5.
=
"Becomes"
"becomes"
"BEAR"
" bEautiful"
Используя строки из упражнения
4,
напишите программу, которая ис­
пользует строковые методы для изменения каждой строки, чтобы вызов
. startswith( "Ье")
возвращал
True
для всех строк.
4.4. ВЗАИМОДЕЙСТВИЕ С ПОЛЬЗОВАТЕЛЬСКИМ
вводом
Итак, вы научились работать со строковыми методами
-
теперь давайте зай­
мемся интерактивностью!
Сейчас вы узнаете, как получить информацию от пользователя при помощи
функции
input().
Мы напишем программу, которая предлагает пользователю
ввести некоторый текст, после чего выводит текст в верхнем регистре.
Введите следующую команду в интерактивном окне
»> input()
IDLE:
74
ГЛАВА 4
Строки и строковые методы
Казалось бы, при нажатии клавиши
Enter
ничего не происходит. Курсор пере­
мещается в новую строку, но приглашение
»>
в ней не появляется.
Python
ожидает, когда вы введете какой-нибудь текст!
Наберите какой-нибудь ответ и нажмите
Enter:
»> input()
Hello there!
'Hello there ! '
»>
Текст повторяется в новой строке в одинарных кавычках. Это объясняется
тем, что
input() возвращает в виде строки любой текст, введенный пользова­
input() стала чуть более удобной, можно задать при­
телем. Чтобы функция
глашение, которое должно выводиться для пользователя. Оно представляет
собой обычную строку, которая заключается в круглые скобки input( ). Это
может быть все что угодно: слово, знак, фраза
строкой
-
все, что является допустимой
Python.
Функция
input() выводит приглашение и ожидает, когда пользователь введет
Enter, input() возвратит текст
какой-нибудь текст. Если пользователь нажмет
пользователя в виде строки, которую можно присвоить переменной и обрабо­
тать в программе.
Чтобы увидеть, как работает input( ), введите следующий код в окне редактора
IDLE:
prompt = "Неу, what's up? "
user_input = input(prompt)
print("You said: " + user_input)
Запустите программу клавишей
what' s up?
FS.
В интерактивном окне появится текст Неу,
с мигающим курсором.
Для чего нужен пробел в конце строки "Неу, what' s up? "? Когда пользова­
тель начнет вводить текст, он будет отделен от приглашения пробелом. Когда
пользователь введет ответ и нажмет
Enter, его ответ присваивается
user_input.
Пример выполнения программы:
Неу, what's up? Mind your own business.
You said: Mind your own business.
переменной
4.5. Задача: разбор пользовательского ввода
75
Получив от пользователя введенную им информацию, вы можете с ней что­
нибу дь сделать. Например, следующая программа получает текст, преобразует
его к верхнему регистру вызовом
. upper( ),
а затем выводит результат:
response = input("What should 1 shout? ")
shouted_response = response.upper()
print("Well, if you insist ... " + shouted_response)
Попробуйте напечатать код этой программы в окне редактора
IDLE
и запу­
стить ее.
Какие еще операции можно выполнить с информацией, которую вводит поль­
зователь?
Упражнения
1.
Напишите программу, которая получает ввод от пользователя и воспро­
изводит его на экране.
2.
Напишите программу, которая получает ввод от пользователя и выводит
его в нижнем регистре.
3.
Напишите программу, которая получает ввод от пользователя и выводит
количество содержащихся в нем символов.
4.5.
ЗАДАЧА: РАЗБОР ПОЛЬЗОВАТЕЛЬСКОГО
ВВОДА
Напишите программу first _letter.py, которая запрашивает у пользователя ввод
с приглашением
"Tell me your password: ".Затем программа определяет первую
букву пользовательского ввода, преобразует ее к верхнему регистру и выводит ее.
Например, если пользователь ввел
"no", программа должна выдать следующий
результат:
The first letter you entered was: N
Если пользователь не ввел ничего (то есть просто нажал Enter), в вашей про­
грамме может возникнуть ошибка
-
пока не обращайте на это внимания.
В следующей главе я расскажу о паре возможных способов урегулирования
этой проблемы.
ГЛАВА 4
76
Строки и строковые методы
Решение этой задачи, а также многие другие дополнительные ресурсы доступны
в интернете по адресу
realpython.com/python-basics/resources
РАБОТА СО СТРОКАМИ И ЧИСЛАМИ
4.6.
Когда пользователь вводит что-то по запросу функции input(), результатом
всегда является строка. Существует много других ситуаций, когда ввод пере­
дается программе в виде строки. Иногда такие строки содержат числа, которые
должны быть задействованы в вычислениях.
В этом разделе вы научитесь работать со строками, содержащими числовые
данные. Вы увидите, как арифметические операции работают со строками и как
они часто приводят к странным результатам. Также вы научитесь выполнять
преобразования между строками и числовыми типами.
Использование строк с арифметическими
операторами
Вы уже видели, что строковые объекты могут содержать разные символы,
включая цифры. Однако не стоит путать числа в строках с обычными чис­
лами. Например, попробуйте выполнить следующий код в интерактивном
окне
IDLE:
>>> num = "2"
»> num + num
'22'
Оператор+ выполняет конкатенацию двух строк; вот почему результат "2"
"2"
равен
"22",
а не
+
"4".
Строку также можно умножать на число (при условии, что это число является
целым). Введите следующий фрагмент в интерактивном окне:
>>> num = "12"
»> num * З
'121212'
num*З выполняет конкатенацию трех строк
"12"
и возвращает строку
"121212".
Сравните эту операцию с арифметическими вычислениями. Когда вы умно­
жаете число
чисел
12.
12
на число
3,
результат будет таким же, как при сложении трех
Этот принцип относится и к строкам. Иначе говоря, "12"
*з
можно
4.6. Работа со строками и числами
77
интерпретировать как "12" + "12" + "12". В общем случае умножение строки на
целое число
n
выполняет конкатенацию
n копий
этой строки.
Число в правой части выражения num*З можно переместнть в левую часть,
результат от этого не изменится:
»> 3 * num
'121212'
Как вы думаете, что произойдет, если использовать оператор* с двумя строками?
Введите "12" *" з" в интерактивном окне и нажмите
Enter:
>>> "12" * "3"
Traceback (most recent call last):
File "<stdin>", line 1, in cmodule>
TypeError: can't multiply sequence Ьу non-int of type 'str'
Python выдает ошибку TypeError и сообщает, что последовательность (sequence)
нельзя умножить на значение, которое не является целым числом .
ПРИМЕЧАНИЕ
Посnедоватепьностью называется любой объект
Python, поддерживающий
обращение к элементам по индексу. Строки являются последовательностя­
ми. Другие разновидности последовательностей рассматриваются в главе 9.
При использовании оператора *со строкой
Python всегда ожидает, что в другой
части оператора стоит целое число.
Как вы думаете, что произойдет при попытке сложить строку с целым числом?
>>> "3" + 3
Traceback (most recent call last):
File "cstdin>", line 1, in cmodule>
TypeError: сап only concatenate str (not "int") to str
Python выдает ошибку TypeError, потому что ожидается, что объекты с двух
сторон оператора
+
относятся к одному типу.
Если объект с одной стороны + является строкой,
Python пытается выполнить
конкатенацию строк. Сложение выполняется только в том случае, если оба
объекта являются числами. Таким образом, чтобы выполнить сложение "з" + З
и получить 6, необходимо сначала преобразовать строку "з" в число.
78
ГЛАВА 4
Строки и строковые методы
Преобразование строк в числа
Примеры с
TypeError из предыдущего раздела подчеркивают ти11ичную пробле­
му несоответствия типов, которая возникает при передаче пользовательского
ввода операции, требующей числа, а не строки.
Рассмотрим пример. Сохраните и запустите следующую программу:
num = input("Enter а number to
douЫed_num = num * 2
Ье douЬled:
")
print(douЫed_num)
Если ввести число
равен
4.
2 после
приглашения, можно ожидать, что результат будет
Но в данном случае вы получите
22.
input () всегда возвращает строку, поэтому при вводе 2 перемен­
num присваивается строка "2", а не целое число 2. Следовательно, выражение
num*2 возвращает строку "2", объединенную с самой собой, то есть "22".
Напомню, что
ной
Чтобы выполнить арифметические действия с числами, содержащимися в стро­
ке, необходимо сначала преобразовать их из строкового типа в число. Для этой
цели существуют две функции:
int ()
и
float ().
Функция int () преобразует объекты в целые числа, а float () преобразует
объекты в дробные числа. Примеры использования обеих функций в интерак­
тивном окне:
>» int("12")
12
>» float( "12")
12.0
Обратите внимание, что
float() добавляет к числу дробную часть. Числа
с плавающей точкой всегда имеют хотя бы один знак в дробной части. По этой
причине вы не сможете преобразовать строку, содержащую представление
числа с плавающей точкой, в целое число, потому что вы потеряете всю
дробную часть.
Попробуйте преобразовать строку "12.0" в целое число:
»> int("12.0")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '12.0'
4.6. Работа со строками и числами
И хотя лишний О в дробной части фактически не изменяет число,
превратит
12. 0
в
12,
Python
79
не
потому что это приведет к потере точности.
Вернемся к программе в начале раздела и посмотрим, как ее исправить. По­
вторим ее код:
num = input("Enter а number to
= num * 2
Ье douЫed:
")
douЫed_num
print(douЫed_num)
Проблема возникает в строке douЫed_num = num
строкой, а
2-
* 2,
потому что num является
целым числом.
Проблему можно решить передачей num функции int() или float(). Так как
приглашение предлагает пользователю ввести число, причем необязательно
целое, преобразуем num к формату с плавающей точкой:
num = input("Enter а number to
float(num) * 2
Ье douЫed:
")
douЫed_num =
print(douЫed_num)
Попробуйте запустить программу и ввести 2. Вы получите 4. 0, как и предпо­
лагалось. Убедитесь сами!
Преобразование чисел в строки
В некоторых случаях требуется преобразовать число в строку. Например, такая
необходимость может возникнуть при построении строки из существующих
переменных, которым присвоены числовые значения.
Как вы уже видели, при попытке выполнить конкатенацию числа со строкой
происходит ошибка TypeError:
>>> num_pancakes = 10
>>> "I am going to eat " + num_pancakes + " pancakes."
Traceback (most recent call last):
File "<stdin>", line 1, in cmodule>
TypeError: сап only concatenate str (not "int") to str
Так как num_pancakes является числом,
Python
не может выполнить конка­
тенацию переменной со строкой "I 'm going to eat ". Чтобы построить строку,
необходимо преобразовать num_pancakes в строку вызовом str( ):
>>> num_pancakes = 10
>>> "I am going to eat " + str(num_pancakes) + " pancakes."
'I am going to eat 10 pancakes.'
ГЛАВА 4
80
Строки и строковые методы
Также можно вызвать
str()
для числового литерала:
>>> "I am going to eat " + str(10) + " pancakes."
'I am going to eat 10 pancakes.'
Функция
str() работает даже с арифметическими выражениями:
>>> total_pancakes = 10
>>> pancakes_eaten = 5
»> "Only "+ str(total_pancakes - pancakes_eaten) +" pancakes left."
'Only 5 pancakes left.'
В следующем разделе вы узнаете, как форматировать строки для вывода значе­
ний в удобном и понятном виде. Но прежде чем двигаться дальше, проверьте,
насколько вы поняли материал, на следующих упражнениях.
Упражнения
1.
Создайте строку, содержащую целое число. Преобразуйте строку в целое
число вызовом
int(). Чтобы убедиться в том, что новый объект дей­
ствительно является числом, умножьте его на другое число и выведите
результат.
2.
Повторите предыдущее упражнение, но используйте число с плавающей
точкой и функцию
3.
Создайте строку и целое число. Выведите их рядом друг с другом одной
командой
4.
float ( ) .
print
при помощи
str( ).
Напишите программу, которая дважды вызывает
input ()
для получения
двух чисел от пользователя, перемножает эти числа и выводит результат.
Если пользователь вводит 2 и 4, программа должна вывести следующий
текст:
The product of 2 and 4 is 8.0.
4.7. УПРОЩЕНИЕ
Допустим, имеется строка
heads = 2 и arms = 3.
КОМАНД ВЫВОДА
name
=
"Zaphod"
и две целочисленные переменные:
Вы хотите вывести их в строке
"Zaphod has 2 heads and 3 arms".
Такое включение целых чисел в строку называется строковой интерполяцией.
Одно из возможных решений основано на конкатенации строк:
>>> name + " has " + str(heads) + " heads and " + str(arms) + " arms"
'Zaphod has 2 heads and 3 arms'
4.7. Упрощение команд вывода
81
Код выглядит некрасиво, а следить за тем, что должно быть заключено в ка­
вычки, а что не должно, иногда непросто. К счастью, существует другой способ
интерполяции строк
-
форматированные строковые литералы, чаще их на­
зывают f-строками.
Чтобы понять, как работают f-строки, проще всего посмотреть на них в действии.
Вот как выглядит приведенная выше строка при записи в виде f-строки:
>>> f"{name} has {heads} heads and {arms} arms"
'Zaphod has 2 heads and 3 arms'
В этом примере следует обратить внимание на два важных обстоятельства.
1.
Строковый литерал начинается с буквы f, которая располагается перед
открывающей кавычкой.
2.
Имена переменных, заключенные в фигурные скобки{}, заменяются
соответствующими значениями без использования str( ).
В фигурные скобки также можно заключить выражения
Python. Эти выражения
заменяются в строке результатами их вычисления:
»> п = 3
>» m = 4
>>> f"{n} times {m} is {n*m}"
'3 times 4 is 12'
Выражения, используемые в f-строках, должны быть простыми, насколько это
возможно. Включив несколько сложных выражений в строковый литерал, вы
можете создать код, который трудно читать и сопровождать.
F-строки доступны только в
ях
Python
Python версии 3.6 и
выше. В более ранних верси­
для получения тех же результатов используется метод
В предыдущем примере форматирование строки методом
. fo rmat ().
. format () может
выглядеть примерно так:
>>> "{} has {} heads and {} arms".format(name, heads, arms)
'Zaphod has 2 heads and 3 arms'
F-строки короче кода с использованием
. format (), а иногда и лучше читаются.
В книге мы будем использовать f-строки.
Подробное описание f-строк и сравнение их с другими способами формати­
«Python З's f-Strings: An Improved
String Formatting Syntax ( Guide )» на сайте Real Python (https.j/realpython.com/
python-f-stnngs/).
рования строк вы найдете в руководстве
Строки и строковые методы
ГЛАВА 4
82
Упражнения
1.
Создайте переменную с плавающей точкой weight, содержащую значение
0. 2, и строковый объект animal со значением "newt". Выведите следующую
строку с этими переменными, используя только конкатенацию строк:
0.2 kg is the weight of the newt.
2.
Выведите ту же строку с использованием метода
заполнителей
3.
4.8.
. format ()
и пустых
{}.
Выведите ту же строку с использованием синтаксиса f-строк.
ПОИСК ПОДСТРОКИ В СТРОКЕ
Один из самых полезных строковых методов
- . find () -
позволяет найти
позицию одной строки внутри другой. Искомая строка обычно называется
подстрокой.
Чтобы использовать метод
. find( ),
укажите его после имени переменной или
строкового литерала. Строку, которую вы хотите найти, указывают в круглых
скобках:
>>> phrase = "the surprise is in here somewhere"
>>> phrase.find("surprise")
4
Метод
. find()
возвращает индекс первого вхождения переданной строки.
"surprise" начинается с пятого
который имеет индекс 4,
somewhere",
here
is in
В данном случае
символа строки
"the surprise
потому что нумерация начи­
нается с О.
Если
. find ()
не находит нужную подстроку, вместо индекса возвращается
-1:
>>> phrase = "the surprise is in here somewhere"
>>> phrase.find("eyjafjallajбkull")
-1
Учтите, что проверка совпадений осуществляется символ за символом и с учетом
регистра. Например, если вы попытаетесь найти подстроку "SURPRISE'', вызов
. find ()
вернет
-1:
>>> "the surprise is in here somewhere".find("SURPRISE")
-1
4.8. Поиск подстроки в строке
Если подстрока встречается в строке более одного раза,
. find ()
83
возвращает
индекс только первого вхождения от начала строки:
>>> "I put
string in your string".find("string")
а
8
В строке"!
put а string iп your string" подстрока "string"
8 и 23, но . find () возвращает только 8.
встречается дважды,
с индексами
Метод
. find ()
получает только строку. Если вы хотите найти целое число
в строке, придется передать
Python
выдаст ошибку
. find ()
число в виде строки. В противном случае
TypeError:
>>> "Му number is 555-555-5555".find(S)
Traceback (most recent call last):
File "<stdin>", line 1, in cmodule>
TypeError: must Ье str, not int
>>>
"Му
number is 555-555-5555".find("5")
13
Иногда требуется найти все вхождения конкретной подстроки и заменить их
другой строкой. Так как
. find ()
возвращает индекс первого вхождения под­
строки, этот метод не так просто использовать для замены. Но у строк есть метод
. replace( ),
который заменяет каждое вхождение подстроки другой строкой.
Как и в случае с
. find( ), вызов . replace()
следует после имени переменной или
строкового литерала. Однако на этот раз в круглых скобках
. replace()
необхо­
димо указать две строки, разделив их запятой. Первая строка содержит искомую
подстроку, а вторая
-
текст, которым заменяется каждое вхождение подстроки.
Например, следующий код заменяет каждое вхождение
"! 'm telling you the truth; nothing but the truth"
"the truth"
"lies ":
в строке
строкой
>>> my_story = "I'm telling you the truth; nothing but the truth!"
>>> my_story.replace("the truth", "lies")
"I'm telling you lies; nothing but lies!"
Так как строки являются неизменяемыми объектами,
. replace ()
не изменяет
my_story.
Если немедленно ввести
my_story
в интерактивном окне после выполнения
приведенного примера, то появится исходная строка в неизменном виде:
»> my_story
"I'm telling you the truth; nothing but the truth!"
ГЛАВА 4
84
Строки и строковые методы
Чтобы изменить значение my_story, необходимо присвоить переменной новое
значение, возвращаемое
. replace ():
»> my_story = my_story.replace("the truth", "lies")
»> my_story
"I'm telling you lies; nothing but lies!"
Метод
. replace()
заменяет каждое вхождение подстроки новым текстом.
Чтобы заменить несколько разных подстрок в строке, вызовите
. replace()
несколько раз:
text = "some of the stuff"
new_text = text.replace("some of", "all")
new_text = new_text.replace("stuff", "things")
new_text
'all the things'
»>
»>
>»
»>
Мы поэкспериментируем с
. replace()
в следующем разделе.
Упражнения
1.
В одной строке кода выведите результат вызова. find() для поиска под­
строки "а" в строке "ААА". Результат должен быть равен
2.
Замените каждое вхождение символа
"s"
-1.
на "х" в строке
"Somebody said
something to Samantha."
3.
Напишите программу, которая запрашивает у пользователя ввод функ­
цией
input()
и выводит результат вызова
.find()
для поиска конкретной
буквы во введенном тексте.
4.9.
ЗАДАЧА: ПРЕОБРАЗОВАНИЕ ТЕКСТА
Напишите программу
translate.py,
которая предлагает пользователю ввести
текст со следующим приглашением:
Enter some text:
Используйте
. replace (),чтобы зашифровать текст, введенный пользователем.
Для этого выполните следующие преобразования с буквами нижнего регистра.
•
Буква а преобразуется в 4.
•
Буква Ь преобразуется в 8.
•
Буква е преобразуется в 3.
4.1 О . Итоги и дополнительные ресурсы
•
Буква 1 преобразуется в 1.
•
Буква о преобразуется в 0.
•
Буква
•
Буква t преобразуется в 7.
85
s преобразуется в 5.
Затем ваша программа должна вывести полученную строку. Ниже приведен
пример выполнения программы:
Enter some text: 1 like to eat eggs and spam.
1 likЗ 70 347 ЗggS 4nd Sp4m.
Решение задачи, а также многие другие дополнительные ресурсы доступны
в интернете по адресу
4.1 О.
realpython.com/python-basics/resources.
ИТОГИ И ДОПОЛНИТЕЛЬНЫЕ РЕСУРСЫ
В этой главе мы рассказали о достоинствах и недостатках строковых объектов
Python.
Вы узнали, как обращаться к разным символам строки по индексам
и с помощью срезов и как определить длину строки при помощи функции
len ().
upper () и . lower () преобразуют
rstrip( ),
.endswith()
и
. strip() удаляют пропуски из строк, а. startswith()
Строки также содержат различные методы:.
все символы строки к верхнему и нижнему регистру соответственно,.
. lstrip()
и
проверяют, начинается или завершается строка заданной подстрокой.
Также вы научились сохранять в строке текст, введенный пользователем, при
input () и преобразовывать этот ввод в число, используя int ()
float ( ) . Для преобразования чисел и других объектов в строки применяется
функция str( ).
помощи функции
и
Наконец, вы научились использовать методы
. find ()
и
. replace ()
для поиска
и замены подстрок новыми строками.
ИНТЕРАКТИВНЫЙ ТЕСТ
К этой главе прилагается бесплатный интерактивный тест для проверки усво­
енных вами знаний. Тест доступен на телефоне или компьютере:
realpython.com!quizzes/pybasics-strings
ГЛАВА 4
86
Строки и строковые методы
Дополнительные ресурсы
За дополнительной информацией обращайтесь к следующим ресурсам:
•
«Python String Formatting Best Practices» (https.j/realpython.com/pythonstring-formattingl)
•
«Splitting, Concatenating, andjoining Strings in Python» (https.j/realpython.
com/python-string-split-concatenate-join/)
ГЛАВАS
Числа и математические
вычисления
Чтобы хорошо программировать, не обязательно быть гением в математике.
Откровенно говоря, лишь немногим программистам нужно иметь знания, вы­
ходящие за курс базовой алгебры.
Конечно, уровень необходимых математических знаний задает приложение,
над которым вы работаете. В общем же случае уровень математической под­
готовки, необходимый для программирования, ниже того, что можно было
бы ожидать.
И хотя математика и программирование связаны не так тесно, как считают
некоторые, числа являются неотъемлемой частью любого языка программи­
рования, и
Python
не исключение.
В этой главе вы научитесь:
•
соэдавать целые числа и числа с плавающей точкой;
•
округлять числа до заданной точности;
•
форматировать и выводить числа в строках.
Итак, за дело!
5.1. ЦЕЛЫЕ ЧИСЛА И ЧИСЛА
С ПЛАВАЮЩЕЙ ТОЧКОЙ
В
Python
поддерживаются три встроенных числовых типа данных: целые
числа, числа с плавающей точкой и комплексные числа. В этом разделе рас­
сматриваются целые числа и числа с плавающей точкой
-
два наиболее часто
испольэуемых типа. О комплексных числах я расскажу в разделе
5.7.
88
ГЛАВА 5
Числа и математические вычисления
Целые числа
Целое число не имеет дробной части. Например, 1 - целое число, а 1. 0 Целочисленный тип данных обозначается именем
при помощи функции
нет.
int, в чем нетрудно убедиться
type ( ) :
>» type(l)
<class 'int'>
Чтобы создать целое число, достаточно его ввести. Например, следующий
фрагмент присваивает целое число 25 переменной
»> num
=
num:
25
Когда вы создаете целое число подобным образом, значение 25 называется
целочисленным литералом. Значение целочисленного литерала задается не­
посредственно в коде.
Из главы
4 вы узнали, как преобразовать строковое представление целого чис­
int( ). Например, следующий фрагмент кода
ла в число при помощи функции
преобразует строку "25" в целое число 25:
»> int("25")
25
int ( "25")
не является целочисленным литералом, потому что целое число
создается на основе строки.
Когда вы записываете большие числа вручную, цифры обычно объединяются
в группы по три, разделяемые запятыми или точками 1 • Число
намного проще, чем
В языке
1,000,000 читается
1000000.
Python запятые в целочисленных литералах запрещены,
но вы можете
использовать символы подчеркивания(_). Оба следующих варианта являются
допустимыми способами представления одного миллиона в виде целочислен­
ного литерала:
»> 1000000
1000000
»> 1_000_000
1000000
1
Так принято записывать числа в англоязычных странах.
-
Примеч. ред.
5.1. Целые числа и числа с плавающей точкой
89
Максимальное значение целого числа не ограничено, что удивительно, если
учесть, что компьютеры имеют лимитированный объем памяти. Попробуйте
ввести самое большое число, которое сможете придумать, в интерактивном
окне
IDLE. Python справится с
ним без проблем!
Числа с плавающей точкой
Число с плавающей точкой имеет дробную часть:
1.0 -
число с плавающей точ­
кой, как и -2. 75. Тип данных с плавающей точкой обозначается именем
float:
>» type{l.0)
<class 'float'>
Числа с плавающей точкой, как и целые числа, могут создаваться в виде ли­
тералов с плавающей точкой или преобразованием строки вызовом
float ( ) :
>>> float{"l.25")
1.25
Существуют три способа представления литералов с плавающей точкой. Каж­
дый из следующих вариантов записи создает литерал с плавающей точкой со
значением один миллион:
» > 1000000. 0
1000000.0
»> 1_000_000.0
1000000.0
»>
lеб
1000000.0
Первые два варианта похожи на два способа создания целочисленных литералов.
Третий вариант использует экспоненциальную запись для создания литерала
с плавающей точкой.
ПРИМЕЧАНИЕ
Возможно, экспоненциальную запись вы уже встречали в калькуляторах - она
используется для представления слишком больших чисел, не помещающихся
на экране.
Чтобы записать литерал с плавающей точкой в экспоненциальной форме,
введите число, за которым следует буква «е» и другое число. Pythoп берет
число слева от «е» и умножает его на 1 О в степени справа от «е». Таким об­
разом, запись 1 е6 эквивалентна 1 х 106 •
ГЛАВА 5
90
Числа и математические вычисления
Python также использует экспоненциальную запись для вывода больших чисел
с плавающей точкой:
>>> 200000000000000000.0
2е+17
Число
200000000000000000. 0 выводится в виде 2е+17. Знак+ указывает, что
17 является положительным числом. Также показатель
показатель степени
степени может быть отрицательным:
>» le-4
0.0001
Литерал
le-4 интерпретируется как 10 в степени -4, то есть 1/10000, или 0.0001.
Значение чисел с плавающей точкой, в отличие от целых чисел, ограничено.
Максимальное значение зависит от вашей системы, но число вроде 2е400 вы­
ходит за пределы возможностей большинства машин. Это выражение равно
2 х 10 400 ,
что намного больше общего количества атомов во Вселенной!
При достижении максимального значения числа с плавающей точкой
возвращает специальное значение,
»>
inf
Python
inf:
2е400
inf (от infinity - бесконечность) означает лишь то, что число, которое вы по­
пытались создать, превышает максимальное значение числа с плавающей точкой,
допустимое для вашего компьютера. При этом
inf
имеет тип
float:
»> n = 2е400
»> n
inf
»> type(n)
<class 'float' >
В
Python также используется значение -inf (отрицательная бесконечность).
Оно
представляет отрицательное число с плавающей точкой, которое
меньше наименьшего числа с плавающей точкой, допустимого на вашем
компьютере:
»> -2е400
-inf
5.2. Арифметические операторы и выражения
Скорее всего, вы будете нечасто сталкиваться с
нии
-
91
inf и -inf при программирова­
если, конечно, вам не приходится регулярно работать с очень большими
числами.
Упражнения
1.
Напишите программу, которая создает две переменные, numl и num2. При­
свойте каждой целочисленный литерал 25000000 - с разделителями-под­
черкиваниями и без. Выведите numl и num2 в разных строках.
2.
Напишите программу, которая присваивает литерал с плавающей точкой
175000. 0 переменной num с использованием экспоненциальной записи,
после чего выводит num в интерактивном окне.
3.
В интерактивном окне
степени
N,
вращает
inf.
IDLE попробуйте найти наименьший показатель
2e<N> (где <N> замените вашим числом) воз­
для которого
5.2. АРИФМЕТИЧЕСКИЕ
ОПЕРАТОРЫ
И ВЫРАЖЕНИЯ
Сейчас вы научитесь выполнять в
Python
базовые арифметические операции
с числами (сложение, вычитание, умножение и деление). Заодно вы узнаете
некоторые соглашения для записи математических выражений в коде.
Сложение
Сложение выполняется оператором+:
»> 1 + 2
3
Два числа по обе стороны от оператора
+
называются операндами. В при­
веденном примере оба операнда являются целыми числами, но операнды не
обязательно должны иметь одинаковый тип.
int можно сложить с float без каких-либо проблем:
»> 1.0 + 2
3.0
Числа и математические вычисления
ГЛАВА 5
92
Обратите внимание: результат
Каждый раз, когда
тип
float
1.0 + 2 равен 3.0, и он относится к типу float.
складывается с целым числом, результат также имеет
При сложении двух целых чисел вы всегда получаете
float.
int.
ПРИМЕЧАНИЕ
РЕР 8 рекомендует отделять оба операнда от оператора пробелом.
Pythoп прекрасно справится с вычислением
1+1, но формат 1 + 1 считается
предпочтительным, потому что он проще читается. Это правило применяется
ко всем операторам в этом разделе.
Вычитание
Чтобы вычесть одно число из другого, поставьте между ними оператор-:
>» 1 - 1
0
»>5 . 0-3
2.0
Как и при сложении целых чисел, вычитание двух целых чисел всегда дает
результат типа
int. Если
float .
же один из операндов имеет тип
float,
то результат
также относится к
Оператор
-
также используется для обозначения отрицательных чисел:
»> -3
-3
Отрицательное число можно вычесть из другого числа, но, как видно из при­
мера ниже, такие операции способны запутать читателя:
»>
4
»>
4
»>
4
»>
4
1
-
-3
1 --3
1- -3
1--3
Из этих четырех примеров первый лучше всего соответствует рекомендациям
РЕР 8. При этом вы можете заключить -3 в круглые скобки, чтобы еще нагляднее
показать, что второй знак
-
относится к 3:
5.2. Арифметические операторы и выражения
93
»> 1 - (-3)
4
Использование круглых скобок
-
хорошая практика, потому что с ними код
более явно выражает намерения программиста. Компьютеры выполняют код,
а люди его читают. Все, что делается для упрощения чтения и понимания вашего
кода, можно только приветствовать.
Умножение
Для умножения двух чисел используется оператор*:
»> 3
*
3
9
»>2*8.0
16.0
Тип числа, полученного в результате умножения, подчиняется тем же правилам,
что при сложении и вычитании. При умножении двух целых чисел вы полу­
чите результат int, а при умножении любого числа на float - результат float.
Деление
Оператор
/
предназначен для деления двух чисел:
»> 9 / 3
3.0
»> 5.0 / 2
2.5
В отличие от сложения, вычитания и умножения, деление оператором/ всегда
возвращает float. Если вы хотите гарантированно получить после деления двух
чисел целое число, используйте int() для преобразования результата:
»> int(9 / 3)
3
Помните, что функция int () отбрасывает дробную часть числа:
»> int(5.0 / 2)
2
5. 0 / 2 возвращает число с плавающей точкой 2. 5, а int ( 2. 5)
. 5.
число 2 без дробной части
возвращает целое
94
ГЛАВА 5
Числа и математические вычисления
Целочисленное деление
Если запись
int ( 5. 0 / 2)
кажется слишком навороченной, применяйте второй
оператор деления, предоставляемый
Python, -
оператор целочисленного деления:
>» 9 // 3
3
>» 5.0 // 2
2.0
>» -3 // 2
-2
Оператор
//
сначала делит число слева на число справа, после чего округляет
результат в меньшую сторону до целого числа. Правда, если одно из чисел от­
рицательное, вы можете получить не тот результат, на который рассчитывали.
Например, -3 / / 2 возвращает -2. Сначала
- 3 делится
на 2, результат равен -1. 5.
Затем -1. 5 округляется в меньшую сторону до -2. С другой стороны, 3 / / 2 воз­
вращает 1, потому что оба числа положительные.
Приведенный пример также пока:~ывает, что если один из операндов имеет тип
float, то / / возвращает число с плавающей точкой. Таким образом, 9 / / 3 воз­
вращает целое число 3, а 5. 0 / / 2 возвращает 2. 0 в формате float.
Посмотрим, что произойдет при попытке разделить число на 0:
»> 1 / 0
Traceback (most recent call last):
File "cstdin>", line 1, in <module>
ZeroDivisionError: division Ьу zero
Python выдает ошибку ZeroDivisionError, сообщая вам
о том, что вы попытались
нарушить одно из основополагающих правил Вселенной
-
нельзя делить на ноль!
Возведение в степень
Для возведения числа в степень используется ш1сратор
»> 2 ** 2
4
>» 2 ** 3
8
>» 2 ** 4
16
**:
5.2. Арифметические операторы и выражения
95
Показатель степени не обязательно должен быть целым числом. Он также может
относиться к типу с плавающей точкой:
»> 3 ** 1.5
5.196152422706632
»> 9 ** 0.5
3.0
Возведение числа в степень
0.5
эквивалентно извлечению квадратного корня,
но следует заметить, что, хотя квадратный корень из
Python
9 является целым числом,
возвращает число с плавающей точкой з. 0.
Для положительных операндов оператор** возвращает int, если оба они явля­
ются целыми числами, или
float -
если один из операндов является числом
с плавающей точкой.
Числа также можно возводить в отрицательную степень:
»> 2 ** -1
0.5
>>> 2 ** -2
0.25
Возведение числа в отрицательную степень равносильно делению
1 на число,
возведенное в положительную степень. Таким образом, выражение
2 ** -1 эк­
1 / (2 ** 1), то есть 1 / 2, или 0. 5. Аналогичным образом выражение
2 ** -2эквивалентно1 / (2 ** 2), то есть 1 / 4, или 0.25.
вивалентно
Оператор вычисления остатка
Оператор
% возвращает
остаток от целочисленного деления левого операнда
на правый операнд:
>» 5 % 3
2
»> 20 % 7
6
>» 16 % 8
0
Число
5 делится на 3 с остатком 2, поэтому 5 %3 дает результат 2. Аналогичным
образом
20 делится на 7 с остатком 6. В последнем примере 16 делится на 8 без
16 %8 дает результат 0. Каждый раз, когда число в левой части
нацело делится на число в правой части, результат равен 0.
остатка, поэтому
от%
96
ГЛАВА
Числа и математические вычисления
5
Одно из стандартных применений%
-
определение того, делится ли одно число
на другое без остатка. Например, число
если
n %2 равно
n - четное в том и только в том случае,
%0? Попробуем:
О. Как вы думаете, что возвращает 1
»> 1 % 0
Traceback (most recent call last):
File "cstdin>", line 1, in cmodule>
ZeroDivisionError: integer division or modulo
Ьу
zero
%0 вычисляет остаток от деления 1 на О.
Python выдает ошибку ZeroDivisionError.
Результат выглядит логично : 1
на О делить нельзя, поэтому
Но
1
ПРИМЕЧАНИЕ
При работе в интерактивном окне
IDLE
такие ошибки, как
ZeroDivisionError,
не создают особых проблем . На экране появляется сообщение об ошибке,
а затем открывается новое приглашение, и вы можете продолжить работу.
Но когда Pythoп обнаруживает ошибку в процессе работы программы, вы­
полнение прерывается
-
другими словами, в вашей программе происходит
непредвиденный сбой. В главе
8
я покажу, как обрабатывать ошибки, чтобы
избежать преждевременного завершения программ.
При использовании оператора% с отрицательными числами ситуация немного
усложняется:
»> 5 % -3
-1
»> -5 % 3
1
»> -5 % -3
-2
Хотя на первый взгляд эти результаты выглядят странно, они объясняются
Python. Для вычисления остатка r от
Python используется формула r = х - (у* (х //у)).
четкой логикой выполнения оператора в
деления числах на число у в
% -3, Python сначала вычисляет
5 / -3 равен приблизительно -1.67, это означает,
что 5 // -3 дает -1. Теперь Python умножает это значение на -3, получается 6.
Наконец, Python вычитает 6 из 5 и получает -1.
Например, чтобы определить результат 5
(5 // -3).
Так как результат
5.2. Арифметические операторы и выражения
97
Арифметические выражения
Операторы могут объединяться для формирования сложных выражений. Вы­
ражение
- это совокупность чисел, операторов и
Python может вычислить для получения значения.
круглых скобок, которое
Примеры арифметических выражений:
»>
5
2*3 -
1
»>
4/2
2**3
+
10.0
>>> -1 +
-3
(-3*2
+
4)
При вычислении выражений действуют те же правила, что и в классической
арифметике. Вероятно, вы изучали эти правила в школьном курсе математики,
где они назывались порядком действий.
Операторы*,/,// и% имеют одинаковый приоритет в выражениях, и каждый
из них обладает более высоким приоритетом, чем операторы + и
выражение
2*3 - 1
- . Вот почему
2*3,
возвращает 5, а не 4. Сначала вычисляется результат
потому что* обладает более высоким приоритетом, чем оператор-.
Возможно, вы заметили, что в выражениях этого примера не соблюдается
правило о включении пробелов по обе стороны от оператора. В РЕР
8 об этом
говорится следующее:
«Если используются операторы с разными приоритетами, рассмотрите
возможность включения пробелов рядом с операторами, обладающими
самым низким приоритетом(-ами). Руководствуйтесь здравым смыслом;
тем не менее никогда не используйте более одного пробела и всегда
используйте одинаковое количество пробелов с двух сторон бинарного
оператора».
РЕР
Еще одна полезная практика
-
8, «Other Recommendations» (https.j/pep8.org/)
использование круглых скобок для обозначения
порядка выполнения операций, даже если эти скобки не являются необходи­
мыми. Например, выражение
(2 * 3) - 1 более понятно,
чем
2*3 - 1.
ГЛАВА 5
98
Числа и математические вычисления
5.3. ЗАДАЧА: ВЫПОЛНЕНИЕ ВЫЧИСЛЕНИЙ
С ПОЛЬЗОВАТЕЛЬСКИМ ВВОДОМ
Напишите программу
exponent.py,
которая получает от пользователя два числа
и выводит результат возведения первого числа в степень, заданную вторым числом.
Результат выполнения программы должен выглядеть примерно так (с вводом
от пользователя):
Enter а base: 1.2
Enter an exponent: 3
1.2 to the power of 3
1.7279999999999998
Не забывайте
1. Прежде чем делать что-либо с пользовательским вводом, необходимо
присвоить результаты обоих вызовов input() новым переменным.
2.
Функция
input ()
возвращает строку, поэтому введенные значения необ­
ходимо преобразовать в числа для выполнения арифметических операций.
3.
Для вывода результата можно задействовать f-строку.
4.
Предполагается, что пользователь вводит числа.
Решение задачи, а также многие другие дополнительные ресурсы доступны
в интернете по адресу
realpython.com/python-basics/resources.
5.4. КОГДА РУТНОN ГОВОРИТ НЕПРАВДУ
Как вы думаете, сколько будет
по­
смотрим, что об этом думает
ин­
0.1+0.2? Получится 0.3, верно? Давайте
Python. Попробуйте ввести следующий код в
терактивном окне:
»> 0.1 + 0.2
0.30000000000000004
Выглядит". почти правильно. Что же происходит? Может, создатели
Python
ошиблись?
Нет, это не ошибка! Это погрешность представления числа с плавающей точкой,
которая не имеет никакого отношения к
Python. Она связана с особенностями
хранения чисел с плавающей точкой в памяти компьютера.
5.4. Когда Python говорит неправду
99
Число 0 .1 может быть представлено в виде дроби 1/10. И число 0 .1, и дробь
1/10 - десятичные представления, то есть представления в десятичной системе
счисления. Однако компьютеры хранят вещественные числав двоичном пред­
ставлении.
В двоичном представлении с десятичным числом 0. 1 происходит нечто не­
ожиданное. Дробь 1/3 не имеет конечного десятичного представления, то есть
1/3 = 0. 3333". с бесконечным количеством троек в дробной части. То же самое
происходит с дробью 1/10 в двоичном представлении.
Двоичное представление 1/10 выглядит как бесконечная дробь:
0.00011001100110011001100110011 ...
Объем памяти компьютеров лимитирован, так что число 0. 1 приходится хра­
нить в приближенном виде, а не как точное значение. Приближенное значение
в памяти чуть больше точного и выглядит примерно так:
0.1000000000000000055511151231257827021181583404541015625
Но когда вы приказываете вывести значение 0 .1,
Python выводит 0 .1, а
не это
приближенное значение:
>» 0.1
0.1
Не стоит думать, что
Python просто отсекает цифры в двоичном представлении
0 .1. На самом деле происходит нечто более сложное.
Так как 0 .1 в двоичной записи является именно приближенной записью, нельзя
исключать, что несколько десятичных чисел будут иметь одинаковое двоичное
представление.
Например, как 0. 1, так и 0. 10000000000000001 имеют одинаковое двоичное
представление.
Python
выводит самое короткое десятичное число с таким
представлением.
Это объясняет, почему в первом примере этого раздела результат 0. 1 + 0. 2 не
равен 0. 3.
Python суммирует двоичные представления 0 .1 и 0. 2, и вы получаете
число, которое не является двоичным приближением 0. 3.
У вас от этого, наверное, голова идет кругом, но не огорчайтесь! Если только вы
не пишете программы для финансовых или научных вычислений, вам не стоит
беспокоиться о погрешности вычислений с плавающей точкой.
ГЛАВА 5
100
5.5.
Числа и математические вычисления
МАТЕМАТИЧЕСКИЕ ФУНКЦИИ
И ЧИСЛОВЫЕ МЕТОДЫ
Python содержит
ряд встроенных функций для работы с числами. В этом раз­
деле рассматриваются три наиболее распространенные функции:
1) rou nd ( )
для округления чисел до заданной точности;
2) abs()
для получения абсолютного значения (модуля) числа;
3) pow()
для возведения числа в степень.
Также вы узнаете о методе, который применяют к числам с плавающей точкой
для проверки того, содержат ли они целое значение.
Функция
round()
Функцию round () применяют для округления числа до ближайшего целого:
>» round(2.3)
2
»> round(2.7)
3
Если дробная часть числа равна
. 5,
функция round () ведет себя довольно не­
ожиданно:
»> round(2.5)
2
>»
round(З.5)
4
Число 2. 5 округляется до 2, а 3. 5 округляется до 4. Обычно мы ожидаем, что
число с дробной частью . 5 округляется в большую сторону, поэтому разберемся
более детально, что здесь происходит.
Python 3
округляет числа в соответствии со стратегией, которая называется
округлением нейтральных чисел до ближайшего четного. Нейтральным на­
зывается любое число, последняя цифра которого равна
являются нейтральными, а
5.
Так, 2. 5 и 3. 1415
1. 37 - нет.
При округлении нейтральных чисел до ближайшего четного сначала проверя­
ется предпоследняя цифра. Если она четная, то число округляется в меньшую
сторону, а если нечетная
а
3.5 -
до4.
-
в большую. Именно поэтому 2. 5 округляется до 2,
5.5. Математические функции и числовые методы
101
ПРИМЕЧАНИЕ
Применение стратегии округления нейтральных чисел до ближайшего четного
рекомендовано
инженеров
IEEE (lnstitute of Electrical and Electronics Engineers - Институт
no электротехнике и электронике), потому что она помогает огра­
ничить последствия округления для операций со многими числами.
IEEE поддерживает стандарт IEEE 754 для работы с числами с плавающей
1985 году, в настоящее
точкой на компьютерах. Стандарт был опубликован в
время он широко используется производителями оборудования.
Число также можно округлить до заданного количества знаков в дробной части,
для чего
round () следует передать второй аргумент:
>>> round(3~14159, 3)
3.142
>>> round(2.71828, 2)
2. 72
Число 3 .14159 округляется до трех знаков в дробной части, и вы получаете
результат з
те
.142,
а число
2. 71828
округляется до двух знаков, и вы получае­
2. 72.
Второй аргумент round () должен быть целым числом. В противном случае
Python выдает ошибку TypeError:
>>> round(2.65, 1.4)
Traceback (most recent call last):
File "<pyshell#0>", line 1, in cmodule>
round(2.65, 1.4)
TypeError: 'float' object cannot Ье interpreted as an integer
Иногда round () дает неточный результат:
>>> # Expected value: 2.68
>>> round(2.675, 2)
2.67
Число 2. 675 является нейтральным, потому что оно расположено ровно по­
середине между числами 2. 67 и 2. 68. Так как
Python
округляет нейтральные
числа до ближайшего четного, можно ожидать, что round ( 2. 675, 2) вернет 2. 68,
однако мы получаем
2.67.
Эта ошибка является результатом погрешности представления чисел с плава­
ющей точкой, а не некорректной реализацией round ().
102
ГЛАВА 5
Числа и математические вычисления
Работа с числами с плавающей точкой создает немало затруднений, но они
характерны не только для
Python.
Аналогичные проблемы возникают во всех
языках, поддерживающих стандарт чисел с плавающей точкой
IEEE, включая
C/C++,Java иjavaScript.
Впрочем, в большинстве случаев незначительной погрешностью, встречающей­
ся при работе с плавающей точкой, можно пренебречь, и результаты round()
вполне пригодны для использования.
Функция
abs()
Абсолютное значение (модуль) числа п равно п, если п положительно, или
если п отрицательно. Например, абсолютное значение
значение
-n,
3 равно 3, а абсолютное
-5 равно 5.
Чтобы получить абсолютное значение числа в
Python,
используйте функцию
abs():
» > abs ( 3)
3
»> abs(-5.0)
5.0
Функция
abs () всегда возвращает положительное число с таким же типом,
как у его аргумента. Другими словами, абсолютное значение целого числа
всегда является положительным целым числом, а абсолютное значение числа
с плавающей точкой всегда является положительным числом с плавающей
точкой.
Функция
Из раздела
pow()
5.2
вы узнали, как возвести число в степень оператором **.Для
получения того же результата можно воспользоваться и функцией pow ( ) .
Функция pow() получает два аргумента. Первый определяет основание (число,
возводимое в степень), а второй
-
показатель степени.
Например, следующая команда использует pow() для возведения 2 в сте­
пень з:
>» pow(2, 3)
8
5.5. Математические функции и числовые методы
Как и в случае с оператором**, показатель степени в
103
pow() может быть отри­
цательным:
»> pow(2, -2)
0.25
Итак, чем же
Функция
pow()
отличается от**?
pow() получает необязательный третий аргумент, который вычисляет
результат возведения первого числа в степень второго числа, после чего вы­
числяет остаток от деления на третье число. Иначе говоря, вызов
эквивалентен (х **у)%
pow(x,
у,
z)
z.
В следующем примере х =
2, у = з и z = 2:
»> pow(2, 3, 2)
0
Сначала
2 возводится в степень 3 и получается 8. Затем вычисляется выражение
8 %2, результат которого равен О, потому что 8 делится на 2 без остатка.
Проверка числа с плавающей точкой
на целочисленность
В главе
и
3 я рассказывал о таких строковых методах, как .lower(), .upper()
. find (). Для целых чисел и чисел с плавающей точкой тоже существуют
методы.
Числовые методы используются не так часто, но среди них есть один, ко­
торый может вам пригодиться. У чисел с плавающей точкой имеется метод
. is_integer( ),
который возвращает
True,
если число является целым, то есть
не имеет дробной части. В противном случае возвращается
False:
»> num = 2.5
>>> num.is_integer()
False
»> num = 2.0
>>> num.is_integer()
True
Метод
. is_integer()
может оказаться полезным при проверке пользователь­
ского ввода. Например, если вы пишете приложение для заказа пиццы через
интернет, нужно проверить, является ли количество заказанных порций целым
ГЛАВА 5
104
Числа и математические вычисления
числом или нет. О том, как выполнять подобные проверки, мы расскажем
в главе
8.
Упражнения
1.
Напишите программу, которая предлагает пользователю ввести число,
а затем выводит его округленным до двух цифр. Выполнение вашей про­
граммы должно выглядеть примерно так:
Enter а number: 5.432
5.432 rounded to 2 decimal places is 5.43
2.
Напишите программу, которая предлагает пользователю ввести число,
а затем выводит абсолютное значение этого числа. Выполнение вашей
программы должно выглядеть примерно так:
Enter а number: -10
The absolute value of -10 is 10.0
3.
Напишите программу, которая предлагает пользователю ввести два чис­
ла, используя
input ()
дважды, а затем сообщает, является ли разность
этих двух чисел целым числом. Выполнение вашей программы должно
выглядеть примерно так:
Enter а number: 1.5
Enter another number: .5
The difference between 1.5 and .5 is an integer? True!
4.
Если пользователь вводит два числа, разность которых не является целым
числом, вывод должен выглядеть примерно так:
Enter а number: 1.5
Enter another number: 1.0
The difference between 1.5 and 1.0 is an integer? False!
5.6.
ОФОРМЛЕНИЕ ЧИСЕЛ ПРИ ВЫВОДЕ
Чтобы вывести число для пользователя, необходимо вставить его в строку.
В главе
3 было показано,
как это делать с f-строками, для чего переменная, со­
держащая число, заключается в фигурные скобки:
»> n = 7.125
>>> f"The value of n is {n}"
'The value of n is 7.125'
Фигурные скобки поддерживают простой язык форматирования, позволя­
ющий изменить внешний вид отформатированной строки. Например, чтобы
5.6. Оформление чисел при выводе
отформатировать значение
105
n в приведенном примере до двух знаков, замените
{n:. 2f}:
содержимое фигурных скобок в f-строке на
»>n=7.125
>>> f"The value of n is {n:.2f}"
'The value of n is 7.12'
Двоеточие
(: )
после переменной
n указывает,
что все последующие символы
являются частью спецификации формата. В данном случае спецификация
формата имеет вид
. 2f.
Часть . 2 в
. 2f округляет число до двух знаков в дробной части, а f приказывает
Python вывести n в виде числа с фиксированной точкой. Это означает, что число
выводится ровно с двумя знаками в дробной части, даже если в исходном числе
знаков было меньше.
Если n = 7 .125, то результат {n:. 2f} равен 7 .12. Как и в случае с round( ),
Python
округляет нейтральные значения до четных даже при форматировании чисел
внутри строк. Таким образом, если заменить n = 7. 125 на n = 7. 126, то с
{ n: . 2f}
будет выведен результат 7 .13:
»> n = 7 .126
>>> f"The value of n is {n:.2f}"
'The value of n is 7.13'
Чтобы округлить выводимое число до одного знака, замените
. 2 на .1:
»> n = 7 .126
>>> f"The value of n is {n:.lf}"
'The value of n is 7.1'
Когда число форматируется как число с фиксированной точкой, оно всегда
выводится с заданным количеством знаков в дробной части:
»> n = 1
>>> f"The value
'The value of n
>>> f"The value
'The value of n
of
is
of
is
n is {n:.2f}"
1.00'
n is {n:.3f}"
1.000'
Если мы хотим отформатировать большое число так, чтобы разряды в нем
разделялись запятыми, то нужно добавить запятую внутрь фигурных скобок:
>>> n = 1234567890
>>> f"The value of n is {n:,}"
'The value of n is 1,234,567,890'
106
ГЛАВА
5
Числа и математические вычисления
Чтобы округлить число до заданного количества знаков, а также сгруппировать
разряды, включите
»>
п
,
перед
.
в спецификацию:
= 1234. 56
п is {n:,.2f}"
is 1,234.56'
>>> f"The value of
'The value of
п
Спецификатор
, . 2f хорошо подходит для
вывода денежных сумм:
>>> balance = 2000.0
>>> spent = 256.35
>>> remaining = balance - spent
»> f"After spending ${spent:.2f}, I was left with ${remaining:,.2f}"
'After spending $256.35, I was left with $1,743.65'
Другой полезный спецификатор % используется для вывода значений в про­
центах. Число умножается на
100
и выводится в формате с фиксированной
точкой, а после него выводится знак процента.
Спецификатор % всегда следует располагать в конце спецификации формата,
кроме того, его нельзя использовать совместно с режимом
f. Например, .1%
выводит число в процентах с одним знаком в дробной части:
»> ratio = 0.9
>» f"Over {ratio:.1%} of Pythonistas say 'Real Python rocks! '"
"Over 90.0% of Pythonistas say 'Real Python rocks! "'
>>> # Display percentage with 2 decimal places
>>> f"Over {ratio:.2%} of Pythonistas say 'Real Python rocks! '"
"Over 90.00% of Pythonistas say 'Real Python rocks! '"
Мини-язык форматирования
-
очень мощный и многообразный. Здесь мы
познакомили вас только с основными возможностями. За дополнительной
информацией обращайтесь к официальной документации
(https.j/docs.python.
org/3/library/string.html #f ormat-string-syntax_).
Упражнения
1.
Выведите результат вычисления з
**
.125 в формате с фиксированной
точкой с тремя знаками в дробной части.
2.
Выведите число 150000 как денежную сумму с разделением групп раз­
рядов запятыми. Денежные суммы должны выводиться с двумя знаками
в дробной части.
5.7. Комплексные числа
107
Выведите результат 2 / 10 в процентах без дробной части (то есть должно
3.
выводиться значение 20%).
КОМПЛЕКСНЫЕ ЧИСЛА
5.7.
Python - один из немногих языков программирования, имеющий встроенную
поддержку комплексных чисел. Хотя комплексные числа не так часто встре­
чаются вне сферы научных вычислений и компьютерной графики, поддержка
их в
Python -
одна из сильных сторон языка.
ПРИМЕЧАНИЕ
Если тема работы с комплексными числами в Pythoп вас не интересует, смело
пропускайте этот раздел. В других частях книги эта информация не исполь­
зуется.
Из курсов алгебры и начал анализа вы, вероятно, помните, что комплексное
число состоит из двух частей: вещественной и мнимой.
Чтобы создать комплексное число в
Python, просто запишите его вещественную
часть, затем поставьте знак + и добавьте мнимую часть с суффиксом j:
>» n
=
1 + 2j
Если проверить значение
n, вы увидите, что Python заключает число в круглые
скобки:
>» n
{1+2j)
Это соглашение помогает не спутать выведенное значение со строкой или ма­
тематическим выражением.
Комплексные числа содержат два свойства,
. real и . imag, которые возвращают
вещественную и мнимую составляющие числа соответственно:
»> n.real
1.0
»> n. imag
2.0
Заметим, что
типа
float,
Python возвращает вещественную и мнимую составляющие в виде
даже если они изначально задавались как целые числа.
ГЛАВА 5
108
Числа и математические вычисления
Для комплексных чисел также есть свой метод
. conj ugate (),
который возвра­
щает комплексно-сопряженное значение для числа:
>>> n.conjugate()
(1-2j)
Для любого комплексного числа сопряженным называется комплексное число
с такой же вещественной частью и такой же мнимой частью (по абсолютной
величине), но с обратным знаком . В данном примере сопряженным значением
для
1 + 2j
является
1 - 2j.
ПРИМЕЧАНИЕ
После свойств
.real и .imag не нужно ставить круглые скобки {в отличие от
. coпjugate{)).
Метод .coпjugate{) представляет собой функцию, которая выполняет действие
.real и .imag никаких действий не выполня­
- они просто возвращают некоторую информацию о числе.
с комплексным числом, тогда как
ют
Различия между методами и свойствами являются важным аспектом объ­
1 О.
ектно-ориентированноrо проrраммирования . Вы узнаете о них в главе
Кроме оператора целочисленного деления
//
все арифметические операторы,
работающие с числами с плавающей точкой и целыми числами, также работают
с комплексными числами.
Эта книга
-
не учебник по математике, поэтому мы не будем обсуждать механи­
ку вычислений с комплексными числами. Но мы познакомим вас с примерами
использования комплексных чисел с арифметическими операторами.
>»
>»
а
1 + 2j
з - 4j
ь
»> а +
(4-2j)
ь
»> а ( -2+бj)
ь
»> а *
(11+2j)
ь
»>
а
**
Ь
(932.1391946432212+95.9465ЗЗ6603415j)
»>
а
/
Ь
5.8. Итоги и дополнительные ресурсы
109
(-0.2+0.4j)
»>а//Ь
Traceback (most recent call last):
File "<stdin>", line 1, in cmodule>
TypeError: can't take floor of complex number.
Интересно (хотя и неудивительно с математической точки зрения), что объекты
int
и
float
содержат свойства.
real
и
. imag,
как и метод
. conjugate( ):
>» х = 42
»> x.real
42
»> x.imag
0
»> х. conjugate ()
42
>» у = 3.14
»> y.real
3.14
»> у. imag
0.0
>>> y.conjugate()
3.14
Для чисел с плавающей точкой и целых чисел
. real и . conjugate() всегда воз­
. imag всегда возвращает О. Однако следует заметить, что
n. real и n. imag возвращают целое число, если n является целым числом, и число
с плавающей точкой, если n является числом с плавающей точкой.
вращают само число, а
После знакомства с основами комплексных вычислений может возникнуть во­
прос: придется ли вам когда-нибудь использовать их? Если вы изучаете
Python
для неб-программирования, обработки данных или программирования общего
назначения, скажу честно: скорее всего, никогда не придется.
С другой стороны, без комплексных чисел не обойтись в научных вычислениях
и компьютерной графике. Если вы подвизаетесь в этих областях, встроенная
поддержка комплексных чисел в
5.8.
Python может
вам пригодиться.
ИТОГИ И ДОПОЛНИТЕЛЬНЫЕ РЕСУРСЫ
В этой главе мы рассказали, как работать с числами в
существуют два основных типа чисел
а еще в
Python
-
Python.
Вы узнали, что
целые и числа с плавающей точкой,
реализована встроенная поддержка комплексных чисел.
ГЛАВА 5
11 О
Числа и математические вычисления
Вы научились выполнять основные математические операции с числами
с использованием операторов+,-,*,/ и%. Также вы освоили арифметические
выражения и приемы форматирования арифметических выражений в коде
программы, определенные в РЕР
8.
Далее мы рассказали о числах с плавающей точкой и их представлении, ко­
торое не всегда бывает точным на 100%. Данное ограничение действует не
только в
Python. Это
особенность современных вычислительных технологий,
обусловленная способом представления чисел с плавающей точкой в памяти
компьютера.
Затем вы узнали, как округ./Iять числа до заданной точности функцией round ():
она округляет нейтральные числа до ближайшего четного, и такой способ округ­
ления несколько отличается от того, что мы изучали в школе . Также я показал
некоторые способы форматирования чисел для вывода.
В завершающей части главы была описана встроенная поддержка комплексных
чисел в
Python.
ИНТЕРАКТИВНЫЙ ТЕСТ
К этой главе прилагается бесплатный интерактивный тест для проверки усво­
енных вами знаний. Тест доступен на телефоне или компьютере:
realpython.com/quizzes/ pybasics-numbers
Дополнительные ресурсы
За дополнительной информацией обращайтесь к следующим ресурсам:
•
•
«Basic Data Types in Python» (https//realpython.com/python-data-types/)
«How to Round Numbeгs in Python» (https//realpython.com/python-
rounding/)
ГЛАВА6
Функции и циклы
Функции являются строительными блоками практически любой программы
Python.
Обычно именно в них происходят основные события!
Вы уже видели, как использовать некоторые функции, например
len ()
и
round ().
print( ),
Все эти функции называются встроенными, потому что они
реализованы непосредственно в языке
Python.
Вы также можете создавать
пользовательские функции для решения конкретных задач.
Функции разбивают код на меньшие блоки. Их используют для определения
действий, которые должны неоднократно выполняться в программном коде. Вам
не придется писать один и тот же код каждый раз, когда программе потребуется
выполнить эту задачу, достаточно вызвать функцию!
Но иногда некоторую часть кода требуется повторить несколько раз подряд.
Для этого используются циклы.
В этой главе вы:
•
создадите пользовательские функции;
•
научитесь работать с циклами for и while;
•
узнаете, что такое область видимости и почему она важна.
Итак, за дело!
6.1.
ЧТО ЖЕ ТАКОЕ ФУНКЦИЯ?
В предыдущих главах мы использовали функции
print()
и
len()
для вывода
текста и определения длины строки. Давайте разберемся с функциями более
подробно.
В этом разделе на примере
полняется.
len ()
я покажу, что такое функция и как она вы­
ГЛАВА 6
112
Функции и циклы
Функции как значения
Одна из самых важных особенностей функций в языке
что функции
-
Проверим имя
Python
состоит в том,
это значения, которые могут присваиваться переменным.
len
в интерактивном окне
IDLE.
Для этого введите его после
приглашения:
»> len
<built-in function len>
Python
сообщает, что
len является встроенной функцией. По аналогии с тем,
int; а строки - тип str, значения­
как целочисленные значения имеют тип
функции также имеют тип:
»> type(len)
<class 'builtin_function or method'>
Однако при желании с именем
len
можно связать другое значение:
»> len = "I'm not the len you're looking for."
»> len
"I'm not the len you're looking for."
Теперь имя
len связано со строковым значением. Вы можете убедиться в том,
str, при помощи функции type( ):
что оно ОТНОСИТСЯ к типу
»> type(len)
<class 'str'>
И хотя значение, связанное с именем
len, можно изменить, делать так обычно не
len только усложнит чтение вашего кода,
рекомендуется. Изменение значения
потому что новое имя
len легко перепутать со встроенной функцией.
Сказанное
относится к любой встроенной функции.
ВАЖНО!
После выполнения этих примеров кода встроенная функция len станет недо­
ступной в IDLE. Чтобы вернуть ее , введите команду :
>>> del len
Ключевое слово
сокращение от
del
используется для отмены присваивания переменной. Это
delete
(удалить), но значение не удаляется. Вместо этого связь
имени со значением разрывается и удаляется имя.
6.1.
Обычно после выполнения
Что же такое функция?
11 З
del при попытке использования имени удаленной
NameError. Однако в данном случае имя len
переменной происходит ошибка
не удаляется:
»> len
<built-in function len>
Так как
len является именем
встроенной функции, оно снова связывается с ис­
ходным значением-функцией.
Какой же вывод из этого можно сделать? У функций есть имена, но эти имена
не имеют жесткой связи с функцией, и им можно присваивать другие значения.
Когда вы пишете собственные функции, будьте внимательны и не присваивайте
им значения, используемые встроенными функциями.
Как
Python работает с функциями
Давайте более детально разберемся в том, как
Python выполняет функции.
Прежде всего следует заметить, что функцию невозможно выполнить, просто
указав ее имя. Необходимо вызвать функцию, чтобы сообщить
Python, как она
должна выполняться.
Посмотрим, как работает механизм вызова на примере
>>> # Печать имени функции не приводит к ее
>>> # IDLE проверяет переменную как обычно.
»> len
<built-in function len>
len ():
исполнению.
»> # Используем скобки для вызова функции.
»> len()
Traceback (most recent call last):
File "<pyshell#З>", line 1, in <module>
len()
TypeError: len() takes exactly one argument (0 given)
В этом примере
Python выдает ошибку TypeError при вызове len( ), потому что
функция
ожидает получить аргумент.
len ()
Аргумент представляет собой значение, которое передается функции. Некоторые
функции вызываются без аргументов, другие могут получать сколько угодно
аргументов. Функция
len() должна получать только один аргумент. Завершив
выполнение, функция возвращает значение. Возвращаемое значение обычно
хотя и не всегда -
-
зависит от значений других аргументов, передаваемых функции.
ГЛАВА 6
114
Функции и циклы
Функция выполняется в три этапа.
1.
Функция вызывается, и все аргументы передаются ей в качестве входных
значений.
2.
Функция выполняется, и с ее аргументами выполняются некоторые
действия.
3.
Функция возвращает управление, а исходный вызов функции заменяется
возвращаемым значением.
Давайте рассмотрим конкретный пример и разберемся, как
Python выполняет
следующую строку кода:
»> num_letters = len("four")
Сначала
"four",
len ()
вызывается с аргументом
которая равна
4.
Затем
len()
"fou r".
Вычисляется длина строки
возвращает число
4
и заменяет вызов
функции полученным значением.
После выполнения функции строка кода будет выглядеть так:
>>> num_letters
Затем
Python
=4
присваивает значение
4
переменной
num_letters и продолжает
выполнение остальных строк кода в программе.
Функции могут обладать побочными эффектами
Вы узнали, как вызывать функции и что функции возвращают значение при
завершении выполнения. Тем не менее иногда функции не ограничиваются
простым возвращением значения.
Если функция изменяет что-то в программе за пределами своего собственного
кода, говорят, что она имеет побочный эффект. Вы уже видели одну функцию
с побочным эффектом:
print().
print ( ) со строковым аргументом, строка выводится в обо­
Python в текстовом виде. Однако print () не возвращает текстового значения.
Когда вы вызываете
лочке
Чтобы понять, что возвращает
print ()
print(), присвойте возвращаемое значение
переменной:
»> return_value = print("What do I return?")
What do I return?
>» return_value
>»
6.2. Написание ваших собственных функций
115
print("What do I return?") переменной return_value,
"What do I return? ".Но при проверке значения return_ value
Когда вы присваиваете
выводится строка
ничего не выводится.
print() возвращает специальное значение None, которое указывает
на отсутствие данных; None относится к типу, который называется NoneType:
Функция
>>> type(return_value)
<class 'NoneType'>
>>> print(return_value)
None
При вызове
print ()
выводимый текст не является возвращаемым значением,
это побочный эффект
6.2.
print ().
НАПИСАНИЕ ВАШИХ СОБСТВЕННЫХ
ФУНКЦИЙ
При написании более сложных программ может оказаться, что вам нужно
многократно выполнять несколько строк кода
-
например, вычислять одну
и ту же формулу для разных входных значений.
Возможно, у вас появится искушение скопировать код в другие части про­
граммы и изменить его по мере надобности, но так поступать не стоит! Если
вы обнаружите ошибку в скопированном коде, исправление придется вносить
во все копии. А это огромная работа!
В этом разделе вы научитесь создавать собственные функции, чтобы избежать
дублирования кода при его многократном использовании.
Анатомия функции
Каждая функция состоит из двух частей.
1.
Сигнатура функции определяет имя функции и все входные данные,
которые она ожидает получить.
2.
Тело функции содержит код, который выполняется при каждом исполь­
зовании функции.
Напишем функцию, которая получает два числа на входе и возвращает их про­
изведение. Вот как может выглядеть функция (сигнатура и т,ело обозначены
в комментариях):
ГЛАВА
116
6
def multiply(x, у):
# Тело функции
product = х * у
return product
Функции и циклы
#Сигнатура функции
Создание функции для чего-то настолько простого, как оператор*, выглядит
странно. Пожалуй, mul tiply() - не та функция, которую вы будете использо­
вать в реальной программе. Но это хороший первый пример, чтобы понять, как
создаются функции!
ВАЖНО!
Когда вы определяете функцию в интерактивном окне
жать
Enter дважды
IDLE, необходимо на­
после строки, содержащей команду returп, чтобы функция
была зарегистрирована
>>> def multiply(x,
Python:
у):
product = х * у
return product
# <--- Здесь нужно
нажать
Enter
во второй раз.
>»
Разобьем функцию на части и посмотрим, что в какой момент происходит.
Сигнатура функции
Первая строка кода функции называется сигнатурой функции. Она всегда на­
чинается с ключевого слова def (сокращение от
Рассмотрим повнимательнее сигнатуру
def multiply(x,
define -
определить).
multiply( ):
у):
Сигнатура функции состоит из четырех частей.
1.
Ключевое слово
2.
Имя функции
3.
Список параметров (х, у)
4.
Двоеточие(:) в конце строки .
Когда
def.
multiply.
Python читает строку,
начинающуюся с ключевого слова
def, он создает
новую функцию. Эта функция присваивается переменной, имя которой со­
впадает с именем функции.
117
6.2. Написание ваших собственных функций
ПРИМЕЧАНИЕ
Так как имена функций становятся переменными, они должны подчиняться
правилам имен переменных, о которых вы узнали в главе 3. Таким образом,
имя функции может содержать только цифры, буквы и подчеркивания и не
должно начинаться с цифры.
Список параметров представляет собой список имен, заключенный в круглые
скобки . Он определяет ожидаемые входные значения функции; {х, у)
параметров для
mul tiply{),
который определяет два параметра
-
-
список
х и у.
Параметр является разновидностью переменной, но не имеет собственного
значения. Параметр замещает фактические значения, которые будут переданы
при вызове функции с одним или несколькими аргументами.
Код в теле функции использует параметры так, как если бы они являлись пере­
менными с реальными значениями. Например, тело функции может содержать
строку с выражением х
* у.
Так как х и у значений не имеют, произведение х
* у тоже не имеет значения.
Python сохраняет выражение в виде шаблона и подставляет недостающие зна­
чения при выполнении функции.
Функция может получать любое количество параметров, в том числе и ни одного.
Тело функции
Тело функции содержит код, который выполняется при использовании функции
в программе. Тело функции для
def multiply(x,
#
multiply{)
выглядит так:
у):
Тело функции
product = х * у
return product
multiply -
совсем простая функция. Ее тело содержит всего две строки кода.
Первая строка тела функции создает переменную с именем
product
и при­
сваивает ей значение х *у . Так как х и у еще не содержат значений, эта строка
в действительности определяет шаблон для значения, которое будет присвоено
product
при выполнении функции.
return. Она начинается
переменная product. Достигнув
Вторая строка тела функции называется командой
с ключевого слова
return,
за которым следует
118
Функции и циклы
ГЛАВА 6
return, Python прекращает выполнение функции и возвращает зна­
product.
команды
чение
Обратите внимание: обе строки кода в теле функции снабжены отступами.
Это очень важно! Каждая строка с отступом, расположенная под сигнатурой
функции, считается принадлежащей телу функции.
Например, вызов функции
print ()
в следующем примере не входит в тело
функции, потому что эта строка не имеет отступа:
def multiply(x, у):
product = х * у
return product
print("Where am !?")
# Не принадлежит телу функции
print (), эта строка станет частью тела функ­
строку между print() и предыдущей строкой:
Если добавить отступ в строку с
ции, даже несмотря на пустую
def multiply(x, у):
product = х * у
return product
print("Where am !?")
#
В теле функции
При добавлении отступов в тело функции необходимо соблюдать одно правило:
отступы всех строк должны содержать одинаковое количество пробелов.
Попробуйте сохранить следующий код в файле с именем
нить его из
def multiply(x, у):
product = х * у
return product
IDLE
multiply.py
и выпол­
IDLE:
#
С одним лишним пробелом
не будет выполнять этот код! На экране появится диалоговое окно с со­
общением об ошибке
«unexpected indent»
ожидает, что команда
return
(«неожиданный отступ»).
Python
имеет такое же количество пробелов в отступе,
как и в строке над ней.
Другая ошибка возникает тогда, когда размер отступа в строке меньше, чем
у строки над ней и отступ не соответствует никакой из предшествующих строк.
Измените файл
multiply.py, чтобы он выглядел так:
def multiply(x, у):
product = х * у
return product #
Отступ меньше,
чем в предыдущей строке
6.2. Написание ваших собственных функций
Сохраните и запустите файл.
об ошибке
IDLE
unindent does not match
119
прерывает его выполнение с сообщением
апу
outer
iпdeпtatioп
level
(отступ не со­
ответствует ни одному внешнему уровню). Количество пробелов в отступе
команды returп отличается от любой строки в теле функции.
ПРИМЕЧАНИЕ
Хотя в Pythoп нет правил, определяющих количество пробелов в отступах кода
в теле функции, РЕР 8 рекомендует использовать отступы из четырех пробелов.
В книге соблюдается именно это правило.
После выполнения команды returп функция останавливается и возвращает
значение. Если под командой returп располагается код, отступ которого указы­
вает на то, что он является частью тела функции, этот код выполнен не будет.
Например, в следующей функции команда priпt () никогда не выполняется:
def multiply(x, у):
product = х * у
return product
print("You can't see me!")
Эта версия
multiply()
никогда не выведет строку
"You
сап'
t see me ! ".
Вызов функции, определенной пользователем
Пользовательская функция вызывается точно так же, как и любая другая: вы
вводите имя функции, за которым указываете список аргументов в круглых
скобках.
Например, чтобы вызвать
mul tiply()
с аргументами
2 и 4,
просто введите сле­
дующую команду:
multiply(2, 4)
В отличие от встроенных, пользовательские функции становятся доступными
только после того, как они будут определены с ключевым словом def. Необхо­
димо определить функцию, прежде чем вызывать ее.
Введите следующую программу в окне редактора
num = multiply(2, 4)
print(num)
IDLE:
120
ГЛАВА 6
Функции и циклы
def multiply(x, у):
product = х * у
return product
Сохраните файл и нажмите
определения,
Python
FS.
multiply() вызывается до ее
multiply и выдает ошибку NameError:
Так как функция
не распознает имя
Traceback (most recent call last):
File "C:Usersdaveamultiply.py", line 1, in cmodule>
num = multiply{2, 4)
NameError: name 'multiply' is not defined
Чтобы исправить ошибку, переместите определение функции в начало файла:
def multiply(x, у):
product = х * у
return product
num = multiply(2, 4)
print(num)
Снова сохраните файл и нажмите
FS,
чтобы запустить программу. На этот раз
в интерактивном окне выводится значение
8.
Функции без команды return
Python возвращают значение, даже если
всем функциям необходима команда return.
Все функции в
не
это
None. Тем не менее
Например, следующая функция вполне допустима:
def greet(name):
print(f"Hello, {name}!")
greet() не содержит команды return, но прекрасно работает:
>>> greet("Dave")
Hello, Dave!
И несмотря на отсутствие команды
>» return_value = greet("Dave")
Hello, Dave!
>>> print(return_value)
None
return, greet ()
возвращает значение:
6.2. Написание ваших собственных функций
Строка "Не llo,
121
Dave ! " выводится, хотя результат greet ( "Dave" ) присваивается
"Hello, Dave! "?Тогда
переменной. Может, вы не ожидали увидеть на экране
вы столкнулись с одной из проблем побочных эффектов
-
они бывают не­
ожиданными!
При создании функций всегда следует документировать, что они делают. Это
позволит другим разработчикам прочитать ваши заметки и понять, как поль­
зоваться функциями и чего от них ожидать.
Документирование функций
Для получения справки о функции в интерактивном окне
функция
ID LE
используется
help():
>» help(len)
Help on built-in function len in module builtins:
len(obj, /)
Return the number of items in
Функции
а
container.
help() передается имя переменной или функции, а она выводит по­
лезную информацию об указанной переменной или функции. В данном случае
help() сообщает, что len() является встроенной функцией, которая возвращает
количество элементов в контейнере.
ПРИМЕЧАНИЕ
Контейнер
-
специальный термин для объекта, который содержит другие
объекты. Строка является контейнером, потому что она содержит символы.
О других типах контейнеров я расскажу в главе
Посмотрим, что происходит при вызове
>>>
Help
help(multiply)
оп function multiply in module
multiply(x,
Функция
help()
9.
для
multiply():
~main~
у)
help() выводит сигнатуру функции, но не предоставляет никакой
информации о том, что эта функция делает. Чтобы лучше документировать
функцию multiply(), следует предоставить dос-строку
в утроенных кавычках в начале тела функции.
-
строковый литерал
ГЛАВА 6
122
Функции и циклы
Dос-строка описывает, что делает функция и какие виды параметров она ожи­
дает получить:
def multiply(x,
у):
"""Возвращает произведение двух чисел х и у."""
product = х * у
returп product
mul tiply() с dос-строкой. Теперь можно воспользоваться
help() в интерактивном окне для просмотра dос-строки:
Перепишите функцию
функцией
>>> help(multiply)
Help оп fuпctioп multiply
multiply(x,
8
~maiп~
у)
Возвращает
В РЕР
module
iп
произведение двух чисел
х
и у.
о dос-строках не сказано почти ничего, кроме того, что они должны
определяться в каждой функции (https.j/pep8.org/#docuтentation-strings).
Существует ряд стандартизированных форматов dос-строк, но мы не будем
в них углубляться. Некоторые общие рекомендации по написанию dос-строк
можно найти в РЕР
257 (https.j/www.python.org/dev/peps/pep-0257/).
Упражнения
1.
Напишите функцию
cube (),
которая получает один числовой параметр
и возвращает значение указанного числа в третьей степени. Протести­
руйте функцию
-
вызовите
cube()
для нескольких разных чисел и вы­
ведите результаты.
2.
Напишите функцию
с именем
greet( ), которая
получает один строковый параметр
name и выводит текст «Hello <пате>!», где <пате> заменяется
name.
значением параметра
6.3.
ЗАДАЧА: КОНВЕРТЕР ТЕМПЕРАТУР
Напишите программу teтperature.py, которая определяет две функции.
1. convert_cel_to_far()
получает один параметр
float,
представляющий
температуру по шкале Цельсия, и возвращает значение
float,
пред­
ставляющее ту же температуру по шкале Фаренгейта. Преобразование
выполняется по следующей формуле:
6.4. Циклическое выполнение
123
F =С* 9/5 + 32
2. convert_far _to_cel()
получает один параметр
float,
представляющий
температуру по шкале Фаренгейта, и возвращает значение
float, пред­
ставляющее ту же температуру по шкале Цельсия. Преобразование вы­
полняется по следующей формуле:
C=(F-32)*5/9
Программа должна делать следующее.
1.
Запрашивать у пользователя температуру в градусах по шкале Фарен­
гейта, а затем выводить температуру, преобразованную к шкале Цельсия.
2.
Запрашивать у пользователя температуру в градусах по шкале Цельсия,
а затем выводить температуру, преобразованную к шкале Фаренгейта.
3.
Выводить все преобразованные температуры, округленные до двух знаков
в дробной части.
Пример выполнения программы:
Enter а temperature in degrees F: 72
72 degrees F = 22.22 degrees С
Enter а temperature in degrees
37 degrees с = 98.60 degrees F
С:
37
Решение этой задачи, а также многие другие дополнительные ресурсы доступны
в интернете по адресу
6.4.
realpython.com/python-basics/resources.
ЦИКЛИЧЕСКОЕ ВЫПОЛНЕНИЕ
Одно из самых замечательных свойств компьютеров заключается в том, что им
можно приказать делать одно и то же снова и снова и они никогда не пожалу­
ются! Цикл представляет собой блок кода, который выполняется многократно
заданное количество раз или до выполнения некоторого условия. В
поддерживаются две разновидности циклов: циклы
while
и циклы
for.
Python
В этом
разделе я расскажу, как использовать обе разновидности.
Цикл
Циклы
while
while повторяют блок кода, пока некоторое условие не станет истинным.
while состоит из двух частей.
Каждый цикл
ГЛАВА 6
124
1.
Команда
Функции и циклы
while
начинается с ключевого слова
while,
за которым следует
проверяемое условие и двоеточие.
2.
Тело цикла содержит код, который повторяется на каждом шаге цикла.
Каждая строка тела снабжается отступом из четырех пробелов .
В процессе выполнения цикла while
Python
проверяет условие и определяет,
истинно оно или ложно. Если условие истинно, то
Python выполняет код в теле
цикла и снова возвращается к проверке условия. В r:~ротивном случае код тела
пропускается и выполняется дальнейший код программы.
Рассмотрим пример. Введите следующий код в интерактивном окне:
>>> n = 1
»> while n < 5 :
print(n)
n = n + 1
1
2
з
4
Сначала целое число
с условием n
< 5.
1 присваивается переменной n. Затем создается цикл while
Это условие проверяет, что значение n меньше 5.
Если n меньше 5, выполняется тело цикла
-
две строки кода. В первой строке
значение n выводится на экран. Во второй строке n увеличивается на 1.
Выполнение цикла состоит из пяти шагов (итераций).
НОМЕР
WАГА
ЭНАЧЕ·
НИЕN
УСЛОВИЕ
ЧТО ПРОИСХОДИТ
1 < 5 (true)
Выводит
1; увеличивается до 2
Выводит
2;
увеличивается до З
2
2
2 < 5 (true)
з
з
З
4
4
4 < 5 (true)
Выводит
5
5
5 < 5 (false)
Ничего; цикл завершается
< 5 (true)
Выводит З; увеличивается до
4
увеличивается до
5
4;
Если действовать неосторожно, можно создать бесконечный цикл. Такая си­
туация возникает, когда условие цикла всегда истинно. Бесконечный цикл не
завершается никогда, а его тело выполняется снова и снова.
6.4. Циклическое выполнение
125
Пример бесконечного цикла:
>>> n = 1
>» while n < 5:
print(n)
Единственное отличие этого цикла while от предыдущего в том, что
n не увели­
n остается равной 1.
Таким образом, условие п < 5 всегда будет истинным, а число 1 будет выводиться
чивается в теле цикла. На каждом шаге цикла переменная
снова и снова.
ПРИМЕЧАНИЕ
Бесконечные циклы не всегда плохи . Иногда это именно то, что нужно. Напри­
мер, код для работы с оборудованием может в бесконечном цикле проверять,
была ли активизирована некоторая кнопка или переключатель.
Если вы запустили программу, которая вошла в бесконечный цикл, прикажи­
те
Python немедленно завершить программу, нажав сочетание клавиш Ctrl+C.
Python прекращает выполнение программы и выдает ошибку Keyboardinterrupt:
Traceback (most recent call last):
File "<pyshell#8>", line 2, in <module>
print(n)
Keyboardlnterrupt
Рассмотрим пример практического использования цикла
возможных применений цикл
while
while.
В одном из
проверяет, удовлетворяет ли пользова­
тельский ввод некоторому условию, и если нет
-
запрашивает у пользователя
новые данные, пока не будет получен корректный ввод.
Например, следующая программа многократно запрашивает у пользователя
положительное число, пока не будет введено именно положительное число:
num = float(input("Enter
а
positive number: "))
while num <= 0:
print("That's not а positive number!")
num = float(input("Enter а positive number: "))
Сначала программа выводит сообщение, предлагающее ввести положительное
число. Условие num <= 0 проверяет, что значение num меньше либо равно О.
Если значение num положительно, условие нарушается. Тело цикла пропускается,
и программа завершается.
Функции и циклы
ГЛАВА 6
126
В противном случае, если значение
num равно О или отрицательно, выполняется
тело цикла. Программа оповещает пользователя о том, что введенное значение
недопустимо, и предлагает повторить попытку.
Циклы
while идеально подходят для
повторения части кода, пока выполняется
заданное условие. С другой стороны, они плохо работают при повторении части
кода фиксированное количество раз.
Цикл for
Цикл
for
выполняет часть кода по одному разу для каждого элемента в кол­
лекции. Количество выполнений кода определяется количеством элементов
в коллекции.
Цикл
1.
for,
как и
Команда
while,
состоит из двух частей .
for начинается с ключевого слова for, за которым следует вы­
ражение принадлежности, и завершается двоеточием:
2.
Тело цикла for содержит код, выполняемый на каждом шаге цикла. Каждая
строка содержит отступ из четырех пробелов.
Рассмотрим пример. Следующий цикл
букву строки
for
последовательно выводит каждую
"Python":
for letter in "Python":
print(letter)
for имеет вид for letter in "Python",
- letter in "Python".
В этом примере команда
принадлежности
На каждом шаге цикла переменной
строки
"Python",
а выражение
letter присваивается очередная буква
letter.
после чего выводится значение
Цикл выполняется по одному разу для каждого символа строки
"Python",
так
что тело будет выполнено шесть раз. В следующей таблице кратко описано
выполнение цикла.
НОМЕР
ЗНАЧЕНИЕ LEПER
ЧТО ПРОИСХОДИТ
11 р11
Выводит Р
2
"у"
Выводит у
3
//tlf
Выводитt
ШАГА
6.4. Циклическое выполнение
НОМЕР
ЗНАЧЕНИЕ LEПER
ЧТО ПРОИСХОДИТ
4
"t"
Выводит
5
"о"
Выводит о
6
lln"
Выводит п
ШАГА
127
h
Чтобы понять, почему циклы for лучше подходят для перебора коллекций
элементов, перепишем цикл
for
из предыдущего примера в форме цикла while.
Для этого можно сохранить индекс следующего символа строки в переменной.
На каждом шаге цикла выводится символ с текущим индексом, после чего
индекс увел ичивается .
Цикл остановится, как только значение индексной переменной достигнет дли­
ны строки. Помните, что индексы начинаются с 0, а следовательно, последний
индекс в строке
"Python"
равен
5.
Этот код можно было бы записать так:
word = "Python"
index = 0
while index < len(word):
print(word[index])
index = index + 1
Эта версия намного сложнее версии с
for!
Впрочем, цикл for не только проще - он выглядит более естественно. Он ближе
к тому, как бы вы описали цикл на человеческом языке.
ПРИМЕЧАНИЕ
.
Иногда приходится слышать, как люди называют какой-нибудь код питони­
ческим. Обычно этим термином обозначается код понятный, компактный
и широко использующий встроенные возможности Pythoп .
Таким образом, использование цикла for для перебора коллекции элементов
следует признать более питоническим, чем цикл
while.
Иногда требуется перебрать числа в некотором диапазоне. В
Python существует
удобная встроенная функция raпge( ), предназначенная именно для создания
диапазона чисел.
128
ГЛАВА 6
Функции и циклы
Например, range(З) возвращает диапазон целых чисел от
0
доз (не включая
последнее). Таким образом, range(З) создает диапазон чисел 0, 1 и 2.
Вызов range ( n), где n - любое положительное число, может использоваться для
выполнения цикла ровно
"Python"
n раз.
Например, следующий цикл выводит строку
три раза:
for n in range(3):
print("Python")
Также можно задать начальную точку диапазона. Например, вызов
создает диапазон из чисел
а второй
-
range ( 1, 5)
1, 2, з и 4. Первый аргумент задает начальное число,
конечную точку, которая в диапазон не включается.
Следующий цикл
for,
использующий версию
выводит квадраты всех чисел от
10 до 20
range()
с двумя аргументами,
(не включая последнее):
for n in range(10, 20):
print(n * n)
Рассмотрим пример реальной программы. Она предлагает пользователю ввести
сумму счета, а потом показывает, как эта сумма будет делиться между двумя,
тремя, четырьмя и пятью людьми:
amount = float(input("Enter an amount: "))
for num_people in range(2, 6):
print(f"{num_people} people: ${amount / num_people:,.2f} each")
Цикл for перебирает числа 2, з, 4 и 5 и выводит количество людей и сумму,
которую должен заплатить каждый участник. Форматный спецификатор
, . 2f
форматирует сумму как число с фиксированной точкой, округленное до двух
знаков и с разделением групп разрядов запятыми.
Если запустить программу и ввести значение 10, будет получен следующий
результат:
Enter an amount: 10
2 people: $5.00 each
3 people: $3.33 each
4 people: $2.50 each
5 people: $2.00 each
Циклы for обычно используются в
Python чаще, чем циклы while.
Как правило,
циклы for более компактны и проще читаются, чем эквивалентные циклы while.
6.4. Циклическое выполнение
129
Вложенные циклы
Циклы могут вкладываться внутрь других циклов
-
при условии, что вы пра­
вильно используете отступы в коде.
Введите следующую команду в интерактивном окне
IDLE:
for n in range{l, 4):
for j in range{4, 7):
print(f"n = {n} and j = {j}")
В ходе выполнения первого цикла
менной
for Python присваивает значение 1 пере­
for и переменной j присваивается
выведенном результате n = 1 и j =4.
Затем выполняется второй цикл
n.
значение 4. В первом
После выполнения
print () Python
возвращается к внутреннему циклу
for,
n = 1 и j = 5. Python не возвра­
к внешнему циклу for, потому что внутренний цикл for, находящийся
тела внешнего цикла for, еще не завершен.
присваивает 5 переменной j, после чего выводит
щается
внутри
Далее
Python
присваивает 6 переменной j, после чего выводит
n =1
и j
= 6.
В этой точке внутренний цикл
for завершил выполнение, поэтому управление
Python присваивает значение 2 переменной n,
после чего внутренний цикл for выполняется во второй раз. Этот процесс по­
вторяется в третий раз, когда n присваивается значение з.
возвращается внешнему циклу for.
Окончательный вывод выглядит так:
n
n
n
n
n
n
n
n
n
1
1
1
2
2
2
3
3
3
and
and
and
and
and
and
and
and
and
j
j
j
j
j
j
j
j
j
4
5
6
4
5
6
4
5
6
Цикл, находящийся внутри другого цикла, называется вложенным циклом.
Вложенные циклы встречаются достаточно часто. Разрешается вкладывать
циклы
while
в циклы
for
и наоборот. Более того, возможно вложение циклов
на глубину более двух уровней.
Циклы
-
мощный инструмент в арсенале программиста. Они используют одно
из самых больших достоинств компьютеров: повторять одни и те же действия
огромное количество раз, не уставая и не жалуясь.
ГЛАВА 6
130
Функции и циклы
ВАЖНО!
Вложение циклов неизбежно повышает сложность вашего кода. Об этом сви­
детельствует значительное увеличение числа шагов в приведенном примере
по сравнению с предыдущими примерами с одним циклом
Вложенные циклы
for.
- иногда единственный способ решения задачи, но слиш­
ком большое количество вложенных циклов может отрицательно сказаться
на быстродействии программы .
Упражнения
1.
зованием
2.
for, который выводит целые числа от 2 до 1О с исполь­
range( ). Каждое число должно выводиться в новой строке.
Напишите цикл
Напишите цикл
while,
который выводит целые числа от
2 до 10.
(Под­
сказка: сначала необходимо создать новое целое число.)
3.
Напишите функцию douЫes (),которая получает число и увеличивает его
вдвое. Используйте douЫes () в цикле, чтобы трижды увеличить вдвое
число
2, с выводом каждого результата в отдельной строке.
Пример вывода:
4
8
16
6.5. ЗАДАЧА:
ОТСЛЕЖИВАНИЕ ПРИБЫЛИ
ПО ВКЛАДУ
В этой задаче вам надо написать программу
invest.py для отслеживания
увели­
чения вклада со временем.
Исходная сумма вклада называется основной. Каждый год основная сумма
увеличивается на фиксированный процент
-
годовой процент прибыли.
Например, основная сумма $100.00 с годовым процентом 5 по истечении года
равна $105. 00. Во второй год приращение составит
5 процентов от $105. 00,
или
$5. 25, а общая сумма увеличится до $110. 25.
Напишите функцию
invest, которая выводит три
параметра: основную сумму,
годовой процент прибыли и количество лет. Сигнатура функции может вы­
глядеть примерно так:
def invest(amount, rate, years):
6.6. Область видимости в Python
131
Затем функция должна выводить сумму вклада, округленную до двух знаков
в дробной части, на конец каждого года в заданном диапазоне лет.
Например, вызов
year
year
year
year
1:
2:
3:
4:
invest ( 100, . 05, 4)
должен выводить следующий результат:
$105.00
$110.25
$115. 76
$121.55
Чтобы завершить программу, запросите у пользователя основную сумму, годо­
вой процент прибыли и количество лет. Затем вызовите
invest()
для вывода
расчетов для значений, введенных пользователем.
Решение этой задачи, а также многие другие дополнительные ресурсы доступны
в интернете на
6.6.
realpython.com/python-basics/resources.
ОБЛАСТЬ ВИДИМОСТИ В РУТНОN
Любое обсуждение функций и циклов в
областей видимости
Python будет неполным без упоминания
(scope).
Эта тема может оказаться одной из самых сложных для понимания в про­
граммировании, поэтому здесь мы будем рассказывать о ней, не углубляясь
в детали. К концу этого раздела вы уясните, что такое область видимости
и почему она важна. Также вы узнаете о правиле
LEGB
для разрешения об­
ластей видимости.
Что такое область видимости?
Присваивая значение переменной, вы тем самым связываете значение с именем.
Имена уникальны. Например, невозможно присвоить одно имя двум разным
числам:
>>>
>>>
х
= 2
х
2
»>
»>
х
3
х
3
Когда вы присваиваете неременной х значение з, вы уже не сможете получить
значение
2
по имени х.
ГЛАВА
132
6
Функции и циклы
И такое поведение вполне логично. В конце концов, если бы переменная х имела
значения 2 и з одновременно, то как бы вы вычисляли х + 2? Сколько должно
получиться
- 4 или 5?
Оказывается, присвоить одному имени два разных значения все же возможно.
Просто это потребует некоторых ухищрений.
Откройте новое окно редактора в
х
IDLE и
введите следующую программу:
= "Hello, World"
def func():
х
=2
print(f"Inside 'func',
func()
print(f"Outside 'func',
х
х
has the value
has the value
{х}")
{х}")
В этом примере переменной х присваиваются два разных значения:
World"
в начале и
"Hello,
2 внутри func().
Вывод программы выглядит немного странно:
Inside 'func', х has the value 2
Outside 'func', х has the value Hello, World
Как получилось, что х все еще содержит значение
"Hello, World"
после вызова
func (),изменяющего значение хна 2?
Дело в том, что func() имеет другую область видимости, нежели код за предела­
func () можно присвоить объекту
func(), и Python будет хранить эти
ми этой функции. Другими словами, внутри
такое же имя, как у объекта за пределами
два объекта по отдельности.
Тело функции обладает так называемой локальной областью видимости с соб­
ственным набором доступных имен. Код за пределами тела функции принад­
лежит глобальной области видимости.
Область видимости можно рассматривать как набор имен, связанных с объ­
ектами. Когда вы используете в своем коде конкретное имя (например, имя
переменной или функции),
Python
проверяет текущую область видимости,
чтобы определить, существует ли такое имя.
Разрешение областей видимости
Области видимости образуют иерархию. Например, возьмем следующий
фрагмент:
6.6. Область видимости в Python
х
1 ЗЗ
= 5
def outer_func():
у
=3
def inner_func():
z =х + у
return z
return inner_func()
ПРИМЕЧАНИЕ
Функция inner_func() называется внутренней, потому что она определяется
внутри другой функции. Оказывается, как и в случае с вложенными циклами,
функции также могут определяться внутри других функций!
О внутренних функциях можно больше узнать из статьи «lnner Functions-What
Are Тhеу Good For?>> (https://realpythoп.com/inner-functions-what-are-they-good­
for/) на сайте Real Python.
Переменная
z существует в локальной области видимости inner_ func (). Когда
Python выполняет строку z =х +у, сначала осуществляется поиск переменных х
и у в локальной области видимости. Ни одна из них в локальной области ви­
димости не существует, поэтому
Python переходит вверх к области видимости
outer_func().
Область видимости outer_func() является внешней по отношению к inner_
func (). Это все еще не глобальная область видимости, но и не локальная
inner_func( ). Она находится посередине между этими двумя областями.
Переменная у определена в области видимости
outer_func( ), и ей присвое­
но значение з . Тем не менее х в этой области видимости не существует, по­
этому
Python
снова перемещается вверх к глобальной области видимости.
Здесь обнаруживается имя х со значением 5. Итак, именах и у успешно раз­
решены, и Python может выполнить строку z = х +у, которая присваивает z
значение
8.
Правило
LEGB
Чтобы запомнить, как в
Python происходит разрешение областей видимости,
LEGB. Это сокращение (Locai, Enclosing,
Global, Built-in) описывает порядок, в котором Python производит поиск имен
можно воспользоваться правилом
по областям видимости.
134
ГЛАВА
Функции и циклы
6
Краткое описание поможет вам запомнить, как работает процесс поиска.
1.
Локальная
(Local):
локальная, или текущая, область видимости может
быть телом функции или областью видимости верхнего уровня файла
с кодом. Она всегда представляет область видимости, в которой интер­
претатор
2.
Внешняя
Python работает в
(Enclosing):
настоящий момент.
внешняя область видимости находится на один
уровень выше локальной. Если локальная область видимости соответ­
ствует внутренней функции, то внешней становится область видимости
внешней функции. Если область видимости соответствует функции
верхнего уровня, то внешняя область видимости соответствует гло­
бальной.
3.
Глобальная
(Global): глобальная область видимости находится на самом
верхнем уровне иерархии. Она содержит все имена, определенные в коде,
которые не содержатся в теле функции.
4.
Встроенная
(Built-in): встроенная область видимости содержит все имена,
Python (например, ключевые слова). Такие функции, как
встроенные в
round () и abs (), принадлежат встроенной области видимости. Все, что
вы можете использовать, не определяя самостоятельно, содержится во
встроенной области видимости.
Области видимости
-
тема, сложная для понимания. Чтобы усвоить эту кон­
цепцию, вам потребуется некоторая практика. Не волнуйтесь, если с первого
раза вам не удалось в ней разобраться. Просто продолжайте практиковаться
и используйте правило
LEGB.
Нарушение правил
Как вы думаете, что выведет следующий код?
total = 0
def add_to_total(n):
total = total + n
add_to_total(5)
print(total)
Программа должна вывести значение 5, не так ли? Попробуйте запустить ее
и посмотрите, что произойдет.
6.6. Область видимости в Python
135
А происходит нечто неожиданное! Вы получите ошибку:
Traceback (most recent call last):
File "C:/Users/davea/stuff/python/scope.py", line 6, in <module>
add_to_total(S)
File "C:/Users/davea/stuff/python/scope.py", line 4, in add_to_total
total = total + n
UnboundLocalError: local variaЫe 'total' referenced before assignment
LEGB, Python должен был понять, что имя total
Минутку! Согласно правилу
не существует в локальной области видимости
add_to_total( ), и перейти к гло­
бальной области видимости для разрешения имени.
Проблема в том, что код пытается выполнить присваивание переменной
total,
что приводит к созданию нового имени в локальной области видимости. Затем,
когда Python выполнит правую часть присваивания, он найдет в локальной об­
ласти видимости имя
total,
которому еще ничего не присвоено.
Подобные ошибки весьма коварны. Это одна из причин для использования
уникальных имен переменных и функций независимо от того, какой области
видимости они принадлежат.
Проблему можно обойти при помощи ключевого слова
total
=
global:
0
def add_to_total(n):
global total
total = total + n
add_to_total(S)
print(total)
На этот раз выводится ожидаемый результат
Строка
5. Почему?
global total сообщает Python о том, что имя total следует искать
total = total + n не создает
в глобальной области видимости. И тогда строка
новую локальную переменную.
И хотя это «исправляеТ» программу, использование ключевого слова
global
обычно считается признаком плохого стиля.
Если вы обнаружите, что вам приходится использовать global для решения
подобных проблем, остановитесь и подумайте: нельзя ли переписать ваш код
по-другому? Часто оказывается, что это возможно!
ГЛАВА 6
136
6.7.
Функции и циклы
ИТОГИ И ДОПОЛНИТЕЛЬНЫЕ РЕСУРСЫ
В этой главе вы познакомились с двумя основополагающими концепциями
программирования: функциями и циклами.
Сначала вы научились определять собственные функции. Как было показано,
функция состоит из двух частей.
1.
Сигнатура функции, обозначаемая ключевым словом def, включает имя
функции и параметры функции.
2.
Тело функции содержит код, который выполняется при каждом исполь­
зовании функции.
Функции помогают избежать дублирования одинакового кода в программе;
фактически они создают фрагменты, пригодные для повторного использования.
Они упрощают чтение и сопровождение вашего кода.
Далее вы познакомились с двумя разновидностями циклов
1.
2.
Цикл
while
Python.
повторяет код, пока заданное условие остается истинным.
Циклы for повторяют код для каждого элемента в заданном наборе объ­
ектов.
И, наконец, вы узнали, что такое область видимости и как в
разрешение имен по схеме
Python организовано
LEGB.
ИНТЕРАКТИВНЫЙ ТЕСТ
К этой главе прилагается бесплатный интерактивный тест для проверки усво­
енных вами знаний. Тест доступен на телефоне или компьютере:
realpython.com!quizzes!pybasics-funetions-loops
Дополнительные ресурсы
За дополнительной информацией обращайтесь к следующим ресурсам:
•
«Python 'while' Loops (Indefinite Iteration)» (https.j/realpython.com/
python-while-loop/)
•
«Python 'for' Lo~ps (Definite Iteration)» (https.j/realpython.com/pythonfor-loop/)
ГЛАВА
7
Поиск и исправление
ошибок в коде
Все совершают ошибки
IDLE
-
даже опытные профессиональные разработчики!
неплохо справляется с выявлением синтаксических ошибок и ошибок
времени выполнения, но существует третья разновидность ошибок, с которой
вы, возможно, уже сталкивались. Логические ошибки происходят тогда, когда
в целом правильная программа делает не то, что предполагал ее создатель.
Логические ошибки приводят к непредвиденному поведению программы.
Устранение ошибок называется отладкой, а специальный инструмент, который
помогает находить последствия логических ошибок и понять причины их воз­
никновения,
-
отладчиком.
Умение находить и исправлять ошибки в коде
-
тот навык, которым вы будете
пользоваться на протяжении всей карьеры программиста!
В этой главе вы:
•
научитесь использовать окно
Debug Control в IDLE;
•
отработаете навыки отладки на примере функции, содержащей ошибки.
Итак, за дело!
7.1.
ИСПОЛЬЗОВАНИЕ ОКНА
Основным интерфейсом отладчика
DEBUG CONTROL
IDLE является окно Debug Control, которое
Debug. Чтобы открыть окно Debug,
далее для краткости я буду называть окном
выберите команду Debug ~ Debugger в меню интерактивного окна.
Каждый раз, когда окно
Debug открыто, в интерактивном окне рядом с пригла­
шением выводится сообщение [DEBUG ON]. Оно показывает, что отладчик активен.
ГЛАВА 7
138
Поиск и исправление ошибок в коде
ВАЖНО!
Если команда Debug отсутствует в строке меню , щелкните на интерактивном
окне , чтобы передать ему фокус ввода .
Теперь откройте новое окно редактора и разместите три окна на экране, чтобы
они были видны одновременно.
В этом разделе вы узнаете, как устроено окно
Debug, научитесь выполнять код
в отладчике по строкам, а также устанавливать точки останова для ускорения
процесса отладки.
Окно Debug Control: обзор
Чтобы понять, как работает отладчик, начнем с простой программы, не содер­
жащей ошибок. Введите следующий код в окне редактора:
for i in range(l, 4):
j = i
*
2
print(f"i is {i} and j is {j}")
Сохраните файл, оставьте окно
Debug
открытым и нажмите FS. Выполнение
длится недолго.
Окно
Debug выглядит
примерно так:
[t. Debug Control
D
17 Stack
Г
Source
Г
Globals
х
Go Step Over Out Quit
t----'---'---'--~~г-
Locals
debug_example.py: 1: <module>O
'Ьdb'.run Q,
line 585: exec(and, globals, locals)
!> ·__111а111 •.'.<morJule>Q, lirie 1. lor 1 in range{1. 4)
v
Locals
_annotations_ {)
'Ьu iltins'
{built· in)>
_ builtins_
<module
_ doc_
None
_file_
'C:/Users/ davea/Desktop/debug_example.py'
_ loader_
<dass '_frozen_importlib.Builtinlmporter' >
_ name_
'_ main_ '
_package_
None
_ spec_
None
7.1 . Использование окна Debug Control
139
Обратите внимание: на панели Stack в верхней части окна выводится следующее
сообщение:
>
·~main~'.<module>(),
line
1:
for i in range(l, 4):
1 (содержащая
Оно указывает, что строка
код
for i
iп
range(l, 4):) готова
.module() этого
к выполнению, но еще не начала выполняться. Часть' _main_'
сообщения указывает на тот факт, что вы сейчас находитесь в основном разделе
программы
в отличие от, допустим, определения функции перед основным
-
блоком кода.
Под панелью
Stack располагается панель Locals,
ные слова вроде
где перечислены всякие стран­
_annotations_, _buil tins_, _doc_ и
т. д. Это внутренние
системные переменные, на которые пока можно не обращать внимания . При
выполнении программы переменные, объявленные в коде, будут отображаться
в этом окне, чтобы вы могли отслеживать их значения .
В левом верхнем углу окна
и
Quit.
Debug расположены пять кнопок: Go, Step, Over, Out
Они управляют перемещением отладчика по вашему коду.
Сейчас мы исследуем назначение каждой из этих кнопок, начиная со
Кнопка
Step.
Step
Щелкните на кнопке
Step в левом верхнем углу окна Debug. Окно Debug не­
много изменится, теперь оно выглядит примерно так:
ШJ, Debug Control
о
Stack
Г
Source
locals
debug_example.py:2: <module >O
Г
Globals
Р"
х
Go Step Over Out Quit
r-~-~-~-~~г-
'Ьdb".runQ, line 585: exec(cmd, globals, locals)
!> '_ main_' <module>Q, l111e 2. J = 1 • 2
•
v
locals
_ annotations_ {}
'Ьuiltins'
(built-in) >
_ builtins_
<module
_ doc_
None
_ file_
'C:/Users/davea/Deskto p/debug_example.py'
_ loader_
<class '_frozen_importlib.Builtinlmpo rter' >
_ name_
_ main_
_package_
None
_spec_
None
140
Поиск и исправление ошибок в коде
ГЛАВА 7
Здесь стоит обратить внимание на два изменения. Во-первых, сообщение на
панели
>
Stack заменяется
·~main~'.<module>(),
В этой точке строка
следующим:
line 2: j
1 вашего
=
i
*
2:
кода была выполнена, а отладчик остановился
непосредственно перед выполнением строки
Во-вторых, на панели
Locals
2.
появилась новая переменная
i, которой присвое­
но значение 1. Это объясняется тем, что цикл for в первой строке кода создал
переменную
i и присвоил ей значение 1.
Продолжайте нажимать кнопку
Step,
чтобы выполнить код в пошаговом ре­
жиме, и понаблюдайте за происходящим в окне отладчика. Когда выполнение
достигнет строки
print(f"i is {i} and j is {j}" ),
можно увидеть, что результаты
выводятся в интерактивном окне по частям.
Что еще важнее, вы можете отслеживать растущие значения i и j в процессе
перебора в цикле for. Наверное, вы представляете, как полезно это может быть
при поиске ошибок в программе. Информация о значении каждой переменной
в каждой строке кода поможет понять, где что-то пошло не так.
Точки останова и кнопка
Go
Часто вы чувствуете, что ошибка находится в конкретной части вашего кода,
но не знаете, где именно. Вместо того чтобы щелкать на кнопке
Step с
утра до
ночи, можно установить точку останова. Тем самым вы приказываете отладчику
выполнять код, пока не будет достигнута точка останова.
Точки останова сообщают отладчику, когда следует приостановить выполне­
ние, чтобы вы могли проанализировать текущее состояние программы. Так
что на самом деле выполнение программы не прерывается, а только приоста­
навливается.
Чтобы установить точку останова, щелкните правой кнопкой мыши (Сtгl+щелчок
на Мае) на той строке кода в окне редактора, где вы хотите приостановить вы­
полнение, и выберите команду
цветом
-
Set Breakpoint. IDLE
выделяет строку желтым
это признак установленной точки останова. Чтобы удалить точку
останова, щелкните правой кнопкой мыши на строке с точкой останова и вы­
берите команду
Clear Breakpoint.
Нажмите кнопку
Quit в
верхней части окна
Debug,
чтобы временно отключить
отладчик. Окно при этом не закроется, и вам стоит и далее держать его откры­
тым, потому что вскоре мы к нему вернемся.
7.1. Использование окна Debug Control
Установите точку останова в строке кода с командой
print( ).
141
Окно редактора
должно выглядеть примерно так:
!} debug_example.py - C:/Users/davea/Deskt."
Eile .Edit F2rmat Run Qp'tions Window tlelp
for i in range(l, 4):
j = i * 2
print (f"i i3 {i} and j i3 {j}
о
х
")1
"
Ln: 3 Col: 35
Сохраните и запустите файл. Как и прежде, панель Stack окна
Debug показы­
1. Щелкните на
Debug.
вает, что отладчик запустился и ожидает выполнения строки
кнопке Go и понаблюдайте за тем, что происходит в окне
etr Debug Control
о
х
r;; Stack Г Source
Go Step Over Out Quit
_....._..____,1;7 locals
1--~-_....._
debug_example.py:З:
Г
Globals
<module>O
bdb'.runQ, line 585: exec(cmd, globals, locals)
!> '_main_.' <nюdule'O. l111e 3 rmnt(f"t is {t} a11d J is U}")
locals
_ annotations_ {}
'Ьuiltins'
_ builtins_
<module
_ doc_
None
_file_
'C:/Users/davea/Desktop/debug_example.py'
(built-in)>
_ loader_
<class 'Jrozen_importlib.Builtinlmporter'>
_ name_
'_ main_ '
_package_
None
_ spec_
None
2
На панели Stack теперь выводится сообщение о том, что ожидается выполнение
строки
3:
> '_main_ ' .<module>(), line 3: print(f"i is {i} and j is {j}")
На панели
Local мы видим, что переменные i
и
j теперь содержат значения 1 и 2
соответственно. Щелкнув на кнопке Go, вы приказываете отладчику выполнять
Поиск и исправление ошибок в коде
ГЛАВА 7
142
код, пока не будет достигнута точка останова или конец программы. Снова на­
жмите
Go.
Окно
Debug теперь выглядит так:
Gfr Debug Control
о
Р'
Stack
Г
Source
1--~-~-~-~~Р'
locals
Г
Globals
х
Go Step Over Out Quit
debug_example.py:З:
<module>O
'Ьdb'.runQ,
line 585: exec(cmd, globals, locals)
> '_mан1_'. <niodt1le>Q, line 3: print(f"i 1s [1) and j is {j\")
locals
_ annotations_ {}
_ builtins_
<module 'builtins' (built-in) >
_ doc_
None
_ file_
'C:/Users/davea/Desktop/debug_example.py'
_ loader_
<class '_frozen_importlib.Builtinlmporter'>
_пате_
'_main_'
_package_
None
None
_ spec_
2
4
Вы видите, что изменилось? На панели
Stack выводится
то же сообщение, что
и прежде: оно указывает, что отладчик собирается снова выполнить строку
3.
i и j теперь содержат 2 и 4. В интерактивном окне также
выводится результат выполнения строки с командой print() при первом про­
Однако переменные
ходе цикла.
Каждый раз, когда вы нажимаете кнопку Go, отладчик выполняет код в обычном
режиме, пока не достигнет следующей точки останова . Так как точка останова
была установлена в строке
3,
находящейся внутри цикла
for, отладчик оста­
навливается в этой строке при каждом прохождении цикла.
Go в третий раз. Теперь i и j содержат значения з и 6. Как вы думаете,
Go еще один раз? Так как цикл for выполняется
только три раза, при следующем нажатии Go программа завершится.
Нажмите
что произойдет, если нажать
Кнопки
Кнопка
Over
Over
и
Out
работает как комбинация кнопок
Step
и
Go.
без захода в функцию или цикл. Если вы дошли кнопкой
Она выполняет код
Step
в отладчике до
7 .2. Исправление ошибок
143
какой-то функции, то вы сможете выполнить код этой функции без пошагово­
го прохождения всех ее строк кнопкой
Step.
Кнопка
Over переводит
вас прямо
к результату выполнения функции. А если вы уже находитесь_внутри функции
или цикла, кнопка
Out выполняет
оставшийся код в теле функции или цикла,
после 'чего приостанавливает выполнение.
В следующем разделе мы рассмотрим код, содержащий ошибку, и вы узнаете,
как исправить ее в
7.2.
IDLE.
ИСПРАВЛЕНИЕ ОШИБОК
Теперь, когда вы знаете, как работает окно
Debug Control, давайте рассмотрим
программу, содержащую ошибки.
В следующем коде определяется функция
в аргументе один строковый объект
add_underscores (),которая получает
word и возвращает новую строку с копией
word, где каждая буква окружена символами подчеркивания. Например, вызов
add_underscores( "python") должен возвращать "_p_y_t_h_o_n_".
Код с ошибкой:
def add_underscores(word):
new_word = "_"
for i in range(len(word)):
new_word = word[i] +
return new_word
phrase = "hello"
print(add_underscores(phrase))
Введите этот код в окне редактора, сохраните файл и запустите программу на­
жатием
FS.
Программа должна вывести
_h_e_l_l_o_, но вместо этого выводится
о_, то есть буква о, за которой следует один символ подчеркивания.
Если вы уже видите, в чем проблема этого кода, пока не исправляйте ее. Этот
раздел должен научить вас пользоваться отладчиком
IDLE для поиска ошибок.
ПРИМЕЧАНИЕ
Отладка может потребовать значительных усилий и времени, а ошибки бывают
коварными и трудноуловимыми.
Хотя в этом разделе рассматривается относительно простая ошибка, описан­
ная схема анализа кода и поиска ошибок остается неизменной и для более
сложных случаев.
ГЛАВА
144
7
Поиск и исправление ошибок в коде
Если же вы не опознали проблему, не огорчайтесь! К концу этого раздела вы
найдете ее и научитесь отыскивать похожие проблемы в рабочем коде.
Отладка
-
практический навык, и по мере накопления опыта вы научитесь раз­
рабатывать собственные приемы отладки. В этом разделе представлен простой
алгоритм из четырех шагов, который поможет вам на начальном этапе.
1.
Предположите, в какой части кода может находиться ошибка.
2.
Установите точку останова и проанализируйте код, выполняя проблемную
часть кода в пошаговом режиме с отслеживанием важных переменных.
Найдите строку кода с ошибкой (если она есть) и внесите изменения для
3.
решения проблемы.
Повторяйте шаги
4.
Шаг
1-3, пока код не заработает так, как ожидалось.
1. Предположите, где находится ошибка
Прежде всего следует определить часть кода, которая с большой вероятностью
содержит ошибку. Возможно, вам не удастся сразу найти точную позицию
ошибки, но обычно можно предположить, в какой части кода ее следует искать.
Обратите внимание: наша программа делится на две части
-
определение
функции (где определяется add_underscores ()) и основной блок кода, в кото­
phrase со значением "hello", а затем
add_underscores ( phrase).
ром определяется переменная
результат вызова
выводится
Взгляните на основную часть:
phrase = "hello"
print(add_underscores(phrase))
Как вы думаете, может ли проблема скрываться здесь? Маловероятно, не правда
ли? Все в этих двух строках кода выглядит нормально. А значит, проблема, воз­
можно, содержится в определении функции:
def add_underscores(word):
new_word = "_"
for i in range(len(word)):
new_word = word[i] +
return new_word
Первая строка кода внутри функции создает переменную
нием
"_".
new_word
со значе­
Здесь все хорошо, и мы можем заключить, что проблема возникла
где-то в теле цикла
for.
7.2. Исправление ошибок
145
Шаг 2. Установите точку останова
и проанализируйте код
-
Итак, вы определили, где предположительно находится ошибка. Установите
точку останова в начале цикла for, чтобы отследить в окне
Debug, что именно
происходит внутри кода.
(} squash_some_bugs.py - C:/Users/davea/Desktop/squash_so...
о
х
file fdit Format Bun Qptions Window J:!elp
def add_underscores(word):
new wot:d = " "
:for-1 in rADqe (О, len (word))
new_word = word[i) +
:I
return new word
phrase = "hello "
print(add_underscores(phrase))
v
Ln: 3 Col: 33
Откройте окно
Debug и
запустите файл. Выполнение прерывается в первой
строке, то есть в определении функции.
Нажмите кнопку Go, чтобы выполнить
Debug должно выглядеть примерно так:
код вплоть до точки останова. Окно
(} DeЬug Conttol
о
Go Step Over Out Quit
Р" Stack
1--_.__....__....___,~_. Р"
squash_some_bugs.py:З:
locals
х
Г Source
г;
Globals
add_underscoresQ
'ЬdЬ' .runQ, line 585: exec(cmd, globals, locals)
'_main_' .< module> О, line 8: print(add_underscores(phrase))
> '_ma1n_'.add_underscoresQ, line 3: for 11n range(O, len(\•JOrd)):
locals
new_word •_•
word
'hello '
В этой точке код приостанавливается непосредственно перед входом в цикл
for в функции add_underscores( ) . Обратите внимание: на панели Locals
отображаются две локальные переменные, word и new_word. В настоящее
время word содержит значение "hello", а new_word - значение"_", как и ожи­
далось.
146
ГЛАВА
Поиск и исправление ошибок в коде
7
Щелкните на кнопке Step, чтобы войти в цикл for. Окно
панели
Locals появляется
Debug изменяется, и на
новая переменная i со значением О.
х
о
[} Debug Control
17 Stack
Go Step Over Out Quit
_ ......._ _.__ __._ __._ _, 17 Locals
Г Source
Г
Globals
squash_some_bugs.py.4: add_underscoresO
'bdb'.runO. line 585: exec(cmd, globals, locals}
'_main_'. < module> О. line 8: print(add_underscores(phrase)}
> '_main_'.add_underscoresQ, line 4: ne.-1y1ord = \Vord[1) + "_'·
Locals
о
new_word '_'
'hello '
word
i - счетчик, используемый в цикле for. Значение i показывает, какая итерация
цикла выполняется в настоящий момент.
Щелкните на кнопке
переменная
new_word
Step
еще раз. Взглянув на панель
приняла значение
о
Go Step Over Out Quit
Гv Stack
! - - - ' - - - ' - - - ' - - ' - - - - ' Гv
вы увидите, что
"h_".
i:. DeЬug Control
squash_some_bugs.py:З:
Locals,
х
Г Source
Locals Г Globals
add_underscoresQ
'bdb' .runQ, line 585: exec(cmd, globals, locals)
'_main_'.<module>Q, line 8: print(add_underscores(phrase))
> '_main_·.add_underscoresQ, l1ne 3: for 1 in range(O, len("ord)):
Locals
о
new_word 'h_'
word
'hello •
Что-то не так. Изначально
"
new_word
содержит значение "_",а при второй ите­
"_h_". Если щелкнуть
new_word присваивается е_,
рации цикла переменная должна содержать значение
на кнопке
потом
Step
1_ и т. Д.
еще несколько раз, вы увидите, что
7.2. Исправление ошибок
147
Шаг 3. Найдите ошибку и попытайтесь
исправить ее
На данный момент можно заключить, что при каждой итерации цикла
переменная
new_word
перезаписывается следующим символом строки
и завершающим символом подчеркивания. Так как цикл
for
for
"hello"
содержит всего
одну строку кода, проблема наверняка находится здесь:
new_word = word[i] + "_"
Повнимательнее присмотритесь к этой строке. Она приказывает
Python полу­
чить следующий символ слова, присоединить к нему символ подчеркивания
и присвоить полученную строку переменной
new_word. Именно такое поведение
for.
вы наблюдали при пошаговом выполнении цикла
Чтобы исправить проблему, необходимо приказать
тенацию строки
кнопку Quit в окне
Debug, но пока не закрывайте окно.
и замените строку в цикле
new_word
Python
выполнить конка­
word[i] +"_"с существующим значением new_word. Нажмите
for
Откройте окно редактора
следующей:
new_word + word[i] + "_"
=
Шаг 4. Повторяйте шаги 1-3, пока ошибка
не исчезнет
Сохраните изменения в программе и снова запустите ее. В окне
кнопку
Go,
Debug нажмите
чтобы выполнить код до точки останова.
ПРИМЕЧАНИЕ
Если вы закрыли отладчик на предыдущем шаге без нажатия
вторном открытии окна
You
сап
Quit,
при по­
Debug может появиться следующая ошибка:
only toggle the debugger when idle
(Вы можете переключать отладчик только в режиме ожидания)
При завершении сеанса отладки всегда щелкайте на кнопке
Go
или
Quit,
или
у вас возникнут проблемы с его повторным открытием. Чтобы избавиться от
ошибки, необходимо закрыть и снова запустить IDLE.
Программа приостанавливается перед входом в цикл
Нажимайте кнопку
for
в
add_underscores( ).
Step и следите за тем, что происходит с переменной new_word
при каждой итерации. Успех! Все работает, как и предполагалось!
ГЛАВА 7
148
Поиск и исправление ошибок в коде
Первая попытка исправления ошибки оказалась успешной, так что повторять
шаги
1-3 больше не нужно.
Впрочем, так будет не всегда. Иногда процесс при­
ходится повторять многократно, прежде чем ошибку удастся исправить.
Альтернативные способы поиска ошибок
Работа с отладчиком может быть нетривиальной и долгой, но это самый надеж­
ный способ выявления ошибок в коде. Впрочем, отладчики доступны не всегда.
Системы с ограниченными ресурсами
«интернета вещей~
-
-
например, компактные устройства
часто не содержат встроенных отладчиков.
В таких ситуациях для поиска ошибок можно воспользоваться отладочным
выводом. Он использует
print()
для вывода на консоль текста, который сооб­
щает, где выполняется программа и в каком состоянии находятся переменные
программы в определенных точках кода.
Например, вместо отладки предыдущей программы в окне
чить следующую строку в конец цикла
print(f"i
=
{i}; new_word
=
for
в
Debug можно вклю­
add_underscores( ):
{new_word}")
Измененный код будет выглядеть так:
def add_underscores(word):
new_word = "_"
for i in range(len(word)):
new_word = word[i] + "_"
print(f"i = {i}; new_word
return new_word
{new_word}")
phrase = "hello"
print(add_underscores(phrase))
При выполнении файла в интерактивном окне выводится следующий результат:
i
i
i
i
i
о
= 0;
1·,
= 2·,
= 3;
= 4;
new_word
new_word
new_word
new_word
new_word
hе
-
11-
о_
-
Вывод показывает, какое значение принимает
цикла
for.
появилась в результате выполнения
программы.
new_word
при каждой итерации
Последняя строка, содержащая только символ подчеркивания,
print ( add_underscore ( phrase))
в конце
7.3. Итоги и дополнительные ресурсы
149
Просматривая эти результаты, можно прийти к тому же заключению, как и при
работе с окном
Debug.
Проблема в том, что new_word перезаписывается при
каждой итерации.
Отладочный вывод работает, но у него есть несколько недостатков по сравнению
с отладчиком. Прежде всего, вам приходится выполнять всю программу каждый
раз, когда вы захотите проанализировать значения переменных. Это может при­
вести к значительным затратам времени по сравнению с использованием точек
останова. Также придется не забывать об удалении вызовов функции print()
после завершения отладки!
Пример цикла в этом разделе неплохо поясняет процесс отладки, но это не
лучший пример питонического кода. Использование индекса i указывает на
то, что существует более удобный способ записи цикла. Одно из возможных
усовершенствований - прямой перебор символов word. Один из возможных
способов выглядит так:
def add_underscores(word):
new_word = "_"
for letter in word:
new_word = new_word + letter +
return new_word
Процесс переработки существующего кода для того, чтобы он был более на­
глядным, удобочитаемым и понятным или лучше соответствовал стандар­
там, установленным для команды, называется рефакторинzом. В этой книге
рефакторингу уделяется не много времени, но он очень важен для создания
профессионального кода.
7.3. ИТОГИ
И ДОПОЛНИТЕЛЬНЫЕ РЕСУРСЫ
В этой главе вы освоили работу с окном
Debug в IDLE. Я показал, как анализи­
ровать значения переменных, вставлять точки останова и использовать кнопки
Step, Go, Over и Out.
Вы немного потренировались в отладке функции, содержащей ошибку, для
чего применили четырехшаговый процесс выявления и устранения ошибок.
1.
Вырабатываем предположение, где находится ошибка.
2.
Устанавливаем точку останова и анализируем код.
3.
Ищем ошибку и пытаемся ее исправить.
4.
Повторяем шаги
1-3, пока ошибку не удастся исправить.
150
ГЛАВА
Отладка
-
способом
7
Поиск и исправление ошибок в коде
не только наука, но и искусство. И освоить ее можно только одним
-
побольше практиковаться!
Один из способов тренировки
-
открыть окно
Debug Control
и использовать
его для пошагового выполнения кода других упражнений и задач в книге.
ИНТЕРАКТИВНЫЙ ТЕСТ
К этой главе прилагается бесплатный интерактивный тест для проверки усво ­
енных вами знаний . Тест доступен на телефоне или компьютере:
realpython.com/quizzes! pybasics-debugging
Дополнительные ресурсы
За дополнительной информацией обращайтесь к следующим ресурсам:
статья
(https.j/realpython.com/python-
•
«Python Debugging With pdb1>,
debugging-pdb/)
•
«Python Debugging With pdb1>, видеокурс (https.j/realpython.com/courses/
python-debugging-pdb/)
ГЛАВА В
Условная логика
v
и управление программои
Почти весь код, встречавшийся вам в книге до сих пор, был безусловным. Дру­
гими словами, в коде не было вариантов выбора. Все строки кода выполнялись
в том порядке, в котором они были записаны, или в порядке вызова функций
с возможными повторениями внутри циклов.
В этой главе я научу вас пользоваться условной логикой для написания про­
грамм, выполняющих разные действия в зависимости от конкретных условий.
В сочетании с функциями и циклами условная логика позволяет писать сложные
программы, способные обрабатывать различные ситуации.
В этой главе вы научитесь:
•
сравнивать значения двух и более переменных;
•
использовать команды
if
для управления последовательностью выпол­
нения вашей программы;
и
•
обрабатывать ошибки конструкциями
•
11рименять условную логику для создания простых моделей.
try
except;
Итак, за дело!
8.1. СРАВНЕНИЕ ЗНАЧЕНИЙ
Условная логика основана на выполнении разных действий в зависимости от
того, истинно или ложно некоторое выражение, которое называется условием.
Эту идею используют не только компьютеры. Люди постоянно пользуются
условной логикой для принятия решений.
Например, в США по закону алкогольные напитки можно покупать только
с
21
года. Утверждение «если вам не менее
21
года, вы можете купить пиво»
ГЛАВА 8
152
Условная логика и управление программой
может рассматриваться как пример условной логики. Часть «если вам не менее
21
года» определяет условие (возраст не менее
21
года), которое может быть
либо ИСТИННЫМ, либо ложным.
В программировании условия часто задаются в форме сравнения двух зна­
чений
-
например, что одно значение больше другого или что два значения
равны. Для выполнения сравнения используется стандартный набор условных
знаков, называемых булевскими операторами сравнения; вероятно , многие из
них вам уже знакомы.
Булевские операторы сравнения перечислены в следующей таблице.
ОПЕРАТОР
СРАВНЕНИЯ
ПРИМЕР
смысл
>
а>Ь
а больше Ь
<
а<Ь
а меньше Ь
>=
а>= Ь
а больше или равно Ь
<=
а<= Ь
а меньше или равно Ь
!=
а!= Ь
а не равно Ь
а== Ь
а равно Ь
Термин «булевский» происходит от фамилии английского математика Джорджа
Буля, работы которого легли в основу современных вычислительных техно­
логий. В честь Буля условная логика иногда называется булевской логикой.
Существует фундаментальный логический тип данных
bool (сокращение от
который всегда принимает только одно из двух возможных значений.
Boolean),
В Python этим двум значениям присвоены
имена True и False:
»> type(True)
<class 'bool'>
»> type(False)
<class 'bool'>
Обратите внимание: и True, и False начинаются с буквы верхнего регистра.
Результат вычисления условия всегда является логическим значением:
>>> 1 == 1
True
>» 3 > 5
False
8.1. Сравнение значений
В первом примере, так как 1 равно 1, результат 1
== 1 равен True.
153
Во втором
примере з не больше 5, поэтому результат равен False.
ВАЖНО!
Распространенная ошибка при записи условий
присваивания
=
вместо
== для
- использование оператора
проверки двух значений на равенство.
К счастью, при обнаружении такой ситуации Pythoп выдает ошибку SyntaxErrorи вы узнаете об этом еще до запуска программы.
Булевские операторы сравнения удобно рассматривать как вопросы, относящи­
еся к двум значением: а
== Ь спрашивает, содержат ли а и Ь одинаковое значение.
! = Ь спрашивает, содержат ли а и Ь разные значения.
Аналогичным образом а
Условные выражения не ограничиваются сравнением чисел. Их также можно
использовать для сравнения других значений
>>> "а" ==
True
"а' 1
>>>
"Ь"
"а"
-
например, строк:
False
>>> "а" <
True
"Ь"
>>>
" Ь"
"а"
>
False
Последние два примера могут показаться кому-то странными. Как одна строка
может быть больше или меньше другой?
С числами операторы сравнения
< и > представляют понятия
«больше» и «мень­
ше», но в более общем смысле они представляют концепцию упорядочения .
В этом отношении "а"
< "Ь"
проверяет, что строка "а" предшествует строке "Ь".
Но как упорядочиваются строки?
В
Python
строки упорядочиваются лексикоrрафически
-
этот хитроумный
термин означает, что они следуют в том порядке, в котором идут в словаре . Та­
ким образом, можно считать, что "а"
< "Ь" проверяет, предшествует ли буква а
букве Ь в словаре.
Лексикографическое упорядочение также действует и для строк из двух и более
символов, для чего проверяется каждая порядковая буква строки:
ГЛАВА
154
8
Условная логика и управление программой
>>> "apple" < "astronaut"
True
>>> "beauty" > "truth"
False
Так как строки могут содержать и другие символы, кроме алфавитных, упорядо­
чение также должно распространяться и на эти символы. Каждому символу со­
ответствует число, называемое кодовым пунктом Юникода. Чтобы сравнить два
символа,
Python берет кодовые пункты этих символов и сравнивает два числа.
Мы не будем углубляться в подробности работы кодовых пунктов Юникода.
На практике операторы сравнения
< и > чаще
всего используются с числами,
а не со строками.
Упражнения
1.
Для каждого из следующих условных выражений предположите, какой
результат будет получен при их обработке
- True или False. Затем введите
их в интерактивном окне, чтобы проверить свои ответы:
1 <= 1
1 != 1
1 != 2
"good" != "bad"
"good" ! = "Good"
123 == "123"
2.
Для каждого из следующих выражений заполните пустые места ( обозна­
ченные_) соответствующим булевским оператором сравнения, чтобы
выражение давало результат
3
10
5
"jack" _
42
True:
4
"jill"
"42"
8.2. ДОБАВИМ
НЕМНОГО ЛОГИКИ
Кроме булевских операторов сравнения
Python использует специальные клю­
чевые слова (которые называются логическими операторами) для объединения
булевских выражений. Существуют три логических оператора
- and, or
и not.
Логические операторы используются для конструирования составных логиче­
ских выражений. По большей части их смысл аналогичен смыслу в естественном
языке, хотя в
Python действуют намного более точные правила их использования.
8.2. Добавим немного логики
Ключевое слово
155
and
Рассмотрим следующие утверждения.
1.
У кошек четыре ноги.
2.
У кошек есть хвост.
В общем случае оба эти утверждения истинны.
Если объединить эти два утверждения связкой and, то полученное утвержде­
ние «у кошек четыре ноги
and
у кошек есть хвост~> также является истинным.
Если же изменить смысл обоих утверждений на противоположный, составное
утверждение «у кошек не четыре ноги and у кошек нет хвоста~> будет ложным.
Даже если объединить истинное утверждение с ложным, составное утвержде­
ние будет ложным. Оба утверждения
- «у кошек четыре ноги and у кошек нет
- будут ложными.
хвоста» и «у кошек не четыре ноги and у кошек есть хвост»
Когда два утверждения, Р и
Q, объединяются связкой and, составное утвержде­
ние «Р and Q» будет истинным в том и только в том случае, если истинно как
Р, так и
Q.
Оператор and в
Python
работает точно так же. Четыре примера составных вы­
ражений с использованием
>>> 1 < 2 and 3 < 4 #
and:
Оба выражения истинны
(True)
True
Оба выражения истинны, поэтому их комбинация также истинна (True ).
>>> 2 < 1 and 4 < 3 #
False
Оба
выражения ложны
(False)
Оба выражения ложны, поэтому их комбинация ложна
>>> 1 < 2 and 4 < 3 #
False
Второе
выражение ложно
(False).
(False)
Выражение 1
ложна
< 2 истинно, но выражение 4 < З ложно, поэтому их комбинация
(False).
>>> 2 < 1 and 3 < 4 #
Первое выражение ложно
(False)
False
Выражение 2
ложна
<1
(False).
ложно, а выражение з
<4
истинно, поэтому их комбинация
ГЛАВА
156
Условная логика и управление программой
8
В следующей таблице приведена краткая сводка использования оператора
РЕЗУЛЬТАТ
КОМ&ИНАЦИR С AND
True
апd
and.
True
True
True апd False
False
False апd True
False
False апd False
False
Каждое из этих правил можно проверить в интерактивном окне:
»> True and True
True
»> True and False
False
>» False and True
False
»> False and False
False
Ключевое слово
or
Когда мы используем слово «ИЛИ»
ключающее или»
-
(or)
в быту, иногда подразумевается «ис­
то есть истинным может быть либо первый вариант, либо
второй, но не оба сразу.
Например, в утверждении «Я могу остаться
or я
могу уйти» используется «ис­
ключающее или»: нельзя одновременно остаться и уйти. Истинным может быть
только что-то одно.
Однако в языке
Python
говоря, если Р и
Q-
ключевое слово
or
исключающим не является. Иначе
два выражения, то выражение Р or
Q истинно в любой из
следующих комбинаций.
1.
р истинно.
2. Q истинно.
3.
Истинно как Р, так и
Q.
Рассмотрим несколько примеров со сравнениями чисел:
>>> 1
True
с
2 or 3
с
4
# Оба выражения истинны
(True)
8.2. Добавим немного логики
>>> 2 < 1 or 4 < 3
False
# Оба выражения ложны
»> 1 < 2 or 4 < 3 #
157
(False)
Второе выражение ложно
(False)
Первое выражение ложно
(False)
True
>>> 2 < 1 or 3 < 4 #
True
Обратите внимание: если хотя бы одна часть составного выражения равна True,
то, даже если другая часть равна
False,
результат все равно равен
True.
В следующей таблице приведена краткая сводка использования оператора
КОМ&ИНАЦИЯ С OR
РЕЗУЛЬТАТ
True orTrue
True
True or False
True
False or True
True
False or False
False
И снова все результаты можно проверить в интерактивном окне:
»> True or True
True
»> True or False
True
»> False or True
True
>» False or False
False
Ключевое слово
Ключевое слово
not
not
вычисляет логическое отрицание одного операнда:
ИСПОЛЬЗОВАНИЕ NОТ
РЕЗУЛЬТАТ
not True
False
not False
True
or.
158
ГЛАВА 8
Условная логика и управление программой
В этом также можно убедиться в интерактивном окне:
»> not True
False
»> not False
True
Следует помнить, что в сочетании с операторами сравнения (такими, как==)
not не всегда ведет себя так, как вы ожидаете.
Например,
not True == False
возвращает
True,
но
False == not True
выдаст
ошибку:
False
>» not True
True
>>> False == not True
File "cstdin>", line 1
False == not True
SyntaxError: invalid syntax
Это происходит из-за того, что
Python разбирает логические операторы в соот­
ветствии с приоритетом операторов, аналогично вычислениям с применением
арифметических операторов.
Приоритеты логических и булевских операторов в порядке убывания приве­
дены в следующей таблице. Операторы в одной строке обладают одинаковыми
приоритетами.
ПРИОРИТЕТЫ ОПЕРАТОРОВ (В ПОРЯДКЕ У&ЫВАНИЯ)
<, <=,
==,
>=, >
not
and
or
False == not True. Так как not обладает более
Python сначала пытается вычислить False == not,
Снова взгляните на выражение
низким приоритетом, чем==,
а это синтаксически некорректно.
Чтобы избежать ошибки
>>> False
True
==
(not True)
SyntaxError, заключите not True в круглые скобки:
8.2. Добавим немного логики
159
Группировка выражений в круглых скобках помогает прояснить, какие опе­
раторы принадлежат той или иной части составного выражения. Даже если
круглые скобки не обязательны, желательно использовать их, чтобы составные
выражения проще читались.
Построение сложных выражений
Ключевые слова and, or или not можно объединять с True и False для создания
более сложных выражений. Вот пример такого выражения:
True and not (1 != 1)
Как вы думаете, какое значение будет получено при его вычислении?
Чтобы узнать это, разобьем выражение, начиная с правой стороны. Выраже­
ние
1 ! = 1 дает False, так как значение 1 равно самому себе. Следовательно,
приведенное выражение можно упростить до следующего вида:
True and not (False)
Теперь
not ( False)
эквивалентно
not False,
то есть
True.
Следовательно, вы­
ражение можно упростить еще раз:
True and True
Наконец,
True and True
дает просто
становится понятно, что
True. Итак, несколько
True and not (1 ! = 1) дает True.
итераций
-
и нам
При работе со сложными выражениями лучше всего начать с самой сложной
части выражения и продвигаться к более простым.
Например, попробуем вычислить следующее выражение:
("А"
!=
"А")
or not (2 >=
З)
Начните с двух выражений в круглых скобках."А" ! ="А" дает результат
потому что значение "А" равно самому себе.
False,
2 >= 3 также дает False, потому
что 2 меньше з. В результате мы получим эквивалентное, но более простое
выражение:
(False) or not (False)
Так как
not обладает более высоким приоритетом, чем or, приведенное выше
выражение эквивалентно следующему:
False or (not False)
ГЛАВА 8
160
not False дает
Условная логика и управление программой
результат
True,
поэтому выражение можно упростить дополни­
тельно:
False or True
Наконец, поскольку любое составное выражение с or истинно, если истинно
хотя бы одно из выражений с двух сторон от or, можно заключить, что ("А" ! =
"А")
or not (2 >= 3)
дает результат
True.
В составном условии группировка выражений при помощи круглых скобок
упрощает чтение кода. Иногда круглые скобки даже необходимы для получения
ожидаемого значения.
Например, на первый взгляд можно предположить, что следующая команда
выведет
True, тогда как
на самом деле она возвращает
False:
>>> True and False == True and False
False
Дело в том, что оператор
этому
== обладает более высоким приоритетом, чем and, по­
Python интерпретирует выражение в форме True and ( False == True) and
False. Так как False == True дает False, это
False and False, что дает результат False.
выражение эквивалентно
Можно добавить круглые скобки, чтобы выражение давало результат
>>> (True and False)
True and
True:
== (True and False)
True
Логические операторы и булевские операторы сравнения не очень понятны
с первого раза, так что, если материал этого раздела вам не удастся усвоить
с первого раза,
-
не беспокойтесь!
После небольшой практики вы научитесь разбираться, что происходит в таких
выражениях, и строить собственные составные условные команды, когда они
вам потребуются.
Упражнения
1.
Определите, какой результат
(True
или
False)
будет получен при вы­
числении следующих выражений. Введите их в интерактивном окне
и проверьте свои ответы:
(1 <= 1) and (1 != 1)
not (1 != 2)
8.3. Управление последовательностью выполнения программы
161
("good" != "bad") or False
("good" != "Good") and not (1 == 1)
2.
Добавьте круглые скобки там, где необходимо, чтобы каждое из следую­
щих выражений давало результат True:
False == not True
True and False == True and False
not True and "А" == "В"
8.3. УПРАВЛЕНИЕ
ПОСЛЕДОВАТЕЛЬНОСТЬЮ
ВЫПОЛНЕНИЯ ПРОГРАММЫ
Вы узнали, как сравнивать значения булевскими операторами сравнения и стро­
ить условные команды с логическими операторами. Теперь добавим логику
в код, чтобы он мог выполнять разные действия для разных условий.
Команда
if
Python выполнить блок кода только в том случае, если
Команда i f приказывает
выполняется некоторое условие.
Например, следующая команда i f выводит 2 and 2
2 + 2 == 4
дает результат
is 4 в том случае, если условие
True:
if 2 + 2 == 4:
print("2 and 2 is 4")
Команда
i f, как и цикл while, состоит из трех частей.
1.
Ключевое слово
2.
Условие, за которым следует двоеточие.
3.
if.
Блок кода (с отступом), который выполняется в том случае, если условие
истинно.
В этом примере проверяется условие
выполнение команды if в
IDLE
2 + 2 == 4. Так как это выражение истинно,
выводит сообщение 2 and 2
Если условие ложно, например 2 + 2 == 5,
Python
is 4.
пропускает блок с отступом
и продолжает выполнение со следующей строки без отступа.
Так, следующая команда
if
ничего не выводит:
if 2 + 2 == 5:
print("Is this the mirror universe?")
ГЛАВА 8
162
Условная логика и управление программой
Вселенная, в которой условие 2 + 2 == 5 оказывается истинным, была бы до­
вольно странной!
ПРИМЕЧАНИЕ
При отсутствии двоеточия после условия в команде
if
выдается ошибка
SyntaxError:
>>> if 2 + 2 == 4
SyntaxError: invalid syntax
После выполнения блока с отступом в команде if
Python
продолжает выпол­
нять дальнейший код.
Пример:
grade
=
95
if grade >= 70:
print("You passed the class!")
print("Thank you for attending.")
Результат выглядит так:
You passed the class!
Thank you for attending.
(Вы сдали экзамен!
Спасибо за участие.)
Так как переменная
grade
содержит значение
95,
кода и выводит строку
grade >= 70 истинно
Python выполняет остаток
условие
и выводится строка "You passed the class ! ".Затем
"Thank you for attending."
Если изменить значение
grade на 40, результат будет выглядеть так:
Thank you for attending.
print ( "Thank you for attending. ") выполняется независимо от того,
grade значения 70 или нет, потому что она следует после
блока кода с отступом в команде if.
Строка
больше ли переменная
Студент не узнает о своей неудаче, если все, что он увидит, - сообщение "Thank
you for attending." Добавим другую команду i f, которая будет сообщать сту­
денту, что он не сдал экзамен, если его оценка grade меньше 70:
8.3. Управление последовательностью выполнения программы
grade
=
163
40
if grade >= 70:
print("You passed the class!")
if grade < 70:
print("You did not pass the class :(")
print("Thank you for attending.")
Теперь результат выглядит так:
You did not pass the class :(
Thank you for attending.
В естественном языке альтернативу можно было бы описать словом «иначе».
Например: «Если вы набрали
70 и
более баллов, экзамен сдан. Иначе экзамен
не сдан».
К счастью, существует ключевое слово, которое делает в
Python
то же, что
и слово «иначе».
Ключевое слово else
Ключевое слово
else
используется после команды
только в том случае, если условие команды
В следующем примере
if
if
для выполнения кода
ложно.
else используется для вывода сообщения о том, сдал ли
студент экзамен, тем самым уменьшая объем кода:
grade
=
40
i f grade >= 70:
print("You passed the class!")
else:
print("You did not pass the class :(")
print("Thank you for attending.")
Эти команды
i f и else читаются так: «Если значение grade не менее 70, вы­
вести сообщение "Vou passed the class ! " Иначе вывести сообщение "You did
not pass the class : ( ".
Обратите внимание: ключевое слово
else не имеет условия и за ним следует
else выполняет код при на­
двоеточие. Условие не нужно, потому что команда
рушении условия команды
i f.
164
ГЛАВА
8
Условная логика и управление программой
ВАЖНО!
При отсутствии двоеточия после ключевого слова
else Python выдаст ошибку
SyntaxError:
== 5:
print("Who broke my math?")
>>> if 2 + 2
else
SyntaxError: invalid syntax
В этом примере выводится следующий результат:
Vou did not pass the class :(
Thank you for attending.
else Python все равно выполнит
"Thank you for attending."
Даже при выполнении блока с отступом после
строку, которая выводит сообщение
Ключевые слова
i f и else хорошо работают совместно, если вы проверяете
условие, имеющее ровно два состояния. Однако иногда требуется проверить
три и более варианта. В таких случаях используется ключевое слово eli f.
Ключевое слово elif
Ключевое слово
elif
(сокращение от
else if)
может использоваться для до­
бавления дополнительных условий после команды i f.
Команды
elif,
как и
if,
состоят из трех частей.
1.
Ключевое слово
2.
Условие, за которым следует двоеточие.
3.
elif.
Блок с отступом, который выполняется в том случае, если условие ис­
тинно.
ВАЖНО!
При отсутствии двоеточия в конце команды elif выдается ошибка SyntaxError:
>>> if 2 + 2
==
5:
print("Who broke my math? " )
elif 2 + 2 == 4
SyntaxError: invalid syntax
8.3. Управление последовательностью выполнения программы
165
Следующая программа объединяет i f, eli f и else для вывода буквенной оценки
успеваемости студента:
grade = 85
# 1
if grade >= 90: # 2
print("You passed the class
elif grade >= 80: # 3
print("You passed the class
elif grade >= 70: # 4
prir1t ( "You passed the class
else: # 5
print("You did not pass the
print("Thanks for attending.")
with an
А.")
with
а в.")
with
а С.")
class : ( ")
#
6
Оба условия, grade >= 80 и grade >= 70, истинны, когда переменная grade равна
85, поэтому можно ожидать, что будут выполнены оба блока eli f в строках #3
и #4. Однако выполнен будет только первый блок, для которого проверяемое
условие окажется истинным. Все остальные блоки eli f и else пропускаются.
Программа выдает следующий результат:
You passed the class with
Thanks for attending.
а В.
Проанализируем работу кода поэтапно.
1.
В строке
2.
Условие
3.
Условие grade >= 80 истинно, поэтому выполняется блок под командой
elif в строке 3 и выводится сообщение "You passed the class with а в.".
4.
1 grade
присваивается значение
grade >= 90 ложно,
5.
поэтому команда
Команды eli f и else в строках
нено условие команды
elif
if
в строке
2 пропускается.
4 и 5 пропускаются, так как было выпол­
3.
в строке
Наконец, выполняется строка
atteпding.
85.
6
и выводится сообщение "Thanks for
".
Ключевые слова if, eli f и else принадлежат к числу наиболее часто исполь­
зуемых в
Python.
Они позволяют писать код, который по-разному реагирует
на различные условия.
Команда i f позволяет решать более сложные задачи, чем код без условной
логики. Команды
if
даже можно вкладывать друг в друга для реализации не­
имоверно сложной логики!
ГЛАВА
166
8
Условная логика и управление программой
Вложенные команды
if
while можно вкладывать друг в друга, также до­
пустимо вложить команду i f внутрь другой команды i f, чтобы создать сложную
По аналогии с тем, как циклы
структуру принятия решений.
Рассмотрим следующий сценарий. Два человека играют в спортивную игру.
Требуется определить победителя на основании счета и игры.
•
Если они играют в баскетбол, то побеждает игрок с большим счетом.
•
Если они играют в гольф, то побеждает игрок с меньшим счетом.
•
В любом виде спорта при равенстве счетов игра заканчивается вничью.
Следующая программа решает эту задачу с использованием вложенных команд
i f,
отражающих описанные выше условия:
sport = input ( "Enter а sport: ")
int(input("Enter player 1 score: "))
pl_score
p2_score = int(input("Enter player 2 score: "))
# 1
if sport.lower() == "basketball":
if pl_score == p2_score:
print("The game is а draw.")
elif pl_score > p2_score:
print("Player 1 wins.")
else:
print("Player 2 wins.")
# 2
"golf":
elif sport.lower()
if pl_score == p2_score:
print("The game is а draw.")
elif pl_score < p2_score:
print("Player 1 wins.")
else:
print("Player 2 wins.")
# 3
else:
print("Unknown sport")
Сначала программа предлагает пользователю ввести вид спорта и счета двух
игроков.
#1 выполняется в том случае, если переменная sport
содержит значение "basketball". Метод . lower () гарантирует, что такие ва­
рианты ввода, как "Basketball" или "BasketBall ",будут интерпретироваться
Команда i f в разделе
одинаково.
8.3. Управление последовательностью выполнения программы
167
Если счета обоих игроков равны, то игра завершается вничью. В противном
случае игрок с более высоким счетом побеждает.
Команда i f в разделе #2 выполняется в том случае, если переменная sport со­
держит значение
"golf".
Если счета равны, то игра завершается вничью. В про­
тивном случае игрок с меньшим счетом побеждает.
i f в разделе #3 выполняется в том случае, если переменная sport со­
"basketball" или "gol f". В этом
случае выводится сообщение "Unknown sport" (неизвестный вид спорта).
Команда
держит какое-то другое значение, отличное от
Результат выполнения программы зависит от входных значений. Пример вы­
полнения со вводом
"basketball":
Enter а sport: basketball
Player 1 score: 75
Player 2 score: 64
Player 1 wins.
А вот как выглядит результат с теми же счетами при вводе
"golf":
Enter а sport: golf
Player 1 score: 75
Player 2 score: 64
Player 2 wins.
Если указать любой другой вид спорта, кроме баскетбола или гольфа, программа
выводит сообщение Unknown sport.
В итоге возможно семь вариантов выполнения программы.
ВИД СПОРТА
СЧЕТ ДВУХ ИГРОКОВ
'Ъasketball"
p1_score == p2_score
'Ъasketba 11"
р 1_score
> p2_score
'Ъasketba ll"
р 1_score
< p2_score
"golf"
р 1_score ==
"golf"
р 1_sco re
> p2_sco re
"golf"
р 1_score
< p2_score
Любые другие
Любые комбинации
p2_score
Вложенные команды i f дают много возможных вариантов выполнения кода.
Если программа содержит более двух уровней вложенных команд i f, число
возможных вариантов стремительно возрастает.
ГЛАВА 8
168
Условная логика и управление программой
ПРИМЕЧАНИЕ
При слишком большой глубине вложения команд
if программа
значительно
усложняется и становится трудно предсказать поведение программы при
заданных условиях.
По этой причине использовать вложенные команды
if
обычно не рекомен­
дуется.
Давайте упростим приведенную выше программу, сократив число вложенных
команд
if.
Прежде всего, независимо от вида спорта, игра завершается вничью, если
pl_score равно p2_score. Таким образом, проверку равенства можно вывести
из вложенных команд i f для каждого вида спорта и преобразовать в одну
команду
if:
if pl_score == p2_score:
print("The game is а draw.")
elif sport. lower() == "basketball":
if pl_score > p2_score:
print("Player 1 wins.")
else:
print("Player 2 wins.")
eli f sport. lower() == "golf":
if pl_score < p2_score:
print("Player 1 wins.")
else:
print("Player 2 wins.")
else:
print("Unknown sport.")
Осталось только шесть возможных вариантов выполнения.
Впрочем , и это немало. Нельзя ли еще как-то упростить программу?
Одно из возможных решений: игрок
1 выигрывает при выполнении любого из
следующих условий.
1. sport содержит "basketball", а pl_score больше p2_score.
2. sport содержит "golf", а pl_score меньше p2_score.
8.3. Управление последовательностью выполнения программы
169
Это можно описать составными условными выражениями:
sport = sport.lower()
pl_wins_bball = (sport == "basketball") and (pl_score > p2_score)
pl_wins_golf = (sport == "golf") and (pl_score < p2_score)
pl_wins = playerl_wins_basketball or playerl_wins_golf
Код достаточно плотный, поэтому мы разберем его последовательно.
Сначала строка, присвоенная sport, преобразуется к нижнему регистру, чтобы
значение можно было сравнивать с другими строками, не беспокоясь о возмож­
ных ошибках, связанных с расхождениями в регистре символов.
В следующей строке находится структура, которая на первый взгляд кажется
немного странной. За оператором присваивания
ратором проверки равенства
==.
= следует выражение с
логическое выражение и присваивает результат переменной
(sport
опе­
Эта строка вычисляет следующее составное
== "basketball") and (pl_score
pl_wins_bball:
> p2_score)
Если sport содержит "basketball ", а pl_score больше p2_score, значение
pl_wins_bball истинно.
Затем аналогичная операция выполняется с переменной
sport
содержит
"golf",
а
pl_score
меньше
p2_score,
pl_wins_gol f. Если
pl_wins_golf
значение
истинно.
Наконец, переменная
или гольф, и
False
pl_wins содержит True, если игрок 1 выиграл в баскетбол
в противном случае.
С этими изменениями программу можно довольно серьезно упростить:
sport = sport.lower()
if pl_score == p2_score:
print("The game is а draw.")
elif (sport == "basketball") or (sport == "golf"):
pl_wins_bball = (sport == "basketball") and (pl_score > p2_score)
pl_wins_golf = (sport == "golf") and (pl_score < p2_score)
pl_wins = pl_wins_bball or pl_wins_golf
if pl_wins:
print("Player 1 wins.")
else:
print("Player 2 wins.")
else:
print("Unknown sport")
170
ГЛАВА
8
Условная логика и управление программой
В обновленной версии программы есть всего четыре варианта выполнения и код
получается более понятным.
Иногда без вложенных команд i f не обойтись. Но если вы заметите, что вам
приходится писать их слишком много, возможно, стоит остановиться и поду­
мать, как упростить код.
Упражнения
1.
Напишите программу, которая запрашивает у пользователя слово при
помощи функции iпput() и сравнивает длину слова с числом
5.
Про­
грамма должна вывести один из следующих результатов н зависимости
от длины пользовательского ввода:
"Длина ввода меньше
"Длина ввода больше
5
5
символов"
символов"
"Длина ввода составляет
2.
5
символов"
Напишите программу, которая выводит сообщение "Я загадал(а) число
от 1до10. Угадайте его". Получите число от пользователя при помощи
функции iпput (). Если пользователь вводит число
3, программа должна
вывести сообщение "Вы победили! ",а для любого другого ввода программа
выводит сообщение "Вы проиграли".
8.4. ЗАДАЧА: ПОИСК МНОЖИТЕЛЕЙ ЧИСЛА
Множителем положительного целого числа п называется любое положительное
целое число, меньшее либо равное п, на которое п делится без остатка.
Например, 3 является множителем 12, потому что при делении 12 на 3 полу­
чается 4 без остатка. С другой стороны, 5 множителем 12 не является, потому
что
12 делится
на
5
с остатком
Напишите программу
2.
f actors.py, которая запрашивает у пользователя положи­
тельное целое число, а затем выводит список множителей этого числа. Пример
запуска программы с выводом результата:
Enter а positive integer: 12
1 is а factor of 12
2 is а factor of 12
3 is а factor of 12
4 is а factor of 12
6 is а factor of 12
12 is а f actor of 12
8.5. Управление циклами
Подсказка: вспомните, о чем мы говорили в главе
171
оператор % может ис­
5, -
пользоваться для получения остатка от деления двух чисел.
Решение задачи, а также многие другие дополнительные ресурсы доступны
в интернете по адресу
realpython.com/python-basics/resources.
8.5. УПРАВЛЕНИЕ
В главе
ЦИКЛАМИ
6 я рассказывал, как выполнять блок кода в циклах for или while.
Циклы
удобны для выполнения повторяющихся операций и для обработки большого
количества разных входных значений по одной схеме.
В этом разделе речь пойдет о команде
if, вложенной в циклы for. Также я по­
знакомлю вас с двумя ключевыми словами,
break и continue, которые позволяют
управлять выполнением цикла.
Команды
if и циклы for
Блок кода в цикле
команда
for не отличается от любого другого блока. Это означает, что
i f может размещаться внутри цикла for точно так же, как и в любом
другом фрагменте кода.
В следующем примере цикл
for
с командой
if
используется для вычисления
и вывода суммы всех четных целых чисел, меньших
100:
sum_of_evens = 0
for n in range(101):
if n % 2 == 0:
sum_of_evens
sum_of_evens + n
print(sum_of_evens)
Сначала переменная
sum_of_evens
инициализируется значением
грамма в цикле перебирает числа от О до
к
sum_of_evens.
Итоговое значение
100
0.
Затем про­
и прибавляет четные значения
sum_of _evens
равно
2550.
break
Ключевое слово
break приказывает Python выйти из цикла. Другими словами,
цикл немедленно останавливается, а выполнение продолжается с кода, следу­
ющего после цикла.
ГЛАВА
172
8
Условная логика и управление программой
Например, следующий код в цикле перебирает числа от 0 до з, но цикл пре­
рывается при достижении числа
2:
for n in range(4):
if n == 2:
break
print(n)
print(f"Finished with n
=
{n}")
В результате выводятся только первые два числа:
0
1
Finished with n
2
continue
Ключевое слово continue пропускает весь оставшийся код в теле цикла и пере­
ходит к следующей итерации.
Например, следующий код перебирает числа от 0 до з и выводит каждое число,
кроме
2:
for i in range(4):
if i == 2:
continue
print(i)
print(f"Finished with i
=
{i}")
В вывод включаются все числа, кроме 2:
0
1
3
Finished with i
3
ПРИМЕЧАНИЕ
Всегда желательно присваивать переменным короткие, но содержательные
имена, по которым вы легко определите , что они должны представлять .
Буквы
i, j, k и n являются
исключениями , потому что они очень часто встреча ­
ются в программировании .
Эти буквы почти всегда используются для временных переменных с коротким
сроком жизни в качестве счетчиков в цикле .
8.5. Управление
Подведем итог: ключевое слово
циклами
173
break прерывает цикл при выполнении условия,
а ключевое слово coпtiпue пропускает оставшийся код текущей итерации цикла
при выполнении условия.
Циклы
for ... else
Хотя эта структура используется не так часто, циклы
чать секцию
else.
Python тоже могут вклю­
Рассмотрим пример:
phrase = "it marks the spot"
for character in phrase:
if character == "Х":
break
else:
print("There was по
Цикл
'Х'
in the phrase")
for в этом примере перебирает символы строки "it marks the spot" и оста­
навливается при обнаружении буквы "Х". Выполнив код примера, вы увидите,
что на консоль выводится сообщение
Теперь измените текст на "Х
There was по 'Х' iп the phrase.
marks the spot".
При выполнении того же кода
с новым текстом не будет выведено ничего. Что происходит?
Код в блоке else выполняется только в том случае, если предшествующий цикл
for завершился без выполнения команды break.
phrase = "it marks the spot" строка кода
break выполнена не будет, потому что фраза не содержит символа Х.
Вместо этого выполняется блок else и выводится строка "There was по 'Х' iп
the phrase".
Таким образом, при выполнении кода с
с командой
phrase = "Х marks the spot" будет
break; таким образом, блок else не будет вы­
С другой стороны, при выполнении кода с
выполнена строка с командой
полняться и вывод отсутствует.
В следующем примере пользователю предоставляются три попытки для ввода
пароля:
in range(З):
password = input("Password: ")
if password == "I<ЗBieber":
break
print("Password is incorrect.")
else:
print("Suspicious activity. The authorities have
for
п
Ьееп
alerted.")
ГЛАВА 8
174
Условная логика и управление программой
Программа перебирает числа от 0 до 2. При каждой итерации пользователю
предлагается ввести пароль. Если введенный пароль правилен, команда break
используется для выхода из цикла. В противном случае программа сообщает,
что пароль неправильный, и пользователь повторяет попытку.
После трех безуспешных попыток цикл
for завершается без выполнения строки
кода, содержащей break. В этом случае выполняется блок else и пользователь
получает сообщение о подозрительной активности .
ПРИМЕЧАНИЕ
Этот раздел мы посвятили циклам
for, потому что они используются чаще
других.
Тем не менее все упоминаемые возможности работают и в циклах
while. Дру­
гими словами, команды break и coпtinue можно выполнять и в циклах while.
Цикл
while
даже может иметь секцию
else!
Условная логика в теле цикла открывает новые возможности управления
выполнением кода. Цикл можно остановить ключевым словом
break или про­
пустить текущую итерацию ключевым словом continue. Можно даже сделать
так, что некоторый код будет выполняться только в том случае, если цикл был
завершен без выполнения команды break.
Это чрезвычайно мощные инструменты, которые пригодятся любому про­
граммисту.
Упражнения
1.
Используя команду
break, напишите программу, которая многократно
запрашивает у пользователя данные и завершается только тогда, когда
пользователь введет
2.
или
"Q".
Используя команду continue, напишите программу, которая перебирает
числа от
8.6.
"q"
1 до 50 и выводит все числа, не кратные 3.
ВОССТАНОВЛЕНИЕ ПОСЛЕ ОШИБОК
Ошибки в коде неприятны, но абсолютно нормальны! Ошибки случаются даже
у лучших программистов.
Программисты часто называют ошибки времени выполнения «исключениями».
Таким образом, когда вы сталкиваетесь с ошибкой, поздравьте себя! Вы заста­
вили свой код сотворить нечто исключительное.
8.6. Восстановление после ошибок
175
Чтобы создавать надежные программы, необходимо иметь возможность обраба­
тывать ошибки, вызванные некорректным пользовательским вводом или другим
непредсказуемым источником. В этом разделе я расскажу, как это делается.
Зоопарк исключений
Когда в вашей программе происходит исключение, полезно знать, что именно
пошло не так. В
Python
существует ряд встроенных типов исключений для
описания разных видов ошибок.
Некоторые уже встречались вам в этой книге. Для удобства я собрал их в этом
разделе, а также добавил к ним ряд новых представителей.
ValueError
Ошибка ValueError происходит, когда операция обрабатывает недействитель­
ное значение.
Например, попытка преобразования строки "not а number" в целое число при­
водит к ошибке ValueError:
>>> int("not а number")
Traceback (most recent call last):
File "cpyshell#l>", line 1, in cmodule>
int("not а number")
ValueError: invalid literal for int() with base 10: 'not
а
number'
Последняя строка выводит имя исключения и описание проблемы. Этот общий
формат используется для всех исключений
Python.
TypeError
Ошибка TypeError происходит при выполнении операции со значением не­
правильного типа. Например, попытка суммирования строки и целого числа
приведет к ошибке TypeError:
>>>"1"+2
Traceback (most recent call last):
File "cpyshell#l>", line 1, in cmodule>
"1" + 2
TypeError:
сап
only concatenate str (not "int") to str
NameError
Ошибка NameError происходит при попытке использования имени переменной,
которое не было определено в программе:
ГЛАВА
176
8
Условная логика и управление программой
>>> print(does_not_exist)
Traceback (most recent call last):
File "cpyshell#З>", line 1, in cmodule>
print(does_not_exist)
NameError: name 'does_not_exist' is not defined
ZeroDivisionError
Ошибка
ZeroDivisionError происходит в том случае, если делитель в операции
деления равен нулю:
»> 1 / 0
Traceback (most recent call last):
File "cpyshell#4>", line 1, in cmodule>
1 / 0
ZeroDivisionError: division
Ьу
zero
OverflowError
Ошибка OverflowError происходит, если результат арифметической операции
оказывается слишком большим. Например, при попытке возвести значение 2. 0
в степень
1_000_000 вы получите ошибку OverflowError:
>>> pow(2.0, 1_000_000)
Traceback (most recent call last):
File "cpyshell#б>", line 1, in cmodule>
pow(2.0, 1_000_000)
OverflowError: (34, 'Result too large')
В главе
5 мы уже говорили, что целые числа в Python обладают неограниченной
точностью. Это означает, что ошибки
OverflowError могут происходить только
2 в степень 1_000_000
с числами с плавающей точкой. Возведение целого числа
не приведет к ошибке
OverflowError!
Python приведен в официальной до­
(https.j/docs.python.org/3/library/exceptions.html).
Полный список встроенных исключений
кументации
Ключевые слова
try
и
except
Иногда можно предусмотреть возможность того или иного исключения. Вместо
того чтобы позволить программе аварийно завершиться, можно перехватить
ошибку в момент ее возникновения и попытаться скорректировать ее.
Например, можно предложить пользователю ввести целое число. Если он ука­
жет значение, не являющееся целым числом (например, строку "а"), следует
сообщить ему, что введено недопустимое значение.
8.6. Восстановление после ошибок
177
Для предотвращения аварийного завершения программы можно воспользо­
ваться ключевыми словами
try
и
except.
Рассмотрим пример:
try:
number = int(input("Enter ап integer: "))
except ValueError:
print("That was not ап integer")
try обозначает начало блока try, и после него ставится двое­
try, выполняется в программе. В данном
пользователю предлагается ввести целое число. Так как input () воз­
Ключевое слово
точие. Код с отступом, следующий за
случае
вращает строку, пользовательский ввод преобразуется в целое число вызовом
int(),
а результат присваивается переменной
number.
int () выдает
ValueError. В таком случае будет выполнен код с отступом, располо­
женный под строкой except ValueError. Таким образом, вместо аварийного
завершения программы выводится строка "That was not an integer". Если поль­
зователь введет допустимое целое значение, то код в блоке except ValueError
Если введенное значение не является целым числом, операция
ошибку
выполняться не будет.
Но при исключении другого типа (например,
TypeError)
программа аварийно
завершится. В приведенном выше примере обрабатывается только один тип
исключения
- ValueError.
Однако секция
except может обрабатывать сразу несколько типов исключений.
Для этого следует разделить имена исключений запятыми и заключить список
имен в круглые скобки:
def divide(numl, num2):
try:
print(numl / num2)
except (TypeError, ZeroDivisionError):
print("encountered а proЫem")
В этом примере
зультат деления
divide() получает
numl на num2.
два параметра,
numl
и
num2,
и выводит ре­
divide() вызывается со строковым аргументом, то операция деления вы­
TypeError. Кроме того, если аргумент num2 равен нулю, появляется
сообщение об ошибке ZeroDivisionError.
Если
дает ошибку
Строка
except (TypeError, ZeroDivisionError) обрабатывает оба исключения
"encountered а proЫem" при возникновении любого из двух
и выводит строку
исключений.
ГЛАВА 8
178
Условная логика и управление программой
Впрочем, во многих случаях полезно перехватывать ошибки по отдельности; это
позволит вывести более осмысленный текст. Для этого после блока try следует
определить несколько блоков
except:
def divide(numl, num2):
try:
print(numl / num2)
except TypeError:
print("Both arguments must Ье numbers")
except ZeroDivisionError:
print("num2 must not Ье 0")
В этом примере ошибки TypeError и ZeroDivisionError обрабатываются по от­
дельности. Это позволяет вывести более точное сообщение, если что-то пойдет
не так.
Если одно из значений
numl
или
num2
не является числом, то программа выдает
ошибку TypeError и выводит сообщение "Both arguments must Ье numbers ". Если
значение num2 равно нулю, выдается ошибка ZeroDivisionError и выводится
сообщение "num2 must not Ье 0".
except без исключения
Ключевое слово
except также может использоваться само по себе, без указания
конкретных исключений:
try:
#
Много потенциально опасных операций
except:
print("Something bad happened!")
Если при выполнении кода в блоке try произойдет любое исключение, будет вы­
полнен блок except и на экране появится сообщение "Something bad happened ! "
Казалось бы, это отличный способ предотвратить аварийное завершение ваших
программ. Но на самом деле поступать так не рекомендуется!
Для этого есть несколько причин, но начинающим программистам важно звать,
что перехват всех исключений скроет ошибки в вашем коде и создаст ложное
чувство уверенности в том, что он работает, как ожидалось.
Если вы будете перехватывать только конкретные исключения, то при обна­
Python выведет трассировку и подробности
- и вы получите больше информации для отладки вашего кода.
ружении непредвиденных ошибок
ошибки
8.7. Моделирование событий и вычисление вероятностей
179
Упражнения
1.
Напишите программу, которая предлагает пользователю ввести целое
число. Если пользователь ввел что-то другое, программа должна пере­
хватить ошибку ValueError и вывести сообщение "Try again."
Когда пользователь задаст целое число, программа должна вывести это
число и завершить работу без сбоев.
2.
Напишите программу, которая предлагает пользователю ввести строку
и целое число
n, а затем выводит символ с индексом n во введенной строке.
Используйте механизм обработки ошибок для того, чтобы предотвратить
аварийное завершение программы, если пользователь задаст что-то кроме
целого числа или если индекс выходит за границы массива. Программа долж­
на выводить разные сообщения об ошибках в зависимости от типа ошибки.
8.7. МОДЕЛИРОВАНИЕ СОБЫТИЙ
И ВЫЧИСЛЕНИЕ ВЕРОЯТНОСТЕЙ
В этом разделе я покажу, как применить некоторые концепции циклов и услов­
ной логики, о которых мы рассказали ранее, при решении реальной задачи
-
моделировании событий и вычислении вероятностей.
Мы проведем простое моделирование, известное под названием эксперимента
Монте-Карло. Каждый эксперимент состоит из испытания
-
некоторого процесса,
который можно повторить (например, броска монеты). Каждое испытание генери­
рует результат (например, падение монеты орлом или решкой вверх). Испытание
повторяется многократно для вычисления вероятности того или иного исхода.
Для программирования этой схемы необходимо включить в код источник
случайности.
Модуль
Python
random
предоставляет набор функций для генерирования случайных чисел
в модуле random. Стандартная библиотека
Python -
организованная коллекция
модулей, которую можно импортировать в ваш код для решения различных задач.
Чтобы импортировать модуль random, введите следующую команду винтер­
активном окне
IDLE:
>>> import random
Теперь вы можете использовать функции из модуля
random в своем коде.
180
ГЛАВА 8
Условная логика и управление программой
ПРИМЕЧАНИЕ
Модули и команды
import более подробно рассматриваются в главе 11 «Мо ­
дули и пакеты».
Функция
randint()
модуля
random имеет два обязательных параметра а
и Ь, оба
должны быть целыми числами. Функция возвращает случайное целое число,
которое больше или равно а, но меньше или равно Ь. Например, следующий код
создает случайное целое число от
1 до 10:
>>> random.randint(l, 10)
9
Так как функция возвращает случайный результат, скорее всего, вы получите
другой вывод вместо
9.
Если снова выполнить тот же код, он с большой веро­
ятностью вернет иное число.
Так как функция
randint()
При использовании
random, для ее использования
random и точку.
принадлежит модулю
перед именем необходимо указать имя модуля
randint ()
важно помнить, что оба параметра, а и Ь, должны
быть целыми числами, а вывод равен а, Ь или любому числу между ними. На­
пример, random.randint(0, 1) возвращает одно из двух случайно выбранных
чисел
- 0 или 1.
Все целые числа в диапазоне от а до Ь будут возвращаться randint() с равной
вероятностью. Таким образом, для randint(l, 10) все целые числа от 1до10
будут возвращаться с вероятностью
вращения О составляет
10%. Для randint(0, 1)
вероятность воз­
50%.
Симметричная монета
Воспользуемся функцией randint () для моделирования бросков симметрич­
ной монеты. Под симметричной понимается монета, при броске которой орел
и решка выпадают с равной вероятностью.
Одним испытанием в эксперименте будет один бросок монеты. В результате
должен быть получен либо орел, либо решка. Вопрос в том, каким окажется
соотношение падения монеты орлом и решкой вверх при очень большом ко­
личестве бросков монеты.
Давайте подумаем, как решать эту задачу. Необходимо хранить информацию
о том, сколько раз выпали орел или решка; для этого потребуются два счетчика.
Каждое испытание состоит из двух фаз.
8.7. Моделирование событий и вычисление вероятностей
1.
2.
181
Бросить монету.
Если монета упала орлом вверх, обновить счетчик орлов. Если монета
упала решкой вверх, обновить счетчик решек.
Испытание повторяется много раз
-
операций хорошо подходят циклы
for
допустим, десять тысяч. Для подобных
по диапазону
range(10_000).
Итак, план готов. Теперь можно написать функцию
coin_flip( ), которая слу­
чайным образом возвращает строку "heads" или "tails". Эта задача решается
вызовом random. randint(0, 1). Значение 0 интерпретируется как орел ("heads"),
а 1 - как решка ("tails").
Код функции
coin_flip( ):
import random
def coin_flip():
"""Возвращает случайно выбранную строку
if random.randint(0, 1)
return "heads"
else:
return "tails"
==
'heads'
или
'tails' ."""
0:
Если
random. randint(0, 1) возвращает 0, функция coin_flip()
"heads". В противном случае coin_flip() возвращает "tails".
возвращает
Теперь можно написать цикл for, который десять тысяч раз моделирует бро­
сок монеты и обновляет соответствующие счетчики падения монеты орлом
и решкой:
#
Счетчики
инициализируются
нулями
heads_tally = 0
tails_tally = 0
for trial in range(10_000):
if coin_flip() == "heads":
heads_tally
heads_tally + 1
else:
tails_tally
tails_tally + 1
Сначала создаются две переменные-счетчики
heads_tally и tails_tally,
кото­
рые инициализируются целым числом О.
Затем цикл
for выполняется десять тысяч раз, при каждой итерации вызы­
coin_flip(). Если функция coin_flip() вернула строку "heads", то
переменная heads_tally увеличивается на 1. В противном случае tails_tally
вается
увеличивается на
1.
182
ГЛАВА 8
Условная логика и управление программой
Наконец, программа выводит соотношение падения монеты орлом и решкой:
ratio = heads_tally / tails_tally
print(f"The ratio of heads to tails is {ratio}")
Сохранив этот код в файле и выполнив его несколько раз, вы увидите, что
результат обычно оказывается в интервале от
range(10_000)
близятся к
в цикле
for,
допустим, до
0,98
до
1,02.
range(50_000),
Если увеличить
то результаты при­
1,0.
Такое поведение выглядит разумно. Так как монета симметрична, следует ожи­
дать, что после множества бросков количество падений монеты орлом вверх
будет примерно равно количеству падений решкой вверх.
Однако в жизни идеальная симметрия встречается редко. Монета может быть
слегка деформирована, из-за чего чаще будет выпадать орел (или, наоборот,
решка). Как же смоделировать такую несимметричную монету?
Несимметричные монеты
randint() возвращает 0 или 1 с равной вероятностью. Если 0 пред­
Функция
ставляет решку, а
1 -
орла, то для моделирования несимметричной монеты
необходимо повысить вероятность возвращения 0 или 1.
Функция
random() вызывается без аргументов и возвращает число с плавающей
точкой, большее или равное 0. 0, но меньшее 1. 0. Все возвращаемые значения
равновероятны. В теории вероятностей это называется равномерным распре­
делением.
Одно из следствий равномерного распределения заключается в том, что для за­
n в интервале от О до 1 вероятность того, что random() вернет число
меньше n, равна n. Например, вероятность того, что random() вернет значение мень­
ше 0.8, равна 0.8, а вероятность того, что результат random() меньше 0.25, равна 0.25.
данного числа
Используя этот факт, можно написать функцию, которая моделирует бросок
монеты, но возвращает решку с заданной вероятностью:
import random
def unfair_coin_flip(probability_of_tails):
if random.random() < probability_of_tails:
return "tails"
else:
return "heads"
Например,
unfair_coin_flip (. 7)
вернет
"tails"
с вероятностью
70%.
8.8. Задача: моделирование эксперимента с броском монеты
183
Перепишем программу, созданную ранее, используя в каждом испытании не­
симметричный бросок
unfair_coin_flip( ):
heads_tally = 0
tails_tally= 0
for trial in range(10_000):
i f unfair_coin_flip(.7) == "heads":
heads_tally = heads_tally + 1
else:
tails_tally = tails_tally + 1
ratio = heads_tally / tails_tally
print(f"The ratio of heads to tails is {ratio}")
После многократного моделирования вы увидите, что соотношение падений
монеты орлом к падениям решкой, которое было равно 1 в эксперименте с сим­
метричной монетой, составляет около
Итак, вы познакомились с функциями
0. 43.
randint()
и
random()
из модуля
random
и узнали, как использовать условную логику и циклы для моделирования
бросков монеты. Подобные модели используются во многих дисциплинах для
прогнозирования и компьютерного моделирования реальных событий.
Модуль
random предоставляет множество полезных функций для генерирования
random
случайных чисел и моделирования. За дополнительной информацией о
обращайтесь к статье
"Generating RandomData in Python (Guide)» (https://
realpython.com/python-random/) на сайте Real Python.
Упражнения
1.
Напишите функцию
roll( ),
которая использует
randint()
для модели­
рования броска симметричного кубика, возвращающего случайное целое
число от
2.
1 до 6.
Напишите программу, которая моделирует
10
ООО бросков игрального
кубика и выводит среднее значение из тех, что выпали.
8.8.
ЗАДАЧА: МОДЕЛИРОВАНИЕ ЭКСПЕРИМЕНТА
С БРОСКОМ МОНЕТЫ
Допустим, вы многократно бросаете симметричную монету, пока орел и решка
не выпадут хотя бы по одному разу. Другими словами, после первого броска вы
продолжаете бросать монету, пока она не упадет другой стороной вверх.
ГЛАВА 8
184
Условная логика и управление программой
Бросая монету, вы получаете последовательность орлов и решек. Например,
при первом проведении эксперимента (в первой попытке) вы можете получить
последовательность «орел, орел, решка~>.
Сколько в среднем понадобится бросков, чтобы в последовательности оказались
как орел, так и решка?
Напишите программу для моделирования ситуации, когда эксперимент со­
ставляют
10 ООО попыток и надо вывести среднее число подбрасываний монеты
в каждой попытке.
8.9. ЗАДАЧА:
МОДЕЛИРОВАНИЕ ВЫБОРОВ
При помощи модуля
random и использования условной логики можно построить
модель выборов, в которых участвуют два кандидата.
Допустим, два кандидата, А и В, претендуют на должность мэра города, в кото­
ром три избирательных участка. Последние опросы показывают, что кандидат А
имеет следующие шансы на победу в каждом участке.
•
Участок
1: 87%
•
Участок
2: 65%
•
Участок
3: 17%
Напишите программу, которая моделирует выборы
1О
ООО раз и выводит про­
цент выборов, когда победил кандидат А.
Для простоты считайте, что кандидат одержал верх на выборах, если он победил
как минимум на двух из трех участков.
8.1 О.
ИТОГИ И ДОПОЛНИТЕЛЬНЫЕ РЕСУРСЫ
В этой главе вы познакомились с условными командами и условной логикой.
Вы научились сравнивать значения с такими операторами, как
и
==,
<, >, <=, >=, ! =
и узнали, как строить сложные условные выражения с использованием
операторов
and, or
и
not.
Затем вы освоили управление логикой выполнения программы при помощи
i f. Вы узнали, как создавать ветви выполнения в программе команда­
if... else и i f ... eli f ... else, а также научились управлять выполнением кода
внутри блоков i f командами break и continue.
команд
ми
8.1 О. Итоги и дополнительные ресурсы
185
Далее мы изучили синтаксис try ... except для обработки ошибок, которые могут
происходить во время выполнения. Это важная конструкция, позволяющая
программам корректно обрабатывать неожиданные ситуации и не огорчать
пользователей сбоями.
Наконец, мы применили инструменты, о которых шла речь в этой главе, и вос­
пользовались модулем
random для построения простых моделей.
ИНТЕРАКТИВНЫЙ ТЕСТ
К этой главе прилагается бесплатный интерактивный тест для прове р ки усво­
енных вами знаний . Тест доступен на телефоне или компьютере:
realpython.com/quizzes!pybasics-conditiona/-logic
Дополнительные ресурсы
Один умный вулканец однажды сказал: «Логика
далеко не конец»
( Спок.
-
лишь начало мудрости, но
«Звездный путы-).
Дополнительную информацию об условной логике вы найдете на следующих
ресурсах:
•
«Operators and Expressions in Python» (https.j/realpython.com/pytho noperators-expressions/)
•
«Conditional Statements in Python» (https.j/realpython .com/pythonconditional-statements/)
ГЛАВА
9
Кортежи, списки и словари
До настоящего момента вы работали с фундаментальными типами данных
такими, как
str, int
и
float.
-
Многие реальные задачи проще решаются при
объединении простых типов данных в более сложные структуры.
Структура данных моделирует такие наборы данных, как списки чисел, строки
электронной таблицы или записи в базе данных. Чтобы написать простой и эф­
фективный код, выберите структуру данных, которая лучше всего моделирует
данные для вашей программы.
Python поддерживает три встроенные структуры данных, которым и посвящена
эта глава: кортежи, списки и словари.
В этой rлаве вы:
•
научитесь работать с кортежами, списками и словарями;
•
узнаете, что такое неизменяемость и почему она важна;
•
узнаете, когда используются разные структуры данных.
Итак, за дело!
9.1.
КОРТЕЖИ КАК НЕИЗМЕНЯЕМЫЕ
ПОСЛЕДОВАТЕЛЬНОСТИ
Пожалуй, простейшей составной структурой данных является последователь­
ность элементов.
Последовательность представляет собой упорядоченный список значений.
Каждому элементу последовательности присваивается индекс
-
целое число,
обозначающее позицию элемента в последовательности. Как и в случае символов
в строках, индекс первого значения в 11ослсдоватсльности раuен
0.
9.1. Кортежи как неизменяемые последовательности
187
Например, буквы латинского алфавита образуют последовательность, первым
элементом которой является буква А, а последним
-
буква
Z. Строки также
6 элементов, на­
являются последовательностями. Строка "Python" состоит из
чиная с "Р" (индекс 0) и заканчивая
"n"
(индекс 5).
Вот несколько примеров последовательностей из реальной жизни: последова­
тельность значений, ежесекундно измеряемых датчиком; последовательность
оценок студента на экзаменах за год; последовательность ежедневных цен на
акции некоторой компании за некоторый период времени.
В этом разделе вы научитесь использовать встроенный тип данных
tuple
для
создания последовательностей значений.
Что такое кортеж?
Термин «кортеж» пришел из математики, где он используется для описания
конечной упорядоченной последовательности значений.
Обычно математики записывают кортежи, перечисляя элементы, разделенные
запятыми, в круглых скобках. Например, ( 1, 2, 3) - кортеж из трех целых чисел.
Кортежи упорядочены, потому что их элементы следуют в определенном поряд­
ке. Первым элементом кортежа
Python заимствует
(1, 2, 3) является 1, вторым - 2, а третьим - 3.
название и способ записи кортежей из математики.
Как создать кортеж
В
Python
предусмотрено несколько способов создания кортежа. Мы рассмо­
трим два из них.
1.
Литералы кортежей.
2.
Встроенная функция
tuple().
Литералы кортежей
Как говорилось выше, строковый литерал представляет собой строку, кото­
рая явно создается посредством заключения некоторого текста в кавычки.
Аналогичным образом литерал кортежа представляет собой кортеж, который
явно записывается в виде заключенной в круглые скобки последовательности
значений, разделенных запятыми.
Пример литерала кортежа:
>>> my_first_tuple = (1, 2, 3)
ГЛАВА 9
188
Кортежи, списки и словари
2 и з и присваивает его пере­
Эта строка создает кортеж с целыми числами 1,
менной с именем
my_first_tuple.
Чтобы убедиться в том, что
зоваться функцией
my_first_tuple содержит кортеж, можно восполь­
type( ):
>>> type(my_first_tuple)
<class 'tuple'>
В отличие от строк, которые состоят из последовательности символов, кортежи
могут содержать значения любых типов, в том числе и разных. Кортеж ( 1, 2. 0,
"three")
вполне допустим.
Также существует специальный кортеж, не содержащий никаких значений. Он
называется пустым кортежем, а для его создания следует ввести две круглые
скобки, между которыми нет ни одного символа:
>>> empty_tuple
= ()
На первый взгляд пустой кортеж может показаться странным и бесполезным,
но на самом деле он имеет практический смысл.
Допустим, вам поручено предоставить кортеж со всеми целыми числами, ко­
торые одновременно являются четными и нечетными. Таких целых чисел не
существует, но пустой кортеж позволит вам выполнить требование.
А как создать кортеж, содержащий ровно один элемент?
Попробуйте выполнить следующий фрагмент в
IDLE:
»> х = (1)
»> type(x)
<class 'int'>
Если заключить значение в круглые скобки, но не включить ни одной запятой,
Python
интерпретирует его не как кортеж, а как значение в круглых скобках.
Таким образом,
целого числа
( 1)
становится всего лишь экзотическим способом записи
1.
Чтобы создать кортеж, содержащий единственное значение 1, необходимо по­
ставить после
>»
х
1
= (1, )
»> type(x)
<class 'tuple'>
запятую:
9.1. Кортежи как неизменяемые последовательности
189
Кортеж из одного элемента может показаться таким же странным, как и пустой.
Почему бы не отказаться от возни с кортежами, а просто использовать само
значение?
Это зависит от задачи.
Если вам понадобится создать кортеж всех четных простых чисел, то это будет
(2, ), потому что 2 - единственное четное простое число. Значение 2 не подой­
дет, потому что это не кортеж.
На первый взгляд может показаться, что это проявление излишнего педантизма,
но программирование часто требует изрядной доли такого качества. В конце
концов, никто не сравнится с компьютером по части педантизма.
Встроенная функция
tuple()
Для создания кортежа из последовательности другого типа
ки
-
можно воспользоваться встроенной функцией
-
например, стро­
tuple( ):
>>> tuple("Python")
(
'Р',
'у',
Функция
't',
'h',
'о',
'n')
tuple() принимает только один параметр, поэтому вам не удастся
просто перечислить нужные значения как отдельные аргументы. Если вы по­
пытаетесь это сделать,
Python выдаст ошибку ТуреЕ rror:
>>> tuple(l, 2, 3)
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
tuple(l, 2, 3)
TypeError: tuple expected at most 1 arguments, got 3
Ошибка
TypeError выдается и в том случае, если переданный tuple() аргумент
не может быть интерпретирован как список значений:
»> tuple(l)
Traceback (most recent call last):
File "cpyshell#l>", line 1, in cmodule>
tuple(l)
TypeError: 'int' object is not iteraЫe
Слова
not iteraЫe (не итерируемый) в сообщении об ошибке означают, что
одно целое число не может использоваться для перебора, то есть целый тип
данных не содержит нескольких значений, которые можно перебирать одно
за ОДНИМ.
190
ГЛАВА 9
Кортежи, списки и словари
Единственный параметр tuple() не является обязательным. Опустив его, вы
получите пустой кортеж:
>» tuple()
()
Но программисты
Python обычно предпочитают более короткую запись ()
для
создания пустых кортежей.
Сходство между кортежами и строками
У кортежей и строк много общего. Как кортежи, так и строки являются типами
последовательностей конечной длины, поддерживают индексацию и работу со
срезами, являются неизменяемыми, а их содержимое можно перебирать в цикле.
Главное отличие строк и кортежей заключается в том, что элементами кортежа
могут быть любые значения, а строки содержат только символы.
Рассмотрим сходства и различия строк и кортежей более подробно.
У кортежей есть длина
И строки, и кортежи обладают длиной. Длина строки равна количеству со­
держащихся в ней символов. Длина кортежа равна количеству содержащихся
в нем элементов.
Как и в случае со строками, для определения длины кортежа можно восполь­
зоваться функцией
>>> numbers = (1, 2,
»> len(numbers)
len():
З)
3
Кортежи поддерживают индексацию и срезы
Вспомните, о чем говорилось в главе
4: для
обращения к отдельным символам
строки можно воспользоваться индексом:
>>> name = "David"
»> name(l]
,
а,
Индекс [1] после имени переменной приказывает Pythoп получить символ
с индексом 1 в строке
"David".
Так как нумерация индексов начинается с О,
символом с индексом 1 является буква "а".
9.1. Кортежи как неизменяемые последовательности
191
Кортежи также поддерживают индексную запись:
>>> values = (1, 3, 5, 7, 9)
>» values[2]
5
Еще одна особенность, общая для строк и кортежей,
-
работа со срезами. На­
помню, что синтаксис срезов используется для извлечения подстрок:
>>> name = "David"
>» name[2:4]
"vi"
Обозначение
[ 2: 4]
держит символы
после имени переменной создает новую строку, которая со­
name, начиная с символа в позиции 2 и до символа в позиции 4
(не включая его).
Создание среза из кортежа:
= (1, 3, 5, 7, 9)
»> values[2:4]
>>> values
( 5, 7)
Синтаксис
values,
values[2:4]
создает новый кортеж, содержащий все целые числа из
начиная с позиции
2 и до
позиции
4
(не включая ее).
Правила, относящиеся к срезам строк, также распространяются на срезы корте­
жей . Возможно, вам стоит вернуться к примерам работы со срезами из главы
4
и попробовать применить их к кортежам.
Кортежи неизменяемы
Кортежи, как и строки, являются неизменяемыми , то есть значение элемента
кортежа невозможно изменить после его создания.
-
ПРИМЕЧАНИЕ
Хотя кортежи являются неизменяемыми, в некоторых ситуациях значения
в кортеже все же могут изменяться .
Эти странности и аномалии подробно рассматриваются в видеокурсе
«lmmutaЬility in Python» (https://realpython . com/courses/immutaЬility- python/)
на сайте Real Python.
При попытке изменить в кортеже значение с заданным индексом
дает ошибку TypeError:
Python
вы­
ГЛАВА 9
192
Кортежи, списки и словари
>>> values[0] = 2
Traceback (most recent call last):
File "cpyshell#l>", line 1, in cmodule>
values[0] = 2
TypeError: 'tuple' object does not support item assignment
Кортежи итерируемы
Кортежи, как и строки, итерируемы, то есть их содержимое можно перебирать
в цикле:
>>> vowels =("а", "е", "i",
>>> for vowel in vowels:
print(vowel.upper())
"о",
"u")
А
Е
I
о
u
Цикл
for
в этом примере работает точно так же, как и циклы
использовали в главе
6 для
перебора в числовых диапазонах
На первой итерации цикла из кортежа
for, которые
range( ).
vowels извлекается значение "а". Оно
преобразуется в букву верхнего регистра строковым методом.
ным в главе
4,
мы
а затем выводится вызовом
upper( ), описан­
print().
На следующем шаге цикл извлекает значение "е", преобразует его к верхнему
регистру и выводит. То же самое происходит со значениями
"i",
"о" и
"u".
Итак, вы научились создавать кортежи и выполнять с ними некоторые базовые
операции. Рассмотрим несколько стандартных сценариев использования кортежей.
Упаковка и распаковка кортежей
Существует третий, менее распространенный способ создания кортежей. Можно
ввести список значений, разделенных запятыми, без круглых скобок:
>>> coordinates = 4.21, 9.29
>>> type(coordinates)
cclass 'tuple'>
Все выглядит так, словно одной переменной
coordinates
присваиваются два
значения. В каком-то смысле так и есть, хотя в результате два значения упако­
вываются в один кортеж. Чтобы убедиться в том, что
coordinates действительно
type().
является кортежем, можно воспользоваться функцией
9.1. Кортежи как неизменяемые последовательности
193
Если значения можно упаковать в кортеж, то вполне логично, что кортеж также
можно распаковать:
>>>
»>
х,
у
=
coordinates
х
4.21
»>
у
9.29
Значения, содержащиеся в одном кортеже
переменные
-
coordinates, распаковываются в две
х и у.
Объединение упаковки с распаковкой кортежа позволяет выполнить несколько
присваиваний в одной строке:
>>> name, age, occupation
»> name
'David'
»> age
34
»> occupation
'programmer'
=
"David", 34, "programmer"
Этот фрагмент работает, потому что сначала в правой части команды присва­
ивания значения
"David", 34
и
"programmer"
упаковываются в кортеж. Затем
значения распаковываются в три переменные,
name, age
и
occupation,
в ука­
занном порядке.
ПРИМЕЧАНИЕ
Хотя присваивание значений нескольким переменным в одной строке может
уменьшить число строк в программе, так делать не рекомендуется .
Если значения присваиваются более чем двум или трем переменным, вам
будет труднее определить, какое значение связывается с той или иной пере­
менной .
Учтите, что количество имен переменных в левой части выражения присваива­
ния должно соответствовать количеству значений в кортеже из правой части.
В противном случае
Python выдает ошибку ValueError:
>>> а, Ь, с, d = 1, 2, 3
Traceback (most recent call last):
File "<pyshell#0>", line 1, in cmodule>
а,
Ь,
с,
d = 1, 2, 3
ValueError: not enough values to unpack (expected 4, got 3)
Кортежи, списки и словари
ГЛАВА 9
194
Сообщение об ошибке указывает, что кортеж в правой части содержит слишком
мало значений для распаковки по четырем переменным в левой части.
Python также выдает ошибку ValueError, если
количество значений в кортеже
превышает количество имен переменных:
>>> а, Ь, с = 1, 2, 3, 4
Traceback (most recent call last):
File "cpyshell#l>", line 1, in <module>
а,
Ь,
с
= 1,
2, 3, 4
ValueError: too many values to unpack (expected 3)
Теперь сообщение об ошибке указывает, что кортеж содержит слишком много
значений для распаковки по трем переменным.
Проверка существования значений
Чтобы проверить, содержится ли значение в кортеже, используйте ключевое
слово
in:
>>> vowels = ("а",
>>> "о" in vowels
True
>>> "х" in vowels
False
"е",
"i",
"о",
Если значение в левой части
результат равен
True.
in
"u")
присутствует в кортеже из правой части, то
В противном случае результат равен
False.
Возвращение нескольких значений из функции
Кортежи часто используются для возвращения нескольких значений из одной
функции:
>>> def adder_subtractor(numl, num2):
... return (numl + num2, numl - num2)
>>> adder_subtractor(3, 2)
( 5, 1)
У функции
adder _subtractor()
два параметра
- numl
и
num2.
Она возвращает
кортеж, где первый элемент содержит сумму двух чисел, а второй содержит их
разность.
- всего лишь два примера встроенных типов последо­
Python. И строки, и кортежи являются неизменяемыми, они
Строки и кортежи
вательностей
9.2. Списки: изменяемые последовательности
195
итерируемы (поддерживают перебор), их можно индексировать и создавать
из них срезы.
В следующем разделе я расскажу о третьем типе последовате.llьностей, у кото­
рого есть существенное отличие от строк и кортежей: он является изменяемым.
Упражнения
1.
Создайте литерал кортежа с именем
строки
2.
"first", "second"
и
"third"
Используя индексирование и
cardinal_numbers,
содержащий
в указанном порядке.
print (),
выведите строку с индексом
1 из
cardinal_numbers.
3.
В одной строке кода распакуйте значения из
вые строки с именами
cardinal_numbers в три но­
posi tionl, posi tion2 и posi tionЗ. Затем выведите
каждое значение в отдельной строке.
4.
Используя функцию
с именем
5.
Проверьте, входит ли символ "х" в
слова
6.
tuple() и строковый литерал,
my _name, содержащий буквы вашего имени.
создайте кортеж
my_name, при помощи ключевого
in.
Используя синтаксис сегментов, создайте новый кортеж, содержащий
все буквы
my _name, кроме первой.
9.2. СПИСКИ:
ИЗМЕНЯЕМЫЕ
ПОСЛЕДОВАТЕЛЬНОСТИ
Структура данных
list (список) - еще один тип последовательностей в Python.
Как строки и кортежи, списки состоят из элементов, индексируемых целыми
числами (начиная с О).
На первый взгляд списки своим внешним видом и поведением сильно напо­
минают кортежи. Списки можно индексировать, создавать из них срезы, про­
верять существование элемента при помощи
в цикле
in, а также перебирать элементы
for.
Однако, в отличие от кортежей, списки являются изменяемыми; это означает,
что вы можете изменить значение с заданным индексом даже после того, как
список будет создан.
В этом разделе я расскажу, как создавать списки. Затем мы сравним списки
с кортежами.
ГЛАВА
196
9
Кортежи, списки и словари
Создание списков
Литерал списка очень похож на литерал кортежа, но вместо круглых скобок он
заключается в квадратные скобки
[ ]:
>>> colors = ["red", "yellow", "green",
»> type(colors)
<class 'list' >
При проверке списка
"Ыuе"]
Python выводит его
в виде литерала:
>>> colors
['red', 'yellow', 'green',
'Ыuе']
Значения в списках, как и значения в кортежах, не обязаны иметь один тип.
Литерал
[ "one", 2,
з. 0] вполне допустим.
Кроме литералов, можно воспользоваться встроенной функцией
list() для
создания нового объекта списка на основе любой другой последовательности.
Например,
list()
можно передать кортеж (1,
2,
З) для создания списка [1,
2, 3 ]:
»> list((l, 2,
[1, 2,
З))
З]
Список даже можно создать на основе строки:
>>> list("Python")
['Р', 'у', 't', 'h',
'о',
'n']
Каждая буква строки становится элементом списка.
Существует еще один способ создания списка на основе строки. Если имеется
строка с перечнем элементов, разделенных запятыми, воспользуйтесь строко­
вым методом
. split( ):
>>> groceries = "eggs, milk, cheese"
>» grocery_list = groceries.split(", ")
>» grocery_list
[ 'eggs', 'milk', 'cheese']
Строковый аргумент, передаваемый
. spli t (),
называется разделителем. Из­
меняя разделитель, можно разбивать строку на списки разными способами:
>>> # Разбиение по точке
>>> "a;b;c".split(";")
с запятой
9.2. Списки: изменяемые последовательности
['а',
·ь·,
197
'с']
>>> # Разбиение по пробелам
>>> "The quick brown fox".split(" ")
[ 'The', 'quick', 'brown', 'fox']
символам
>>> # Разбиение по нескольким
>>> "abbaabba".split("ba")
['аЬ',
'аЬ',
'']
В последнем примере строка разбивается по вхождениям подстроки "Ьа", кото­
рая встречается сначала в позиции с индексом 2, а затем с индексом 6. Так как
разделитель состоит из двух символов, элементами списка становятся только
символы с индексами
. spli t ()
0, 1, 4
и
5.
всегда возвращает список, длина которого на
1 превышает количество
разделителей в строке. Разделитель "Ьа" встречается в "аЬЬааЬЬа" дважды, так
что список, возвращаемый
split( ),
состоит из трех элементов.
Обратите внимание: последним элементом списка является пустая строка. Это
происходит из-за того, что за последним "Ьа" никакие другие символы не следу­
ют. Если разделитель вообще не входит в строку,
. spli t ()
возвращает список,
единственным элементом которого является исходная строка:
>>> "abbaabba".split("c")
['аЬЬааЬЬа']
И так, существуют три способа создания списков.
1.
Литерал списка.
2.
Встроенная функция
3.
Строковый метод
list ().
. spli t ().
Списки поддерживают те же операции, что и кортежи.
Основные операции списков
Операции индексирования и сегментации работают со списками так же, как
и с кортежами.
К элементам списков можно обращаться по индексам:
>>> numbers = [1, 2,
» > numbers [ 1]
2
з,
4]
198
ГЛАВА 9
Кортежи, списки и словари
Также можно создать новый список на основе существующего, используя син­
таксис сегментации:
»>
пumbers[l:З]
[ 2, 3]
Для проверки существования элементов списков используется оператор iп:
>>> # Проверка существования
>>> "ВоЬ" in numbers
элемента
False
Списки итерируемы; это означает, что их содержимое можно перебрать в цик­
ле
for:
>» # Выводит только четные
>>> for number in numbers:
if number % 2 == 0:
print(number)
числа
в списке
2
4
Главное отличие списка от кортежа заключается в том, что элементы списка
могут изменяться, тогда как элементы кортежа изменяться не могут.
Изменение элементов списка
Список можно рассматривать как последовательность пронумерованных ячеек.
Каждая ячейка содержит значение, и каждая ячейка в любой момент времени
должна быть заполнена. Однако значение в заданной ячейке всегда можно за­
менить новым.
Возможность замены значений в списке другими значениями называется из­
меняемостью. Списки являются изменяемыми. Элементы кортежей не могут
заменяться новыми значениями, поэтому кортежи называются неизменяемыми.
Чтобы заменить одно значение в списке другим, присвойте новое значение
в позиции, заданной индексом:
>>> colors = ["red", "yellow", "green",
>>> colors[0] = "burguпdy"
"Ыuе"]
Значение с индексом 0 изменяется с "red" на "burgundy":
»> colors
[ 'burgundy', 'yellow', 'green',
'Ыuе']
9.2. Списки: изменяемые последовательности
199
Вы можете изменить сразу несколько значений в списке, используя срезы:
>>>
colors[l:З]
= ["orange", "magenta"]
»> colors
[ 'burgundy', 'orange', 'magenta',
'Ыuе']
colors [ 1: з] выбирает ячейки с индексами 1 и 2. Значениям в этих позициях
"orange" и "magenta" соответственно.
присваиваются строки
Список, присваиваемый срезу, не обязан иметь такую же длину, как и срез. На­
пример, можно присвоить список из трех элементов срезу из двух элементов:
>>> colors = ["red", "yellow", "green",
"Ыuе"]
»> colors[l:З] = ["orange", "magenta", "aqua"]
»> colors
[ 'red', 'orange', 'magenta', 'aqua',
Значения
в списке
'Ыuе'
]
"orange" и "magenta" заменяют исходные значения "yellow" и "green"
colors с индексами 1 и 2. Затем в позиции с индексом 4 создается
новая ячейка, которой присваивается значение "Ыuе". Наконец, в позиции
с индексом з присваивается строка
"aqua".
Если длина списка, присвоенного срезу, меньше длины среза, то общая длина
исходного списка уменьшается:
>>> colors
[ 'red', 'orange', 'magenta', 'aqua',
= ["yellow", "green"]
»> colors
[ 'red', 'yellow', 'green', 'Ыuе' ]
'Ыuе'
]
>>> colors[1:4]
"yellow" и "green" заменяют значения "orange" и "magenta" в списке
colors с индексами 1 и 2. Затем значение "Ыuе" заполняет позицию с индек­
сом 3, а индекс 4 полностью удаляется из colors. Этот пример показывает, как
Значения
изменять списки путем индексации и срезов. Кроме того, для изменения списка
можно использовать методы списков.
Методы списков для добавления
и удаления элементов
Конечно же, вы можете добавлять и удалять элементы с использованием ин­
дексации и срезов, но методы списков предлагают более естественный и удобо­
читаемый механизм изменения.
Мы рассмотрим несколько методов списков для выполнения разных операций,
начиная со вставки одного значения в позицию с заданным индексом.
Кортежи, списки и словари
ГЛАВА 9
200
list.insert()
Метод
list. insert ()
используется для вставки нового значения в список. Он
получает два параметра
зицию с индексом
-
индекс
i
и значение х
-
и вставляет значение х в по­
i:
>>> colors = ["red", "yellow", "green", "Ыuе"]
»> # Вставляет "orange" во вторую позицию
>>> colors.insert(l, "orange")
>>> colors
[ 'red', 'orange', 'yellow', 'green', 'Ыuе']
В этом примере стоит обратить внимание на пару важных моментов.
Первый применим ко всем методам. Чтобы использовать методы, вы сначала
указываете имя списка, с которым хотите выполнить операцию, затем точку
и имя метода списка.
Таким образом, чтобы вызвать
colors. insert( ).
insert() для списка colors, используйте запись
Такой синтаксис уже знаком вам по строковым и числовым
методам.
Также обратите внимание на то, что при вставке значения
с индексом
1 значение "yellow"
Если значение параметра индекса
списка,
"orange" в позицию
и все следующие значения сдвигаются вправо.
. insert ()
превышает наибольший индекс
значение вставляется в конец списка:
>>> colors.insert(10, "violet")
»> colors
[ 'red', 'orange', 'yellow', 'green',
'Ыuе',
'violet' ]
"violet" вставляется в позицию
что метод . insert () был вызван с индексом 10.
Здесь значение
Также методу
. insert ()
с индексом
5
несмотря на то,
могут передаваться отрицательные индексы:
>>> colors.insert(-1, "indigo")
»> colors
[ 'red', 'orange', 'yellow', 'green',
Фрагмент вставляет строку
'Ыuе',
'indigo', 'violet' ]
"indigo" в позицию с индексом -1, что соответ­
"violet" сдвигается вправо на
ствует последнему элементу списка. Значение
одну позицию.
Если вы можете вставить значение по заданному индексу, вполне логично, что
также существует возможность удаления элемента с заданным индексом.
9.2. Списки: изменяемые последовательности
201
ВАЖНО!
При вставке значения в список методом .iпsert() результат не нужно присва­
ивать исходному списку.
Например, следующий код фактически стирает содержимое списка
colors:
>>> colors = colors.insert(-1, "indigo")
>>> print(colors)
None
Метод .iпsert() изменяет
colors на месте. Это справедливо для всех методов
списков, которые не возвращают значения.
list.pop()
Метод
list. рор()
получает один параметр
индекс i - и удаляет из списка зна­
-
чение в позиции с заданным индексом. Удаляемое значение возвращается методом:
>>> color = colors.pop(З)
»> color
'green'
>» colors
['red', 'orange', 'yellow',
'Ыuе',
'indigo', 'violet']
Значение "greeп" с индексом З удаляется и присваивается переменной
При проверке переменной
colors
можно убедиться в том, что строка
color.
"green"
действительно была удалена.
В отличие от.
insert( ), Python выдает ошибку IndexError при передаче. рор()
аргумента, превышающего последний индекс:
>>> colors.pop(10)
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
colors.pop(10)
IndexError: рор index out of range
Отрицательные индексы также работают с
>>> colors.pop(-1)
'violet'
»> colors
[ 'red ', 'orange ', 'yellow ',
Если при вызове
. рор()
последний элемент:
'Ыuе
. рор ():
', 'indigo']
значение не передается, то метод удаляет из списка
Кортежи, списки и словари
ГЛАВА 9
202
» > colors. рор ()
'indigo'
>» colors
[ 'red', 'orange', 'yellow',
'Ыuе']
Удаление последнего элемента вызовом
. рор()
без указания индекса обычно
считается более питоническим подходом.
list.append()
Метод
list. append ()
используется для добавления нового элемента в конец
списка:
>>> colors.append("indigo")
>>> colors
[ 'red', 'orange', 'yellow',
Вызов
'Ыuе',
'indigo' ]
увеличивает длину списка на
. append ()
в последнюю позицию. Следует помнить, что
месте», как и
1 и вставляет значение "indigo"
. append() изменяет список «на
. insert ().
Использование
. append()
эквивалентно вставке элемента с индексом, большим
или равным длине списка. Приведенный выше пример можно записать и таким
образом:
>>> colors.insert(len(colors), "indigo")
Вызов
короче и содержательнее такого использования
. append ()
. insert ().
- более
Обычно считается, что этот способ добавления элемента в конец списка
питонический.
list.extend()
Метод
list. extend () используется для добавления нескольких новых элемен­
тов в конец списка:
>>> colors.extend(["violet", "ultraviolet"])
»> colors
[ 'red ', 'orange ', 'yellow ',
Метод
. extend ()
'Ыuе
', 'indigo', 'violet', 'ultraviolet']
получает один параметр, который должен быть итерируемым
типом. Элементы этого объекта присоединяются в конец списка в том же по­
рядке, в котором они указываются в аргументе, передаваемом
Как и
. insert()
и
. append(), . extend()
. extend ().
изменяет список «на месте».
9.2. Списки: изменяемые последовательности
Как правило, передаваемый
. extend ()
203
аргумент содержит другой список, но
это также может быть кортеж. Например, приведенный выше пример можно
записать в следующем виде:
>>>
colors.exteпd(("violet",
"ultraviolet"))
Четыре метода списков, о которых мы рассказали в этом разделе, используются
чаще других. В следующей таблице приведено краткое описание этих методов.
МЕТОД СПИСКА
. insert( i,
ОПИСАНИЕ
х)
Вставляет значение х в позицию с индексом
i
. append (х)
Вставляет значение х в конец списка
. extend(i teraЬle)
Вставляет все значения из iteraЬle в конец списка с сохра­
нением порядка
.pop(i)
Удаляет и возвращает элемент с индексом
Кроме методов списков
i
Python также содержит несколько полезных встроенных
функций для работы со списками чисел.
Списки чисел
Одна из самых распространенных операций, используемых со списками чисел,
суммирование всех значений. Для этой цели можно воспользоваться циклом
for:
»> nums = [1, 2, 3, 4, 5]
>» total = 0
>» for number in nums:
total = total + number
>» total
15
Сначала переменная
total инициируется значением 0. Затем программа в ци­
кле перебирает все числа в nums и прибавляет их к total. В итоге мы получаем
значение
15.
Хотя решение с циклом вполне прямолинейно, в
Python
существует другой,
куда более компактный вариант:
»> sum([l, 2, 3, 4, 5])
15
Встроенная функция
sum()
всех значений в списке.
получает список в аргументе и возвращает сумму
204
ГЛАВА 9
Кортежи, списки и словари
Если список, переданный sum( ), содержит значения, которые не являются
числовыми, выдается ошибка TypeError:
»> sum([l, 2, 3, "four", 5))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Кроме sum(), существуют две другие полезные встроенные функции для работы
со списками: min () и max (). Эти функции возвращают наименьшее и наибольшее
значение в списке соответственно:
»> min([l, 2, 3, 4, 5 ])
1
»> max([l, 2, 3, 4, 5))
5
Обратите внимание: функции sum( ), min() и max() также работают с кортежами:
>>> sum((l, 2, 3, 4, 5))
15
»> min((l, 2, 3, 4, 5))
1
»> max((l, 2, 3, 4, 5))
5
Тот факт, что функции sum(), min() и max() встроены в
Python,
подсказывает,
что они используются достаточно часто. Скорее всего, вы не раз примените их
в своих программах!
Генераторы списков
Для создания списка на основе существующей последовательности с поддерж­
кой перебора также можно воспользоваться генератором списка:
>>> numbers
>>> squares
»> squares
(1, 2, 3, 4, 5)
[num**2 for num in numbers]
[1, 4, 9, 16, 25)
Генератор списка является сокращенной формой записи для цикла
for. В при­
веденном примере литерал кортежа, содержащий пять чисел, создается и при­
сваивается переменной numbers. Во второй строке генератор списка перебирает
numbers, возводит его в квадрат и включает в новый список
squares.
каждое число в
с именем
9.2. Списки: изменяемые последовательности
205
Чтобы создать список squares с традиционным циклом for, вам придется создать
пустой список, перебрать числа из
numbers и присоединить квадрат каждого
элемента к списку:
>» squares = []
>>> for num in numbers:
squares.append(num**2)
»> squares
[1, 4, 9, 16, 25]
Генераторы списков часто применяются для преобразования значений в списке
к другому типу.
Допустим, вы хотите преобразовать список строк, содержащих числа с плаваю­
щей точкой, в список объектов
float. Задача решается следующим генератором
списка:
»> str_numbers
["1.5", "2.3", "5.25"]
>>> float_numbers = [float(value) for value in str_numbers]
>>> float_numbers
[1.5, 2.3, 5.25]
Генераторы списков поддерживаются не только в
Python, но они принадлежат
к числу его самых популярных возможностей. Если вы видите, что вам при­
ходится создавать пустой список, перебирать некоторую последовательность
и присоединять новые элементы к списку, то, скорее всего, этот код можно за­
менить генератором списка.
Упражнения
1.
Создайте список
2.
Присоедините к
3.
Добавьте в
4.
food,
food
содержащий два элемента:
строку
"broccoli"
методом
"rice"
и
"beans".
.append().
food строки "bread" и "pizza" при помощи метода . extend ().
Выведите первые два элемента списка
food,
используя функцию
print()
и синтаксис сегментов.
5.
Выведите последний элемент
food, используя функцию print() и син­
таксис сегментов.
6.
Создайте список
breakfast из строки "eggs, fruit, orangejuice" при
split( ).
помощи строкового метода.
7.
Убедитесь в том, что breakfast содержит три элемента, при помощи
функции
len ().
8.
Кортежи, списки и словари
ГЛАВА 9
206
Создайте генератором списка новый список
всех строк из списка
lengths, содержащий длины
breakfast.
9.3. ВЛОЖЕНИЕ, КОПИРОВАНИЕ
КОРТЕЖЕЙ И СПИСКОВ
И СОРТИРОВКА
Итак, вы узнали, что собой представляют кортежи и списки, научились создавать
их и выполнять базовые операции. Рассмотрим еще три концепции:
1)
вложение;
2)
копирование;
3)
сортировка.
Вложенные списки и кортежи
Списки и кортежи могут содержать значения произвольных типов. Это озна­
чает, что списки и кортежи могут содержать списки и кортежи как значения.
Вложенный список или кортеж представляет собой список или кортеж, который
содержится как значение в другом списке или кортеже.
Например, следующий список содержит два значения, каждое из которых само
по себе является списком:
>» two _ Ьу _two
= [ [1, 2] , [ 3, 4] ]
>>> # two_by_two имеет
>>> len(two_by_two)
длину
2
2
>>> # Оба элемента two_by_two
>>> two_by_two[0]
являются
списками
[ 1, 2]
>>> two_by_two[l]
[3, 4]
Так как
two_by_two[l] возвращает список [З, 4], для обращения к элементу
вложенного списка можно воспользоваться записью с двумя индексами:
>>> two_by_two[1][0]
3
Сначала
Python
вычисляет [З,
вычисляет two_by _ two [ 1] и возвращает
4] [0]
и возвращает первый элемент з.
[ 3, 4].
Затем
Python
9.3. Вложение, копирование и сортировка кортежей и списков
207
Очень упрощенно список списков или кортеж кортежей можно представить
себе как таблицу со строками и столбцами.
Список two_by_two содержит две строки: [1, 2] и [З, 4]. Столбцы состоят из
соответствующих элементов каждой строки. Таким образом, первый столбец
содержит элементы
1 и з, а второй - элементы 2 и 4.
Впрочем, аналогия с таблицей
списка списков. Например,
- не более чем неформальное представление
Python не требует, чтобы все списки в списке спи­
сков имели одинаковую длину; в этом случае аналогия с таблицей не годится.
Копирование списка
Иногда требуется скопировать один список в другой. Однако вы не сможете
просто присвоить один объект списка другому объекту списка, потому что
получите следующий (возможно, неожиданный) результат:
>>> animals = ["lion", "tiger", "frumious Bandersnatch"]
>>> large_cats = animals
>>> large_cats.append("Tigger")
»> animals
[ 'lion', 'tiger', 'frumious Bandersnatch', 'Tigger']
В этом примере список, хранящийся в переменной
animals, присваивается пере­
large_cats, после чего в список large_cats добавляется новая строка
"Тigger". Но при выводе содержимого animals мы видим, что исходный список
менной
тоже изменился.
Такое поведение относится к особенностям объектно-ориентированного про­
граммирования, но оно было реализовано намеренно. После выполнения
команды
large_cats = animals
переменные
large_cats
и
animals
относятся
к одному и тому же объекту.
Имя переменной в действительности представляет собой ссылку на область
памяти компьютера. Вместо того чтобы скопировать все содержимое объекта
списка и создать новый список, команда
large_cats = animals присваивает
large_cats ту область памяти, на которую ссылается animals. В результате обе
переменные ссылаются на один и тот же объект в памяти
-
и любые изменения,
внесенные в одну переменную, влияют на другую.
Чтобы получить независимую копию списка
animals, можно воспользоваться
срезом; эта запись возвращает новый список, содержащий те же значения:
>>> animals = ["lion", "tiger", "frumious Bandersnatch")
>>> large_cats = animals[:]
208
ГЛАВА 9
Кортежи, списки и словари
»> large_cats.append("leopard")
»> large_cats
[ 'lion', 'tiger', 'frumious Bandersnatch', 'leopard']
»> animals
["lion", "tiger", "frumious Bandersnatch"]
Так как в срезе
[:]
индексы не указаны, возвращаются все элементы списка
от начала до конца. Список
и
animals,
large_cats
теперь содержит те же элементы, что
и они следуют в том же порядке, но к нему можно присоединять
элементы вызовом
. append ()
без изменения списка, связанного с animals.
Если вы захотите создать копию списка списков, можно воспользоваться
записью
( : ] , как было
показано выше:
>>> matrixl = [[1, 2], [З, 4]]
>>> matrix2 = matrixl[:]
>>> matrix2[0] = [5, 6]
»> matrix2
[[5, 6],
[З,
4]]
»> matrixl
[[1, 2],
[З,
4]]
Посмотрим, что происходит при изменении первого элемента второго списка
в
matrix2:
>>> matrix2[1][0]
»> matrix2
1
[[5, 6], [1, 4]]
»> matrixl
[[1, 2], [1, 4]]
Обратите внимание: второй список в matrixl тоже изменился!
Это происходит из-за того, что список на самом деле содержит не сами объ­
екты, а ссылки на эти объекты в памяти. Срез
[:)
возвращает новый список,
содержащий те же ссылки, что и исходный список. На жаргоне программистов
этот способ копирования списка называется поверхностным копированием.
Чтобы скопировать список и все его элементы, необходимо создать глубокую
копию. Она является действительно независимой копией объекта. Для созда­
ния глубокой копии списка можно воспользоваться функцией
модуля Pythoп сору:
» > import
сору
>>> matrixЗ = copy.deepcopy(matrixl)
>>> matrix3[1][0] = З
>»
matrixЗ
deepcopy()
из
9.3. Вложение, копирование и сортировка кортежей и списков
[[5, 6],
[З,
209
4]]
»> matrixl
б],
[[5,
[1, 4]]
matrixЗ создается как глубокая копия
соответствующий элемент
matrixl
matrixl. При изменении элемента matrixЗ
не изменяется.
ПРИМЕЧАНИЕ
За дополнительной информацией о поверхностном и глубоком копирова­
нии обратитесь к статье «Shallow vs Deep Copyiпg of Pythoп Objects» (https://
realpythoп . com/copyiпg-pythoп-objects/) на сайте Real Pythoп .
Сортировка списков
У списков есть метод
. sort (),
который сортирует элементы по возрастанию.
По умолчанию список сортируется в алфавитном или числовом порядке в за­
висимости от типа элементов в списке:
>>> # Списки строк сортируются по алфавиту
>>> colors = ["red", "yellow", "green", "Ыuе"]
>>> colors.sort()
»> colors
[ 'Ыuе', 'green', 'red', 'yellow']
>>>
>>>
>>>
>>>
#
Списки
чисел
сортируются
numbers = [1, 10, 5,
numbers.sort()
numbers
[1, 3, 5, 10]
Обратите внимание:
по порядку
З]
. sort ()
сортирует список ~на месте», так что присваивать
результат вызова переменной необязательно .
. sort()
также поддерживает необязательный параметр
key, который может ис­
key получает функ­
пользоваться для настройки сортировки списка. Параметр
цию, а список сортируется на основании возвращаемого значения этой функции.
Например, чтобы отсортировать список строк по длине, можно передать в
функцию
len:
>>> colors = ["red", "yellow", "green",
>>> colors.sort(key=len)
»> colors
[ 'red ', 'Ыuе ', 'green ', 'yellow' ]
"Ыuе"]
key
21 О
ГЛАВА 9
Кортежи, списки и словари
Вызывать функцию, передаваемую в параметре
key, не нужно.
Просто укажите
имя функции без круглых скобок. Например, в предыдущем примере
дается имя
len,
а не
Функция, передаваемая
Также
key
key пере­
len().
key, должна
получать только один аргумент.
можно передавать функции, определяемые пользователем . В следу­
ющем примере функция get_second_elemeпt() используется для сортировки
списка кортежей по их вторым элементам :
>>> def get_second_element(item):
return item[l)
»> items = [(4, 1), (1, 2), (-9, 0))
>>> items.sort(key=get_second_element)
»> items
[(-9, 0), (4, 1), (1, 2))
Помните, что функция, передаваемая в параметре
key, должна получать только
один аргумент.
Упражнения
1.
Создайте кортеж
data,
содержащий два значения . Первым значением
должен быть кортеж (1, 2), а вторым
2.
Напишите цикл
-
кортеж (З, 4).
for, который перебирает data и выводит сумму каждого
вложенного кортежа. Результат должен выглядеть примерно так:
Row 1 sum: 3
Row 2 sum: 7
З,
3.
Создайте список
4.
Создайте копию списка
5.
Отсортируйте список
9.4. ЗАДАЧА:
[ 4,
2, 1]
и присвойте его переменной
numbers
numbers
с использованием записи
numbers.
[ : ].
в числовом порядке методом
. sort ().
СПИСОК СПИСКОВ
Напишите программу, в которой создается следующий список списков:
universities = [
['California Institute of Technology', 2175, 37704],
9.4. Задача: список списков
211
['Harvard', 19627, 39849),
['Massachusetts Institute of Technology', 10566, 40732),
['Princeton', 7802, 37000),
['Rice', 5879, 35551),
['Stanford', 19535, 40569),
['Yale', 11701, 40500)
Определите функцию
enrollment_stats (),получающую один
параметр. Этим
параметром должен быть список списков, в котором каждый вложенный список
содержит три элемента:
1)
название университета;
2)
общее количество зачисленных студентов;
3)
ежегодная плата за обучение.
Функция
enrollment_stats()
должна возвращать два списка. Первый содер­
жит все данные о зачисленных зарегистрированных студентах, а второй
-
все
данные о плате за обучение.
Затем определите две функции
- mean()
и
median( ),
которые получают один
списковый аргумент и возвращают среднее значение и медиану по каждому
списку соответственно.
Используя
universities, enrollment_stats(), mean()
и
median(),
вычислите
общее количество студентов, общую плату за обучение, среднее и медианное
количество студентов, а также среднюю и медианную плату за обучение.
Наконец, выведите все значения и отформатируйте вывод, чтобы он выглядел
так:
******************************
Total students: 77,285
Total tuition: $ 271,905
Student mean: 11,040.71
Student median: 10,566
Tuition mean: $ 38,843.57
Tuition median: $ 39,849
******************************
Решение задачи, а также многие другие дополнительные ресурсы доступны
в интернете по адресу
realpython.com/python-basics/resources.
ГЛАВА 9
212
Кортежи, списки и словари
9.5. ЗАДАЧА:
ПРИСТУП ВДОХНОВЕНИЯ
Сейчас вы напишете программу, которая генерирует стихи.
Создайте пять списков для разных типов слов:
1.
Существительные:
2.
Глаголы:
3.
Прилагательные:
4.
Предлоги:
5.
Наречия
["fossil", "horse", "aardvark", "judge", "chef",
"mango", "extrovert", "gorilla"]
["kicks", "jingles", "bounces", "slurps", "meows", "explodes",
"curdles"]
["furry", "balding", "incredulous", "fragrant",
"exuberant", "glistening"]
["against", "after", "into", "beneath", "upon", "for", "in",
"like", "over", "within"]
[ "curiously", "extravagantly", "tantalizingly", "furiously",
"sensuously"]
Случайным образом выберите следующее количество элементов из каждого
списка:
•
три существительных;
•
три глагола;
•
три прилагательных;
•
два предлога;
•
одно наречие.
Для этого можно воспользоваться функцией
choice()
из модуля
random.
Она
получает список и возвращает случайно выбранный элемент списка.
Пример использования
из списка
[ .. а .. ,
random. choice()
для получения случайного элемента
"Ь •1, ••с'']:
import random
random_element = random.choice(["a",
"Ь",
"с"])
Используя случайно выбранные слова, сгенерируйте и выведите стихотво­
рение, структура которого аналогична стилю Клиффорда Пиковера
en.wikipedia.org/wiki/Clifford_A._Pickover):
(https:j/
9 .6. Храните отношения в словарях
{A/An}
{прилl} {сущl}
{A/An}
{прилl} {сущl} {глl} {предлl}
{наречl},
the
the
the
21 З
{прил2} {сущ2}
{сущl} {гл2}
{сущ2} {глЗ} {предл2} а {прилЗ} {сущЗ}
Пример стихотворения, которое могла бы сгенерировать ваша программа:
А
furry horse
furry horse curdles within the fragrant mango
extravagantly, the horse slurps
the mango meows beneath а balding extrovert
А
При каждом запуске программы должно генерироваться новое стихотворение.
Решение задачи, а также многие другие дополнительные ресурсы доступны
в интернете по адресу
9.6. ХРАНИТЕ
realpython.com/python-basics/resources.
ОТНОШЕНИЯ В СЛОВАРЯХ
Одной из самых полезных структур данных
Python является словарь.
В этом разделе вы узнаете, что такое словарь, чем словари отличаются от списков
и кортежей и как создавать и использовать словари в вашем коде.
Что такое словарь?
Обычно словарем называется текст, содержащий определения слов. Каждая
запись в словаре состоит из двух частей: определяемого слова и его определения.
Словари
Python, как и списки и кортежи, предназначены для хранения коллек­
ций объектов. Но вместо того, чтобы хранить объекты в последовательности,
словари хранят информацию в виде пар данных «ключ
-
значение». Другими
словами, каждый объект в словаре состоит из двух частей: ключа и значения.
Ключ в паре «ключ
-
значение» представляет собой уникальное имя, по кото­
рому можно идентифицировать значение. Если провести параллель с обычным
словарем, ключ можно сравнить с определяемым словом, а значение
-
с его
определением.
Например, словарь можно использовать для хранения названий штатов и их
столиц.
214
ГЛАВА 9
Кортежи, списки и словари
КЛЮЧ
ЗНАЧЕНИЕ
"California"
"Sacramento"
"NewYork"
"Albany"
"Texas"
"Austin"
В этой таблице ключами словаря являются названия штатов, а значениями
-
названия их столиц.
Обычный словарь отличается от словаря
Python тем, что в Python отношение
между ключом и его значением полностью произвольно. С любым ключом
можно связать любое значение.
Например, следующая таблица пар «ключ
-
значение» вполне допустима:
ЗНАЧЕНИЕ
ключ
"Sunday"
1
'red 11
12:45pm
17
True
Ключи в этой таблице логически не связаны со значениями . Единствен­
ная связь заключается в том, что с каждым ключом связано то или иное
значение.
В этом смысле словарь Python больше похож на отображение, чем на обычный
словарь. Термином «отображение» в математике называется отношение ( соот­
ветствие) между двумя множествами.
Представление словарей как отображений особенно полезно. С этой точки
зрения, обычный словарь представляет собой отображение, связывающее слова
с их определениями.
Словарь
Python представляет собой структуру данных, которая связывает мно­
жество ключей с множеством значений. С каждым ключом связывается одно
значение, которое определяет отношения между двумя множествами.
Создание СflОварей
Следующий фрагмент создает литерал словаря, содержащий названия штатов
и их столиц:
9.6. Храните отношения в словарях
215
»> capitals = {
"California": "Sacramento",
"New York" : "Albany",
"Texas": "Austin",
}
Обратите внимание: каждый ключ отделяется от своего значения двоеточием,
пары «ключ
-
значение» разделяются запятыми, а весь словарь заключается
в фигурные скобки.
Словарь можно также создать из последовательности кортежей при помощи
встроенной функции
dict( ):
>>> key_value_pairs = (
("California", "Sacramento"),
("New York", "Albany"),
("Texas", "Austin"),
>>> capitals
= dict(key_value_pairs)
При проверке переменной, содержащей словарь, ее содержимое выводится
в форме литерала словаря независимо от того, как она была создана:
»> capitals
{'California': 'Sacramento', 'New York': 'Albany', 'Texas' : 'Austin'}
ПРИМЕЧАНИЕ
Если вы воспроизводите наши примеры в версии
Python до 3.6, вы заметите,
что порядок вывода словарей в интерактивном окне отличается от приве­
денного в примерах.
До выхода Pythoп 3.6 порядок пар «ключ - значение» в словаре Pythoп был
случайным . В более поздних версиях этот порядок гарантированно совпадает
с порядком их вставки.
Пустой словарь создается в форме литерала или вызовом
dict( ):
»> {}
{}
»> dict()
{}
Итак, словарь создан. Посмотрим, как обратиться к хранящимся в нем значе­
ниям.
216
ГЛАВА 9
Кортежи, списки и словари
Обращение к значениям словаря
Чтобы обратиться к значению в словаре, укажите ключ в квадратных скоб­
ках
( []) после словаря или имени переменной, который был присвоен словарь:
>>> capitals["Texas"]
'Austin'
Запись с квадратными скобами, используемая для обращения к значению в сло­
варе, похожа на ту, которая использовалась для получения значений из строк,
списков и кортежей. Тем не менее словарь
-
это структура данных, принципиаль­
но отличающаяся от структуры типа последовательность (списка или кортежа).
Чтобы увидеть различия, сделаем шаг назад и заметим, что словарь capitals
можно было бы определить в виде списка:
>>> capitals_list
= ["Sacramento", "Albany", "Austin"]
Индексная запись позволяет получить названия столиц всех трех штатов из
словаря
capi tals:
>>> capitals_list[0] #
Столица Калифорнии
'Sacramento'
>>> capitals_list[2] #
Столица Техаса
'Austin'
Важная особенность словарей заключается в том, что они могут наделять кон­
текстом содержащиеся в них значения. Смысл выражения
capi tals [ "Texas"]
более понятен, чем смысл capitals_list[2], и вам не придется запоминать
порядок следования значений в длинном списке или кортеже.
Концепция упорядочения составляет главное различие между обращением
к элементам из последовательности и элементам из словаря.
Для обращения к значениям в последовательности используются индексы
-
целые числа, отражающие порядок элементов в последовательности.
Обращение к элементам словаря производится по ключу. Ключи не определяют
порядок элементов словаря. Они только предоставляют условную метку, которая
может использоваться для обращения к значению.
Добавление и удаление значений в словарях
Словари, как и списки, являются изменяемыми структурами данных. Это
означает, что элементы можно добавлять и удалять из словаря.
9.6. Храните отношения в словарях
Добавим столицу Колорадо в словарь
>>> capitals("Colorado"]
При вводе
=
217
capitals:
"Denver"
"Colorado" в качестве ключа мы применим квадратные скобки, как
при получении значения. Затем оператор присваивания
присваивания значения
При проверке
со значением
"Denver"
= мы используем для
по новому ключу.
capitals мы видим, что в словаре появился новый ключ "Colorado"
"Denver":
»> capitals
{ 'Cali fornia' : 'Sacramento', 'New York': 'Albany', 'Texas': 'Austin',
'Colorado': 'Denver'}
Каждому ключу в словаре может быть присвоено только одно значение.
Если ключу присваивается новое значение, то
Python
заменяет им старое
значение:
>>> capitals["Texas"] = "Houston"
»> capitals
{'California': 'Sacramento', 'New York': 'Albany', 'Texas': 'Houston',
'Colorado': 'Denver'}
Чтобы удалить элемент из словаря, используйте ключевое слово
del с ключом,
связанным с удаляемым значением:
>>> del capitals["Texas"]
» > capi tals
{'California': 'Sacramento', 'New York': 'Albany',
'Colorado': 'Denver'}
Проверка существования ключа
При попытке обратиться к словарю по несуществующему ключу
ошибку
Python выдает
KeyError:
>>> capitals["Arizona"]
Traceback (most recent call last):
File "<pyshell#l>", line 1, in cmodule>
capitals["Arizona"]
KeyError: 'Arizona'
KeyError - самая распространенная ошибка при работе со словарями. Каждый
раз, когда вы ее встречаете, это означает, что программа попыталась обратиться
к значению по несуществующему ключу.
218
Кортежи, списки и словари
ГЛАВА 9
Чтобы проверить, существует ли ключ в словаре, воспользуйтесь ключевым
словом
in:
>>> "Arizona" in capitals
False
>>> "California" in capitals
True
Ключевое слово
in
позволяет убедиться в том, что ключ существует, прежде
чем вы будете пытаться что-то сделать со значением:
>>> if "Arizona" in capitals:
Вывести только в том случае, если ключ "Arizona" существует
print(f"The capital of Arizona is {capitals['Arizona']}.")
#
Важно помнить, что
in
проверяет существование ключей, а не значений:
>>> "Sacramento" in capitals
False
"Sacramento" является значением для существующего ключа "California"
capitals, проверка его существования возвращает False.
Хотя
в
Перебор содержимого словаря
Словари, как списки и кортежи, итерируемы, то есть поддерживают перебор.
Но он несколько отличается от перебора списка или кортежа. Когда вы пере­
бираете словарь в цикле
for, перебор ведется по ключам словаря:
>>> for key in capitals:
print(key)
California
New York
Colorado
capitals и вывести "The
- название штата, а У - его столица, это можно сде­
Таким образом, если вы хотите перебрать словарь
capi tal of Х is
У", где Х
лать так:
>>> for state in capitals:
print(f"The capital of {state} is {capitals[state]}")
The capital of California is Sacramento
The capital of New York is Albany
The capital of Colorado is Denver
9.6. Храните отношения в словарях
219
Впрочем, существует и более компактная запись, использующая метод словаря
. i tems ();она возвращает объект, похожий на список и содержащий кортежи пар
- значение». Например, capi tals. i tems () возвращает список кортежей
«ключ
штатов и их столиц:
>>> capitals.items()
dict_items( [ ( 'Cali fornia', 'Sacramento'), ( 'New York', 'Albany'),
( 'Colorado' , 'Denver')])
Объект, возвращаемый
. i tems (),
в действительности списком не является.
Он относится к специальному типу
dict_i tems:
>>> type(capitals.items())
<class 'dict_items'>
Не беспокойтесь о том, что собой представляет тип dict_items. Обычно вам
не придется работать с ним напрямую. Важно знать, что метод
. i tems ()
может
использоваться для перебора ключей словаря одновременно со значениями.
Перепишем предыдущий цикл с
. items ():
>>> for state, capital in capitals.items():
print(f"The capital of {state} is {capital}")
The capital of California is Sacramento
The capital of New York is Albany
The capital of Colorado is Denver
При переборе
capitals. items () каждая итерация цикла создает кортеж, со­
state,
capital обеспечивает распаковку компонентов в переменные state и capital.
держащий название штата и его столицы. Присваивание этого кортежа
Ключи словаря и неизменяемость
В словаре
capitals, с которым мы работали в этом разделе, каждый ключ был
строкой. Тем не менее ю1ючи словаря вовсе не обязаны относиться к одному типу.
Например, можно добавить в
capi tals ключ, который является целым числом:
>>> capitals[50] = "Honolulu"
»> capitals
{ 'Cali fornia' : 'Sacramento', 'New York' : 'Albany',
'Colorado': 'Denver', 50: 'Honolulu'}
Существует только одно ограничение на то, какие значения являются валид­
ными ключами словаря. Допустимы только неизменяемые типы. В частности,
это означает, что список не может быть ключом словаря.
ГЛАВА 9
220
Кортежи, списки и словари
Подумайте: что должно произойти, если список используется в качестве ключа
словаря, а потом в коде этот список изменится? Должен ли новый список быть
связан с тем же значением, что и старый список в словаре? Или значение, свя­
занное со старым ключом, должно быть полностью удалено из словаря?
Python
не строит предположения, он выдает исключение:
>>> capitals[[l, 2, З]] = "Bad"
Traceback (most recent call last):
File "cstdin>", line 1, in cmodule>
TypeError: unhashaЫe type: 'list'
Может показаться несправедливым, что одни типы могут быть ключами,
а другие
-
нет, но в языке программирования очень важно четко определенное
поведение. Программа не должна угадывать, что имел в виду программист!
Для справки ниже мы перечислили все типы данных, упоминавшиеся ранее,
которые являются валидными для ключей словарей.
ВАllИДНЬIЕ ТИПЫ кnlОЧЕЙ СЛОВАРЕЙ
Ц елые числа
Числа с плавающей точкой
Строки
Логические
Кортежи
В отличие от ключей, значения словарей могут относиться к любому типу
Python, в том числе быть другими
словарями.
Вложенные словари
По аналогии со списками внутри других списков и кортежами внутри других
кортежей допустимы и вложенные словари. Изменим словарь
продемонстрировать эту возможность:
»> states = {
"California": {
"capital": "Sacramento",
"flower": "Cali fornia Рорру"
},
"New York": {
"capital": "Albany",
"flower": "Rose"
capi tals, чтобы
9.6. Храните отношения в словарях
221
},
"Texas": {
"capital": "Austin",
"flower": "Bluebonnet"
},
Здесь мы не связывали названия штатов со столицами, мы создали словарь,
который связывает название штата со словарем, содержащим название столицы
и цветок
-
символ штата. Значением для каждого ключа является словарь:
>>> states["Texas"]
{ 'capi tal' : 'Austin', 'flower' : 'Bluebonnet'}
Чтобы узнать цветок, который является символом штата Техас, сначала полу­
чите значение с ключом
"Texas ",а затем
значение с ключом
"flower":
>>> states["Texas"]["flower"]
'Bluebonnet'
Вложенные словари встречаются достаточно часто. Они особенно полезны
при работе с данными, передаваемыми по сети. Вложенные словари также
хорошо подходят для моделирования структурированных данных (например,
электронных таблиц или реляционных баз данных).
Упражнения
1.
Создайте пустой словарь с именем
2.
Используя синтаксис с квадратными скобками, включите следующие
captains.
данные в словарь поочередно:
• 'Enterprise': 'Picard'
• 'Voyager': 'Janeway'
• 'Defiant ': 'Sisko'
3.
Напишите две команды, которые проверяют, существуют ли в словаре
ключи
"Enterprise" и "Discovery".
"unknown".
Если ключи не существуют, свяжите
с ними значения
4.
Напишите цикл
for для вывода названия корабля и имени капитана,
содержащихся в словаре. Результат должен выглядеть примерно так:
The Enterprise is captained
5.
Удалите
"Discovery"
Ьу
Picard.
из словаря.
222
6.
ГЛАВА 9
Кортежи, списки и словари
Дополнителыю: создайте тот же словарь с использованием
dict()
и пере­
дайте исходные значения сразу при создании словаря.
9.7. ЗАДАЧА:
ЦИКЛ ПО СТОЛИЦАМ
Вернемся к столицам штатов и применим полученные знания о словарях и ци­
кле
while!
Сначала заполните словарь названиями остальных штатов и их столиц и со­
храните данные в файле
capitals.py:
capitals_dict = {
'Alabama': 'Montgomery',
'Alaska' : 'Juneau',
'Arizona': 'Phoenix',
'Arkansas': 'Little Rock',
'California': 'Sacramento',
'Colorado': 'Denver',
'Connecticut': 'Hartford',
'Delaware': 'Dover',
'Florida': 'Tallahassee',
'Georgia': 'Atlanta',
}
Затем выберите из словаря случайное название штата и присвойте название
штата и его столицы двум переменным. Первой строкой кода своей программы
импортируйте модуль
random.
Затем выведите название штата и предложите пользователю указать столицу.
Если пользователь задал неправильный ответ, повторяйте вопрос, пока поль­
зователь не ответит правильно или не введет команду
"exi t"
для завершения.
Если пользователь ответил правильно, выведите сообщение
"Correct" и за­
вершите программу. Но если пользователь завершает программу без указания
правильного ответа, выведите правильный ответ и слово
"Goodbye".
ПРИМЕЧАНИЕ
Проследите за тем, чтобы пользователь не пострадал из-за регистра символов.
Другими словами, ответ «Denver» считается идентичным «deпver». Сделайте
то же самое для команды выхода - «EXIT» и «Exit» должны работать точно так
же, как и
«exit». -
Решение задачи, а также многие другие дополнительные ресурсы доступны
в интернете по адресу
realpython.com/python-basics/resources.
9.9. Задача: коты в шляпах
9.8.
223
КАК ВЫБРАТЬ СТРУКТУРУ ДАННЫХ
Итак, мы рассказали вам о трех структурах данных, реализованных в
Python:
списках, кортежах и словарях.
Возникает вопрос: как понять, когда использовать ту или иную структуру
данных? Отличный вопрос, который возникает у многих новичков в
Python.
Тип структуры данных зависит от решаемой задачи, и не существует никаких
однозначных правил, которые регламентировали бы выбор. Всегда необходимо
хорошенько обдумать задачу и сознательно выбрать структуру, которая лучше
всего подойдет для конкретного случая.
К счастью, все-таки есть некоторые рекомендации, которые помогут вам:
•
•
•
Используйте список, если выполняются следующие условия:
•
у данных имеется естественный порядок;
•
данные будут обновляться или изменяться во время выполнения;
•
структура данных будет использоваться в основном для перебора.
Используйте кортеж, если выполняются следующие условия:
•
у данных имеется естественный порядок;
•
данные не будут обновляться или изменяться во время выполнения;
•
структура данных будет использоваться в основном для перебора.
Используйте словарь, если выполняются следующие условия:
•
данные не упорядочены или их порядок роли не играет;
•
данные будут обновляться или изменяться во время выполнения;
•
структура данных будет использоваться в основном для выборки
значений.
9.9. ЗАДАЧА:
КОТЫ В ШЛЯПАХ
У вас сто котов. Однажды вы решаете рассадить всех своих котов в большой
круг. Изначально ни один из них не носит шляпы. Вы сто раз обходите круг,
всегда начиная с первого кота (кот №
1).
Каждый раз, когда вы останавливае­
тесь рядом с котом, вы проверяете, есть ли на нем шляпа. Если шляпы нет, то
вы надеваете шляпу на кота, а если есть
1.
-
снимаете ее.
На первом круге вы останавливаетесь у каждого кота и надеваете на него
шляпу.
ГЛАВА
224
2.
9
Кортежи, списки и словари
На втором круге вы останавливаетесь у каждого второго кота (№
2, No 4,
No 6, No 8 и т. д.).
3.
На третьем круге вы останавливаетесь у каждого третьего кота
(No 3,
No 6, No 9, No 12 и т. д.).
4.
Процесс продолжается до тех пор, пока вы не обойдете круг сто раз. При
последнем обходе вы останавливаетесь только у кота
No 100.
Напишите программу, которая выводит, на каких котах будут шляпы после
всех обходов.
ПРИМЕЧАНИЕ
Эта задача не очень простая, но и не настолько сложная, как может показаться
на первый взгляд. Подобная задача часто встречается на собеседованиях при
приеме на работу, так как она показывает вашу сообразительность.
Сохраняйте спокойствие. Начните с диаграммы и псевдокода. Найдите за­
кономерность, а потом переходите к программированию!
Решение задачи, а также многие другие дополнительные ресурсы доступны
в интернете по адресу
9.10.
realpython.com/python-basics/resources.
ИТОГИ И ДОПОЛНИТЕЛЬНЫЕ РЕСУРСЫ
В этой главе я рассказал о трех структурах данных: списках, кортежах и словарях.
Списки являются изменяемыми последовательностями объектов. Для взаимо­
. аррепd ()
. sort (). Для об­
действия со списками используются различные методы, включая
и
. exteпd ().
Сортировка списков осуществляется методом
ращения к отдельным элементам списков используется запись, сходная с ана­
логичной записью строк. К спискам можно применять срезы.
Кортежи, как и списки, являются последовательностями объектов. Главное
отличие списков от кортежей заключается в том, что кортежи неизменяемы.
Созданный кортеж модифицировать невозможно. К элементам кортежей, как
и к элементам списков, можно обращаться по индексам и применять срезы.
Словари хранят данные в виде пар «ключ
-
значение~.>. Они не являются
последовательностями, а значит, к элементам словаря нельзя обратиться по
порядковому номеру. Для обращения к элементу указывают ключ. Словари
прекрасно подходят для хранения отношений и для быстрого доступа к данным.
Словари, как и списки, являются изменяемыми.
9.1 О. Итоги и дополнительные ресурсы
225
Списки, кортежи и словари являются итерируемыми, то есть их элементы
можно перебирать в цикле. Мы рассмотрели примеры циклического перебора
всех трех структур в циклах
for.
ИНТЕРАКТИВНЫЙ ТЕСТ
К этой главе прилагается бесплатный интерактивный тест для проверки усво­
енных вами знаний. Тест доступен на телефоне или компьютере :
realpython.com/quizzes/ pybasics-tuples-lists-dicts
Дополнительные ресурсы
Дополнительную информацию о списках, кортежах и словарях вы найдете на
ресурсах:
•
«Lists and Tuples in Python» (https:j/realpython.com/python-lists-tuples/)
•
«Dictionaries in Python» ( https:j/realpython.com/python-dicts/)
ГЛАВА
10
Объектно-ориентированное
программирование (ООП)
Объектно-ориентированное программирование (ООП) представляет собой
методологию структурирования программы за счет упаковки взаимосвязанных
свойств и поведения в отдельные объекты.
Представьте программу как своего рода сборочную линию: на каждом этапе
сборки отдельный узел системы (объект) обрабатывает материал, а в конечном
итоге из сырья получается конечный продукт.
Объект содержит данные (аналог сырья или предварительно обработанных
материалов на каждом этапе сборочной линии) и задает поведение (действие,
выполняемое каждым компонентом сборочной линии).
В этой главе вы научитесь:
это аналог чертежа для создания объектов;
•
создавать класс
•
использовать классы для создания новых объектов;
•
моделировать системы с наследованием классов.
-
Итак, за дело!
10.1. ОПРЕДЕЛЕНИЕ
КЛАССА
Примитивные структуры данных
-
такие, как числа, строки, списки,
-
созда­
вались для представления простой информации: стоимости килограмма яблок,
названия стихотворения, ваши любимые цвета ... А если надо представить что-то
более сложное?
Допустим, вы хотите создать базу данных сотрудников в организации. Требуется
хранить о каждом из них основную информацию: имя, возраст, должность и год
поступления на работу.
10.1. Определение класса
Одно из возможных решений
-
227
представить информацию о каждом сотруднике
в виде списка:
kirk = ["James Kirk", 34, "Captain", 2265)
spock
["Spock", 35, "Science Officer", 2254)
mccoy = ["Leonard МсСоу", "Chief Medical Officer", 2266)
У такого подхода масса недостатков.
Во-первых, усложняется сопровождение файлов с большим количеством кода.
Если вы обратитесь к kirk[0] спустя какое-то время после объявления списка
kirk, будете ли вы помнить, что элемент с индексом О содержит имя работника?
Во-вторых, возможно возникновение ошибки, если для разных работников
списки содержат разное количество элементов. В списке mccoy из приведенно­
го выше примера возраст не указан, поэтому
строку с должностью
mccoy[l]
вместо возраста вернет
"Chief Medical Officer".
Если вы хотите сделать такой код более управляемым и удобным в сопрово­
ждении, используйте классы
- это отличный выход.
Классы и экземпляры
Классы применяются для создания пользовательских структур данных. Клас­
сы определяют функции, называемые методами; в них задаются поведение
и действия, которые объект, созданный на основе класса, сможет выполнять
со своими данными.
В этой главе мы создадим класс Dog для хранения информации о свойствах
и поведении собак.
Класс представляет собой «чертеж», то есть прототип для определения объектов.
Он не содержит никаких данных. Класс Dog указывает, что кличка и возраст
необходимы для определения собаки, но он не содержит клички и возраста
никакой конкретной собаки.
Если класс можно сравнить с чертежом, то экземпляр представляет собой объ­
ект, построенный на основе класса; он содержит реальные данные. Экземпляр
класса Dog уже не является чертежом. Он представляет конкретную собаку
-
например, это может быть Майлз четырех лет от роду.
Иначе говоря, класс можно сравнить с бланком или анкетой, а экземпляр
-
это
анкета, заполненная информацией. Подобно тому как один вид анкеты разные
люди могут заполнить данными, уникальными для каждого из них, на основе
одного класса можно создать множество экземпляров.
228
ГЛАВА 10
Объектно-ориентированное программирование (ООП)
Как определить класс
Все определения классов начинаются с ключевого слова
class,
за которым
следует имя класса и двоеточие. Любой код с отступом, расположенный под
определением класса, считается частью тела класса.
Пример класса
Dog:
class Dog:
pass
Тело класса
Dog состоит из одной команды: ключевого слова pass. Оно часто
используется в качестве заполнителя, обозначающего, где в будущем появится
код. Это позволит запустить код, при этом
Python не будет выдавать сообще­
ния об ошибках.
ПРИМЕЧАНИЕ
В
Python имена классов принято записывать по ВотТакойСхеме, то есть слова
сшиваются без пробелов и нижнего подчеркивания, через использование
заглавных букв . Например, имя класса для конкретной породы собак может
быть записано в виде JackRussellTerrier.
Класс
Dog пока не особо интересен. Давайте немного улучшим его, добавив
Dog. Таких свойств
свойства, которые должны быть общими для всех объектов
может быть достаточно много: кличка, возраст, цвет, порода и т. д. Для простоты
мы ограничимся кличкой и возрастом.
Свойства, общие для всех объектов
Dog, должны определяться в методе
._init_(). При создании нового объекта Dog метод ._init_() задает ис­
ходное состояние объекта, присваивая значения свойствам объекта. Иначе
говоря, метод
._init_()
инициализирует каждый экземпляр класса.
Методу
._init_() может передаваться любое количество параметров, но
sel f. При создании
нового экземпляра класса экземпляр автоматически передается в параметре sel f
при вызове ._init_( ), чтобы для объекта можно было задать новые атрибуты.
первым параметром всегда является переменная с именем
Обновим класс
и
Dog и добавим метод ._init_(), создающий атрибуты .name
.age:
class Dog:
def
~init~(self,
name, age):
10.1. Определение класса
229
self .name = name
self .age = age
Обратите внимание: сигнатура метода
пробелов, а тело метода
сообщают
В теле
менная
Python,
-
._init_() имеет отступ из четырех
из восьми пробелов. Эти отступы очень важны. Они
что метод ._init_() принадлежит классу Dog.
._init_()
sel f.
содержатся две команды, в которых используется пере­
1. self. name = name
чение параметра
создает атрибут с именем
name и присваивает ему зна­
name.
2. sel f. age = age создает атрибут с именем age и присваивает ему значение
параметра
age.
Атрибуты, созданные в ._init_(), называются атрибутами экземпляров.
Значение атрибута экземпляра относится к конкретному экземпляру класса.
Все объекты
Dog обладают атрибутами name и age, но значения name и age зависят
Dog.
от конкретного экземпляра
С другой стороны, атрибуты класса имеют одно и то же значение для всех
экземпляров класса. Атрибут класса можно определить, присваивая значение
переменной
name
за пределами
._init_().
Например, следующий класс
Dog содержит атрибут класса с именем species
"Canis familiaris":
и значением
class Dog:
#
Атрибут класса
species
def
=
"Canis familiaris"
name, age):
self .name = name
self .age = age
~init~(self,
Атрибуты класса определяются непосредственно под первой строкой имени
класса с отступом в четыре пробела. Им всегда должно присваиваться исходное
значение. При создании экземпляра класса атрибуты класса формируются ав­
томатически, и им присваиваются исходные значения. Используйте атрибуты
класса для определения свойств, которые должны иметь одно и то же значение
для всех экземпляров класса. Используйте атрибуты экземпляров для свойств,
которые отличаются в разных экземплярах.
Итак, класс
Dog
готов. Переходим к созданию экземпляров!
ГЛАВА
230
1О
Объектно-ориентированное программирование (ООП)
10.2. СОЗДАНИЕ
ЭКЗЕМПЛЯРОВ
(ИНСТАНЦИРОВАНИЕ)
Откройте интерактивное окно в
IDLE
и введите следующий фрагмент:
»> class Dog:
pass
Тем самым вы создаете новый класс Dog, не содержащий ни атрибутов, ни
методов.
Создание нового объекта класса называется инстанцированием.
Чтобы создать новый объект Dog, введите имя класса, за которым следует пара
круглых скобок:
»> Dog()
<~main~.Dog
object at 0xl06702d30>
Теперь новый объект Dog существует по адресу
ка из букв и цифр
-
Ox106702d30.
Загадочная стро­
адрес памяти, указывающий, где объект Dog хранится
в памяти компьютера. Учтите, что адрес, который вы увидите в своей системе,
будет другим.
Теперь создайте второй объект Dog:
»> Dog()
<~main~.Dog
object at
0х0004ссс90>
Новый экземпляр Dog располагается по другому адресу памяти. Дело в том, что
это совершенно новый экземпляр, который никак не связан с первым объектом
Dog,
созданным ранее.
Можно посмотреть на это иначе. Введите следующий фрагмент:
»> а = Dog()
»> Ь = Dog()
>>> а == Ь
False
В этом коде создаются два объекта Dog, которые присваиваются переменным а
и Ь. При сравнении а и Ь оператором== будет получен результат False. Хотя
как а, так и Ь являются экземплярами класса
объекта в памяти.
Dog,
они представляют два разных
10.2. Создание экземпляров (инстанцирование)
231
Атрибуты класса и экземпляра
Теперь создайте новый класс Dog с атрибутом класса
тами экземпляров
. name
и
. species
и двумя атрибу­
. age:
»> class Dog:
species = "Canis familiaris"
def ~init~(self, name, age):
self .name = name
self .age = age
»>
Чтобы инстанцировать объект класса Dog, необходимо задать значения для name
и age. Если этого не сделать,
Python
выдаст ошибку TypeError:
»> Dog()
Traceback (most recent call last):
File "<pyshell#б>", line 1, in <module>
Dog()
TypeError: ~init~() missing 2 required positional
arguments: 'name' and 'age'
Чтобы передать аргументы для параметров name и age, укажите соответствующие
значения в круглых скобках после имени класса:
>>> buddy
>>> miles
Dog("Buddy", 9)
Dog("Miles", 4)
Этот фрагмент создает два экземпляра
собаку по кличке Buddy, а другой
Метод
._init_()
класса
Dog
-
Dog -
один представляет девятилетнюю
четырехлетнюю собаку по кличке Miles.
получает три параметра, тогда почему в этом
примере ему передаются только два аргумента?
При инстанцировании объекта Dog
Python создает новый экземпляр и передает
._ini t_()" задавая self, и вам придется думать только
age.
его в первом параметре
о параметрах
name
и
После того как экземпляры Dog будут созданы, вы можете обращаться к их
атрибутам экземпляров с использованием точечной нотации:
» > buddy. name
'Buddy'
» > buddy. age
9
»> miles.name
232
ГЛАВА
1О
Объектно-ориентированное программирование (ООП)
'Miles'
>>> miles.age
4
Аналогичным образом можно обращаться к атрибутам класса:
>>> buddy.species
'Canis familiaris'
Одно из самых больших преимуществ использования классов для организа­
ции данных заключается в том, что экземпляры гарантированно содержат все
заданные атрибуты. Все экземпляры
и
. age,
Dog содержат атрибуты . species, . name
и вы можете обращаться к ним с полной уверенностью в том, что они
всегда возвращают какое-либо значение.
Хотя атрибуты заведомо существуют, их значения можно менять динамически:
>>> buddy.age = 10
» > buddy. age
10
>>> miles.species
>>> miles.species
= "Felis silvestris"
'Felis silvestris'
В этом примере вы меняете значение атрибута
. age
в объекте
buddy на 10. За­
тем атрибут . species объекта miles изменяется на "Felis sil vestris" - одну
из разновидностей кошек. Конечно, собака получается странная, но сам код на
языке
Python абсолютно допустим!
Главный вывод заключается в том, что пользовательские объекты по умолча­
нию изменяемы. Вспомните: объект называется изменяемым, если он может
изменяться динамически во время выполнения. Например, списки и словари
являются изменяемыми, а строки и кортежи неизменяемы.
Методы экземпляров
Методами экземпляров называют функции, которые определяются внутри
класса и могут вызываться только из экземпляра этого класса. Как и в случае
с
._init_(), первым
параметром метода экземпляра всегда является
Откройте новое окно редактора в
class Dog:
species
"Canis familiaris"
IDLE
и задайте следующий класс
self.
Dog:
10.2. Создание экземпляров (инстанцирование)
def
#
233
name, age):
self.name = name
self. age = age
~init~(self,
Метод экземпляра
def description(self):
return f"{self.name} is {self.age} years old"
# Другой метод экземпляра
def speak(self, sound):
return f"{self.name} says {sound}"
Этот класс
Dog
1.
Метод
2.
Метод
содержит два метода экземпляра.
. description()
. speak()
возвращает строку с кличкой и возрастом собаки.
получает один параметр
sound
и возвращает строку
с кличкой собаки и транскрипцией звуков, которые она издает.
Сохраните измененный класс
вишей
FS.
Dog в файле dog.py и запустите программу кла­
Затем откройте интерактивное окно и введите следующие команды,
чтобы посмотреть на методы экземпляров в действии:
>>> miles
=
Dog("Miles", 4)
>>> miles.description()
'Miles is 4 years old'
>>> miles.speak("Woof Woof")
'Miles says Woof Woof'
>>> miles.speak("Bow Wow")
'Miles says Bow Wow'
В этом классе
Dog метод .description() возвращает строку, содержащую инфор­
мацию об экземпляре Dog из переменной miles. При написании собственных
классов желательно создать метод, который возвращает строку с полезной
информацией об экземпляре класса. Тем не менее метод
.description() - не
самый питонический способ.
При создании объекта типа
list можно воспользоваться функцией print () для
вывода строки, которая выглядит как список:
>>> names = ["David", "Dan", "Joanna", "Fletcher"]
»> print(names)
[ 'David', 'Dan', 'Joanna', 'Fletcher']
ГЛАВА 1 О
234
Объектно-ориентированное программирование (ООП)
А теперь посмотрим, что происходит при выводе объекта
miles функцией
print():
»> print(miles)
object at 0x00aeff70>
<~main~.Dog
При вызове
print(miles) вы получаете загадочное сообщение, из которого мож­
Dog находится в памяти по адресу Ox00aeft70. Пользы от
но узнать, что объект
такого сообщения немного. Чтобы изменить выводимую информацию, следует
определить специальный метод экземпляра с именем
._str_( ).
В окне редактора измените имя метода.
класса
class Dog:
# Остальные
#Замените
def
части
класса
.description()
Dog
description()
остаются без
Dog на ._str_( ):
изменений
на ~str~()
~str~(self):
return f"{self.name} is {self.age} years old"
Сохраните файл и нажмите
FS.
Теперь при вызове
print(miles)
вы получите
намного более понятный вывод:
>>> miles - Dog("Miles", 4)
»> print(miles)
'Miles is 4 years old'
Такие методы, как
( dunder -
._init_() и ._str _(),называются duпdеr-методами
сокращение от douЫe
underscores,
то есть двойное подчеркива­
ние). Существует много dunder-мeтoдoв, которые могут использоваться для
настройки поведения классов в
Python.
И хотя эта тема слишком сложна для
книги начального уровня, понимание dunder-мeтoдoв очень важно для освоения
ООП на языке
Python.
В следующем разделе вы увидите, как создавать классы на основе других классов.
Но сначала проверьте, насколько вы усвоили материал, выполнив несколько
упражнений.
Упражнения
1.
Dog и включите в него третий атрибут экземпляра с име­
coat_color, в котором будет храниться цвет шерсти собаки в виде
Измените класс
нем
строки. Сохраните новый класс в файле и протестируйте его, добавив
следующий фрагмент в конец программы:
10.3. Наследование от других классов
235
philo = Dog("Philo", 5, "brown")
print(f"{philo.name}'s coat is {philo.coat_color}.")
Программа должна выводить следующий результат:
Philo's coat is brown.
2.
Создайте класс
Car с двумя атрибутами экземпляров: . color для хранения
. mileage для хранения пробега в милях
в виде целого числа. Создайте два объекта Car - синюю машину с про­
цвета машины в виде строки и
бегом
20
ООО и красную с пробегом
30
ООО. Выведите данные на каждую
машину. Результат должен выглядеть так:
The Ыuе car has 20,000 miles.
The red car has 30,000 miles.
3.
Измените класс
Car и добавьте в него метод экземпляра .drive(), который
получает числовой аргумент и прибавляет его к атрибуту . mileage. Убе­
дитесь в том, что ваше решение работает; создайте экземпляр с нулевым
пробегом, вызовите .drive(100) и выведите атрибут .mileage, чтобы
убедиться в том, что он принял значение 100.
10.3.
НАСЛЕДОВАНИЕ ОТ ДРУГИХ КЛАССОВ
Наследованием называется процесс, в котором один класс получает атрибуты
и методы другого класса. Создаваемый класс называется производным, или
дочерним, классом, а классы, от которых наследуют производные классы, на­
зываются родительскими.
Дочерние классы могут переопределять или расширять атрибуты и методы
родительских классов. Другими словами, они наследуют все атрибуты и мето­
ды родительских классов, но также могут задавать свои уникальные атрибуты
и методы.
Хотя аналогия не идеальна, но наследование объектов можно рассматривать
как некое подобие наследования генетического. Возможно, вы унаследовали
цвет волос от своей матери. Это атрибут, с которым вы родились. Допустим,
вы решили окрасить волосы в фиолетовый цвет. Вы успешно переопределили
атрибут цвета волос, унаследованный от матери (предполагается, что ее волосы
не были фиолетовыми).
Также вы в каком-то смысле наследуете от родителей язык общения. Если ваши
родители говорят на английском, то и вы будете говорить на английском. Те­
перь представьте, что вы решили выучить второй язык
-
допустим, немецкий.
236
ГЛАВА
1О
Объектно-ориентированное программирование (ООП)
В таком случае набор атрибутов расширяется, потому что вы добавили атрибут,
которого не было у ваших родителей.
Пример с собачьей площадкой
Вообразите, что вы находитесь на собачьей площадке. На ней много собак раз­
ных пород, и все они заняты своими собачьими делами.
Вы хотите смоделировать площадку при помощи классов
Python.
Класс Dog,
написанный в предыдущем разделе, позволяет различать собак по кличке и воз­
расту, но не по породе.
Мы можем изменить класс
Dog в окне редактора и добавить в него атрибут . breed:
class Dog:
species = "Canis familiaris"
def
name, age, breed):
self .name = name
self .age = age
self.breed = breed
~init~(self,
Методы экземпляров, определенные ранее, здесь не указаны, потому что они
для данного обсуждения не важны.
Нажмите
FS, чтобы сохранить файл. Теперь вы можете смоделировать собачью
площадку, задав параметры нескольких разных собак в интерактивном окне:
>>> miles = Dog("Miles", 4, "Jack Russell Terrier")
>>> buddy = Dog("Buddy", 9, "Dachshund")
»> jack = Dog("Jack", З, "Bulldog")
»> jim = Dog("Jim", 5, "Bulldog")
Разные породы собак имеют разные отличительные особенности. Например,
высокий и пронзительный.
у бульдогов лай низкий, а у такс
-
Если использовать только класс
Dog,
можно передавать методу
. speak
строку
с транскрипцией лая каждый раз, когда этот метод вызывается для экземпляра
>>> buddy.speak("Vap")
'Buddy says
Уар'
>>> jim.speak("Woof")
'Jim says Woof'
>>> jack.speak("Woof")
'Jack says Woof'
Dog:
10.3. Наследование от других классов
Впрочем, решение с передачей строки при каждом вызове
. speak()
237
однообраз­
но и неудобно. Более того, строка, представляющая звук, издаваемый каждым
экземпляром Dog, должна определяться его атрибутом . breed, а нам приходится
вручную передавать правильную строку
. speak ()
при каждом вызове.
Чтобы с классом Dog было удобнее работать, мы создадим отдельный дочерний
класс для каждой породы собак. Это позволит расширить функциональность,
наследуемую каждым производным классом, включая определение аргумента
по умолчанию для
. s реа k ( ) .
Родительские классы и дочерние классы
Создадим дочерний класс для каждой из трех пород, упоминавшихся ра­
нее: джек-рассел-терьер
Uack Russell Terrier),
такса
(Dachshund)
и бульдог
(Bulldog).
Для удобства приведу полное определение класса Dog:
class Dog:
species • "Canis familiaris"
name, age):
self .name • name
self .age • age
def
~init~(self,
def
~str~(self):
return f"{self.name} is {self.age} years old"
def speak(self, sound):
return f"{self.name} says {sound}"
Чтобы создать дочерний класс, создайте новый класс с соответствующим именем
и укажите имя родительского класса в круглых скобках. Следующий фрагмент
создает три новых класса, производных от
Dog:
class JackRussellTerrier(Dog):
pass
class Dachshund(Dog):
pass
class Bulldog(Dog):
pass
После того как дочерние классы будут определены, можно создать экземпляры
собак конкретных пород:
238
>>>
>>>
>>>
>>>
ГЛАВА
1О
Объектно-ориентированное программирование (ООП)
miles = JackRussellTerrier("Miles", 4)
buddy = Dachshund("Buddy", 9)
jack = Bulldog("Jack", З)
jim = Bulldog("Jim", 5)
Экземпляры дочерних классов наследуют все атрибуты и методы родительского
класса:
>>> miles.species
'Canis familiaris'
>>> buddy.name
'Buddy'
»> print(jack)
Jack is З years old
>>> jim.speak("Woof")
'Jim says Woof'
Чтобы увидеть, к какому классу принадлежит объект, воспользуйтесь встро­
енной функцией
type():
»> type(miles)
<class '~main~.JackRussellTerrier'>
А если вы захотите узнать, является ли
В этом вам поможет встроенная
miles также экземпляром
функция isinstance( ):
класса
Dog?
>>> isinstance(miles, Dog)
True
Обратите внимание: функция
isinstance() получает два аргумента, объект
и класс. В данном случае isinstance() проверяет, является ли miles экземпля­
ром класса Dog, и возвращает True.
Все объекты,
miles, buddy, jack и jim, являются экземплярами Dog, но miles
Bulldog, а jack не является экэемпляром Dachshund:
не является экземпляром
>>> isinstance(miles, Bulldog)
False
>>> isinstance(jack, Dachshund)
False
В более общем понимании все объекты, созданные на основе дочерних классов,
являются экземплярами родительского класса, хотя при этом не являются
экземплярами других дочерних классов.
10.3. Наследование от других классов
239
Итак, мы создали дочерние классы для нескольких пород собак. Теперь назначим
каждой породе транскрипцию звука, который она издает.
Расширение функциональности
родительского класса
Так как разные породы собак лают по-разному, мы хотим предоставить значение
по умолчанию для аргумента
sound
этого необходимо переопределить
в соответствующем методе
. speak()
. speak ().
Для
в определении класса для каждой
породы.
Чтобы переопределить метод, заданный в родительском классе, вы опреде­
ляете одноименный метод в дочернем классе. Вот как это делается в классе
JackRussell Terrier:
class JackRussellTerrier(Dog):
def speak(self, sound="Arf"):
return f"{self.name} says {sound}"
Теперь метод
. speak()
по умолчанию для
определен в классе JackRussellТerrier с аргументом
sound,
которому присвоено значение
"Arf".
Обновите файл
dog.py новой версией класса JackRussell Terrier и нажмите FS,
. speak() для
JackRussell Terrier без передачи аргумента для sound:
чтобы сохранить и запустить файл. Теперь можно вызвать метод
экземпляра
>>> miles = JackRussellTerrier("Miles", 4)
>>> miles.speak()
'Miles says Arf'
В некоторых ситуациях одна и та же собака издает разные звуки, так что, если
Майлз рассердится и зарычит, вы все еще сможете вызвать
. speak()
с другим
звуком:
>>> miles.speak("Grrr")
'Miles says Grrr'
Имея дело с наследованием классов, важно помнить, что изменения в роди­
тельском классе автоматически распространяются на дочерние классы
-
при
условии, что изменяемый атрибут или метод не был переопределен в дочернем
классе.
Например, измените в окне редактора строку, возвращаемую методом
из класса
Dog:
. speak()
ГЛАВА
240
1О
Объектно-ориентированное программирование (ООП)
class Dog:
#
Другие атрибуты и методы остаются неизменными
.speak()
def speak(self, sound):
return f"{self.name} barks: {sound}"
# Изменяется строка, возвращаемая
FS. Теперь при создании нового экземпляра Bulldog
jim метод jim. speak() вернет новую строку:
Сохраните файл и нажмите
с именем
>>> jim • Bulldog("Jim", 5)
>>> jim.speak("Woof")
'Jim barks: Woof'
Однако при вызове
. speak()
для экземпляра
JackRussell Terrier новый
формат
вывода не используется:
>>> miles • JackRussellTerrier("Miles", 4)
>>> miles.speak()
'Miles says Arf'
Иногда полное переопределение метода из родительского класса оправданно.
Но в данном случае мы не хотим, чтобы в классе
JackRussell Terrier терялись
Dog. speak( ).
изменения, внесенные в форматирование выходной строки
Как и прежде, задача решается определением метода . speak() в дочернем классе
JackRussell Terrier. Но вместо того, чтобы явно определять выходную строку,
мы вызовем метод . speak () класса Dog внутри метода . speak () дочернего класса
с теми же аргументами, которые передавались JackRussell Terrier. speak( ).
Для обращения к родительскому классу из метода дочернего класса использу­
ется вызов
super( ):
class JackRussellTerrier(Dog):
def speak(self, sound•"Arf"):
return super().speak(sound)
Когда вы вызываете
super(). speak ( sound) внутри JackRussell Terrier, Pythoп
Dog метод . speak () и вызывает его с переменной
ищет в родительском классе
sound.
Обновите файл
dog.py
обновленной версией класса
храните файл и нажмите
FS,
>>> miles • JackRussellTerrier("Miles", 4)
>>> miles.speak()
'Miles barks: Arf'
JackRussell Terrier. Со­
чтобы протестировать его в интерактивном окне:
10.3. Наследование от других классов
Теперь при вызове
241
miles. speak() вывод будет отражать новое форматирование
в классе.
ВАЖНО!
В этом примере иерархия классов очень проста: у класса
всего один родительский класс
Dog. В
JackRussellTerrier
реальных примерах иерархии классов
могут быть достаточно сложными.
Вызов
.super() не ограничивается простым поиском метода или атрибута
в родительском классе. Он обходит всю иерархию классов в поисках подхо­
дящего метода или атрибута. При малейшей невнимательности
super() может
привести к удивительным результатам.
В следующем разделе мы объединим все, что вы узнали о классах, для создания
модели фермы. Но прежде чем браться за задание, проверьте, насколько вы по­
няли материал, выполнив несколько упражнений.
Упражнения
1.
Создайте класс
GoldenRetriever, наследующий от класса Dog. Задайте
sound метода GoldenRetriever. speak () значение по умолча­
"Bark". Используйте следующий код для родительского класса Dog:
аргументу
нию
class Dog:
species
=
"Canis familiaris"
def
~init~(self,
name, age):
self.name = name
self.age = age
def
~str~(self):
return f"{self.name} is {self.age} years old"
def speak(self, sound):
return f"{self.name} says {sound}"
2.
Напишите класс
Rectangle, представляющий прямоугольник. Экзем­
. length и . width.
Добавьте в класс метод . а rea (), который возвращает площадь прямо­
угольника (length * width).
пляры класса должны создаваться с двумя атрибутами:
Затем напишите класс
Square, представляющий квадрат. Этот класс дол­
Rectangle и создаваться с одним атрибутом
. side_length. Протестируйте класс Square: создайте экземпляр Square
с атрибутом . side_length, равным 4. Вызов . area () должен возвращать 16.
жен наследовать от класса
242
ГЛАВА 1 О
Объектно-ориентированное программирование (ООП)
. width вашего экземпляра Square значение 5.
. area (). Метод должен вернуть значение 20.
Задайте свойству
снова вызовите
Затем
Этот пример показывает, что наследование классов не всегда наилучшим
образом моделирует отношения между подмножествами. В математике
любой квадрат является прямоугольником, но в компьютерных про­
граммах это не всегда так.
Хорошо продумывайте варианты поведения, чтобы они выполняли
именно то, что вы задумали, и используйте иерархии классов с осто­
рожностью.
10.4. ЗАДАЧА:
МОДЕЛЬ ФЕРМЫ
В этой задаче вы создадите упрощенную модель фермы. Не упускайте из виду,
что правильных ответов может быть несколько.
В этой задаче на первый план выходит не столько синтаксис классов
Python,
сколько проектирование программных структур вообще, а это в высшей степени
субъективно. Задачу мы намеренно сформулировали открытой, чтобы вы по­
думали над тем, как организовать ваш код по классам.
Прежде чем начинать программирование, возьмите бумагу и ручку и набросай­
те модель своей фермы, включая классы, атрибуты и методы. Подумайте над
наследованием. Как предотвратить дублирование кода? Не жалейте времени
и проработайте столько вариантов модели, сколько посчитаете нужным.
Требования задачи открыты для интерпретации, но постарайтесь придержи­
ваться следующих рекомендаций.
1.
В программе должно быть по крайней мере четыре класса: родительский
Animal и не
ющих от Animal.
класс
2.
менее трех производных классов животных, наследу­
Каждый класс должен содержать несколько атрибутов и по крайней
мере один метод, моделирующий поведение, присущее конкретному
животному или всем животным,
-
они должны ходить, бегать, есть,
спать и т. д.
3.
Не усложняйте. Используйте наследование. Предусмотрите воэможность
вывода подробной информации о животных и их поведении.
Решение задачи, а также многие другие дополнительные ресурсы доступны
в интернете по адресу
realpython.com/python-basics/resources.
10.5. Итоги и дополнительные ресурсы
243
10.5. ИТОГИ И ДОПОЛНИТЕЛЬНЫЕ РЕСУРСЫ
В этой главе вы узнали об объектно-ориентированном программировании
(ООП)
-
парадигме программирования, используемой во многих современных
языках, включаяjаvа, С#, С++ и
Python.
Вы узнали, как определить класс
-
своего рода чертеж для строительства
объектов, а также создавать экземпляры на основе класса. Также вы узнали
об атрибутах, соответствующих свойствам объекта, и методах, определяющих
особенности поведения и действия объекта.
Наконец, вы узнали, как работает механизм наследования, создающий производ­
ные классы на основе родительского класса. Далее я показал, как обращаться
к методу родительского класса при помощи вызова
наследует ли объект от другого класса, при помощи
super() и как
isinstance().
проверить,
ИНТЕРАКТИВНЫЙ ТЕСТ
К этой главе прилагается бесплатный интерактивный тест для проверки усво­
енных вами знаний. Тест доступен на телефоне или компьютере:
realpythoп.com/quizzes/pybasics-oop
Дополнительные ресурсы
В этой главе вы познакомились с основами ООП, но вам предстоит еще очень
много узнать по этой теме! За дополнительной информацией обращайтесь
к следующим ресурсам:
•
официальная документация
Python (https.j/docs.python.org/3/tutorial/
classes.html)
•
статьи об ООП на сайте
Real Python (https.j/realpython.com/search?q=oop)
ГЛАВА
11
Модули и пакеты
По мере накопления опыта вы начнете браться за программирование больших
проектов, и в какой-то момент вам станет ясно, что хранить весь код проекта
в одном файле очень неудобно.
Но есть выход: использовать не один файл, а распределить код по нескольким
файлам
-
модулям. Далее вы в любой момент можете собрать отдельные
модули вместе и построить из них большое приложение, как строят дом из
кирпичей.
В этой главе вы научитесь:
•
создавать собственный модуль;
•
использовать модуль в других файлах командой
•
объединять несколько модулей в пакет.
import;
Итак, за дело!
11.1. РАБОТА
Модуль
-
С МОДУЛЯМИ
это файл, содержащий код
пользоваться в других файлах с кодом
Python,
Python.
Собственно говоря, каждый файл с кодом
который может повторно ис­
Python, который вы создали при изу­
чении этой книги, был модулем, но вы пока не знаете, как использовать код из
одного модуля в другом модуле.
Разбиение программы на модули имеет четыре важных достоинства.
1.
Простота: модуль решает одну задачу.
2.
Удобство сопровождения: маленькие файлы лучше больших.
11.1. Работа с модулями
3.
245
Удобство повторноrо использования: модули сокращают дублирование
кода.
4.
Определение областей видимости: модули имеют собственные про­
странства имен.
Этот раздел посвящен модулям. Вы научитесь создавать их в
IDLE, импортиро­
вать один модуль в другой, а также узнаете, как модули создают пространства
имен.
Создание модулей
Запустите
IDLE
и откройте новое окно редактора командой File ~ New File или
нажатием клавиш Ctrl+N. В окне редактора определите функцию add (), которая
возвращает сумму двух своих параметров:
adder.py
def add(x,
return
#
у):
х
+
у
Выберите команду File ~ Save или нажмите клавиши Ctrl+S, чтобы сохранить
файл с именем
Python!
adder.py в новом каталоге myproject/. Файл adder.py- это модуль
Он не является законченной программой, но от модулей этого и не
требуется.
Теперь откройте другое окно редактора клавишами Ctrl+N и введите следующий
код:
main.py
value = add(2, 2)
print(value)
#
Сохраните файл с именем
main.py в только что созданной папке myproject/.
На­
жмите FS, чтобы выполнить модуль.
При выполнении модуля в интерактивном окне
IDLE
выводится ошибка
NameError:
Traceback (most recent call last):
File "//Documents/myproject/main.py", line 1, in cmodule>
value = add(2, 2)
NameError: name 'add' is not defined
Появление ошибки NameError неудивительно, потому что функция add () опре­
деляется в файле
adder.py,
а не
main.py.
Чтобы использовать add() в
необходимо сначала импортировать модуль adder.
main.py,
ГЛАВА 11
246
Модули и пакеты
Импортирование одного модуля в другой
В окне редактора добавьте следующую строку в начало файла
main.py
import adder
main.py:
#
# <-- Добавьте эту строку
# Дальнейший код остается без изменений
value = add(2, 2)
print(value)
Когда вы импортируете (командой
import) один
модуль в другой, содержимое
импортированного модуля становится доступно в другом модуле. Модуль
с командой
import
называется вызывающим модулем. В данном примере
модуль, а
adder.py- импортируемый
Нажмите
FS.
к
main.py-
вызывающий модуль.
Ctrl+S, чтобы сохранить main.py, а затем запустите модуль
клавишей
NameError! Дело в том, что обращение
пространства имен adder.
И на этот раз появляется ошибка
add ()
возможно только из
Пространство имен представляет собой набор имен
и классов. Каждый модуль
-
Python имеет собственное
переменных, функций
пространство имен.
Чтобы обратиться к переменным, функциям и классам модуля из этого же
модуля, достаточно указать их имя. Именно так мы поступали в примерах
до настоящего момента. Но для импортированных модулей этот способ не
ГОДИТСЯ.
Чтобы обратиться из вызывающего модуля к имени, содержащемся в импорти­
рованном модуле, укажите имя импортированного модуля, точку и нужное имя:
<модуль>. <имя>
Например, чтобы вызвать
запись
add() в модуле adder, необходимо использовать
adder. add ().
ВАЖНО!
Имя, используемое для импортирования модуля, совпадает с именем файла
модуля.
По этой причи~е имена файлов модулей должны быть допустимыми иден ­
тификаторами Pythoп. Это означает, что они могут содержать только буквы
верхнего и нижнего регистра, цифры и символы подчеркивания и не могут
начинаться с цифры.
11.1. Работа с модулями
Обновите код
247
main.py:
main.py
import adder
#
value = adder.add(2, 2) # <-print(value)
Измените эту строку
Сохраните файл и запустите модуль. В интерактивном окне выводится значение 4.
При выполнении команды
import <модуль> в начале файла импортируется все
пространство имен модуля. Все новые переменные и функции, добавленные
в
adder.py, становятся доступными в main.py, дополнительно ничего импорти­
ровать не придется.
Откройте окно редактора для
adder.py
и добавьте следующую функцию перед
add():
adder.py
# Этот код
def add(x,
return
#
def
остается без изменений
у):
х
+
у
douЫe(x):
#<--Добавьте эту функцию
return
х
х
+
Сохраните файл. Откройте окно редактора для
main.py и добавьте следующий код:
main.py
import adder
#
value = adder.add(2, 2)
douЫe_value
= adder.douЫe(value)
print(douЫe_value)
# <--
Сохраните и запустите
# <--
main.py.
При выполнении модуля в интерактивном окне
8. Так как douЫe()
adder, ошибка NameError не выдается.
выводится значение
Разновидности команды
Команда
Добавьте эту строку
Измените эту строку
уже существует в пространстве имен
import
import весьма гибкая. Существуют две ее разновидности, о которых
необходимо :шать.
1. import
<модуль>
as <другое_имя>
2. from <модуль> import <имя>
Рассмотрим каждую из этих разновидностей более подробно.
Модули и пакеты
248
ГЛАВА
import
<модуль>
11
as
<другое_имя>
Ключевое слово
as
позволяет изменить имя импортирования:
import
as
<другое_имя>
<модуль>
Когда вы импортируете модуль этим способом, для обращения к пространству
имен модуля используется <друюе_uмя> вместо имени <модуль>.
Например, модифицируйте команду
import adder as
#
а
<--
#
import
в
main.py следующим образом:
Измените эту строку
Последующий код остается без изменений
value
=
adder.add(2, 2)
douЫe_value = adder.douЫe(value)
print(douЫe_value)
Сохраните файл и нажмите
FS. Python выдает ошибку NameError:
Traceback (most recent call last):
File "//Mac/Home/Documents/myproject/main.py", line 3, in <module>
value = adder.add(2, 2)
NameError: name 'adder' is not defined
Имя
adder
теперь не распознается, потому что модуль был импортирован
с именем а вместо
adder.
Чтобы программа
main.py снова заработала, необходимо заменить adder. add ()
иadder.douЫe() нaa.add() иа.dоuЫе():
import adder as
value
=
а
a.add(2, 2)
douЫe_value
# <-- Измените эту строку
= a.douЬle(value) # <-- И эту строку тоже!
print(douЫe_value)
Сохраните файл и запустите модуль. Ошибка
терактивном окне выводится значение
NameError
не возникает, а в ин­
8.
from <модуль> import <имя>
Вместо импортирования всего пространства имен можно импортировать только
конкретное имя из модуля. Для этого команда
командой:
from
<модуль>
import
<имя>
import
заменяется следующей
11.1. Работа с модулями
Например, замените в
main.py
from adder import add
249
main.py команду import следующей:
#
<--
#
Измените эту строку
value = adder.add(2, 2)
2)
douЫe_value = adder.douЫe(2,
print(douЫe_value)
Сохраните файл и нажмите
FS.
Происходит исключение
Traceback (most recent call last):
File "//Documents/myproject/main.py", line
value = adder.add(2, 2)
NameError: name 'adder' is not defined
З,
in <module>
Трассировка сообщает, что имя adder не определено. Из
ется только имя
дуля
main.py.
add,
NameError:
adder.py
импортиру­
которое помещается в локальное пространство имен мо­
Это означает, что add() можно использовать без полной записи
adder.add().
Замените
adder. add ()
и
adder. douЫe ()
в
main.py на add ()
и douЫe ():
main.py
from adder import add
#
value = add(2, 2)
#
<--
Измените эту строку
douЫe_value = douЫe(value)
<--
#
И эту строку тоже!
print(douЫe_value)
Сохраните файл и запустите модуль. Как вы думаете, что произойдет?
Возникает еще одна ошибка NameError:
Traceback (most recent call last):
File "//Documents/myproject/main.py", line 4, in <module>
= douЫe(value)
douЫe_value
NameError: name
'douЫe'
is not defined
На этот раз NameError сообщает, что имя douЫe не определено; это доказывает,
что из модуля adder было импортировано только имя add.
Чтобы импортировать имя douЫe, включите его в команду import в
main.py
from adder import add,
#
douЫe
#
<--
Измените эту строку
# Дальнейший код остается без изменений
value = add(2, 2)
douЫe_value
= douЬle(value)
print(douЫe_value)
main.py:
Модули и пакеты
ГЛАВА 11
250
Сохраните и запустите модуль. Теперь он выполняется без ошибки NameError.
В интерактивном окне выводится значение
Сводка команд
8.
import
В следующей таблице суммируется то, что вы узнали об импортировании
модулей.
КОМАНДА
import
IMPORT
РЕЗУЛЬТАТ
Импортирует все пространство имен модуля <модуль>
<модуль>
под именем <модуль>. Для обращения к именам из вы­
зывающего модуля используется запись <модуль>. <имя>
import
<модуль>
Импортирует все пространство имен модуля <модуль>
as
под именем <другое_имя>. Для обраще ния к именам
<другое_имя>
из вызывающего модуля используется запись <дру­
гое_имя>. <имя>
from
<модуль>
<имяl>,
import
Импортирует из модуля <модуль> только имена <имяl>,
<имя2> и т. д. Имена добавляются в локальное про­
<имя2> ...
странство имен вызывающего модуля, и к ним можно
обращаться напрямую
Разделение пространств имен
-
одно из главных преимуществ разбиения кода
на модули. Давайте разберемся, чем же так важны пространства имен и почему
вам стоит о них знать.
Для чего нужны пространства имен
Допустим, каждому человеку на нашей планете присвоен идентификатор. Чтобы
людей можно было отличать друг от друга, все идентификаторы должны быть
уникальными. Да, идентификаторов понадобится довольно много!
Мир разделен на страны, поэтому мы можем сгруппировать людей по стране
рождения. Если присвоить каждой стране уникальный код, его можно при­
соединить к идентификатору. Например, тому, кто родился в Соединенных
Штатах, может быть присвоен идентификатор
в Великобритании,
-
идентификатор
US-357,
а тому, кто родился
GB-246.
В такой схеме двум людям из разных стран могут быть присвоены одинаковые
идентификаторы. Их удастся различить, потому что в начале идентификаторов
указаны разные коды страны. У всех людей из одной страны идентификаторы
должны быть уникальными , но глобально уникальные идентификаторы уже
не нужны.
11.1. Работа с модулями
251
Коды стран в этом сценарии можно сравнить с пространствами имен, и они
демонстрируют три основные причины их использования:
1.
Они обеспечивают группировку имен в логических контейнерах.
2.
Они предотвращают конфликты между совпадающими именами.
3.
Они предоставляют контекст для имен.
Пространства имен в коде обладают теми же преимуществами.
Мы уже рассказали о трех разных способах импортирования одного модуля
в другой. Если вы будете помнить о преимуществах пространства имен, это
поможет вам определить, какая команда импортирования окажется наиболее
подходящей в каждом конкретном случае. В общем случае предпочтительнее
всего команда
import <модуль>, потому что она полностью отделяет простран­
ство имен импортируемого модуля от пространства имен вызывающего модуля.
Более того, для обращения к именам из импортируемого модуля в вызывающем
модуле используется формат <модуль>.<имя>, по которому сразу видно, из
какого моду ля происходит имя.
Формат
import <модуль> as <другое_имя> обычно используется в двух ситу­
ациях.
1.
Имя модуля слишком длинное, и вы хотите импортировать его сокра­
щенную версию.
2.
Имя модуля конфликтует с существующим именем в вызывающем
модуле.
Команда
import <модуль> as <другое_имя> также отделяет пространство имен
импортируемого модуля от пространства имен вызывающего модуля. С другой
стороны, имя, которое вы присваиваете модулю, может не настолько легко
узнаваться, как исходное имя.
Импортирование конкретных имен из модуля обычно считается наименее же­
лательным способом импортирования кода из модуля. Импортируемые имена
добавляются прямо в пространство имен вызывающего модуля, в результате
чего полностью теряется контекст вызываемого модуля.
Иногда модули содержат функцию или класс с таким же именем, как в другом
модуле. Например, в стандартную библиотеку
который содержит класс с именем
Python входит модуль datetime,
datetime.
Допустим, в вашем коде используется команда
import datetime
import:
252
ГЛАВА 11
Модули и пакеты
Команда импортирует модуль
datetime в пространство имен вашего кода, по­
datetime из модуля datetime нужно применять
этому для использования класса
следующую запись:
datetime.datetime(2020, 2, 2)
datetime. В этом примере важно,
datetime. datetime каждый раз, когда вы хотите
классом datetime, и такие громоздкие повторы утомительны.
Пока не отвлекайтесь на то, как работает класс
что вам приходится вводить
воспользоваться
Это превосходный пример того, когда уместно использовать одну из разновид­
ностей команды
мисты на
import. Чтобы не утратить контекст модуля datetime,
Python
програм­
обычно импортируют модуль и переименовывают его в
dt:
import datetime as dt
Чтобы использовать класс
datetime, теперь достаточно ввести dt. datetime:
dt.datetime(2020, 2, 2)
Также класс
datetime
часто импортируется прямо в пространство имен вы­
зывающего модуля:
from datetime import datetime
Это тоже неплохо, потому что контекст не теряется. В конце концов, имена
класса и модуля совпадают.
Если класс
datetime импортируется напрямую, для обращения к нему вам не
придется использовать точечную нотацию:
datetime(2020, 2, 2)
Разные версии
import
позволяют сэкономить время, которое тратится на ввод
слишком длинных имен модулей в точечной нотации. Тем не менее злоупотре­
бление различными формами import может привести к потере контекста, и код
станет менее понятным.
Всегда руководствуйтесь здравым смыслом при импортировании модулей,
чтобы по возможности сохранить контекст.
Упражнения
1.
Создайте моду ль greeter.py, содержащий единственную функцию
Функция должна получать один строковый параметр
name
greet ().
и выводить
11.2. Работа с пакетами
в интерактивном окне текст
Hello
253
{пате}!, где {пате} заменяется аргу­
ментом функции.
Создайте моду ль таiп.ру, который импортирует функцию
2.
greeter.py и
11.2.
вызывает функцию с аргументом
greet ()
из
"Real Python".
РАБОТА С ПАКЕТАМИ
Модули позволяют разделить программу на отдельные файлы, которые можно
повторно использовать по мере надобности. Взаимосвязанный код объединяют
в один модуль и хранят отдельно.
Пакеты позволяют более широко использовать эту организационную структуру,
давая возможность группировать взаимосвязанные модули в одном простран­
стве имен.
Сейчас вы научитесь создавать собственные пакеты
Python
и импортировать
код из этих пакетов в другие модули.
Создание пакетов
Пакет
-
это папка, содержащая один или несколько модулей
Python.
В пакет
должен также входить специальный модуль с именем _iпit_.py. Следующий
пример пакета демонстрирует эту структуру.
mypackage/
E
_init_.py
modute1.py
modute2.py
Моду ль _iпit_.py может вообще не содержать кода! Он только должен суще­
ствовать, чтобы
Python
распознал папку
mypackage/
как пакет
Python.
Запустите на своем компьютере файловый менеджер (Проводник или другую
программу, к которой вы привыкли) и создайте где-нибудь новую папку с име­
нем
packages_example/.
Внутри нее создайте другую папку с именем
Папка
mypackage/.
packages_example/ называется папкой проекта, или корневой папкой про­
packages_examples.
Папка mypackage/ вскоре станет пакетом Python. Сейчас она им не является,
екта, потому что она содержит все файлы или папки проекта
потому что не содержит ни одного модуля.
254
ГЛАВА 11
Откройте
IDLE
Модули и пакеты
и создайте новое окно редактора нажатием клавиш
Ctrl+N.
Включите в начало файла следующий комментарий:
#
main.py
Ctrl+S и сохраните
packages_-example/.
Теперь нажмите
папке
файл с именем
Откройте другое окно редактора клавишами
main.py
Ctrl+N.
в созданной ранее
Включите следующую
строку в начало файла:
#
_init_.py
Сохраните файл с именем
_init_.py
во вложенной папке
mypackage/
папки
packages_example.
Наконец, создайте еще два окна редактора. Сохраните эти файлы с именами
module1.py и module2.py соответственно в папке mypackage/ и вставьте в начало
каждого файла комментарий с именем файла. Когда вы все завершите, у вас
должны быть открыты пять окон
IDLE:
интерактивное окно и четыре окна
редактора.
Итак, структура пакета создана; теперь можно добавить код. Добавьте в файл
module1.py следующую функцию:
modulel.py
def greet(name):
print(f"Hello, {name}!")
#
Добавьте в файл
module2.py следующую
функцию:
module2.py
def depart(name):
print(f"Goodbye, {name}!")
#
Обязательно сохраните оба файла,
module1.py
и
module2.py!
Теперь все готово
к тому, чтобы импортировать и использовать эти модули в модуле
Импортирование модулей из пакетов
Включите в файл
main.py следующий
main.py
import mypackage
#
mypackage.modulel.greet("Pythonista")
mypackage.module2.depart("Pythonista")
фрагмент:
main.py.
11.2. Работа с пакетами
Сохраните
main.py
и нажмите
окне выдается ошибка
FS,
255
чтобы запустить модуль. В интерактивном
AttributeError:
Traceback (most receпt call last):
File "\MacHomeDocumeпtspackages_examplemaiп.py",
liпe
5,
iп
<module>
mypackage.modulel.greet("Pythoпista")
AttributeError: module 'mypackage' has
Когда вы импортируете модуль
#
import
в начале
-
пространства имен
modulel и module2
их тоже необходимо импортировать. Из­
main.py:
maiп.py
import mypackage.modulel # <-#
attribute 'modulel'
mypackage,
не импортируются автоматически
мените команду
по
Измените эту строку
Дальнейший код остается без изменений
mypackage.modulel.greet("Pythoпista")
mypackage.module2.depart("Pythoпista")
Теперь сохраните и запустите модуль
main.py.
В интерактивном окне должен
появиться следующий результат:
Hello, Pythoпista!
Traceback (most receпt call last):
File "\MacHomeDocumeпtspackages_examplemaiп.py",
liпe
6,
iп
<module>
mypackage.module2.depart("Pythoпista")
AttributeError: module 'mypackage' has
по
attribute 'module2'
mypackage. modulel. greet () была вызвана, потому что в интерактивном
окне было выведено сообщение "Hello, Pythoпista ! ".
Функция
mypackage.module2.depart() вызвана не была. Эта строка
mypackage был
только модуль modulel.
Однако функция
создала ошибку атрибутов, потому что на данный момент из
импортирован
Чтобы импортировать
файла
#
module2, добавьте следующую команду import в начало
main.py:
maiп.py
import mypackage.modulel
import mypackage.module2 # <-- Add this
liпe
# Дальнейший код остается без изменений
mypackage.modulel.greet("Pythoпista")
mypackage.module2.depart("Pythoпista")
Если теперь сохранить и выполнить
greet()
и
depart():
main.py,
будут вызваны обе функции,
ГЛАВА
256
11
Модули и пакеты
Hello, Pythonista!
Goodbye, Pythonista!
В общем случае при импортировании модулей из пакетов вводят полное имя
в следующем формате:
import
<имя_пакета>.<имя_модуля>
Сначала указывают имя пакета, затем точку, а после нее имя импортируемого
модуля.
ВАЖНО!
Имена папок пакетов, как и имена файлов модулей, должны быть допустимы­
ми идентификаторами Pythoп. Они могут содержать только буквы верхнего
и нижнего регистра, цифры и символы подчеркивания и не могут начинаться
с цифры.
Как и в случае с модулями, существует несколько разновидностей команды
import, которые
используют для импортирования пакетов .
Разновидности команды
import для
пакетов
Когда я рассказывал об импортировании имен из модулей, то показал вам три
разновидности команды
import.
Для импортирования модулей из пакетов их
четыре:
1. import
<пакет>
2. import
<пакет>
as
<другое_имя>
3. from
<пакет>
import
<модуль>
4. from
<пакет>
import
<модуль>
as
<другое_имя>
Они работают почти так же, как их аналоги . Например, вместо импортирования
mypackage.modulel
и
mypackage.module2
в отдельных строках можно импорти­
ровать оба модуля в одной строке. Внесите изменения в файл таiп.ру:
main.py
from mypackage import modulel, module2
#
modulel.greet("Pythonista")
module2.depart("Pythonista")
Если сохранить и запустить модуль, в интерактивном окне будут выведены те
же результаты.
11.2. Работа с пакетами
257
Имя импортируемого модуля можно изменить при помощи ключевого слова
as:
main.py
from mypackage import modulel as ml, module2 as m2
#
ml.greet("Pythonista")
m2.depart("Pythonista")
Также из модуля пакета допустимо импортировать отдельные имена. Например,
если вы внесете следующие изменения в файл
main.py,
результат, выводимый
при сохранении и запуске модуля, останется неизменным:
# main.py
from mypackage.modulel import greet
from mypackage.module2 import depart
greet("Pythonista")
depart("Pythonista")
Поскольку есть несколько способов импортирования пакета, то естественно
задаться вопросом: какой из них лучше?
Рекомендации по импортированию пакетов
Рекомендации по импортированию имен из модулей действуют и при импор­
тировании модулей из пакетов. Старайтесь сделать импортирование как можно
более явным и однозначным, чтобы модули и имена, импортированные в вы­
зывающий модуль, имели соответствующий контекст.
В общем случае наиболее однозначным является следующий формат:
import
<пакет>. <модуль>
Для обращения к именам из модуля вводится команда вида:
<пакет>. <модуль>. <имя>
При таком подходе у вас не возникнут вопросы, когда вам встретятся имена из
импортируемых модулей. Но иногда имена пакетов и модулей оказываются
слишком длинными, и вам приходится снова и снова вводить конструкцию
<пакет>. <модуль> в своем коде.
Следующий формат позволяет пропустить имя пакета и импортировать в про­
странство имен вызывающего модуля только имя модуля:
from
<пакет>
import
<модуль>
ГЛАВА 11
258
Модули и пакеты
Теперь для обращения к имени из модуля достаточно ввести <модуль>. <имя>.
Хотя такая конструкция уже не показывает, из какого пакета происходит имя,
контекст моду ля остается очевидным.
А вот следующий формат обычно считается неоднозначным, и его стоит ис­
пользовать только в том случае, если риск импортирования из модуля имени,
конфликтующего с именем в вызывающем модуле, полностью отсутствует:
from
<nакет>.<модуль>
import
<имя>
Вы узнали об импортировании модулей из пакетов. Теперь давайте посмотрим,
как вкладывать пакеты в другие пакеты.
Импортирование модулей из подпакетов
Пакет
Python,
всего лишь обычная папка, содержащая один или несколько модулей
одному из которых должно быть присвоено имя
_init_.py.
Таким
образом, следующая структура пакетов вполне допустима.
mypackage/
mysubpackage/
t.:-_init_. ру
L
moduleЗ.py
_init_.py
modulel.py
module2.py
Пакет, вложенный в другой пакет, называется подпакетом. Например, папка
mysubpackage является
подпакетом
mypackage, потому что она содержит модуль
_init_.py, а также второй модуль с именем moduleЗ.py.
Запустите на своем компьютере файловый менеджер и создайте новую пап­
ку с именем
mypackage/,
mysubpackage/.
Удостоверьтесь, что она находится внутри папки
созданной ранее.
Откройте в
IDLE
два новых окна редактора. Создайте файлы
и moduleЗ.py, сохраните оба модуля в папке
_init_.py
mysubpackage/. Включите в файл
moduleЗ.py следующий код:
# moduleЗ.py
people = ["John", "Paul", "George", "Ringo"]
Теперь откройте файл
main.py
из корневой папки проекта
Удалите существующий код и замените его следующим:
packages_examples/.
11.2. Работа с пакетами
259
# main.py
from mypackage.modulel import greet
from mypackage.mysubpackage.moduleЗ import people
for person in people:
greet(person)
Список
people из модуля moduleЗ в пакете mysubpackage импортируется через
mypackage. mysubpackage. moduleЗ.
имя модуля в точечной нотации
Сохраните и запустите
Hello,
Hello,
Hello,
Hello,
main.py.
В интерактивном окне появится результат:
John!
Paul!
George !
Ringo!
Подпакеты удобны для организации кода очень больших пакетов. Они помогают
поддерживать в пакете четкую и упорядоченную структуру папок.
Тем не менее глубокое вложение подпакетов приводит к появлению длинных
имен в точечной нотации. Только представьте, сколько символов вам придется
ввести, чтобы импортировать модуль из подпакета, находящегося в подпакете
входящего в подпакет пакета.
Желательно ограничить подпакеты одним, максимум двумя уровнями вложен­
ности.
Упражнения
1.
package_exercises/ создайте пакет helpers,
_init_.py, string.py и math.py.
В новой папке проекта с именем
состоящий из трех модулей:
Добавьте в мoдyльstring.py функцию с именем
shout( ), которая получает
один строковый параметр и возвращает новую строку с символами, пре­
образованными к верхнему регистру.
Добавьте в модуль
два параметра,
math.py функцию с
length
и
width,
именем
area(), которая получает
length *
и возвращает их произведение
width.
2.
В корневой папке проекта создайте модуль
функции
shout()
и
area().
Используйте
следующего результата:
ТНЕ
AREA OF
А 5-ВУ-8
RECTANGLE IS 40
main.py, который импортирует
shout()
и
area()
для вывода
260
ГЛАВА
11
Модули и пакеты
11.З. ИТОГИ И ДОПОЛНИТЕЛЬНЫЕ РЕСУРСЫ
В этой главе я показал, как создавать модули и пакеты
Python, а также импор­
тировать объекты из других модулей . Вы узнали, что деление кода на модули
и пакеты обладает рядом достоинств.
•
Маленькие файлы с кодом проще больших.
•
Маленькие файлы с кодом создают меньше проблем при сопровождении.
•
Модули можно повторно использовать в проекте.
•
Модули группируют взаимосвязанные объекты в изолированных про­
странствах имен.
ИНТЕРАКТИВНЫЙ ТЕСТ
К этой главе прилагается бесплатный интерактивный тест для проверки усво­
енных вами знаний. Тест доступен на телефоне или компьютере:
reafpython.comlquizzes/pybasics-modules-packages
Дополнительные ресурсы
За дополнительной информацией о модулях и пакетах обращайтесь к следу­
ющим ресурсам :
•
«Python Modules and Packages» (https.j/realpython.com/courses/pythonmodules-packages/)
•
«Absolute vs Relative Imports» (https://realpython.com/ courses/absolutevs-relative-imports-python/ )
ГЛАВА
12
Операции ввода и вывода
с файлами
До сих пор вы писали программы, получающие входные данные от одного из
двух источников: от пользователя или самой программы. Вывод ограничивался
отображением текста в интерактивном окне
IDLE.
Эти методы ввода и вывода бесполезны во многих ситуациях
-
вот несколько
примеров:
•
входные значения неизвестны во время написания программы;
•
программе требуется больше данных, чем пользователь способен ввести
вручную;
•
результаты после запуска программы необходимо передать другим раз­
работчикам.
На помощь приходят файлы.
В этой главе вы научитесь:
•
работать с путями и метаданными файлов;
•
читать и записывать текстовые файлы;
•
читать и записывать файлы в формате
•
создавать, удалять, копировать и перемещать файлы и папки.
CSV;
Итак, за дело!
12.1. ФАЙЛЫ И ФАЙЛОВАЯ СИСТЕМА
Скорее всего, у вас уже есть опыт работы с файлами. Впрочем, даже в этом
случае программисту необходимо знать о файлах то, чего не знают обычные
пользователи.
ГЛАВА 12
262
Операции ввода и вывода с файлами
Сейчас мы познакомим вас с концепциями, необходимыми для работы с фай­
лами в
Python.
Анатомия файла
Существует много типов файлов: текстовые, графические, аудиофайлы, файлы
в формате
PDF
и т. д. Впрочем, независимо от типа файл представляет собой
последовательность байтов, называемую содержимым файла.
Байт
-
целое число со значением в диапазоне от О до
255.
Байты записываются
на физический носитель при сохранении файла. Когда вы обращаетесь к файлу
на своем компьютере, байты файла последовательно считываются с диска.
В файле нет ничего, что указывало бы, как интерпретировать его содержимое. Вы
как программист отвечаете за преобразование байтов в нужный формат, когда
ваша программа открывает файл. Может показаться, что это достаточно сложно,
но
Python
выполняет большую часть тяжелой работы за вас. Например,
Python
может преобразовать числовые байты текстового файла в текстовые символы. Вам
не нужно знать, как выполняется это преобразование. В стандартной библиотеке
имеются инструменты для работы с разными типами файлов, включая графиче­
ские и аудиофайлы. Чтобы обратиться к файлу на устройстве хранения данных,
необходимо знать, где хранится файл и как взаимодействовать с этим устройством.
Эта грандиозная работа выполняется файловой системой вашего компьютера.
Файловая система
Файловая система компьютера решает две задачи.
1.
Она выдает абстрактное представление файлов, хранящихся на компью­
тере и любых устройствах, подключенных к нему.
2.
Она взаимодействует с устройствами для управления записью и чтением
данных файлов.
Python взаимодействует с файловой системой на вашем компьютере.
Возмож­
ности таких взаимодействий ограничиваются операциями, которые поддержи­
ваются файловой системой.
ВАЖНО!
В разных операционных системах (ОС) используются разные файловые систе ­
мы . Важно помн.ить об этом при написании кода, который будет выполняться
в различных ОС.
12.1. Файлы и файловая система
263
Файловая система управляет взаимодействиями между компьютером и физиче­
ским устройством хранения данных. И это очень хорошо. Это означает, что вам
программисту
Python - не придется беспокоиться о таких подробностях, как
обращение к физическому носителю или управление вращением жесткого диска.
Иерархия файловых систем
Файловые системы организуют файлы в иерархию каталогов, также называемых
папками. На верху иерархии находится корневой каталог. В нем хранятся все
остальные файлы и каталоги файловой системы.
ВАЖНО!
В
Windows каждый диск имеет собственную иерархию файлов с корневым
каталогом, представленным именем диска.
В
macOS и Linux каждый диск представляет собой подкаталог единого кор­
невого каталога.
Каждый файл обладает именем, которое должно отличаться от имен всех осталь­
ных файлов в той же папке. Каталоги также могут содержать другие каталоги,
называемые подкаталогами, или вложенными папками.
Следующее дерево каталогов наглядно представляет иерархию файлов и папок
в некоторой файловой системе.
rtoot~pp/
i.:..:.. program.py
data.txt
L
tos/
[
cats/
lion. j pg
С siamese.png
dogs/
i.......:. dachshound.jpg
L jack_russel.gif
root/. Он содер­
photos/. Подкаталог арр/ содержит файл
program.py и файл data.txt. Каталог photos/ содержит два подкаталога с именами
cats/ и dogs/, каждый из которых содержит два графических файла.
В этой файловой системе корневому каталогу присвоено имя
жит два подкаталога с именами арр/ и
264
ГЛАВА 12
Операции ввода и вывода с файлами
Полное имя файла
Чтобы отыскать файл в файловой системе, необходимо последовательно пере­
числить каталоги, начиная с корневого, и в конце цепочки указать имя файла.
Запись местонахождения файла в таком представлении называется путем
к файлу (также используется термин "полное имя~).
Например, путь к файлу jack_russel.gif в файловой системе из предыдущего
примера имеет вид
root/photos/dogs/jack_russel.gif.
Способ записи пути к файлам зависит от операционной системы. Вот примеры
пути к файлам в
Windows, macOS
и
Linux:
1. Windows: C:\Users\David\Documents\hello.txt
2. macOS: /Users/David/Documents/hello.txt
3. Ubuntu Linux: /home/David/Documents/hello.txt
Все три пути ведут к текстовому файлу hello.txt, который находится в подкатало­
ге
Documents каталога пользователя с именем David. Как видите, пути к файлам
в разных операционных системах заметно отличаются.
В
macOS
и
Ubuntu Linux
операционная система использует виртуальную
файловую систему, в которой все файлы и каталоги на всех устройствах рас­
полагаются в одном корневом каталоге, который обычно обозначается косой
чертой/. Файлы и папки внешних устройств хранения данных обычно находятся
в подкаталоге с именем
media/.
В системе Windows единого корневого каталога не существует. Каждое устрой­
ство (диск) обладает отдельной файловой системой с собственным корневым
каталогом, имя которого состоит из буквенного обозначения диска, за которым
следует двоеточие и обратная косая черта
\.
Как правило, жесткому диску, на котором установлена операционная система,
назначается буква С, поэтому корневой каталог файловой системы для этого
диска имеет имя с:
.
Windows, macOS и Ubuntu
Windows разделяются обратной косой
чертой \,тогда как разделителем каталогов в macOS и Ubuntu является обычная
косая черта /.
Другое принципиальное отличие файловых путей
заключается в том, что каталоги в путях
Когда вы пишете программы, которые должны выполняться в разных опера­
ционных системах, очень важно правильно указывать путь. В версиях Python
до 3.4 стандартная библиотека содержит модуль pathlib, упрощающий работу
с путем в разных операционных системах.
12.2.
Работа с путями к файлам в
Python
265
12.2. РАБОТА С ПУТЯМИ К ФАЙЛАМ В РУТНОN
Модуль pathlib из стандартной библиотеки
Python
предоставляет основной
интерфейс для работы с путями к файлам. Прежде чем что-либо делать с мо­
дулем, его необходимо импортировать.
Откройте интерактивное окно в
IDLE
и импортируйте
pathlib
следующей
командой:
>>> import pathlib
Модуль
pathlib
содержит класс с именем
который используется для
Path,
представления пути к файлу.
Создание объекта
Path
Есть несколько способов создания объектов Path.
1.
На основе строки.
2.
Методами класса
3.
Оператором
Path.home()
и
Path.cwd().
/.
Проще всего создать объект Path на основе строки.
Создание объектов
Path из строк
Например, следующий фрагмент создает объект
Path, представляющий путь
к файлу
macOS "/Users/David/Documents/hello. txt ":
>>> path
=
pathlib.Path("/Users/David/Documents/hello.txt")
Однако с путем в
Windows появляется проблема. В Windows каталоги разделя­
ются обратной косой чертой \. Python интерпретирует обратную косую черту
как начало служебной последовательности ( еsсаре-последовательности), пред­
ставляющей специальный символ в строке - например, символ новой строки \n.
При попытке создать объект Path для пути к файлу в Windows "С: \Users\David\
Desktop\hello. txt" выдается ошибка:
>>> path = pathlib.Path("C:\Users\David\Desktop\hello.txt")
SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes
in position 2-3: truncated \UXXXXXXXX escape
Проблему можно решить двумя способами.
ГЛАВА 12
266
Операции ввода и вывода с файлами
Во-первых, в пути к файлам
черту
/
вместо обратной
Windows
можно использовать обычную косую
\:
>>> path
= pathlib.Path("C:/Users/David/Desktop/hello.txt")
Python
нормально интерпретирует эту строку и правильно автоматиче­
ски преобразует ее в путь при взаимодействии с операционной системой
Windows.
Во-вторых, строку можно преобразовать в необработанную (raw string), для
чего перед ней ставят префикс
>>> path
r:
= pathlib.Path(r"C:\Users\David\Desktop\hello.txt")
Тем самым вы приказываете
Python
игнорировать служебные носледователь­
ности и просто прочитать строку в том виде, в котором она записана.
Использование Path.home() и Path.cwd()
Кроме создания объекта Path на основе строки, можно воспользоваться классом
Path. Он содержит методы класса, возвращающие объекты Path для специаль­
ных каталогов. Два самых полезных метода класса - Path. home() и Path. cwd( ).
У каждой операционной системы имеется специальный каталог для хранения
данных текущего пользователя. Он называется домашним каталогом пользо­
вателя. Его местонахождение зависит от ОС:
•
Windows: С:\Usеrs\<uмя_пользователя>
•
macOS:
•
Ubuntu Linux: /home/<uмя пользователя>
/Users/<uмя пользователя>
Метод класса Path. home() создает объект Path, содержащий путь к домашнему
каталогу независимо от того, в какой ОС выполняется код:
>>> home
= pathlib.Path.home()
Проверив переменную home в Windows, вы получите примерно такой результат:
»> home
WindowsPath("C:/Users/David")
Создаваемый объект относится к классу WindowsPath, производному от Path.
В других операционных системах возвращается объект производного класса
с именем
PosixPath.
12.2. Работа с путями к файлам в Python
Например, в
macOS
267
при проверке home будет выведен результат следующего
вида:
»> home
PosixPath("/Users/David")
Далее в примерах вывода этого раздела будут отображаться объекты WindowsPath.
Тем не менее все примеры также работают с объектами PosixPath.
ПРИМЕЧАНИЕ
Объекты WindowsPath и PosixPath содержат одни и те же методы и атрибуты .
С точки зрения программирования две разновидности объектов Path ничем
не отличаются .
Метод класса
лога
Path. cwd () возвращает объект Path для текущего рабочего ката­
(Current Woгking Diгectoгy, CWD). Это динамическая ссылка на каталог,
которая зависит от того, с каким каталогом в настоящий момент работает про­
цесс на вашем компьютере. Она всегда представляет текущий каталог файловой
системы.
При работе в
IDLE текущим рабочим каталогом обычно назначается домашний
каталог текущего пользователя:
>>> pathlib.Path . cwd()
WindowsPath("C:/Users/David/Documents")
Впрочем, это не всегда так. Более того, текущий рабочий каталог может изме­
няться на протяжении жизненного цикла программы.
Метод Path. cwd () удобен, но будьте внимательны при его использовании. Не­
обходимо точно понимать, какой именно каталог считается текущим рабочим.
Использование оператора/
Если у вас уже имеется существующий объект Path, оператор/ можно исполь­
зовать для дополнения пути подкаталогами или именами файлов.
Например, следующий фрагмент создает объект Path, представляющий файл
с именем
hello.txt в подкаталоге Desktop домашнего
вателя:
>>> home / "Desktop" / "hello.txt"
WindowsPath('C:/Users/David/Desktop/hello.txt')
каталога текущего пользо­
268
ГЛАВА 12
Операции ввода и вывода с файлами
В левой части оператора/ всегда должен находиться объект
Path. В правой части
может быть строка, представляющая один файл либо каталог, или же строка,
представляющая путь либо другой объект
Path.
Абсолютные и относительные пути
Путь, начинающийся с корневого каталога в файловой системе, называется
абсолютным. Не все пути являются абсолютными. Путь, который не является
абсолютным, называется относительным.
Пример объекта
>>> path
Path с относительным путем:
= pathlib.Path("Photos/image.jpg")
Обратите внимание: строка пути не начинается с с:
\ или /. Чтобы проверить, яв­
ляется ли путь абсолютным, можно воспользоваться функцией. is_absolute( ):
>>> path.is_absolute()
False
Относительные пути имеют смысл только в том случае, если они рассматрива­
ются в контексте другого каталога. Чаще всего они используются для описания
пути к файлу относительно текущего рабочего каталога или домашнего каталога
пользователя.
Относительный путь можно преобразовать в абсолютный оператором/:
>>> home = pathlib.Path.home()
>>> home / pathlib.Path("Photos/image.png")
WindowsPath('C:/Users/David/Photos/image.png')
Слева от косой черты
/
указывают абсолютный путь к каталогу, содержащему
относительный путь. Справа
-
относительный путь.
Впрочем, у вас не всегда достаточно информации для построения абсолютного
пути. В таких случаях применяйте метод
Если метод
. resolve()
щается новый объект
Path. resolve( ).
вызывается для существующего объекта
Path, возвра­
Path, представляющий абсолютный путь:
pathlib.Path("/Users/David")
»> relative_path
relative_path.resolve()
>>> absolute_path
>>> absolute_path
WindowsPath('C:/Users/David')
Path. resolve() пытается проложить наиболее длинный абсолютный путь.
12.2. Работа с путями к файлам в Python
269
Иногда относительный путь оказывается неоднозначным. В таких случаях
. resolve() возвращает относительный путь. Другими
. resolve() вернет абсолютный путь.
словами, нет гарантий
того, что
После того как объект Path будет создан, вы можете узнать компоненты пути
к тому файлу, на который он ссылается.
Доступ к компонентам пути
Любой путь к файлу содержит список каталогов. Атрибут
. parents объекта Path
возвращает итерируемый объект, содержащий список каталогов из этого пути:
>>> path = pathlib.Path.home() / "hello.txt"
»> path
WindowsPath("C:/Users/David")
>>> list(path.parents)
[WindowsPath("C:/Users/David"), WindowsPath("C:/Users"),
WindowsPath("C:/")]
Учтите, что каталоги возвращаются в порядке, обратном порядку их следования
в пути к файлу. Иначе говоря, последний каталог в пути становится первым
в списке родительских каталогов.
Родительские каталоги можно перебрать в цикле
for:
>>> for directory in path.parents:
print(directory)
C:\Users\David
C:\Users
С:\
Атрибут
. parent возвращает имя первого родительского каталога в пути к файлу
в виде строки:
» > path. parent
WindowsPath('C:/Users/David')
. parent -
это сокращенная запись для
. parents (0).
Если путь к файлу является абсолютным, вы можете обратиться к корневому
каталогу пути при помощи атрибута . anchor:
»> path. anchor
'С:\'
Стоит заметить, что
. anchor
возвращает строку, а не другой объект Path.
270
ГЛАВА 12
Операции ввода и вывода с файлами
Для относительных путей
. anchor
возвращает пустую строку:
>>> path = pathlib.Path("hello.txt")
»> path. anchor
Атрибут . name возвращает имя файла или каталога, на который указывает путь:
>>> home = pathlib.Path.home()
» > home. name
'David'
>>> path = home / "hello.txt"
»> path.name
'hello. txt'
#
C:\Users\David
Имена файлов состоят из двух частей. Часть слева от точки называется основой,
а часть справа
Атрибуты
-
. stem
суффиксом или расширением файла.
и
. suffix
возвращают строки, содержащие каждую из этих
частей имени:
»> path. stem
'hello'
»> path.suffix
'. txt'
Наверное, вас интересует, как сделать что-нибудь полезное с файлом
hello.txt.
В следующем разделе я расскажу, как читать и записывать файлы. Но прежде
чем открыть файл для чтения, желательно проверить, существует ли этот файл.
Проверка существования пути к файлу
Объект
Path для пути к файлу можно создать даже в том случае, если путь не
существует. Конечно, пути, которые не ведут к реальным файлам или каталогам,
особой пользы не принесут, если только вы не создадите их в какой-то момент.
У объектов
Path имеется метод . exists (), который возвращает True или False
в зависимости от того, существует ли путь на компьютере, на котором вьшол­
няется программа.
Например, если в вашем домашнем каталоге отсутствует файл
hello.txt,
. exists () для объекта Path, представляющего этот файл, вернет False:
>>> path = pathlib.Path.home() / "hello.txt"
>>> path.exists()
False
вызов
12.2. Работа с путями к файлам в Python
271
Используя текстовый редактор или другой инструмент, создайте пустой
текстовый файл с именем
hello.txt
в своем домашнем каталоге. Затем снова
выполните приведенный выше пример и убедитесь в том, что path. exists ()
возвращает
True.
Также можно проверить, ведет ли путь к файлу или к каталогу. В первом случае
используется метод
.is_file():
>>> path.is_file()
True
Если файл не существует, то
. is_file ()
возвращает
False.
Чтобы проверить, ведет ли путь к каталогу, используйте метод. is_dir( ):
>>> # "hello.txt"
>>> path.is_dir()
не является каталогом
False
>>> # home является
>>> home.is_dir()
каталогом
True
Работа с путями
-
неотъемлемая часть любого программного проекта, где
выполняется чтение или запись данных на жесткий диск или другой носитель
информации. Понимание различий между путями к файлам в разных опера­
ционных системах и умение работать с объектами pathlib. Path для любой
ОС
-
исключительно важный и полезный навык.
Упражнения
1.
Создайте новый объект Path для файла с именем
my_folder/,
myJile.txt
в папке
находящейся в домашнем каталоге вашего компьютера. При­
свойте этот объект Path переменной с именем file_path.
2.
Проверьте, существует ли файл/каталог для пути, присвоенного
file_
path.
3.
Выведите имя для пути, присвоенного
вывести
4.
file_path.
Программа должна
my_file. txt.
Выведите имя родительского каталога для пути, присвоенного
Программа должна вывести
my_folder.
file_path.
Операции ввода и вывода с файлами
ГЛАВА 12
272
12.З. ОСНОВНЫЕ ОПЕРАЦИИ
ФАЙЛОВОЙ СИСТЕМЫ
Теперь, когда вы умеете работать с путями к файлам при помощи модуля pathlib,
я расскажу о типичных операциях с файлами и об их выполнении в коде
Python.
Создание каталогов и файлов
Чтобы создать новый каталог, воспользуйтесь методом Path .mkdir( ). Введите
в интерактивном окне
IDLE
следующий фрагмент:
>>> from pathlib import Path
»> new_dir = Path.home() / "new_directory"
>>> new_dir.mkdir()
После импортирования класса
нем
new_directory/
менной
new_dir.
Path
мы создаем новый путь для каталога с име­
в своем домашнем каталоге и присваиваем этот путь пере­
Затем используем метод
. mkdir( )для создания нового каталога.
Теперь надо проверить, что новый каталог существует и действительно является
каталогом, а не файлом:
>>> new_dir.exists()
True
>>> new_dir.is_dir()
True
Если вы попытаетесь создать каталог, который уже существует, происходит
ошибка:
>>> new_dir.mkdir()
Traceback (most recent call last):
File "cpyshell#32>", line 1, in cmodule>
new_dir .mkdir()
File "C:\Users\David\AppData\Local\Programs\Python\
Python\lib\pathlib.py", line 1266, in mkdir
self._accessor.mkdir(self, mode)
FileExistsError: [WinError 183] Cannot create а file when
that file already exists: 'C:\\Users\\David\\new_directory'
При вызове
. mkdir() Python
снова пытается создать папку
new_directory/.
Так
как каталог уже существует, попытка завершается неудачей и происходит ис­
ключение
FileExistsError.
А что если вы хотите создать новый каталог только в том случае, если он еще не
существует, а заодно избежать ошибки FileExistsError, если каталог существует?
12.3. Основные операции файловой системы
В таком случае присвойте параметру
exist_ok
метода
.mkdir()
значение
273
True:
>>> new_dir.mkdir(exist_ok=True)
Когда при выполнении
. mkdir()
параметр
exist_ok
равен
True,
то каталог
создается только в том случае, если он еще не существует. Если же каталог
существует, то ничего не происходит.
Присваивание
exist_ok
значения
True
при вызове
.mkdir()
эквивалентно
следующему коду:
>>> if not new_dir.exists():
new_dir.mkdir()
И хотя этот код нормально работает, решение с присваиванием параметру
exist_ok значения True короче без ущерба для удобочитаемости.
Теперь посмотрим, что произойдет при попытке создать подкаталог внутри
несуществующего каталога:
»> nested_dir = new_dir / "folder_a" / "folder_b"
>>> nested_dir.mkdir()
Traceback (most recent call last):
File "<pyshell#38>", line 1, in cmodule>
nested_dir.mkdir()
File "C:\Users\David\AppData\Local\Programs\Python\
Python\lib\pathlib.py", line 1266, in mkdir
self ._accessor.mkdir(self, mode)
FileNotFoundError: [WinError З] The system cannot findthe path
specified: 'C:\\Users\\David\\new_directory\\folder_a\\folder_b'
Проблема в том, что родительский каталог folder_а/ не существует. Как правило,
для создания каталога все родительские папки целевого каталога (в данном
случае
folder_Ь/) уже должны
существовать.
Чтобы сформировать все родительские папки, необходимые для создания целе­
вого каталога, присвойте необязательному параметру
значение
parents метода. mkdir()
True:
>>> nested_dir.mkdir(parents=True)
В этом случае
. mkd ir ( ) создает родительский каталог folder_a/, что необходимо
folder_b/.
для создания целевого каталога
Объединяя все сказанное, мы получаем следующую типичную схему создания
каталогов:
path.mkdir(parents=True, exist_ok=True)
274
ГЛАВА
12 Операции ввода и вывода с файлами
Когда параметрам
parents и exist_ok присвоено значение True, весь путь будет
создан в случае необходимости, а если путь уже существует, то исключение не
выдается.
Эта схема полезна, но такой подход не универсален. Например, если пользо­
ватель введет несуществующий путь, лучше перехватить исключение и пред­
ложить проверить введенный путь. Возможно, пользователь просто сделал
опечатку в имени существующего каталога!
Теперь посмотрим, как создавать файлы. Создайте новый объект
file_path
для пути
»> file_path
В каталоге
=
Path с
именем
new_directory/filel.txt:
new_dir / «filel.txt»
new_directory/
файл с имeнeмfile1.txt отсутствует, так что путь еще
не существует:
>>> file_path.exists()
False
Файл можно создать методом
Path. touch( ):
>>> file_path.touch()
Метод создает новый файл с имeнeмfile1.txt в папке
new_directory/.
Он еще не
содержит никаких данных:
>>> file_path.exists()
True
>>> file_path.is_file()
True
В отличие от
.mkdir( ), метод. touch()
не выдает исключение, если создаваемый
путь уже существует:
»>
#При повторном вызове
.touch()
исключение не выдается
>>> file_path.touch()
Файл, созданный вызовом
. touch (),
не содержит никаких данных. Я покажу,
как записывать данные в файлы в разделе
12.5
«Чтение и запись файлов».
Создать файл в несуществующем каталоге невозможно:
»> file_path = new_dir / "folder _с" / "file2. txt"
>>> file_path.touch()
Traceback (most recent call last):
12.3. Основные операции файловой системы
275
File "<pyshell#47>", line 1, in <module>
file_path.touch()
File "C:\Users\David\AppData\Local\Programs\Python\
Python\lib\pathlib.py", line 1256, in touch
fd = self._raw_open(flags, mode)
File "C:\Users\David\AppData\Local\Programs\Python\
Python\lib\pathlib.py", line 1063, in _raw_open
return self ._accessor.open(self, flags, mode)
FileNotFoundError: [Errno 2] No such file or directory:
'C:\\Users\\David\\new_directory\\folder_c\\file2.txt'
FileNotFoundError возникает из-за того, что в папке new_directory/ нет
Ошибка
вложенной папки
В отличие от
folder_c/.
.mkdir(),
метод
. touch()
не имеет параметра
parents,
позволя­
ющего автоматически создавать родительские каталоги. Это означает, что все
необходимые каталоги должны быть созданы до вызова
Например, можно воспользоваться атрибутом
. parent
к родительской папке для фaйлafile2.txt, а затем вызвать
. touch ().
для получения пути
. mkdir ()
для создания
каталога:
>>> file_path.parent.mkdir()
Так как
. parent
. mkdir()
возвращает объект
Path, можно выполнить сцепленный вызов
для выполнения всей операции в одной строке кода.
После создания каталога folder_c/ файл будет успешно создан:
>>> file_path.touch()
Итак, вы знаете, как создавать файлы и каталоги, и мы можем перейти к чтению
содержимого каталога.
Перебор содержимого каталога
Модуль
pathlib позволяет перебрать содержимое каталога. Например, такая
необходимость может возникнуть для обработки всех файлов в каталоге.
Под термином «обработка» в данном случае понимается чтение файла и извле­
чение некоторых данных, сжатие файлов в каталоге или какая-нибудь другая
операция.
А пока сосредоточимся на том, как получить содержимое заданного каталога.
Далее в этой главе я расскажу, как прочитать данные из файлов.
ГЛАВА 12
276
Операции ввода и вывода с файлами
Все элементы каталога являются либо файлами, либо подкаталогами. Метод
Path.iterdir() возвращает итератор для объектов Path, представляющих все
элементы в каталоге.
Чтобы использовать .iterdir(), сначала необходимо получить объект Path,
представляющий каталог. Используем папку
new_directory/,
создали в домашнем каталоге и присвоили переменной
которую мы ранее
new_dir:
>>> for path in new_dir.iterdir():
print(path}
C:\Users\David\new_directory\filel.txt
C:\Users\David\new_directory\folder_a
C:\Users\David\new_directory\folder_c
В данный момент папка
new_directory/ содержит три элемента:
1)
файл с имeнeмfile1.txt;
2)
каталог с именем
folder_c/;
3)
каталог с именем
folder_a/.
Метод
. i terdir ()
возвращает итерируемый объект, который можно преоб­
разовать в список:
>>> list(new_dir.iterdir())
[WindowsPath('C:/Users/David/new_directory/filel.txt'),
WindowsPath('C:/Users/David/new_directory/folder_a'),
WindowsPath('C:/Users/David/new_directory/folder_c')]
Преобразовывать этот объект в список требуется достаточно редко. Как правило,
вызов . i terdir() используется в цикле for, как было сделано в первом примере.
. i terdir() возвращает только элементы, непосредственно со­
держащиеся в папке new_directory/. Иначе говоря, вы не увидите путь к файлу,
хранящемуся в каталоге folder_c/.
Учтите, что
Способ перебора содержимого каталога и всех его подкаталогов существует,
но легко сделать это с. iterdir() не удастся. Вскоре мы доберемся до решения
этой задачи, но сначала я покажу, как искать файлы в каталогах.
Поиск файлов в каталоге
Иногда требуется перебрать только файлы определенного типа или файлы
с определенными правилами выбора имен. Вы можете вызвать метод Path. glob ()
12.3. Основные операции файловой системы
277
для пути, представляющего каталог, чтобы получить итерируемый объект для
содержимого каталога, удовлетворяющего некоторому критерию.
Может показаться странным, что метод для поиска файлов называется
. glob (),
но здесь есть исторические причины. В ранних версиях операционной системы
U nix программа с именем glob использовалась для расширения шаблонов путей
к файлам до полных путей.
Метод
.glob()
делает нечто похожее. Методу передается строка, содержащая
шаблон с подстановочным символом, а
. glob ()
возвращает список путей, со­
ответствующих этому шаблону.
Подстановочный символ
специальный символ, который используется
-
в шаблонах. При создании конкретного пути к файлу он заменяется другими
символами. Например, в шаблоне
"*. txt" звездочка * является подстановочным
символом, который может быть заменен любым количеством других символов.
Шаблон
"*. txt"
совпадает с любым путем к файлу, который завершается рас­
ширением . txt. Другими словами, если при замене
* в шаблоне всеми символами
в пути к файлу, кроме четырех последних, будет получен исходный путь, то он
совпадет с шаблоном "* . txt".
Рассмотрим пример использования папки
переменной
new_directory/,
ранее присвоенной
new_dir:
»> for path in new_dir.glob("*.txt"):
print(path)
C:\Users\David\new_directory\filel.txt
Метод
. glob (),как и . i terdir (), возвращает итерируемый объект, содержащий
список путей, но в этом случае возвращаются только пути, соответствующие
шаблону "* . txt".
Следует заметить, ч:го метод
. glob ()
возвращает только пути файлов, содержа­
щиеся в папке, для которой он вызывается.
Возвращаемое значение . glob () можно преобразовать в список:
»> list(new_dir.glob("*.txt"))
[WindowsPath('C:/Users/David/new_directory/filel.txt')]
Чаще всего
. glob()
используется в циклах
for.
В следующей таблице описаны наиболее популярные подстановочные символы.
278
Операции ввода и вывода с файлами
ГЛАВА 12
ПОДСТАНОВОЧ·
НЫЙСИМВОЛ
*
ОПИСАНИЕ
ПРИМЕР
СОВПАДАЕТ
Любое количество
"*Ь*"
Ь,аЬ, Ьс,аЬс
"?Ьс"
аЬс,ЬЬс , сЬс
НЕСОВПА·
ДАЕТ
а, с , ас
символов
Один символ
Ьс, ааЬс ,
abcd
(CB)at
Один из символов,
[аЬс)
Cat,Bat
at,cat,bat
перечисленных
в квадратных скобках
Примеры использования каждого подстановочного символа я покажу позднее.
А сейчас мы создадим еще несколько файлов в папке new_directory/, чтобы у нас
было больше возможностей для экспериментов.
Введите следующий код:
>» paths
=[
/
/
new_dir /
new_dir /
new_dir /
пew_dir
пew_dir
"programl.py",
"program2.py",
"folder_a" / "programЗ.py",
"folder_a" / "folder_b" / "imagel.jpg",
"folder_a" / "folder_b" / "image2.png",
>>> for path in paths:
path. touch ()
»>
После выполнения этого фрагмента структура папки
как показано ниже.
new_directory/
t
folder_a/
~d~a~~1.jpg
L
image2.png
programЗ.py
folder с/
L file2.txt
filel.txt
programl. ру
program2.py
new_directory/ будет такой,
12.3. Основные операции файловой системы
279
И так, у нас появилась более интересная структура, с которой можно работать.
Давайте посмотрим, как .glob() работает с каждым из подстановочных сим­
волов.
Подстановочный символ
*
* заменяет любое количество символов в шаблоне.
"*. ру" соответствует всем путям, заканчивающимся суф­
Подстановочный символ
Например, шаблон
фиксом .ру:
>» list(new_dir.glob("*.py"))
[WindowsPath('C:/Users/David/new_directory/programl.py'),
WindowsPath('C:/Users/David/new_directory/program2.py')]
Символ
* может многократно
использоваться в одном шаблоне:
»> list(new_dir.glob("*l*"))
[WindowsPath('C:/Users/David/new_directory/filel.txt'),
WindowsPath('C:/Users/David/new_directory/programl.py')]
Шаблон
"*1 *"
соответствует любому пути, содержащему цифру
которой может следовать любое количество символов. В папке
цифра
1, до и после
new_directory/
1 встречается только в file 1.txt и program 1.ру.
Если опустить первое вхождение* в шаблоне
"*1*"
(получим шаблон
"1*"),
то совпадений не будет:
>» list(new_dir.glob("l*"))
[]
Шаблон
"1 *"
совпадает с путями к файлам, которые начинаются с цифры
которыми следует любое количество символов. В папке
new_directory/
1, за
нет ни
одного файла, путь к которому соответствует этому шаблону, поэтому вызов
. glob ()
ничего не возвращает.
Подстановочный символ
?
Подстановочный символ ? заменяет один символ в шаблоне. Например, шаблон
"program?. ру" соответствует любому пути, начинающемуся со слова program,
за которым следует один символ и суффикс
. ру:
>» list ( new_dir. glob( "program?. ру"))
[WindowsPath('C:/Users/David/new_directory/programl.py'),
WindowsPath('C:/Users/David/new_directory/program2.py')]
Операции ввода и вывода с файлами
280
ГЛАВА 12
Символ
? может многократно
использоваться в одном и том же шаблоне:
»> list(new_dir.glob("?older_?"))
[WindowsPath('C:/Users/David/new_directory/folder_a'),
WindowsPath('C:/Users/David/new_directory/folder_c')]
Шаблон
"?older_?" соответствует путям, начинающимся с любой буквы, за ко­
older _и еще один символ. В папке new_directory/ такими путями
торой следует
являются каталоги
folder_a/ и folder_b/.
Подстановочные символы
* и ? можно объединять в одном шаблоне:
>» list(new_dir.glob("*l. ??"))
[WindowsPath('C:/Users/David/new_directory/programl.py')]
Шаблон "*1. ??"соответствует любому пути, который состоит из цифры 1,
за которой следует точка и еще два символа. В каталоге
new_directory/ этому
шаблону соответствует только program 1.ру. Обратите внимание: file 1.txt этому
шаблону не соответствует, потому что за точкой следуют три символа.
Подстановочный символ
Подстановочный символ
[]
[]
отчасти напоминает
?, потому
что он заменяет
только один символ. Отличие в том, что вместо одного произвольного симво­
ла, как в случае с
?, [ ]
должен совпадать только с одним символом из набора,
заключенного в квадратные скобки.
Например, шаблон "р rogram [ 13] . ру" соответствует любому пути, содержаще­
му слово
program,
за которым следует цифра 1 или 3, а потом расширение .ру.
В новой папке new_directory/folder этому шаблону соответствует только файл
program 1.ру:
»> list(new_dir.glob("program[lЗ].py"))
[WindowsPath('C:/Users/David/new_directory/programl.py')]
Как и в случае с другими подстановочными символами, вы можете многократно
использовать
[]
в одном шаблоне, а также комбинировать его с другими.
Рекурсивный поиск совпадений
и подстановочный символ
**
Как вы уже видели,
. iterdir()
и
.glob()
имеют одно серьезное ограничение:
они возвращают пути только для той папки, для которой они были вызваны.
12.3. Основные операции файловой системы
281
Например, new_dir.glob("*.txt") возвращает только пyтьfile1.txt из new_
directory/. Путь file2.txt из подкаталога folder_c/ не возвращается, хотя он и со­
впадает с шаблоном
"*. txt".
Существует специальный подстановочный символ**, с помощью которого
можно применять шаблон рекурсивно. Для этого в начале шаблона надо поста­
вить префикс
"* */ ". Тем
самым вы приказываете
. glob ()
искать соответствия
шаблону как в текущем каталоге, так и во всех его подкаталогах.
Например, шаблон
"* */ *. txt"
соответствует как filel.txt, так и folder_c/file2.txt:
>» list(new_dir.glob("**/*.txt"))
[WindowsPath('C:/Users/David/new_directory/filel.txt'),
WindowsPath('C:/Users/David/new_directory/folder_c/file2.t xt')]
Аналогичным образом шаблон
в каталоге
new_directory/
"* */ *. ру"
соответствует любым файлам .ру
и всех его подкаталогах:
>» list(new_dir.glob("**/*.py"))
[WindowsPath('C:/Users/David/new_directory/programl.py'),
WindowsPath('C:/Users/David/new_directory/program2.py'),
WindowsPath('C:/Users/David/new_directory/folder_a/programЗ.py')]
Также существует сокращенный метод рекурсивного поиска с именем
. rglob ().
При использовании этого метода передается шаблон без префикса"**/":
»> list(new_dir.rglob("*.py"))
[WindowsPath('C:/Users/David/new_directory/programl.py'),
WindowsPath('C:/Users/David/new_directory/program2.py'),
WindowsPath('C:/Users/David/new_directory/folder_a/programЗ.py')]
Буква
r
в
. rglob()
означает
recursive,
то есть рекурсивный. Некоторые люди
предпочитают использовать этот метод вместо того, чтобы включать в шаблоны
префикс"**/", потому что эта запись немного короче. Обе версии абсолютно
допустимы. В этой книге мы будем использовать
. rglob(),
а не префикс.
Перемещение и удаление файлов и папок
Иногда требуется переместить файл или каталог в новое место либо полностью
удалить. Это можно сделать средствами модуля
pathlib, но учтите, что иногда
такой способ приводит к потере данных, поэтому такие операции следует вы­
полнять с исключительной осторожностью.
Для перемещения файла или каталога используется метод
. replace ().
Операции ввода и вывода с файлами
ГЛАВА 12
282
Например, следующий фрагмент перемещает файл
directory/
во вложенную папку
file1.txt
из папки
new_
folder_a/:
»> source = new_dir / "filel. txt"
»> destination = new_dir / "folder_a" / "filel.txt"
>>> source.replace(destination)
WindowsPath('C:/Users/David/new_directory/folder_a/filel.t xt')
. replace() вызывается для исходного пути. Путь назначения пере­
. replace() в единственном аргументе. Обратите внимание: . replace()
Здесь метод
дается
возвращает путь к новому местонахождению файла.
ВАЖНО!
Если путь назначения уже существует, то вызов .replace() перезаписывает
его исходным файлом без выдачи каких-либо исключений. Если вы будете
действовать неосторожно, это может привести к потере данных.
Возможно, стоит сначала проверить, не существует ли уже файл с таким путем
назначения, и перемещать исходный файл только в том случае, если файла
с путем назначения нет:
if not destination.exists():
source.replace(destination)
Метод
. replace()
также может использоваться для перемещения или переиме­
нования целых каталогов. Например, следующий фрагмент переименовывает
подкаталог
folder_c
каталога
new_directory/ в folder_d/:
»> source = new_dir / "folder_c"
»> destination = new_dir / "folder_d"
>>> source.replace(destination)
WindowsPath('C:/Users/David/new_directory/folder_d')
Как и прежде, если приемная папка уже существует, она полностью заменяется
исходной папкой, что может привести к значительной потере данных.
Для удаления файла используется метод
. unlink( ):
»> file_path = new_dir / "programl.py"
>>> file_path.unlink()
Команда удаляет файл
program1.py
в этом, вызовите метод
.exists():
>>> file_path.exists()
False
в папке
new_directory/.
Чтобы убедиться
12.3. Основные операции файловой системы
Также можно воспользоваться методом
283
. iterdir( ):
>>> list(new_dir.iterdir())
(WindowsPath('C:/Users/David/ new_directory/folder_a'),
WindowsPath('C:/Users/David/n ew_directory/folder_d'),
WindowsPath("C:/Users/David/n ew_directory/program2.py')]
Если путь, для которого вызывается
ключение
. uпliпk(),
не существует, выдается ис­
FileNotFoundError:
>>> file_path.unlink()
Traceback (most recent call last):
File "<pyshell#94>", line 1, in <module>
file_path.unlink()
File "C:\Users\David\AppData\Local\ Programs\Python\
Python\lib\pathlib.py", line 1303, in unlink
self ._accessor.unlink(self)
FileNotFoundError: (WinError 2] The system cannot find the file
specified: 'C:\\Users\\David\\new_directo ry\\programl . py'
Если вы хотите проигнорировать исключение, присвойте необязательному
параметру
missing_ok з начение True:
>>> file_path.unlink(missing_ok=Tr ue)
В данном случае ничего не происходит, потому что файл, представляемый
file_path , не существует.
ВАЖНО!
Удаленный файл пропадает навсегда . Прежде чем удалять его, убедитесь, что
вы действительно этого хотите!
Используйте . uпlink() только с путями, представляющими файлы. Чтобы уда­
лить каталог, используйте метод
. rmdir().
Помните, что каталог должен быть
пустым. В противном случае операция выдает исключение
>» folder_d = new_dir / "folder_d"
>>> folder_d.rmdir()
Traceback (most recent call last):
File "<pyshell#97>", line 1, in cmodule>
folder_d.rmdir()
File "C:\Users\David\AppData\Local\ Programs\Python\
Python\lib\pathlib.py" , line 1314, in rmdir
self ._accessor.rmdir(self)
OSError : (WinError 145] The directory is not empty :
'C:\\Users\\David\\new_directo ry\\folder_d '
OSError:
284
Операции ввода и вывода с файлами
ГЛАВА 12
В папке folder_d/ находится только один фaйлfile2.txt. Чтобы удалить folder_d/,
сначала необходимо удалить все хранящиеся в ней файлы:
>>> for path in folder_d.iterdir():
path.unlink()
>>> folder_d.rmdir()
Теперь папка
folder_d/
удалена:
>>> folder_d.exists()
False
Если вам потребуется удалить весь каталог, даже если он не пуст, pathlib вам
не поможет. Однако встроенный модуль
shutil
включает функцию
rmtree(),
которая применяется для удаления каталогов, содержащих файлы.
Пример использования
rmtree()
для удаления
folder_a/:
>>> import shutil
»> folder_a = new_dir / "folder_a"
>>> shutil.rmtree(folder_a)
Вспомните, что
folder_a/ содержит подкаталог folder_Ь/, в котором хранятся два
файла с именами
При вызове
image 1jpg и image2.png.
rmtree() с передачей объекта folder_a удаляется папка folder_a/
вместе со всем содержимым:
>>> # The folder_a/ directory
>>> folder_a.exists()
по
longer exists
False
>>> # Searching for 'image*.*' files returns nothing
»> list(new_dir. rglob( "image*. *"))
[]
Раздел получился достаточно объемным. Вы узнали, как выполняются неко­
торые стандартные операции файловых систем:
•
создание файлов и каталогов;
•
перебор содержимого каталогов;
•
поиск файлов и папок по шаблону;
•
перемещение и удаление файлов и папок.
Все эти операции требуются достаточно часто. Однако очень важно помнить,
что ваши программы
-
всего лишь «гости» на компьютерах других людей.
12.4. Задача: перемещение всех графических файлов в новый каталог
285
Неосторожность может привести к непреднамеренному повреждению чужой
информации, потере важных документов и других данных.
Всегда будьте внимательны при работе с файловыми системами. Если у вас
возникнут сомнения, перед выполнением операции удостоверьтесь, что путь
существует, и всегда предлагайте пользователю подтвердить предстоящую
операцию!
Упражнения
1.
Создайте в домашней папке новый каталог с именем
2.
Создайте в
my_folder/ три
my_folder/.
файла:
• file1.txt
• file2.txt
• image 1.png
3.
Переместите файл
в каталоге
image1.png в
новый каталог
images/,
находящийся
my_folder/.
4.
Удалите фaйлfile1.txt.
5.
Удалите каталог
my_folder/.
12.4. ЗАДАЧА: ПЕРЕМЕЩЕНИЕ ВСЕХ
ГРАФИЧЕСКИХ ФАЙЛОВ В НОВЫЙ КАТАЛОГ
В папке
practice_files располагается
вложенная папка с именем
documents/.
Она
содержит несколько файлов и вложенных папок. Некоторые файлы хранят
графические изображения
Создайте в папке
-
они имеют расширения
.png, .gif или .jpg.
practice_files подпапку с именем images/, переместите в нее все
графические файлы. Когда это будет сделано, в новой папке должны находиться
четыре файла:
• image 1.png
• image2.gif
•
imageЗ.png
• image4jpg
Решение задачи, а также многие другие дополнительные ресурсы доступны
в интернете по адресу
realpython.com/python-basics/resources.
ГЛАВА 12
286
Операции ввода и вывода с файлами
12.5. ЧТЕНИЕ И ЗАПИСЬ ФАЙЛОВ
В современном мире мы имеем дело с файлами на каждом шагу. Они являются ос­
новным средством хранения и передачи данных в цифровом формате. Скорее всего,
только за сегодняшний день вы уже открывали десятки, если не сотни файлов.
В этом разделе я расскажу, как читать и записывать файлы в
Python.
Что такое файл?
Файл представляет собой последовательность байтов, а байт содержит число от
О до
255. Другими словами,
файл является последовательностью целых чисел.
Чтобы интерпретировать содержимое файла, хранящиеся в нем байты необхо­
димо декодировать.
В стандартную библиотеку
данными в формате
Python
включены модули для работы с текстом,
и аудиофайлами. Также доступны многочисленные
CSV
сторонние пакеты для работы с файлами других типов.
В главе
13
«Установка пакетов с помощью
сторонние пакеты. Глава
14
работе с файлами в формате
pip»
вы узнаете, как устанавливать
«Создание и изменение файлов
PDF»
посвящена
PDF.
А сейчас вы узнаете, как работать с обычными текстовыми файлами.
Текстовые файлы
Текстовые файлы содержат только текст. Пожалуй, с этой разновидностью
файлов работать проще всего. Тем не менее есть пара особенностей, которые
могут создать проблемы при работе с текстовыми файлами:
1)
кодировка символов;
2)
эавершители строк.
Прежде чем переходить к чтению и эаписи текстовых файлов, я расскажу, как
эффективно преодолевать эти проблемы.
Кодировка символов
Текстовые файлы хранятся на диске в виде последовательности байтов. Каждый
байт (или группа байтов в некоторых случаях) представляет отдельный сим­
вол в файле. При эаписи текстовых файлов символы, вводимые с клавиатуры,
12.5. Чтение и запись файлов
287
преобразуются в байты; процесс преобразования называется кодированием.
При чтении текстового файла байты декодируются обратно в текст.
-
Целое число, с которым связывается символ, определяется кодировкой символов файла. Существует много кодировок символов. На практике чаще всего
используются следующие четыре кодировки:
1. ASCII
2. UTF-8
3. UTF-16
4. UTF-32
В некоторых кодировках
-
например, в
ASCII и UTF-8 -
символы кодируются
одинаково. Например, цифры и буквы латинского алфавита одинаково коди­
руются в
ASCII
и
UTF-8.
ASCII и UTF-8 заключается в том, что UTF-8 позволяет коди­
ASCII не позволяет кодировать такие символы, как
fi или ii, а в UTF-8 это возможно. Это означает, что текст в кодировке ASCII
можно декодировать в UTF-8, но декодирование текста в кодировке UTF-8
в ASCII возможно не всегда.
Отличие между
ровать больше символов.
ВАЖНО!
Если для кодирования и декодирования текста используются разные коди­
ровки, могут возникнуть серьезные проблемы.
Например, текст, закодированный в
UTF-8
и декодированный в
UTF-16, может
быть интерпретирован как текст на совершенно другом языке, а вовсе не на
том, который предполагался изначально!
Более подробно о кодировке символов рассказано в статье «Uпicode &
Character Eпcodiпgs iп Pythoп: А Painless Guide» (https://realpython.com/pythonencodings-guide/) на сайте Real Python.
Знать, какая кодировка используется в файле, важно, но это не всегда очевидно.
Windows текстовые
macOS и Ubuntu Linux
На современных компьютерах с системой
UTF-16 или UTF-8.
используется UTF -8.
кодируются в
обычно
В
файлы обычно
по умолчанию
Далее в этом разделе мы будем работать с файлами, для которых используется
кодировка
UTF-8.
Если при этом у вас возникнут проблемы, возможно, вам
следует изменить кодировку примеров .
ГЛАВА 12
288
Операции ввода и вывода с файлами
Завершители строк
Каждая строка текстового файла завершается одним или двумя символами,
которые интерпретируются как метка конца строки. Эти символы обычно не
отображаются в текстовом редакторе, но существуют в данных файла в виде
байтов.
Два символа, используемых для представления конца строки,
курсора и переноса строки. В строках
\r
В
и
\n
Python
-
возврата
это еsсаре-последовательности
соответственно.
Windows
завершители строк по умолчанию представляются комбинацией
этих двух символов. В
macOS и большинстве дистрибутивов Linux завершители
строк представляются единственным символом переноса строки.
При чтении файлов
Windows
в
macOS
или
текста ино­
Linux между строками
гда выводятся лишние пустые строки. Это происходит из-за того, что символ
возврата курсора также является завершителем строки в
Допустим, следующий текстовый файл был создан в
macOS
и
Linux.
Windows:
Pug\r\n
Jack Russell Terrier\r\n
English Springer Spaniel\r\n
German Shepherd\r\n
В
macOS
или
Ubuntu
этот файл интерпретируется с двойными интервалами
между строками:
Pug\r
\n
Jack Russell Terrier\r
\n
English Springer Spaniel\r
\n
German Shepherd\r
\n
На практике использование разных завершителей строк в разных операцион­
ных системах обычно не создает проблем.
Python автоматически
преобразует
завершители строк, и вам не придется беспокоиться о них.
Объекты файлов в Python
Файлы в Python представляются файловыми объектами, которые являются
экземплярами классов, предназначенных для работы с разными типами файлов.
12.5. Чтение и запись файлов
В
289
Python существуют следующие типы файловых объектов.
1.
Объекты текстовых файлов предназначены для работы с текстовыми
файлами.
2.
Объекты двоичных файлов предназначены для непосредственной работы
с байтами, содержащимися в файлах.
Объекты текстовых файлов выполняют кодирование и декодирование байтов.
От вас требуется только указать, какая кодировка символов должна использо­
ваться. Объекты двоичных файлов не выполняют никакого кодирования или
декодирования.
Файловые объекты в
Python создают двумя способами:
1)
методом
2)
встроенной функцией
Path. о реп();
open().
Рассмотрим обе возможности.
Метод
Path.open()
Для использования метода
Path.
>>>
»>
»>
»>
Path. open () необходимо сначала получить объект
Выполните в интерактивном окне
IDLE следующий фрагмент:
from pathlib import Path
path = Path.home() / "hello.txt"
path.touch()
file = path.open(mode="r", encoding="utf-8")
Сначала мы создаем объект
Path для файла hello.txt и присваиваем его пере­
path. Затем метод path. touch () создает файл в домашнем каталоге.
Наконец, . open () возвращает новый файловый объект, представляющий файл
hello.txt, и присваивает его переменной file.
менной
При открытии файла используются два параметра, задаваемых ключевыми
словами.
1.
2.
Параметр mode определяет, в каком режиме должен быть открыт файл.
Аргумент
"r"
Параметр
encoding определяет
открывает файл в режиме чтения.
кодировку символов, используемую для
декодирования файла. Аргумент
UTF-8.
"utf-8"
задает кодировку символов
290
ГЛАВА 12
Операции ввода и вывода с файлами
Вы можете проверить переменную
file
и убедиться в том, что ей присвоен
объект текстового файла:
»> file
<_io.TextIOWrapper name='C:\Users\David\hello.txt' mode='r'
encoding='utf-8'>
Объекты текстовых файлов являются экземплярами класса TextIOWrapper. Вам
никогда не придется создавать экземпляры этого класса напрямую, потому что
их можно создавать методом
Path. open () .
Существует несколько режимов открытия файлов. Они описаны в следующей
таблице.
РЕЖИМ
"r"
ОПИСАНИЕ
Создает объект текстового файла для чтения и выдает ошибку, если
файл не удалось открыть
Создает объект текстового файла для записи и перезаписывает все
существующие данные в файле
"а"
Создает объект текстового файла для присоедин ения данны х в ко­
нец файла
''rb"
Создает объект двоичного файла для чтения и выдает ошибку, если
файл не удалось открыть
"wb"
Создает объект двоичного файла для записи и перезаписывает все
существующие данные в файле
"аЬ"
Создает объект двоичного файла для присоединения данных в ко­
нец файла
В следующей таблице приведены строки для самых распространенных коди­
ровок символов.
СТРОКА
КОДИРОВКА
··ascii"
ASCll
"utf-8"
UTF-8
"utf-16"
UTF-1 6
"utf-32"
UTF-32
Когда вы создаете объект файла вызовом
файл, пока вы не прикажете
. open (), Python
хранит ссылку на
Python закрыть файл или программа не завершится.
12.5. Чтение
и запись файлов
291
ВАЖНО!
Всегда явно приказывайте
Python
Не закрывать за собой файлы
-
закрыть файл .
все равно что не убирать за собой грязь . Когда
программа завершает работу, в системе не должно оставаться лишнего мусора .
Чтобы закрыть файл, вызовите метод
. close()
для объекта файла:
»> file.close()
Если у вас имеется существующий объект
дуется использовать
Path. open ().
Path, для открытия файлов рекомен­
Тем не менее для открытия файлов можно
воспользоваться и встроенной функцией
open ().
Встроенная функция open()
Встроенная функция open () работает почти так же, как метод Path. open (),
только в первом параметре передается строка с путем к открываемому файлу.
Создайте новую переменную
hello.txt,
file_path
и присвойте ей строку с путем к файлу
который был создан ранее:
>>> file_path = "C:/Users/David/hello.txt"
Не забудьте скорректировать путь для вашего компьютера.
Затем создайте новый объект файла встроенной функцией open () и присвойте
его переменной
file:
»> file = open(file_path, mode="r", encoding="utf-8")
Первым параметром
open () должна быть строка пути. Режимы mode и encoding
Path. open (). В данном примере
"r" (режим чтения), а encoding присваивается "utf-8".
аналогичны одноименным параметрам метода
mode
присваивается
Файловый объект, возвращаемый вызовом
возвращаемый
Path. open ()),является
open ()
экземпляром
(как и файловый объект,
TextIOWrapper:
»> file
<_io.TextIOWrapper name='C:/Users/David/hello.txt' mode='r'
encoding='utf-8'>
Чтобы закрыть файл, вызовите метод. close() для файлового объекта:
»> file.close()
292
ГЛАВА 12
Операции ввода и вывода с файлами
Чтобы открыть файлы на основе существующего объекта pathlib. Path, в боль­
Path. open ().Тем не менее, если вся функ­
pathlib вам не нужна, функция open() отлично подойдет
шинстве случаев используется метод
циональность модуля
для быстрого создания файлового объекта.
Команда
with
Когда вы открываете файл, ваша программа получает доступ к данным, внешним
по отношению к самой программе. Операционная система должна управлять
связью вашей программы с физическим файлом. Когда вы вызываете метод
файлового объекта
. с lose ( ) , операционная система разрывает эту связь.
Если ваша программа аварийно завершится после того, как файл будет открыт,
но до его закрытия, то системные ресурсы, задействованные в связи с файлом,
так и останутся зарезервированными, пока операционная система не поймет,
что они более не используются. Чтобы гарантировать, что ресурсы файловой
системы будут освобождены даже в случае сбоя программы, файл можно от­
крыть командой wi th. Схема использования команды wi th выглядит так:
with path.open(mode="r", encoding="utf-8") as file:
#
Работа с файлом
Команда with состоит из двух частей: заголовка и тела. Заголовок всегда начина­
ется с ключевого слова
path. open ()
wi th
и завершается двоеточием. Возвращаемое значение
присваивается имени переменной после ключевого слова
as.
После заголовка команды wi th следует блок кода с отступом. Когда управление
выходит за пределы блока с отступом, объект файла, присвоенный
file,
будет
автоматически закрыт, даже если во время выполнения кода внутри блока про­
изойдет исключение.
Команды wi th также могут использоваться со встроенной функцией
open ():
with open(file_path, mode="r", encoding="utf-8") as file:
#
Работа с файлом
На самом деле нет никаких причин не открывать файлы командой
wi th.
Этот
синтаксис работы с файлами считается питоническим. Далее в книге мы будем
использовать эту схему при каждом открытии файла.
Чтение данных из файла
ранее созданном до­
Запустите текстовый редактор, откройте файл
hello.txt в
машнем каталоге и введите текст
Сохраните файл.
Hello, World.
12.5. Чтение и запись файлов
В интерактивном окне
IDLE
293
введите следующий фрагмент:
>>> path = Path.home() / "hello.txt"
>>> with path.open(mode="r", encoding="utf-8") as file:
text = file.read()
»>
Файловый объект, созданный
Внутри блока
wi th
path. open (),
и присваивает результат переменной
Значение, возвращаемое
значением
присваивается переменной
метод файлового объекта.
. read (),
read ()
file.
читает текст из файла
text.
представляет собой строковый объект со
"Hello, World":
»> type(text)
<class 'str'>
»> text
'Hello, World'
Метод
. read()
читает текст из файла и возвращает его в виде строки
Если файл содержит несколько строк
(lines) 1 текста,
(string).
то они разделяются сим­
волом новой строки
и разместите текст
\n. Снова откройте файл hello.txt в текстовом редакторе
"Hello again"вo второй строке файла. Сохраните файл.
В интерактивном окне
IDLE снова прочитайте текст из файла:
>>> with path.open(mode="r", encoding="utf-8") as file:
text = file.read()
»> text
'Hello, World\nHello again'
Между строками выводится символ
\n.
Вместо чтения всего содержимого файла за один раз также можно читать файл
по строкам текста:
>>> with path.open(mode="r", encoding="utf-8") as file:
for line in file.readlines():
print(line)
Hello, World
Hello again
1
Английские слова
string
и
line переводятся одинаково string означает тип «строка,,.
возникнуть путаница. Здесь
строку текста в файле.
-
Примеч. ред.
«строка,,., поэтому может
в
Python,
а
line -
просто
ГЛАВА
294
12
Операции ввода и вывода с файлами
Метод . readlines () возвращает итерируемый набор текстовых строк файла. При
каждой итерации цикла
for
возвращается и выводится очередная строка файла.
Обратите внимание на дополнительную пустую строку между двумя строками
текста. Ее появление не связано с завершителями строк в файле. Дело в том,
что
print ()
автоматически вставляет символ новой строки в конец каждой
выводимой строки.
Чтобы вывести две строки файла без лишней пустой строки, передайте функции
print()
в необязательном параметре
end
пустую строку:
>>> with path.open(mode="r", encoding="utf-8") as file:
for line in file.readlines():
print(line, end="")
Hello, World
Hello again
Часто бывает удобнее использовать
. readlines ()
вместо
. read ().
Напри­
мер, каждая строка в файле может представлять одну запись. При помощи
. readlines () можно перебрать строки и обработать их так, как требуется.
Если вы попытаетесь прочитать данные из несуществующего файла, то и функция
Path. open (),и встроенная функция open () выдают ошибку FileNotFoundError:
»> path = Path.home() / "new_file.txt"
>>> with path.open(mode="r", encoding="utf-8") as file:
text = file.read()
Traceback (most recent call last):
File "<pyshell#197>", line 1, in <module>
with path.open(mode="r", encoding="utf-8") as file:
File "C:\Users\David\AppData\Local\Programs\Python\
Python\lib\pathlib.py", line 1200, in open
return io.open(self, mode, buffering, encoding, errors, newline,
File "C:Users\David\AppData\Local\Programs\Python\
Python\lib\pathlib.py", line 1054, in _opener
return self ._accessor.open(self, flags, mode)
FileNotFoundError: [Errno 2) No such file or directory:
'C:\\Users\\David\\new_file.txt'
А теперь посмотрим, как записать данные в файл.
Запись данных в файл
Чтобы записать данные в обычный текстовый файл, следует передать соот­
ветствующую строку методу .write() файлового объекта. Файловый объект
12.5. Чтение и запись файлов
295
должен быть открыт в режиме записи, для чего в параметре mode передается
значение
"w".
Например, следующий фрагмент записывает текст "Hi there ! " в файл hello.txt,
хранящийся в домашнем каталоге:
>>> with path.open(mode="w", encoding="utf-8") as file:
file.write("Hi there!")
9
»>
Обратите внимание: после выполнения блока wi th выводится целое число 9. Это
происходит из-за того, что метод
символов. Строка
возвращает
"Hi there!"
. wr i te ( )
возвращает количество записанных
состоит из девяти символов, поэтому
.write()
9.
Когда текст "Hi there ! " записывается в файл
hello.txt,
все существующее со­
держимое перезаписывается. Все выглядит так, как если бы вы удалили старый
файл
hello.txt и создали новый.
ВАЖНО!
Когда вы передаете параметр
mode="w" при вызове .ореп{), содержимое
исходного файла перезаписывается. Это приводит к потере всех исходных
данных в файле!
Чтобы убедиться в том, что файл содержит только текст "Hi there ! ",прочитаем
и выведем содержимое файла:
>>> with path.open(mode="r",
eпcoding="utf-8")
as file:
text = file.read()
»> print(text)
Hi there!
Чтобы данные присоединялись к концу файла, следует открыть файл в режиме
присоединения:
>>> with path.open(mode="a", encoding="utf-8") as file:
file.write("\nHello")
6
Когда файл открывается в режиме присоединения, новые данные записываются
в конец файла, а старые данные остаются на месте. В начало строки включен
Операции ввода и вывода с файлами
ГЛАВА 12
296
символ новой строки, чтобы слово "Hello" выводилось на новой строке в конце
файла.
Без начального символа новой строки слово
"Hello"
окажется в одной строке
с текстом в конце файла.
Чтобы убедиться в том, что слово
"Hello" записано во второй строке, достаточно
открыть файл и прочитать данные:
>>> with path.open(mode="r", encoding="utf-8") as file:
text = file.read()
»> print(text)
Hi there!
Hello
Метод
. wri telines ()
позволяет записать в файл сразу несколько строк. Прежде
всего, создайте список строк:
»> lines_of_text =
"Hello from Line l\n",
"Hello from Line 2\n",
"Hello from Line 3 \n"'
Затем откройте файл в режиме записи и вызовите метод
.writelines( ), чтобы
записать каждую строку из списка в файл:
>>> with path.open(mode="w", encoding="utf-8") as file:
file.writelines(lines_of _text)
»>
Каждая строка из
lines_of_ text
записывается в файл. Обратите внимание
на то, что каждая строка завершается символом новой строки
ясняется тем, что
. wri telines ()
\n.
Это объ­
не выводит каждый элемент списка в новой
строке.
Если вы откроете несуществующий путь в режиме записи,
Python создаст файл
при условии, что все родительские папки в пути существуют:
»> path = Path.home() / "new_file.txt"
>>> with path.open(mode="w", encoding="utf-8") as file:
file.write("Hello!")
6
12.5. Чтение и запись файлов
Так как каталог Path. home () существует, новый файл
newJile.txt
297
создается
автоматически. Тем не менее, если хотя бы один из родительских каталогов не
существует, вызов
.open() выдаст ошибку FileNotFoundError:
>» path = Path.home() / "new_folder" / "new_file.txt"
>>> with path.open(mode="w", encoding="utf-8") as file:
file.write("Hello!")
Traceback (most recent call last):
File "<pyshell#l72>", line 1, in cmodule>
with path.open(mode="w", encoding="utf-8") as file:
File "C:\Users\David\AppData\Local\Programs\Python\
Python\lib\pathlib.py", line 1200, in open
return io.open(self, mode, buffering, encoding, errors, newline,
File "C:\Users\David\AppData\Local\Programs\Python\
Python\lib\pathlib.py", line 1054, in _opener
return self ._accessor.open(self, flags, mode)
FileNotFoundError: [Errno 2] No such file or directory:
'C:\\Users\\David\\new_folder\\new_file.txt'
Если вы хотите записать данные в путь, но не уверены в том, что его родитель­
ские папки существуют, вызовите метод
.mkdir()
с параметром
parents=True,
прежде чем открывать файл в режиме записи:
>>> path.parent.mkdir(parents=True)
>>> with path.open(mode="w", encoding="utf-8") as file:
file.write("Hello!")
6
Из этого раздела вы узнали много нового. Вы узнали, что все файлы являются
последовательностями байтов
-
целых чисел со значениями от О до
255.
Вы изучили кодировки символов, используемые для преобразования между
байтами и текстом, а также освоили отличия завершителей строк в разных
операционных системах. Наконец, вы научились читать и записывать текстовые
файлы с использованием метода
Path. open () и встроенной функции open ().
Упражнения
1.
Запишите следующий текст в файл
starships.txt,
хранящийся в вашем
домашнем каталоге:
Discovery
Enterprise
Defiant
Voyager
Каждое слово должно располагаться в отдельной строке.
298
2.
ГЛАВА 12
Операции ввода и вывода с файлами
Прочитайте файл
starhips.txt,
созданный в упражнении
1,
и выведите
каждую строку текста в файле. В выводе не должно быть лишних пустых
строк между словами.
3.
Прочитайте файл
12.6. ЧТЕНИЕ
starhips.txt и выведите слова, начинающиеся с буквы
И ЗАПИСЬ ДАННЫХ
О.
CSV
Допустим, в вашем доме установлен датчик, который измеряет температуру
каждые четыре часа. Таким образом, за день регистрируются шесть показаний
температуры.
Каждое показание сохраняется в списке
>>> temperature_readings = [68, 65, 68, 70, 74, 72]
Ежедневно датчик генерирует новый список чисел. Чтобы сохранить эти зна­
чения в файле, можно записывать значения за день в новой строке текстового
файла, разделяя их запятыми:
>>> from pathlib import Path
»> file_path = Path.home() / "temperatures.csv"
»> with file_path.open(mode="a", encoding="utf-8") as file:
file.write(str(temperature_readings[0]))
for temp in temperature_readings[l:]:
file.write(f",{temp}")
2
3
3
3
3
3
Фрагмент создает в домашнем каталоге файл с именем
temperatures.csv
и от­
крывает его в режиме присоединения. В новой строке от конца файла первое
значение списка
temperature_readings
записывается в файл. Затем в той же
строке записываются все остальные значения в списке, перед каждым из кото­
рых вставляется запятая.
В итоге в файл будет записана строка текста "68, 65, 68, 70, 74, 72". Чтобы убе­
диться в этом, прочитайте текст из файла:
>>> with file_path.open(mode="r", encoding="utf-8") as file:
text = file.read()
12.6. Чтение и запись данных CSV
299
»> text
'68,65,68,70,74,72'
CSV (Comma-Separated Values - значения,
temperatures.csv называется файлом CSV.
Такой формат называется
ленные запятыми). Файл
Файлы
разде­
CSV хорошо подходят для хранения записей последовательных данных,
CSV может быть прочитана и представлена
потому что каждая строка значений
в виде списка:
>>> temperatures
text.split(",")
»> temperatures
['68', '65', '68', '70', '74', '72']
В разделе
9.2
«Списки: изменяемые последовательности» я показывал, как
создать список на основе строки строковым методом
. spli t ().
В приведенном
выше примере новый список создается на основе текста, прочитанного из файла
temperatures.csv.
Значения в списке
temperatures являются строками, а не целыми числами (в от­
личие от значений, изначально записанных в файл). Дело в том, что значения
всегда читаются из текстовых файлов в строковом формате.
Строки можно преобразовать в целые числа при помощи генератора списка:
>>> int_temperatures = [int(temp) for temp in temperatures]
>>> int_temperatures
[68, 65, 68, 70, 74, 72]
Мы успешно восстановили список, который был записан в файл temperatures.csv!
CSV - обычные текстовые файлы. Используя
12.5 «Чтение и запись файлов», можно сохранить серии
файла CSV, а затем прочитать их из файла и восстановить
Эти примеры показывают, что
инструменты из раздела
значений в строках
данные.
CSV выполняются настолько часто, что в стандартную
csv, упрощающий работу с файлами
CSV. В следующих разделах я покажу, как использовать модуль csv для чтения
и записи файлов CSV.
Чтение и запись файлов
библиотеку
Python
Модуль
csv
Модуль
был включен модуль
csv применяется для чтения
CSV. В этом разделе мы
csv, чтобы показать, какие
и записи файлов
переработаем предыдущий пример с помощью модуля
операции вы можете выполнять с использованием этого модуля.
300
ГЛАВА 12
Операции ввода и вывода с файлами
Для начала импортируйте модуль
csv в интерактивном
окне
IDLE:
»> import csv
Создадим новый файл
CSV с показателями температуры за несколько дней.
Запись файлов CSV с использованием csv.writer
Создайте список списков, содержащий значения температуры за три дня:
>» daily_temperatures =
[68, 65, 68, 70, 74, 72),
[67, 67, 70, 72, 72, 70)'
[68, 70, 74, 76, 74, 73),
]
Теперь откройте файл
temperatures.csv в режиме записи:
»> file_path = Path. home() / "temperatures. csv"
»> file = file_path.open(mode="w", encoding="utf-8", newline="")
wi th, мы создаем файловый объект
file, чтобы вы могли проанализировать каждый
Вместо того чтобы использовать команду
и присваиваем его переменной
шаг в процессе записи данных.
ВАЖНО!
Обратите внимание, что в приведенном выше примере параметру
метода
.open()
newline
присвоено значение"" .
Это связано с тем, что модуль csv выполняет собственные преобразования
новых строк. Если не указать newline="" при открытии файла, то некоторые
системы (например, Windows) интерпретируют символы новой строки не­
корректно и вставляют вторую новую строку после каждой строки в файле.
Теперь создайте новый объект записи
объект
CSV, для чего следует передать файловый
file методу csv .writer():
>>> writer = csv.writer(file)
Метод
csv.writer()
данных в файл CSV.
возвращает объект записи
Например, при помощи метода
в новую строку файла
CSV:
CSV
wri ter. wri terow()
с методами для записи
можно записать список
12.6. Чтение и заnись данных CSV
301
>>> for temp_list in daily_temperatures:
writer.writerow(temp_list)
19
19
19
Как и метод
. wri te()
объекта файла,
. wri terow()
возвращает количество симво­
лов, записанных в файл. Каждый список из daily_temperatures преобразуется
в строку, содержащую значения температуры, разделенные запятыми, и каждая
из этих строк состоит из
19 символов.
Теперь закройте файл:
»> file.close()
Открыв файл
temperatures.csv в текстовом
редакторе, вы увидите в нем следу­
ющий текст:
68,65,68,70,74,72
67,67,70,72,72,70
68,70,74,76,74,73
В приведенных примерах команда
with
не использовалась для записи в файл,
чтобы вы могли проанализировать каждую операцию в интерактивном окне
IDLE.
На практике гораздо лучше использовать
with.
Вот как выглядит код с использованием команды
wi th:
with file_path.open(mode="w", encoding="utf-8", newline="") as file:
writer = csv.writer(file)
for temp_list in daily_temperatures:
writer.writerow(temp_list)
Главное преимущество использования
csv. wri ter
для записи в файл
CSV за­
ключается в том, что вам не нужно беспокоиться о преобразовании значений
в строки, прежде чем записывать их в файл. Объект csv. writer делает это за
вас, в результате чего код получится более компактным и чистым.
Метод
. wri terow()
записывает одну строку данных в файл
писать и сразу несколько строк при помощи
. wri terows ().
CSV,
но можно за­
Код сокращается еще
значительнее, если ваши данные уже находятся в списке списков:
wi th file_path. open(mode="w", encoding="utf-8", newline='"') as file:
writer = csv.writer(file)
writer.writerows(daily_temperatures)
Операции ввода и вывода с файлами
ГЛАВА 12
302
Давайте прочитаем данные из
ков
daily_temperatures,
Чтение файлов
Для чтения файла
temperatures.csv, чтобы восстановить список спис­
использованный для создания файла.
CSV с использованием csv.reader
CSV средствами модуля csv используется
класс
csv. reader.
Объекты csv. reader, как и csv .writer, создаются на основе объекта файла:
>>> file = file_path.opeп(mode="r",
>>> reader = csv.reader(file)
Вызов
eпcodiпg="utf-8",
пewliпe="")
csv. reader() возвращает объект чтения CSV, который может использо­
ваться для перебора строк в файле
>>> for row
iп
CSV:
reader:
priпt(row)
[ '68', '65'' '68'' '70'' '74', '72']
[ '67'' '67'' '70'' '72', '72'' '70']
[ '68'' '70'' '74'' '76'' '74'' '73']
»> file. close()
Каждая строка файла
CSV возвращается в виде списка строк.
Чтобы восстано­
вить список списков daily_temperatures, необходимо преобразовать каждый
список строк в список целых чисел при помощи генератора списка.
CSV командой wi th,
CSV, преобразуем список строк в список целых
Ниже приведен пример, в котором мы открываем файл
читаем каждую строку в файле
чисел, а затем сохраняем каждый список целых чисел в списке списков с именем
daily _temperatures:
>>> # Создание пустого списка
>>> daily_temperatures = []
>» with
file_path.opeп(mode="r",
eпcodiпg="utf-8",
пewliпe="")
as file:
reader = csv.reader(file)
for row iп reader:
#
Преобразование строки в список целых чисел
iпt_row
#
=
[iпt(value)
Присоединение списка
for value
iп
целых чисел
row]
к списку
daily_temperatures
daily_temperatures.appeпd(iпt_row)
>>> daily_temperatures
[[68, 65, 68, 70, 74, 72], [67, 67, 70, 72, 72, 70],
[68, 70, 74, 76, 74, 73]]
Работать с файлами
CSV средствами
модуля
csv намного удобнее, чем приме­
нять стандартные средства чтения и записи простых текстовых файлов.
12.6. Чтение и запись данных CSV
Впрочем, иногда файл
CSV
303
является более сложным, чем просто файл со
строками однотипных значений. Каждая строка может представлять запись
с разными полями, а первой строкой в файле может быть строка заголовка
с именами полей.
Чтение и запись файлов CSV с заголовками
Пример файла
CSV с
несколькими типами данных и со строкой заголовка:
name,department,salary
Lee,Operations,75000.00
Jane,Engineering,85000.00
Diego,Sales,80000.00
Первая строка файла содержит имена полей. Каждая последующая строка со­
держит запись со значениями для каждого поля.
Для чтения файлов
зовать
CSV с такой структурой, как показано выше, можно исполь­
csv. reader( ), но вам придется отдельно обрабатывать строку заголовка,
а каждая строка возвращается в виде обычного списка без имен полей.
Гораздо удобнее возвращать каждую строку в виде словаря, ключами которого
являются имена полей, а значениями
работают объекты
-
значения полей в строке. Именно так
csv. DictReader.
В текстовом редакторе создайте в своем домашнем каталоге новый файл
с именем
employees.csv и
терактивном окне
IDLE
сохраните в нем приведенный выше текст
CSV.
CSV
В ин­
откройте файл employees.csv и создайте новый объект
csv. DictReader:
>>> file_path = Path.home() / "employees.csv"
>>> file = file_path.open(mode="r", encoding="utf-8", newline="")
>>> reader = csv.DictReader(file)
При создании объекта
DictReader предполагается, что первая строка файла CSV
содержит имена полей. Эти значения сохраняются в списке и присваиваются
атрибуту
. fieldnames
экземпляра
DictReader:
>>> reader.fieldnames
[ 'name', 'department', 'salary']
Как и объекты
csv. reader, объекты DictReader поддерживают перебор:
>>> for row in reader:
print(row)
304
ГЛАВА 12
Операции ввода и вывода с файлами
{'name': 'Lee', 'department': 'Operations', 'salary': '75000.000'}
{' name' : 'Jane', 'department' : 'Engineering', 'salary' : '85000. 00'}
{'name': 'Diego', 'department': 'Sales', 'salary': '80000.00'}
»> file. close()
Вместо того чтобы возвращать каждую строку в виде списка, объекты DictReader
возвращают каждую строку виде словаря. Ключами словаря являются имена
полей, а значениями
-
значения полей из каждой строки в файле
CSV.
Обратите внимание: поле salary читается как строка. Так как файлы CSV явля­
ются обычными текстовыми файлами, значения всегда читаются как строки, но
их можно преобразовать в другие нужные вам типы данных. Например, можно
обработать каждую строку функцией, преобразующей ключи к правильным
типам данных:
>>> def process_row(row):
row["salary"] = float(row["salary"])
return row
»> with file_path.open(mode="r", encoding="utf-8", newline="") as file:
reader = csv.DictReader(file)
for row in reader:
print(process_row(row))
{ 'name' : 'Lee', 'department' : 'Operations', 'salary' : 75000. 0}
{' name' : 'Jane', 'department' : 'Engineering', 'salary': 85000. 0}
{'name': 'Diego', 'department': 'Sales', 'salary': 80000.0}
Функция
process_row()
получает словарь, прочитанный из файла
вращает новый словарь с ключом
CSV,
и воз­
"salary", преобразованным в число с плава­
ющей точкой.
Файлы
CSV
с заголовками можно создавать с использованием класса
csv. DictWri ter,
который записывает словари с совместно используемыми
ключами в строки файла
CSV.
Следующий список словарей представляет собой небольшую базу данных
с именами пользователей и их возрастом:
» > people = [
{ "name": "Veronica", "age": 29},
{"name": "Audrey", "age": 32},
{"name": "Sam", "age": 24},
12.6. Чтение и запись данных CSV
Чтобы сохранить данные в списке
с именем
people.csv в
people
в файле
CSV,
305
откройте новый файл
режиме записи и создайте новый объект
csv. DictWri ter
для файлового объекта:
>» file_path = Path.home() / "people.csv"
»> file = file_path.open(mode="w", encoding="utf-8", newline="")
>>> writer = csv.DictWriter(file, fieldnames=["name", "age"])
Когда вы создаете экземпляр
newDictWriter,
файловый объект для записи данных
CSV.
в первом параметре передается
Параметр
fieldnames,
который яв­
ляется обязательным, содержит список строк с именами полей.
ПРИМЕЧАНИЕ
В приведенном примере в параметре
терал
fieldnames передается списковый ли­
[«name», «age»].
Также можно присвоить fieldnames значение
people[O].keys(), так как реорlе[О]­
словарь, ключами которого являются имена полей . Это полезно, если имена
полей неизвестны или же их столько , что списковый литерал становится не­
удобным .
Как и объекты
csv.writer, объекты DictWriter содержат методы .writerow()
.writerows() для записи строк данных в файл. Объекты DictWriter также
содержат третий метод . wri teheader(), который записывает строку заголовка
и
в файл
CSV:
>>> writer.writeheader()
10
Метод
в
. wri teheader() возвращает количество символов, записанных
файл, - в данном случае 10. Записывать строку заголовка необязатель­
но, но это рекомендуется делать, потому что она помогает определить, что
представляют собой данные, содержащиеся в файле
упрощают чтение строк из файлов
класса
CSV. Кроме того, они
CSV в формате словаря с использованием
DictReader.
При наличии записанного заголовка можно записать данные списка
в файл
CSV методом . wri terows ():
>>> writer.writerows(people)
»> file.close()
people
Операции ввода и вывода с файлами
ГЛАВА 12
306
В вашем домашнем каталоге появился файл с именем
people.csv,
содержащий
следующие данные:
name,age
Veronica,29
Audrey,32
Sam,24
Файлы
CSV
предоставляют гибкие и удобные средства хранения данных.
- безусловно,
Их часто применяют в сфере бизнеса, и умение работать с ними
ценный навык!
Упражнения
1.
Напишите программу, которая записывает следующий список списков
в файл
numbers.csv в вашем домашнем
каталоге:
numbers = [
[1, 2, З, 4, 5],
[б, 7, 8, 9, 10],
[11, 12, 13, 14, 15],
2.
numbers.csv из
1 в список списков целых чисел с именем numbers. Выведите
Напишите программу, которая читает числа в файле
упражнения
прочитанный список списков. Результат должен выглядеть так:
[[1, 2,
3.
З,
[б,
4, 5],
7, 8, 9, 10], [11, 12, 13, 14, 15]]
Напишите программу, которая записывает следующий список словарей
в фaйлfavonte_colors.csv в вашем домашнем каталоге:
favorite_colors = [
{"name": "Joe", "favorite_color": "Ыuе"},
{"name": "Anne", "favorite_color": "green"},
{"name": "Bailey", "favorite_color": "red"},
4.
Выходной файл
CSV должен
иметь следующий формат:
name,favorite color
Joe,Ыue
Anne,green
Bailey, red
5.
Напишите программу, которая читает данные из фaйлafavonte_colors.csv
(см. упражнение
3)
в список cлoвapeйfavonte_colors. Выведите список
словарей. Результат должен выглядеть примерно так:
12.8. Итоги и дополнительные ресурсы
307
[{"name": "Joe", "favorite_color": "Ыuе"},
{"name": "Anne", "favorite_color": "green"},
{"name": "Bailey", "favorite_color": "red"}]
12.7. ЗАДАЧА:
В папке
СОЗДАНИЕ СПИСКА РЕКОРДОВ
practice_files находится
файл
CSV scores.csv с данными об
игроках и их
показателях. Первые несколько строк файла выглядят так:
name, score
LLCoolDave,23
LLCoolDave,27
red,12
LLCoolDave,26
tom123,26
Напишите программу, которая читает данные из файла
файл
high_scores.csv,
CSV
и создает новый
в котором каждая строка содержит имя игрока и его наи­
более высокий результат.
Выходной файл
CSV должен
выглядеть примерно так:
name,high_score
LLCoolDave,27
red,12
tom123,26
О_О,22
Misha46,25
Empiro,23
МаххТ,25
L33tH4x,42
johnsmith,30
Решение задачи, а также многие другие дополнительные ресурсы доступны
в интернете по адресу
12.8.
realpython.com/python-basics/resources.
ИТОГИ И ДОПОЛНИТЕЛЬНЫЕ РЕСУРСЫ
В этой главе я рассказал о файловой системе и о путях к файлам, а также пока­
зал, как работать с ними при помощи модуля
Python.
pathlib стандартной библиотеки
Path, обращаться к компонентам
Вы научились создавать новые объекты
путей, а также создавать, перемещать и удалять файлы и папки.
Также вы узнали, как читать и записывать обычные текстовые файлы методом
Path. open () и встроенной функцией open () и как работать с файлами CSV
308
ГЛАВА 12 · Операции ввода и вывода с файлами
(значения, разделенные запятыми) с использованием модуля
ной библиотеки
csv из стандарт­
Python.
ИНТЕРАКТИВНЫЙ ТЕСТ
К этой главе прилагается бесплатный интерактивный тест для проверки усво­
енных вами знаний. Тест доступен на телефоне или компьютере:
rea/python.comlquizzes!pybasics-fi/es
Дополнительные ресурсы
За дополнительной информацией о модулях и пакетах обращайтесь к следу­
ющим ресурсам:
•
«Reading and Writing Files in Python» (https.j/realpython.com/read-writefiles-python/)
•
«Working With Files in Python» (https.j/realpython.com/working-with-filesin-python/)
ГЛАВА
13
Установка пакетов
•
с помощью р1р
До сих пор мы работали в рамках стандартной библиотеки
Python.
Изучая
остальной материал нашего учебного курса, вы будете работать с различными
пакетами, не включенными в
Python по умолчанию.
Для многих языков программирования существует менеджер пакетов, авто­
матизирующий процесс установки, обновления и удаления сторонних пакетов.
И
Python -
не исключение.
Менеджер пакетов, ставший де-факто стандартным для
Python, называется pip.
Python.
Традиционно его приходилось загружать и устанавливать отдельно от
Начиная с версии
3.4 он включен
в большинство дистрибутивов языка.
В этой главе вы узнаете:
•
как установить и управлять сторонними пакетами в
pip;
•
каковы преимущества и риски использования сторонних пакетов.
Итак, за дело!
13.1. УСТАНОВКА СТОРОННИХ
С ПОМОЩЬЮ PIP
Менеджер пакетов
Python - pip -
ПАКЕТОВ
используется для установки и управления
сторонними пакетами. Эта программа существует отдельно от
Python, хотя
весьма вероятно, что она была установлена на вашем компьютере, когда вы
загружали и устанавливали
Python.
pip работает через командную строку.
Это означает, что программа должна за­
пускаться из командной строки или программы-терминала. Способ открытия
терминала зависит от операционной системы.
31 О
ГЛАВА
1З
Установка пакетов с помощью pip
Windows
Windows, затем введите cmd и нажмите Enter, чтобы открыть
приложение Командная строка ( Command Prompt ). На экране появляется окно,
Нажмите клавишу
которое выглядит примерно так, как показано ниже :
Также можно воспользоваться приложением
Windows,
введите powershell и нажмите
Enter,
PowerShell: нажмите клавишу
PowerShell.
появится окно
13.1. Установка сторонних пакетов с помощью pip
311
macOS
Нажмите Сmd+пробел, чтобы открыть окно поиска
Spotlight. Введите команду
terminal и нажмите Enter, чтобы запустить приложение Terminal. Открывшееся
окно выглядит так:
Ubuntu Linux
Щелкните на кнопке
Show Applications
в нижней части панели инструментов
и проведите поиск по строке terminal. Щелкните на значке приложения Terminal,
чтобы открыть терминал. Открывшееся окно терминала выглядит примерно так:
312
Установка пакетов с помощью pip
ГЛАВА 1 З
Проверка установки
pip
Открыв окно терминала, убедимся в том, что программа
pip установлена на
вашем компьютере. Конкретный способ проверки зависит от операционной
системы.
В
Windows
введите следующую команду для проверки
pip:
$ python -m pip --version
В
macOS
и
Linux
наличие установленной копии
pip
проверяется следующей
командой:
$
pythonЗ
-m pip --version
Если программа
pip
установлена, то в окне терминала должна появиться ин­
формация, которая выглядит примерно так:
pip 20.2.З from c:\users\David\appdata\local\programs\python\
python\lib\site-packages\pip (python 3.9)
Из вывода следует, что в настоящее время в системе установлена версия
pip 20.2.3 и она связана с установкой Python 3.9.
ВАЖНО!
Если вы не получили никакого результата при проверке
pip
или было вы­
ведено сообщение об ошибке, попробуйте выполнить команду pythonЗ. 9
-m pip - -version.
Возможно, все последующие команды pythonЗ придется
заменить на pythonЗ.
Обновление
9.
pip до последней версии
Прежде чем двигаться дальше, убедитесь в том, что у вас установлена последняя
версия
pip. Чтобы обновить pip, введите следующую команду в окне терминала
Enter:
и нажмите
$
pythonЗ
-m pip install --upgrade pip
Если доступна б~лее новая версия
pip, она будет загружена и установлена ав­
томатически. В противном случае появится сообщение о том, что в системе уже
установлена самая последняя версия:
что-нибудь в этом роде.
«Requirement already satisfied» -
или
13.1. Установка сторонних пакетов с помощью pip
31 З
ВАЖНО!
В этом разделе все приводимые команды начинаются с pythonЗ
важно для пользователей
macOS
и Liпux, потому что команда
может установить команды для неправильной версии
-m pip. Это
python -m pip
Python.
Но если вы являетесь пользователем Windows, вам придется использовать
python -m pip, потому что команда pythonЗ -m pip не будет работать на вашем
компьютере .
Итак, программа pip обновлена до последней версии
-
давайте посмотрим, что
она может делать!
Вывод списка всех установленных пакетов
Вы можете воспользоваться pip для вывода списка установленных пакетов.
Посмотрим, какие пакеты доступны в настоящий момент. Введите следующую
команду в окне терминала:
$
руthопЗ
-m pip list
Если никакие пакеты еще не установлены (например, если вы начали этот учеб­
ный курс с установки
Package
Python 3.9), pip выведет примерно такую
информацию:
Versioп
pip
19.3.1
setuptools 41.2.0
Как видите, список небольшой. В нем указана сама программа pip, потому что
pip также является пакетом. Возможно, также в списке будет присутствовать
setuptools - пакет, используемый pip для настройки и установки других
пакетов.
Когда вы устанавливаете пакет с использованием pip, он появится в списке.
Вы всегда можете воспользоваться командой pip list для просмотра списка
пакетов (и версий каждого пакета), установленных в системе в настоящее время.
Установка пакета
Сейчас мы покажем, как установить ваш первый пакет
Python.
Для этого
упражнения мы возьмем requests, один из самых популярных пакетов
за всю историю языка.
Python
ГЛАВА 13
314
Установка пакетов с помощью pip
Введите в терминале следующую команду:
$ python3 -m pip install requests
Пока
pip устанавливает пакет requests, выводится информация о ходе уста­
новки:
Collecting requests
Downloading https:// ... /requests-2.22.0-py2.py3-none-any.whl (57kB)
1 •••••••••••••••••••••••••••••••• 1 бlkB 2.0MB/s
Collecting urllib3!=1.25 . 0,!=1.25.1,<1.26,>=1.21.1
Downloading https:// ... urllib3-1.25.7-py2.py3-none-any.whl (125kB)
1 • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • 1 133kB 3.3MB/s
Collecting certifi>=2017.4.17
Downloading https:// ... certifi-2019.11 . 28.py3-none-any.whl (156kB)
1 ••••••••• • ••••••• •• •••• • •••••••• 1 163kB . ..
Collecting chardet<3.1.0,>=3.0.2
Downloading https:// ... chardet-3.0.4-py2.py3-none-any.whl (133kB)
1 • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • 1 143kB 6.8MB/s
Collecting idna<2.9,>=2.5
Downloading https:// ... idna-2.8-py2.py3-none-any.whl (58kB)
1 • •••••• • •••••••••••••• •• •••••••• 1 бlkB 3.8MB/s
Installing collected packages: urllibЗ, certifi, chardet, idna,
requests
Successfully installed certifi-2019.11.28 chardet-3.0.4 idna-2.8
requests-2.22.0 urllib3-1.25.7
ПРИМЕЧАНИЕ
Форматирование было слегка изменено, чтобы вывод нормально помещался
на странице. Результат, который вы увидите на своем компьютере, может не­
сколько отличаться от приведенного.
Обратите внимание: сначала pip сообщает о загрузке requests. Вы видите
URL-
aдpec, с которого pip устанавливает пакет, а также индикатор, демонстрирующий
ход загрузки.
pip устанавливает еще четыре пакета: chardet,
requests. Это
означает, что установка этих пакетов необходима для правильной работы requests.
После этого мы замечаем, что
certifi,
idпa и urllibЗ. Они называются зависимостями пакета
После того как
pip
завершит установку
выполните команду
глядеть так:
$ python3 -m pip list
Version
Package
pip list
requests
и его зависимостей, снова
в окне терминала . На этот раз список будет вы­
13.1. Установка сторонних пакетов с помощью pip
315
---------- ---------certifi
chardet
idna
pip
requests
setuptools
urllib3
2019.11.28
3.0 . 4
2.8
19.3.1
2.22.0
41.2.0
1.25.7
Как видите, в системе установлена версия
висимости
chardet, certifi, idna
По умолчанию
2.22.0
пакета
requests, а также за­
и urllibЗ.
pip устанавливает последнюю версию
пакета. Вы также можете
управлять тем, какая версия пакета будет установлена, при помощи необяза­
тельных спецификаторов версии.
Установка конкретных версий пакетов
Существует несколько способов, как установить именно ту версию, которую
вы хотите. Например:
1)
последнюю версию с номером, большим заданного номера;
2)
последнюю версию с номером, меньшим заданного номера;
3)
последнюю версию с конкретным номером .
Чтобы установить последнюю версию
requests с номером версии 2 и выше,
выполните следующую команду:
$ python3 -m pip install requests>=2.0
>=2. 0 после имени пакета requests. Оно при­
pip установить версию requests, номер которой больше или равен 2.0.
Обратите внимание на выражение
казывает
Выражение
>=
называется спецификатором версии; оно указывает, какая
версия пакета должна быть установлена . Есть несколько разных специфика­
торов, которые вы можете использовать в командах. Чаще всего встречаются
следующие спецификаторы.
-------------------------- --~ -...-.-:~~-- --~-~-
СПЕЦИФИКАТОР ВЕРСИИ
ОПИСАНИЕ
<=, >=
Меньше или больше спецификатора (включительно)
<, >
Меньше или больше спецификатора (не включая)
В точности равно спецификатору
316
ГЛАВА 1 З
Установка пакетов с помощью pip
Рассмотрим несколько примеров.
Чтобы установить последнюю версию, номер которой меньше либо равен за­
данной, используйте спецификатор версии<=:
$
pythonЗ
-m pip install
requests<=З.0
Команда установит последнюю версию
равен
requests,
номер которой меньше либо
3.0.
Спецификаторы версии<= и
>= инклюзивны, то есть они включают номер, сле­
дующий за спецификатором. Также существуют эксклюзивные спецификаторы,
не включающие номер: спецификаторы< и>.
Следующая команда устанавливает последнюю версию
строго меньше
$
pythonЗ
requests, номер которой
3.0:
-m pip install
requests<З.0
Спецификаторы можно объединять, чтобы программа
pip
всегда устанавли­
вала версию в заданном диапазоне номеров. Например, следующая команда
устанавливает последнюю версию
$
pythonЗ
requests
из серии
1.0:
-m pip install requests>=l.0,<2.0
Такие команды стоит использовать в том случае, если ваш проект был совместим
только с версией
1.0 пакета и вы хотите установить последние обновления для
этой серии.
Наконец, зависимости можно привязать к конкретной версии спецификатором
версии==:
$
pythonЗ
-m pip install requests==2.22.0
Эта команда устанавливает фиксированную версию
2.22.0
пакета
request.
Вывод подробной информации о пакетах
Теперь, когда пакет
requests
установлен, вы можете воспользоваться
просмотра подробной информации о пакете:
$ pythonЗ -m pip show requests
Name: requests
Version: 2.22.0
Summary: Python НТТР for Humans.
Home-page: https://requests.readthedocs.io
pip
для
13.1. Установка сторонних пакетов с помощью pip
317
Author: Kenneth Reitz
Author-email: me@kennethreitz.org
License: Apache 2.0
Location: c:\users\David\ ... \python\python\lib\site-packages
Requires: chardet, idna, certifi, urllibЗ
Required-by:
Команда pythonЗ
-m pip show
выводит информацию об установленном пакете,
включая имя автора, адрес электронной почты и домашнюю страницу в интер­
нете, на которой вы сможете больше узнать о том, что делает пакет.
Пакет requests используется для выдачи запросов НТТР из программы
Python.
Этот пакет, чрезвычайно полезный во многих областях, стал зависимостью для
большого числа других пакетов
Python.
Удаление пакета
Если пакет можно установить при помощи
можно удалить. Давайте удалим пакет
pip, то вполне логично, что его также
requests.
Чтобы удалить его, введите следующую команду в окне терминала:
$
pythonЗ
-m pip uninstall requests
ВАЖНО!
Если у вас уже имеются проекты, использующие
requests
или одну из его
зависимостей, возможно, вам не стоит выполнять команды, о которых мы
расскажем далее в этом разделе.
Немедленно появится следующее приглашение:
Uninstalling requests-2.22.0:
Would remove:
c:\users\damos\ ... \requests-2.22.0.dist-info\*
c:\users\damos\a ... \requests\*
Proceed (y/n)?
Прежде чем удалять что-нибудь с вашего компьютера,
pip сначала спрашивает
у вас разрешение. Как предусмотрительно!
Введите у и нажмите
Enter,
подтверждает, что пакет
чтобы продолжить. Появится сообщение, которое
requests был удален:
Successfully uninstalled requests-2.22.0
318
ГЛАВА
1З
Установка пакетов с помощью pip
Снова просмотрите список пакетов:
$ python3 -m pip list
Version
Package
certifi
chardet
idna
pip
setuptools
urllib3
2018.4.16
3.0.4
2.7
10.0.1
39.0.1
1.23
Обратите внимание: pip удаляет requests, но не трогает его зависимости! Такое
поведение реализовано сознательно, а не по ошибке.
Представьте, что вы установили в своей системе несколько пакетов, часть из
которых имеет общие зависимости. Если бы программа
pip удаляла пакеты
вместе с зависимостями, то другие пакеты, для которых необходимы эти за­
висимости, стали бы неработоспособными!
А пока удалите оставшиеся пакеты командой
pip uninstall.
Все четыре пакета
можно удалить одной командой:
$ python3 -m pip uninstall certifi chardet idna urllib3
Когда это будет сделано, снова выполните команду pip list и убедитесь, что
пакеты были удалены. Команда должна вывести тот же список пакетов, который
вы видели в самом начале работы.
Package
Version
10.0.1
pip
setuptools 39.0.1
Экосистема сторонних пакетов
-
одна из самых сильных сторон
пакеты позволяют программистам
Python
Python.
Эти
работать чрезвычайно эффективно
и создавать полнофункциональные программные продукты намного быстрее,
чем это можно сделать па таком языке, как, допустим, С++. Тем не менее ис­
пользование сторонних пакетов имеет некоторые особенности, к которым сле­
дует относиться с осторожностью. Об этом я расскажу в следующем разделе.
13.2. ПОДВОДНЫЕ
КАМНИ СТОРОННИХ ПАКЕТОВ
Элегантность сторонних пакетов заключается в том, что они позволяют легко
добавить в проект новую функциональность, и вам не приходится кодировать
13.2. Подводные камни сторонних пакетов
319
все с нуля. Тем самым достигается значительное повышение производитель­
ности.
Однако чем больше мощность, тем выше ответственность. Как только вы
включаете в свой проект чей-то пакет, вы оказываете огромное доверие тем,
кто занимался его разработкой и сопровождением.
Используя пакет, который разрабатывал кто-то другой, вы отчасти теряете
контроль над своим проектом. Разработчики, занимающиеся сопровождением
пакета, могут выпустить новую версию с изменениями, несовместимыми с той
версией, которая используется в вашем проекте.
По умолчанию
pip устанавливает новейшую версию пакета.
Если вы передадите
свой код другим разработчикам, которые установят обновленную версию исполь­
зуемого в вашей программе пакета, возможно, они не смогут запустить ваш код.
Это создает значительную проблему как для вас, так и для конечного пользо­
вателя. К счастью,
Python предоставляет решение для этой достаточно частой
проблемы: виртуальные среды.
Виртуальная среда создает изолированную и, что самое важное,
-
воспроиз­
водимую среду, которая может использоваться для разработки проекта. Среда
содержит конкретную версию
Python, а также конкретные версии зависимостей
вашего проекта.
Когда вы передаете свой код кому-то другому, у получателя остается возмож­
ность воспроизвести среду и быть уверенным в том, что ему удастся выполнить
код без ошибок.
Виртуальные среды
-
достаточно сложная тема, рассказ о которой выходит за
рамки нашей книги. Чтобы больше узнать о виртуальных средах и о том, как их
использовать, обращайтесь к учебному курсу «Managing Python Dependencies
With Pip and Viгtual Environments» (https//realpython.com/products/managingpython-dependencies/) на сайте Real Python. В этом курсе вы узнаете, как:
•
устанавливать сторонние пакеты
пакетов
Python
с использованием менеджера
pip в Windows, macOS и Liпux на более высоком уровне, чем
описано здесь, а также как их использовать и управлять ими;
•
изолировать эависимости проектов в виртуальных средах для предот­
вращения конфликтов версий в ваших проектах
•
Python;
применять полный процесс поиска и идентификации качественных
сторонних 11акетов, состоящий из семи этапов, в ваших проектах
(и для обоснования ваших решений у коллег и руководства);
Python
ГЛАВА
320
1З
Установка пакетов с помощью pip
создавать воспроизводимые среды разработки и развертывания прило­
•
жений с использованием менеджера пакетов
pip и файлов требований.
13.З. ИТОГИ И ДОПОЛНИТЕЛЬНЫЕ РЕСУРСЫ
В этой главе вы научились устанавливать сторонние пакеты с помощью
менеджера пакетов языка
включая
Python.
pip pip,
Вы узнали несколько полезных команд
pip install, pip list , pip show и pip uninstall .
Также вы узнали о некоторых проблемах, которые иногда возникают при ис­
пользовании сторонних пакетов. Не каждый пакет, который может быть загру­
жен в
pip, подойдет для
вашего проекта. Так как код установленного пакета вы
не контролируете, вам приходится верить, что пакет безопасен и будет хорошо
работать у пользователей вашей программы.
ИНТЕРАКТИВНЫЙ ТЕСТ
К этой главе прилагается бесплатный интерактивный тест для проверки усво­
енных вами знаний. Тест доступен на телефоне или компьютере:
realpython.com!quizzeslpybasics-installing-packages
Дополнительные ресурсы
За дополнительной информацией об управлении сторонними пакетами об­
ращайтесь к следующим ресурсам:
•
« Managing Python Dependencies~ ( https:j/realpython.com/products/
managing-python-dependencies/)
•
«Python Virtual Environments: А Primer~ (https:j/realpython.com/pythonvirtua/-environments-a-primer/)
ГЛАВА
14
Создание и изменение
файлов PDF
PDF
(PortaЫe
Document Format) -
один из самых известных форматов для
распространения документов в интернете. Файлы
PDF моrут содержать текст,
rрафику, таблицы, формы, мультимедийный контент (например, видео или
анимации)
-
и все это в одном файле.
Разнообразие типов контента усложняет работу с
PDF.
Столько разных видов
данных, которые необходимо декодировать при открытии файла
PDF! К сча­
Python существуют отличные пакеты для чтения, обработки
файлов PDF.
стью, в экосистеме
и создания
В этой rлаве вы научитесь:
•
читать текст из файла
•
разбивать файл
•
объединять несколько файлов
•
поворачивать и обрезать страницы в файлах
•
шифровать и дешифровать файлы
•
создавать файл
PDF;
PDF на несколько файлов;
PDF с
PDF;
PDF;
PDF с паролем;
нуля.
Итак, за дело!
14.1. ИЗВЛЕЧЕНИЕ ТЕКСТА ИЗ ФАЙЛА PDF
В этом разделе вы научитесь читать файлы
PDF
и извлекать из них текст при
помощи пакета PyPDF2. Но прежде чем пользоваться пакетом, необходимо
установить его с помощью
$
pythonЗ
pip:
-m pip install PyPDF2
ГЛАВА 14
322
Создание и изменение файлов PDF
Проверьте установку, выполнив следующую команду в терминале:
$ pythonЗ -m pip show PyPDF2
Name: PyPDF2
Version: 1.26.0
Summary: PDF toolkit
Home-page: http://mstamy2.github.com/PyPDF2
Author: Mathieu Fenniak
Author-email: biziqe@mathieu.fenniak.net
License: UNKNOWN
Location: c:\users\david\python\lib\site-packages
Requires:
Required-by:
Обратите внимание на информацию о версии. На момент написания книги
новейшей версией PyPDF2 была версия
1.26.0.
Если у вас открыта среда
IDLE,
ее необходимо перезапустить, чтобы использовать пакет PyPDF2.
Открытие файла
PDF
Начнем с открытия файла PDF и чтения информации об этом файле. В примерах
Pri.de_and_ Prejudice.pdf, хранящийся в папке practice_files
мы используем файл
главы
14.
ПРИМЕЧАНИЕ
Решения упражнений и необходимые файлы можно загрузить со следующей
страницы, если это еще не было сделано ранее:
https:!/github.com/realpython!python-basics-exercises
Откройте интерактивное окно в
пакета
IDLE
и импортируйте класс PdfFileReader из
PyPDF2:
>>> from PyPDF2 import PdfFileReader
Для создания нового экземпляра класса PdfFileReader вам понадобится путь
к файлу
дуля
PDF,
pathlib:
который вы собираетесь открыть. Получим его средствами мо­
>>> from pathlib import Path
»> pdf_path = (
Path.home() /
"python-basics-exercises" /
"ch14-interact-with-pdf-files" /
"practice_files" /
14.1. Извлечение текста из файла PDF
323
"Pride_and_Prejudice.pdf"
Переменная
pdf_path теперь содержит путь к РDF-версии романа Джейн
«Pride and Prejudice»
Остин
(«Гордость и предубеждение»).
ПРИМЕЧАНИЕ
Возможно, вам придется изменить значение pdf_path, чтобы оно соответ­
ствовало местонахождению папки
python-basics-exercises/ на вашем ком­
пьютере.
Теперь создайте экземпляр
PdfFileReader:
>>> pdf = PdfFileReader(str(pdf_path))
Объект pdf _path преобразуется в строку, потому что класс PdfFileReader не
умеет читать данные из объекта pathlib. Path.
Как мы говорили в главе
12 «Операции ввода и вывода с файлами», все откры­
тые файлы следует закрывать до завершения программы. Объект PdfFileReader
делает это за вас, так что вам не придется беспокоиться об открытии или за­
крытии файлов
PDF!
Когда экземпляр PdfFileReader будет создан, вы можете использовать его для
сбора информации о файле
PDF.
Например,
. getNumPages ()
возвращает коли­
чество страниц, содержащихся в файле:
>>> pdf.getNumPages()
234
Возможно, вы заметили, что имя
.getNumPages()
записано в смешанном реги­
стре, а не в нижнем с подчеркиваниями, как рекомендовалось в РЕР
ните: в РЕР
8 содержатся рекомендации,
а не правила. С точки зрения
8. Пом­
Python
смешанный регистр абсолютно законен.
ПРИМЕЧАНИЕ
Пакет
PyPDF2 происходит от пакета pyPdf. Он был написан в 2005 году, всего
через 4 года после публикации РЕР 8.
В это время многие программисты переходили на
Python с языков, в которых
смешанный регистр был более распространенным .
Создание и изменение файлов PDF
ГЛАВА 14
324
Также к информации документа можно обратиться через атрибут
. documentinfo:
>>> pdf.documentlnfo
{'/Title': 'Pride and Prejudice, Ьу Jane Austen', '/Author': 'Chuck',
'/Creator': 'MicrosoftФ Office Word 2007',
'/CreationDate': 'D:20110812174208', '/ModDate': 'D:20110812174208',
'/Producer': 'MicrosoftФ Office Word 2007'}
Объект, возвращаемый . documentinfo, выглядит как словарь, но в действитель­
ности им не является. К каждому элементу в
. documentinfo можно обращаться
как к атрибуту.
Например, для получения названия используется атрибут. title:
>>> pdf.documentlnfo.title
'Pride and Prejudice, Ьу Jane Austen'
Объект
Класс
. documentinfo содержит метаданные PD F, указанные при создании PD F.
PdfFileReader предоставляет все методы и атрибуты, необходимые для
обращения к данным в файлах
с файлом
PDF и
PDF.
Давайте посмотрим, что можно сделать
как это делается.
Извлечение текста из страницы
представлены в
классом
Экземпляры
Страницы
PDF
PageObject
используются для взаимодействия со страницами в файлах
Создавать экземпляры
PyPDF2
PageObject
PageObject.
PDF.
напрямую не нужно. Вместо этого к ним
можно обращаться при помощи метода . getPage() объекта PdfFileReader.
Извлечение текста из одной страницы
1.
2.
Получение
PageObject
вызовом
PDF выполняется
в два этапа.
PdfFileReader.getPage().
Извлечение текста в виде строки методом
. extractтext ()
экземпляра
PageObject.
Файл
Pride_and_Prejudice.pdf состоит из 234 страниц.
Каждой странице при­
своен индекс от 0 до 233. Чтобы получить объект PageObject, представляющий
конкретную страницу, передайте индекс страницы при вызове
der.getPage():
>>> first_page
=
pdf.getPage(0}
.getPage() returns
а
PageObject:
>>> type(first_page)
<class 'PyPDF2.pdf.PageObject'>
PdfFileRea-
14.1. Извлечение текста из файла PDF
Текст страницы извлекается методом
325
PageObj ect. extractText ():
>>> first_page.extractText()
' \п \пТhе Project Guteпberg EBook of Pride апd Prejudice, Ьу Jапе
Austeп\п \п\пThis eBook is for the use of апуопе aпywhere at по cost
апd with\п \пalmost по restrictioпs whatsoever. You may сору it,
give it away оr\п \пrе\п-\пusе it uпder the terms of the Project
Guteпberg Liceпse iпcluded\п \пwith this eBook or опliпе at
www.guteпberg.org\п \п \п \пTitle: Pride апd Prejudice\п \n
\пAuthor: Jane Austen\п \n \nRelease Date: August 26, 2008
[EBook #1342]\п\n[Last updated : August 11, 2011]\n \п \nlaпguage :
Eng\nlish\n \п \nCharacter set eпcodiпg: ASCII\п \n \п***
START OF THIS PROJECT GUTENBERG ЕВООК PRIDE AND PREJUDICE ***\п \п
\n \n \n \nProduced Ьу Aпonymous Volunteers, and David Widger\п
\п \п \п \п \п \п \nPRIDE AND PREJUDICE \п \n \nBy Jane
Austeп \n \n\n \n \nCoпtents\п \n'
Результат был дополнительно отформатирован, чтобы он лучше выглядел на
странице. Вывод, который вы получите на своем компьютере, может быть от­
форматирован иначе.
ПРИМЕЧАНИЕ
В зависимости от способа кодирования файла
PDF текст, прочитанный из PDF,
может содержать странные символы или же в нем могут пропасть разрывы
строк. Это оборотная сторона чтения текста из PDF. В реальных приложениях
текст, прочитанный из
PDF,
иногда приходится чистить вручную .
Каждый объект PdfFileReader содержит атрибут . pages, который может исполь­
PDF. Например,
PDF с текстом романа
зоваться для последовательного перебора всех страниц файла
следующий цикл
«Pride and
for
выводит содержимое всех страниц
Prejudice~ :
>>> for page iп pdf.pages:
print(page.extractText())
Объединим все, что вы узнали, и напишем программу, которая извлекает весь
текст из файла
Pride_and_ Prejudice.pdf и сохраняет его в файле .txt.
Все вместе
Откройте в
IDLE
новое окно редактора и введите следующий код:
from pathlib import Path
from PyPDf 2 import PdfFileReader
ГЛАВА 14
326
#
Создание и изменение файлов
PDF
путь правильным путем для вашего компьютера.
Замените следующий
pdf_path = (
Path.home() /
"python-basics-exercises" /
"ch14-interact-with-pdf-files" /
"practice-files" /
"Pride_and_Prejudice.pdf"
# 1
pdf_reader = PdfFileReader(str(pdf_path))
output_file_path = Path.home() / "Pride_and_Prejudice.txt"
# 2
with output_file_path.open(mode="w") as output_file:
#
з
title = pdf_reader.documentinfo.title
num_pages = pdf_reader.getNumPages()
output_file.write(f"{title}\nNumber of pages: {num_pages}\n\n")
# 4
for page in pdf_reader.pages:
text = page.extractText()
output_file.write(text)
Разберем этот код поэтапно.
1.
PdfFileReader присваивается переменной
pdf_reader. Также создается новый объект Path для файла Pnde_and_
Сначала новый экземпляр
Prejudice.txt в
вашем домашнем каталоге; он присваивается переменной
output_file_path.
2.
output_file_path открывается в режиме записи, а объект файла,
возвращенный вызовом . open (), присваивается переменной output_
file. Команда with, о которой вы узнали в главе 12 «Операции ввода
Затем
и вывода с файлами>->, обеспечивает закрытие файла при выходе из
блока
3.
with.
Далее в блоке
with название PDF и количество страниц записываются
output_file. wri te ().
в текстовый файл вызовом
4.
Наконец, программа в цикле for перебирает все страницы
PDF. На каждом
шаге цикла следующий объект PageObject присваивается переменной
page.
Текст каждой страницы извлекается вызовом
и записывается в
page. extractText ()
output_file.
Сохраните и запустите программу. Она создает в вашем домашнем каталоге
Pnde_and_Prejudice.txt и полным
Pnde_and_Prejudice.pdf. Откройте его и убедитесь сами!
новый файл с именем
текстом документа
14.2. Извлечение страниц из файлов PDF
327
Упражнения
practice_files главы 14 присутствует файл PDF с именем zen.pdf.
Создайте экземпляр PdfFileReader для этого файла PDF.
В папке
1.
Используя экземпляр PdfFileReader из упражнения
2.
количество страниц в файле
Выведите текст первой страницы файла
3.
1,
выведите общее
PDF.
PDF
из упражнения
1.
14.2. ИЗВЛЕЧЕНИЕ СТРАНИЦ ИЗ ФАЙЛОВ PDF
PDF и со­
.txt. Теперь вы узнаете, как извлечь страницу или диапазон
существующего файла PDF и сохранить их в новом файле PDF.
В предыдущем разделе я показал, как извлечь весь текст из файла
хранить его в файле
страниц из
Для создания нового файла
PDF можно воспользоваться объектом PdfFileWri ter.
Познакомимся поближе с этим классом и узнаем, что необходимо для создания
PDF
с использованием
PyPDF2.
Использование класса PdfFileWriter
PdfFileWriter создает новые файлы PDF. В интерактивном окне IDLE
импортируйте класс PdfFileWriter и создайте новый экземпляр с именем
pdf_writer:
Класс
>>> from PyPDF2 import PdfFileWriter
>>> pdf_writer = PdfFileWriter()
Объект PdfFileWriter можно сравнить с пустым файлом
PDF. Прежде чем сохра­
нить страницы в файле, необходимо их включить в файл. Добавим в pdf_wri ter
пустую страницу:
>>> page = pdf_writer.addBlankPage(width=72, height=72)
Обязательные параметры width и height определяют размеры страницы веди­
ницах, которые называются пунктами. Один пункт равен
1/72 дюйма,
так что
приведенный выше код добавляет в pdf_wri ter пустую квадратную страницу
с размером стороны
Метод
1 дюйм.
. addBlankPage()
возвращает новый экземпляр
представляет страницу, добавленную в PdfFileWri ter:
»> type(page)
<class 'PyPDF2.pdf.PageObject'>
PageObject,
который
ГЛАВА 14
328
Создание и изменение файлов PDF
В этом примере экземпляр
PageObject,
возвращенный
.addBlankPage(),
при­
сваивается переменной page, но на практике этого делать обычно не нужно.
Иначе говоря, как правило, при вызове
. addBlankPage()
возвращаемое значение
ничему не присваивается:
>>> pdf_writer.addBlankPage(width= 72, height=72)
Чтобы записать содержимое pdf_writer в файл
файла в режиме записи при вызове
PDF, передайте объект двоичного
pdf_writer.write( ):
>>> from pathlib import Path
»> with Path("Ыank.pdf").open(mode="wb") as output_file :
pdf_writer .write(output_file)
»>
Фрагмент создает в текущем рабочем каталоге новый файл с именем Ыank.pdf.
Открыв этот файл в программе просмотра PDF (например, в Adobe Acrobat),
вы увидите документ с одной пустой квадратной страницей с размером стороны
1 дюйм.
ВАЖНО!
Обратите внимание: при сохранении файла PDF файловый объект передается
методу .write() объекта PdfFileWriter, а не методу .write() файлового объекта.
В частности, следующий код работать не будет:
Path("Ыank.pdf").open(mode= "wb")
»> with
as output_file :
output_file.write(pdf_writer)
Многим программистам-новичкам этот подход кажется противоестественным.
Будьте внимательны и не совершайте этой ошибки!
Объекты PdfFileWriter могут выполнять запись в новые файлы
PDF,
но не
позволяют создать с нуля никакой новый контент, кроме пустых страниц.
На первый взгляд это серьезная проблема, но во многих ситуациях создавать
новый контент не нужно. Часто вы работаете со страницами, извлеченными из
файлов
PDF,
открыты х экземпляром
PdfFileReader.
ПРИМЕЧАНИЕ
О том, как создать файл
PDF с
нуля».
PDF, рассказано в разделе 14.8 «Создание файла
14.2. Извлечение страниц из файлов PDF
В приведенном выше примере создание нового файла
329
PDF средствами PyPDF2
состояло из трех этапов:
1.
Создание экземпляра
2.
Добавление одной или нескольких страниц в экземпляр PdfFileWri ter.
3.
Запись данных в файл вызовом
PdfFileWriter.
PdfFileWriter. write( ).
Эта схема будет часто повторяться, когда мы будем изучать различные способы
добавления страниц в экземпляр PdfFileWriter.
Извлечение одной страницы из файла
Вернемся к файлу
Мы откроем файл
PDF
PDF с книгой «Pгide and Prejudice» из предыдущего раздела.
PDF, извлечем первую страницу и создадим новый файл PDF,
содержащий только одну извлеченную страницу.
Откройте интерактивное окно в
и
PdfFileWriter
из модуля
PyPDF2,
IDLE
и импортируйте
а также класс
Path
PdfFileReader
pathlib:
из модуля
>>> from pathlib import Path
>>> from PyPDF2 import PdfFileReader, PdfFileWriter
Затем откройте файл
>>> #
Приведите
путь
в
Pride_and_Prejudice.pdf экземпляром PdfFileReader:
соответствие
с
вашим компьютером
»> pdf_path = (
Path.home() /
"pythoп-basics-exercises" /
"ch14-interact-with-pdf-files" /
"practice_files" /
"Pride_and_Prejudice.pdf"
)
>>> input_pdf = PdfFileReader(str(pdf_path))
Передайте индекс 0 методу
. getPage (),
PDF:
чтобы получить объект PageObj ect,
представляющий первую страницу
>>> first_page
=
input_pdf.getPage(0)
Теперь создайте новый экземпляр PdfFileWriter и добавьте в него первую
страницу методом
. addPage ():
>>> pdf_writer = PdfFileWriter()
>>> pdf_writer.addPage(first_page)
Метод
. addPage() добавляет страницу в набор страниц объекта pdf _wri ter,
. addBlankPage (). Отличие в том, что этому методу необходим
как и в случае с
существующий объект
PageObject.
330
ГЛАВА
Создание и изменение файлов
14
Теперь запишите содержимое
PDF
pdf_writer в новый файл:
»> with Path("first_page.pdf").open(mode="wb") as output_file:
pdf_writer.write(output_file)
»>
В вашем текущем рабочем каталоге появился новый файл
first_page.pdf,
PDF с именем
PDF «Pride
который содержит самую первую страницу из файла
and Prejudice».
Неплохо!
Извлечение нескольких страниц из файла
Извлечем из файла
файле
PDF
Pride_and_Prejudice.pdf первую главу и сохраним ее в новом
PDF.
Если вы откроете
Pride_and_Prejudice.pdf в программе просмотра PDF, то уви­
дите, что первая глава занимает вторую, третью и четвертую страницы
PDF.
Так как индексирование страниц начинается с 0, потребуется извлечь страницы
с индексами
1, 2
и
3.
В подготовительной фазе следует импортировать необходимые классы и от­
крыть файл
PDF:
>>> from PyPDF2 import PdfFileReader, PdfFileWriter
>>> from pathlib import Path
»> pdf_path = (
Path.home() /
"python-basics-exercises" /
"ch14-interact-with-pdf-files" /
"practice_files" /
"Pride_and_Prejudice.pdf"
>>> input_pdf = PdfFileReader(str(pdf_path))
Наша задача
экземпляр
- извлечь страницы с индексами 1, 2 и 3, добавить их в новый
PdfFileWriter, а затем записать в новый файл PDF.
Одно из возможных решений
-
перебор по диапазону страниц от 1 до 3, извлече­
ние страницы на каждом шаге цикла и добавление ее в экземпляр PdfFileWri ter:
>>> pdf_writer = PdfFileWriter()
>>> for п in range(l, 4):
page = input_pdf.getPage(n)
pdf_writer.addPage(page)
»>
14.2. Извлечение страниц из файлов PDF
331
Цикл перебирает числа 1, 2 и 3, потому что range(l, 4) не включает последнее
число
( 4 ).
На каждом шаге цикла страница с текущим индексом извлекается ме­
тодом . getPage( ), после чего она добавляется в pdf_writer методом . addPage( ).
Теперь pdf_wri ter содержит три страницы, в чем можно убедиться при помощи
.getNumPages( ):
>>> pdf_writer.getNumPages()
3
:Наконец, извлеченные страницы записываются в новый файл
PDF:
»> with Path("chapterl.pdf").open(mode="wb") as output_file:
pdf_writer.write(output_file)
»>
Теперь вы можете открыть файл
chapter1.pdf в
текущем рабочем катало­
ге и убедиться в том, что он содержит первую главу книги
«Pride and Preju-
dice».
Другой способ извлечения нескольких страниц из файла
пользовании того факта, что
PdfFileReader. pages
PDF
основан на ис­
поддерживает нотацию срезов.
Переработаем предыдущий пример, чтобы в нем использовался атрибут
. pages
вместо перебора по объекту range.
Начнем с инициализации нового объекта PdfFileWriter:
>>> pdf_writer = PdfFileWriter()
Переберем срез
. pages
по индексам от 1 до 4:
>>> for page in input_pdf.pages[1:4]:
pdf_writer.addPage(page)
>»
Напомню, что значения в срезе лежат в диапазоне от элемента с первым индек­
сом в срезе до элемента со вторым индексом в срезе (не включая его). Таким
образом,
. pages [ 1: 4]
с индексами
1, 2
и
возвращает итерируемый объект, содержащий страницы
3.
Наконец, содержимое
pdf_wri ter записывается в выходной файл:
»> with Path("chapterl_slice.pdf").open(mode="wb") as output_file:
pdf_writer.write(output_file)
»>
332
ГЛАВА 14
Создание и изменение файлов PDF
chapter1 _slice.pdf из текущего рабочего каталога и срав­
chapter1.pdf, созданным перебором по объекту range. Они
Теперь откройте файл
ните его с файлом
содержат одинаковые страницы!
В некоторых ситуациях требуется извлечь из файла PDF все страницы. Для
этого можно воспользоваться способами, продемонстрированными выше, но
PyPDF2 предоставляет упрощенное решение.
У экземпляров
PdfFileWriter
имеется метод
.appendPagesFromReader(),
ко­
торым можно воспользоваться для присоединения страниц из экземпляра
PdfFileReader.
. appendPagesF romReader (), передайте экземпляр
PdfFileReader в параметре reader метода. Например, следующий код копиру­
ет каждую страницу из файла PDF с книгой «Pгide and Prejudice» в экземпляр
PdfFileWri ter:
Чтобы использовать
>>> pdf_writer = PdfFileWriter()
>>> pdf_writer.appendPagesfromRea der(pdf_reader)
Экземпляр
pdf_wri ter
содержит каждую страницу из
pdf_reader!
Упражнения
1.
Pride_ and_ Prejudice.pdf и со­
last_yage.pdf, находящемся в домашнем каталоге.
Извлеките последнюю страницу из файла
храните ее в файле
2. Извлеките из файла Pride_and_Prejudice.pdf все страницы с четными
индексами (не номерами страниц!) и сохраните их в новом файле
every_
other_yage.pdf в домашнем каталоге.
3. Разбейте файл Pride_and_Prejudice.pdf на два новых файла PDF. Пер­
вый должен содержать первые 150 страниц, а второй - все оставшиеся
страницы. Сохраните оба файла в домашнем каталоге с именами part_ 1.
pdf и part_2.pdf.
14.З. ЗАДАЧА: КЛАСС PDFFILESPLIПER
PdfFileSplitter, который читает данные файла
PDF из существующего экземпляра PdfFileReader и разбивает их на два
новых объекта PDF. При создании экземпляра класса должна передаваться
Создайте класс с именем
строка пути.
14.4. Конкатенация и слияние файлов PDF
Например, следующая команда создает экземпляр
файла
PDF mydoc.pdf в текущем
PdfFileSpli tter
ЗЗЗ
на основе
рабочем каталоге:
pdf_splitter = PdfFileSplitter("mydoc.pdf")
Класс
1.
PdfFileSpli tter
должен содержать два метода.
. spli t () получает один параметр breakpoint, в котором передается
- номер страницы, которая становится последней в первой
разбиваемого файла PDF.
Метод
целое число
части
2.
Метод
. wri te ()
получает один параметр
filename, в котором передается
строка пути.
После вызова
.writerl,
. split()
класс PdfFileSplitter должен содержать атрибут
PdfFileWriter со всеми страни­
breakpoint (не включая ее). Также
которому присваивается экземпляр
цами исходного файла
PDF
до страницы
должен присутствовать атрибут . wri ter2, которому присваивается экземпляр
PdfFileWriter
При вызове
с остальными страницами исходного файла
. write()
записываются два файла
PDF -
PDF.
первый с именем
filename
+ "_1. pdf", а второй с именем filename + "_2. pdf".
В следующем примере файл mydoc.pdf разделяется на две части, первая из которых
заканчивается страницей
с именами
4,
а результаты разбиения записываются в два файла
mydoc_split_ 1.pdf и mydoc_ split_2.pdf:
pdf_splitter.split(breakpoint=4)
pdf_splitter.write("mydoc_split")
Чтобы проверить, как работает созданный класс, разбейте файл
Prejudice.pdf из
папки
practice_files
150.
закончится страницей
главы
14
Pride_and_
на две части, первая из которых
Решение задачи, а также многие другие дополнительные ресурсы доступны
в интернете по адресу
realpython.com/python-basics/resources.
14.4. КОНКАТЕНАЦИЯ
ФАЙЛОВ PDF
При работе с файлами
PDF
И СЛИЯНИЕ
часто выполняются две типичные операции: кон­
катенация и слияние нескольких файлов
PDF
в один.
ГЛАВА 14
334
Создание и изменение файлов PDF
Конкатенация двух и более файлов
PDF
означает, что файлы объединяются
последовательно друг за другом, образуя один документ. Например, компания
в конце месяца может провести конкатенацию нескольких ежедневных отчетов
в один ежемесячный отчет.
PD F также объединяет их в один файл. Но эта операция
PDF к концу первого файла. Слиянием можно
вставить файл после заданной страницы первого файла PD F. Затем все страницы
первого файла PDF, следующие после точки вставки, сдвигаются в позицию
после вставленного файла PDF.
Слияние двух объектов
не присоединяет второй файл
Главное отличие между этими двумя операциями заключается в том, что
PdfFileWriter
может только присоединять страницы в конец списка страниц
(конкатенация), тогда как
PdfFileMerger поддерживает вставку в
произвольной
позиции (слияние).
Создайте свой первый экземпляр
PdfFileMerger.
В интерактивном окне
IDLE
введите следующий код, чтобы импортировать класс PdfFileMerger и создать
новый экземпляр:
>>> from PyPDF2 import PdfFileMerger
>>> pdf_merger = PdfFileMerger()
Только что созданные объекты PdfFileMerger пусты. Прежде чем вы сможете
с ними что-то сделать, в них необходимо добавить страницы.
Добавить страницы в объект pdf_merger можно двумя способами. Выбор зависит
от того, что именно нужно сделать.
•
. append ()
выполняет конкатенацию, то есть присоединяет все страницы
существующего документа
в
•
PDF в конец набора страниц, содержащихся
PdfFileMerger.
. merge ()
вставляет все страницы существующего документа Р D F после
заданной страницы
PdfFileMerger.
В этом разделе будут рассмотрены оба способа. Начнем с
Конкатенация файлов
В папке
practice_files главы 14
. append ().
PDF вызовом .append()
находится подкаталог
expense_reports,
в котором
содержатся три отчета о расходах сотрудника компании.
Вы хотите выполнить конкатенацию трех файлов
дателю в одном файле
связанные с работой.
PDF,
PDF
и передать их работо­
чтобы сотрудник получил компенсацию эа затраты,
ЗЗS
14.4. Конкатенация и слияние файлов PDF
Для начала можно воспользоваться модулем
ектов
Path
для всех трех отчетов в папке
pathlib и получить список объ­
expense_reports/:
>>> from pathlib import Path
>>> reports_dir = (
Path. home () /
"python-basics-exercises" /
"ch14-interact-with-pdf-files" /
"practice_files" /
"expense_reports"
После импортирования класса
expense_reports/.
Path необходимо построить путь к каталогу
Будьте внимательны: возможно, вам придется изменить путь
в приведенном выше коде и привести его в соответствие с путем на вашем
компьютере.
expense_reports/, присвоенный переменной
. glob() для получения итерируемого
набора путей к каталогам файлов PDF.
Когда у вас появится путь к каталогу
reports_dir,
вы можете использовать
Просмотрим содержимое каталога:
>>> for path in reports_dir.glob("*.pdf"):
print(path.name)
Expense report 1.pdf
Expense report 3.pdf
Expense report 2.pdf
В выводе приведены имена трех файлов, но они не упорядочены. Более того,
порядок файлов, которые вы получите на своем компьютере, может отличаться
от показанного.
В общем случае порядок путей, возвращаемых . glob (), не гарантирован, поэтому
вам придется упорядочить их самостоятельно. Для этого можно создать список,
содержащий три пути к файлам, а затем вызвать
. sort()
для этого списка:
>>> expense_reports = list(reports_dir.glob("*.pdf"))
>>> expense_reports.sort()
Напомню, что
. sort ()
сортирует список на месте, так что присваивать воз­
вращаемое значение переменной необязательно. После вызова
. list ()
список
expense_reports будет отсортирован по алфавиту.
Чтобы убедиться в том, что сортировка сработала, снова переберите
reports
и выведите имена файлов:
expense_
336
Создание и изменение файлов
ГЛАВА 14
PDF
>>> for path in expense_reports:
print(path.name)
Expense report 1.pdf
Expense report 2.pdf
Expense report 3.pdf
Так гораздо лучше!
Теперь можно выполнить конкатенацию трех файлов
пользован метод
PdfFileMerger. append( ),
PDF. Для этого будет ис­
которому передается один строковый
PDF. При вызове. append() все стра­
PDF присоединяются к набору страниц в объекте PdfFileMerger.
аргумент, представляющий путь к файлу
ницы из файла
Посмотрим, как это делается на практике. Сначала импортируйте класс
PdfFileMerger
и создайте новый экземпляр:
>>> from PyPDF2 import PdfFileMerger
>>> pdf_merger = PdfFileMerger()
Затем переберите пути из отсортированного списка expense_reports и присо­
едините их к
pdf_merger:
>>> for path in expense_reports:
pdf_merger.append(str(path))
»>
Обратите внимание: каждый объект Path в
в строку вызовом
str()
expense_reports/
перед тем, как он передается в
преобразуется
pdf_merger.append().
После того как все файлы из каталога expense_reports/ будут объединены в объ­
pdf_merger, остается записать все данные в выходной файл PDF. Экзем­
пляры PdfFileMerger содержат метод .write(), который работает аналогично
PdfFileWriter .write( ).
екте
Откройте новый двоичный файл в режиме чтения, после чего передайте фай­
ловый объект методу pdf_merge.write():
»> with Path("expense_reports.pdf").open(mode="wb") as output_file:
pdf_merger.write(output_file)
>»
PDF с именем
expense_reports.pdf. Откройте его в программе просмотра PDF - и вы увидите,
Теперь в вашем текущем рабочем каталоге появился файл
что все три отчета объединены в одном файле.
14.4. Конкатенация и слияние файлов PDF
Слияние файлов
PDF
методом
Для слияния двух и более файлов
ger.merge( ).
Он аналогичен
где в выходном файле
PDF
.append( ),
337
.merge()
используется метод PdfFileMer-
не считая того, что вы должны указать,
PDF должен быть вставлен
контент из объединяемых
файлов.
Рассмотрим пример из практики. Компания
Goggle, Inc.
подготовила квар­
тальный отчет, но забыла включить в него оглавление. Программист заметил
PDF с отсутствующим оглавлением. Теперь необходимо
PDF с исходным отчетом.
ошибку и быстро создал
объединить этот файл
Оба файла
PDF -
в файле
- находятся в подкаталоге quarterly_report/
14. Отчет хранится в файле report.pdf, а оглавление -
отчет и оглавление
папки practice_files главы
toc.pdf.
IDLE импортируйте
файлов report.pdf и toc.pdf:
В интерактивном окне
объекты Path для
класс
PdfFileMerger
и создайте
>>> from pathlib import Path
>>> from PyPDF2 import PdfFileMerger
>>> report_dir = (
Path. home() /
"python-basics-exercises" /
"ch14-interact-with-pdf-files" /
"practice_files" /
"quarterly_report"
»> report_path = report_dir / "report.pdf"
»> toc_path = report_dir / "toc.pdf"
Прежде всего следует присоединить файл
PdfFileMerger
методом
PDF с отчетом к новому экземпляру
. append ():
>>> pdf_merger = PdfFileMerger()
>>> pdf_merger.append(str(report_path))
После того как в
файла
PDF с
pdf_merger
появились страницы, можно провести слияние
оглавлением в нужной позиции. Открыв файл
грамме просмотра
PDF,
report. pdf
вы увидите, что первая страница отчета
Вторая содержит введение, а остальные
-
-
в про­
титульная.
разные разделы отчета.
Мы хотим, чтобы оглавление было вставлено после титульной страницы, но
непосредственно перед введением. Так как индексы страниц
PDF
в
PyPDF2
на­
чинаются с О, оглавление необходимо вставить после страницы с индексом 0,
но перед страницей с индексом 1.
338
ГЛАВА 14
Создание и изменение файлов
Для этого следует вызвать
1.
Первый аргумент
-
pdf_merger. merge ()
целое число
PDF
с двумя аргументами.
1, обозначающее
индекс страницы для
вставки оглавления.
2.
Второй
-
строка пути к файлу
PDF,
содержащему оглавление.
Вот как это выглядит:
>>> pdf_merger.merge(l, str(toc_path))
Все страницы из файла
PDF
с оглавлением будут вставлены перед страницей
1. Так как файл PDF с оглавлением состоит только из одной стра­
ницы, он вставляется в позицию с индексом 1. Страница, которой изначально
был присвоен индекс 1, сдвигается в позицию с индексом 2. Страница, которой
изначально был присвоен индекс 2, сдвигается в позицию с индексом з, и т. д.
с индексом
Теперь запишите файл
PDF, полученный в результате слияния, в выходной файл:
»> with Path("full_report.pdf") .open(mode="wb") as output_file:
pdf_merger.write(output_file)
»>
В текущем рабочем каталоге есть фaйлfull_report.pdf. Откройте его в програм­
PDF и убедитесь, что оглавление было вставлено в правильной
ме просмотра
позиции.
Конкатенация и слияние
PDF -
чрезвычайно распространенные операции.
Хотя примеры в этом разделе выглядят несколько искусственно, только пред­
ставьте, насколько полезной окажется программа в случае слияния тысяч
PD F
или при автоматизации рутинных задач, ручное выполнение которых заняло
бы слишком много времени.
Упражнения
1.
Папка practice_filesглaвы 14 содержит три файла PDF с именами merge1.pdf,
merge2.pdf и mergeЗ.pdf. Используя экземпляр PdfFileMerger, выполните
слияние двух файлов merge1.pdf и merge2.pdf методом .append(). Со­
храните результат в файле с именем concatenated.pdf в вашем домашнем
каталоге.
2.
Создайте новый экземпляр
PdfFileMerger
и испольэуйте
.merge()
для
concatenated.pdf из упражнения 1,
вставив mergeЗ.pdf между двумя страницами concatenated.pdf. Сохраните
новый файл в домашнем каталоге с именем merged.pdf.
слияния файла mergeЗ.pdf и файла
14.5. Поворот и обрезка страниц PDF
Результатом должен стать файл
должно выводиться число
14.5.
1,
339
PDF из трех страниц. На первой странице
- 2 и на третьей - 3.
на второй
ПОВОРОТ И ОБРЕЗКА СТРАНИЦ
PDF
Вы уже узнали, как извлекать текст и страницы из файлов
PDF
и как выпол-
1111ть конкатенацию и слияние двух и более файлов. Это чрезвычайно распро­
страненные операции с
PDF, но PyPDF2 обладает множеством других полезных
возможностей.
Сейчас мы расскажем, как поворачивать и обрезать страницы в файлах
PDF.
Поворот страниц
Начнем с вращения страниц. В этом примере мы воспользуемся файлом
из папки
ugly.pdf
practice_files главы 14.
Файл
ugly.pdf содержит текст сказки Ганса Христиана Андерсена « Ugly
Duckling» («Гадкий утенок»), но каждая нечетная страница повернута на 90°
против часовой стрелки.
Исправим этот недостаток. В новом интерактивном окне
классы
PdfFileReader
pathlib:
и
PdfFileWriter
из
PyPDF2,
IDLE
а также класс
импортируйте
Path
из модуля
>>> from pathlib import Path
>>> from PyPDF2 import PdfFileReader, PdfFilewriter
Со:щайте объект Path для файла
ugly.pdf:
=(
>» pdf _path
Path. home() /
"python-basics-exercises" /
"ch14-interact-with-pdf-files" /
"practice_files" /
"ugly.pdf"
Теперь создайте экземпляры
» > pdf_reader
>» pdf_writer
!lаша цель
-
PdfFileReader
и
PdfFileWriter:
PdfFileReader(str(pdf_path))
PdfF ilewri ter ()
иснользовать
pdf _writer для создания нового файла PDF, в ко­
торо:v1 все страню1ы имеют правильную ориентацию. Четные страницы в
PDr'
340
ГЛАВА 14
Создание и изменение файлов PDF
ориентированы правильно, но нечетные страницы повернуты на
90°
против
часовой стрелки.
Для решения проблемы мы воспользуемся методом
teClockwise() .
PageObject.rota-
Метод получает целочисленный аргумент (в градусах) и по­
ворачивает страницу по часовой стрелке на заданный угол. Например,
поворачивает страницу
. rotateClockwise(90)
PDF по часовой
стрелке на
90°.
ПРИМЕЧАНИЕ
Кроме .rotateClockwise() класс PageObject также содержит файл .rotateCounterClockwise() для поворота страниц против часовой стрелки.
Поворот страниц в
PDF
можно осуществить несколькими способами. Мы
рассмотрим два; оба основаны на методе
. rotateClockwise(),
но по-разному
определяют, какие страницы будут повернуты. Первый способ перебирает
индексы страниц из файла
PDF и проверяет,
соответствует ли каждый индекс
странице, которую необходимо повернуть. Если проверка дает положительный
результат, вызовите . rotateClockwise() для поворота страницы, а затем добавьте
страницу в
pdf_writer.
Реализация выглядит примерно так:
>>> for n in range(pdf_reader.getNumPages()) :
page = pdf_reader.getPage(n)
if n % 2 == 0:
page.rotateClockwise(90)
pdf_writer.addPage(page)
»>
ПРИМЕЧАНИЕ
IDLE вы
.rotateClockwise() воз­
При выполнении приведенного выше цикла в интерактивном окне
увидите довольно обширный вывод. Дело в том, что
вращает экземпляр
PageObject.
Пока не обращайте внимания на этот вывод. При выполнении программ из
окна редактора
IDLE
вы его не увидите.
Поворачиваются страницы с четным индексом. На первый взгляд это кажет­
ся странным, потому что неправильно повернуты нечетные страницы в
Однако номера страниц в
с
0.
PDF.
PDF начинаются с 1, а индексы страниц начинаются
Это означает, что страницы с нечетными номерами имеют четные индексы.
14.5. Поворот и обрезка страниц PDF
341
Если у вас голова идет кругом, не огорчайтесь! На подобных нюансах споты­
каются даже профессиональные программисты, имеющие многолетний опыт
решения подобных задач.
После поворота всех страниц в файле
PDF
можно записать содержимое
pdf _
writer в новый файл и убедиться в том, что все работает:
>» with Path("ugly_rotated.pdf").open(mode="wb") as output_file:
pdf_writer.write(output_file)
>»
В вашем текущем рабочем каталоге должен появиться файл с именем
ugly_
rotated.pdf с правильно повернутыми страницами из файла ugly.pdf.
Недостаток только что продемонстрированного решения с поворотом страниц
из файла
ugly.pdf заключается в том, что вы должны заранее знать, какие стра­
PDF,
ницы нужно повернуть. На практике не всегда возможно просмотреть весь
отмечая, какие из страниц ориентированы неправильно.
На самом деле определить, какие страницы необходимо повернуть, можно на
месте ... Вернее, почти всегда можно.
Посмотрим, как это делается, начиная с создания нового экземпляра
PdfFileReader:
>>> pdf_reader = PdfFileReader(str(pdf_path))
Это необходимо, потому что вы изменили страницы в старом экземпляре
PdfFileReader,
повернув их.
Таким образом, создавая новый экземпляр, вы начинаете все с чистого листа.
Экземпляры
PageObject поддерживают словарь значений, содержащих инфор­
мацию о странице:
>>> pdf_reader.getPage(0)
{'/Contents': [IndirectObject(ll, 0), Indirect0bject(12, 0),
IndirectObject(lЗ, 0), Indirect0bject(14, 0), Indirect0bject(15, 0),
Indirect0bject(16, 0), Indirect0bject(17, 0), Indirect0bject(18, 0)],
'/Rotate': -90, '/Resources': {'/ColorSpace': {'/CSl':
Indirect0bject(19, 0), '/С50': Indirect0bject(19, 0)}, '/XObject':
{'/Im0': Indirect0bject(21, 0)}, '/Font': {'/TTl':
IndirectObject(23, 0), '/ТТ0': Indirect0bject(25, 0)}, '/ExtGState':
{'/GS0': Indirect0bject(27, 0)}}, '/CropBox': [0, 0, 612, 792],
'/Parent': IndirectObject(l, 0), '/MediaBox': [0, 0, 612, 792],
'/Туре': '/Page', '/StructParents': 0}
342
ГЛАВА 14
Создание и изменение файлов PDF
Ничего себе! Однако среди всей тарабарщины в четвертой строке вывода мы
видим ключ с именем
/Rotate.
С этим ключом связано значение
Для обращения к ключу через объект
дексирования, как и для объектов
-90.
PageObject используется синтаксис ин­
Python dict:
>>> page = pdf_reader.getPage(0)
>>> page["/Rotate"]
-90
Проверив ключ
/Rotate для второй страницы
связано значение
в
pdf_reader,
мы видим, что с ним
0:
>>> page = pdf_reader.getPage(l)
>>> page["/Rotate"]
0
Это означает, что странице с индексом О назначен поворот на
она поворачивается против часовой стрелки на
-90°. Иначе говоря,
90°. У страницы с индексом 1
угол поворота равен 0, то есть она вообще не поворачивается.
Если повернуть первую страницу вызовом
/Rotate
изменится с
-90
на
. rotateClockwise (),
то значение
0:
>>> page = pdf_reader.getPage(0)
>» page["/Rotate"]
-90
>>> page.rotateClockwise(90)
>>> page["/Rotate"]
0
Теперь вы знаете, как проверить ключ
поворота страниц в файле
/Rotate,
и сможете использовать его для
ugly.pdf.
Прежде всего необходимо заново инициализировать объекты pdf _reader
и
pdf_wri ter, чтобы начать с чистого листа:
>» pdf_reader
»> pdf_writer
PdfFileReader(str(pdf_path))
PdfFileWriter()
Напишите цикл, который перебирает страницы из итерируемого объекта
pdf_reader. pages,
проверяет значение
это значение равно
-90:
>>> for page in pdf_reader.pages:
if page["/Rotate"] == -90:
page.rotateClockwise(90)
/Rotate
и поворачивает страницу, если
14.5. Поворот и обрезка страниц PDF
343
pdf_writer.addPage(page)
>»
Этот цикл не просто короче цикла из первого решения
-
для него не нужно
знать заранее, какие страницы требуется повернуть. Такой цикл можно исполь­
зовать для поворота страниц в любом файле
PDF, причем вам даже не придется
открывать этот файл и заглядывать в него.
Чтобы завершить решение, запишите содержимое pdf_writer в новый файл:
»> with Path("ugly_rotated2.pdf").open(mode="wb") as output_file:
pdf_writer.write(output_file)
»>
Теперь вы можете открыть файл
и сравнить его с файлом
ugly_rotated2.pdf в текущем рабочем каталоге
ugly_rotated.pdf, сгенерированным ранее. Два файла
должны выглядеть одинаково.
ВАЖНО!
Предупреждение по поводу ключа
/Rotate: его существование для
страницы
не гарантировано.
Если ключ /Rotate не существует, обычно это означает, что страница не была
повернута . Тем не менее это предположение не стопроцентно.
Если PageObject не содержит ключа/Rоtаtе, то при попытке обращения по
этому ключу произойдет исключение
KeyError. Для
его перехвата можно вос­
пользоваться блоком try ... except .
Значение /Rotate не всегда соответствует вашим ожиданиям. Например, если
вы отсканируете бумажный документ, в котором страница была повернута на
90°
против часовой стрелки, будет казаться, что содержимое файла
PDF
по­
вернуто. При этом ключ /Rotate может содержать значение 0.
Это одна из многих странностей, которые усложняют работу с файлами
Иногда достаточно просто открыть
PDF.
PDF в программе просмотра PDF и вручную
выяснить, что здесь происходит.
Обрезка страниц
Другая распространенная операция с файлами
PDF -
обрезка страниц. На­
пример, это нужно для разбиения одной страницы на несколько частей или для
извлечения небольшого фрагмента страницы (подписи, иллюстрации и т. д.).
344
ГЛАВА 14
Создание и изменение файлов PDF
В папке practice_files главы
14 хранится файл с именем half_and_half.pdf. В нем
Little Meгmaid»
содержится часть текста сказки Ганса Христиана Андерсена« The
(«Русалочка»).
Каждая страница файла
PDF состоит из двух столбцов.
Разобьем каждую стра­
ницу на две, чтобы на ней был опубликован только один столбец.
Начнем с импортирования классов
PyPDF2,
а также класса
Path
PdfFileReader
pathlib:
и
PdfFileWriter
из модуля
из модуля
>>> from pathlib import Path
>>> from PyPDF2 import PdfFileReader, PdfFileWriter
Теперь создайте объект Path для файла
half_and_half.pdf:
»> pdf_path = (
Path.home() /
"python-basics-exercises" /
"ch14-interact-with-pdf-files" /
"practice_files" /
"half_and_half.pdf"
Затем создайте новый объект PdfFileReader и получите первую страницу
PDF:
>>> pdf_reader = PdfFileReader(str(pdf_path))
>>> first_page = pdf_reader.getPage(0)
Чтобы выполнить обрезку страницы, давайте поближе познакомимся со струк­
турой страниц. Экземпляры PageObject (такие, как first_page) имеют атрибут
. mediaBox, который представляет прямоугольную область, определяющую
границы страницы.
Атрибут .mediaBox можно исследовать в интерактивном окне
IDLE,
прежде
чем использовать его для обрезки страницы:
>>> first_page.mediaBox
Rectangle0bject([0, 0, 792, 612])
Атрибут .mediaBox возвращает объект RectangleObject. Он определяется в па­
кете PyPDF2 и представляет прямоугольную область страницы.
Список
[ 0, 0, 792, 612]
в выходных данных задает прямоугольную область.
Первые два числа показывают координаты х и у левого нижнего угла прямо­
угольника. Третье и четвертое числа представляют ширину и высоту прямо-
14.5. Поворот и обрезка страниц PDF
345
угольника соответственно. Все значения задаются в пунктах, один пункт равен
1/72 дюйма.
RectangleObject( [0, 0, 792, 612)) представляет прямоугольную область с левым
нижним углом в начале координат, шириной
792 пункта ( 11 дюймов) и высотой
8,5 дюйма. Это размеры стандартного листа формата Letter
в горизонтальной ориентации, который используется в файле PDF с текстом
"Русалочки». Для страницы PDF формата Letter в вертикальной ориентации
612
пунктов, или
будет возвращен объект
RectangleObject( [0, 0, 612, 792) ).
Объект
RectangleObject содержит четыре атрибута, которые возвращают
. lowerleft, . lowerRight, . upperleft
. upperRight. Как и значения width и height, эти координаты указываются
координаты углов прямоугольника:
и
в пунктах.
Четыре свойства определяют координаты каждого угла
RectangleObject:
>>> first_page.mediaBox.lowerleft
(0, 0)
>>> first_page.mediaBox.lowerRight
(792, 0)
>>> first_page.mediaBox.upperleft
(0, 612)
>>> first_page.mediaBox.upperRight
(792, 612)
Каждое свойство возвращает кортеж с координатами заданного угла. К отдель­
ным координатам можно обращаться по индексам, как и к элементам любого
кортежа
Python:
>>> first_page.mediaBox.upperRight[0]
792
>>> first_page.mediaBox.upperRight[l]
612
Чтобы изменить координаты
mediaBox, присвойте новый кортеж одному из его
свойств:
>>> first_page.mediaBox.upperleft
>>> first_page.mediaBox.upperLeft
(0, 480)
При изменении координат
(0, 480)
. upperleft
атрибут
. upperRight
меняется для сохранения прямоугольной формы:
>>> first_page.mediaBox.upperRight
(792, 480)
автоматически из­
346
ГЛАВА 14
Создание и изменение файлов PDF
Изменяя координаты
RectangleObject, возвращаемые .mediaBox, вы фактически
first_page теперь содержит только информацию,
содержащуюся в границах нового объекта RectangleObject.
обрезаете страницу. Объект
Запишите обрезанную страницу в новый файл
PDF:
>>> pdf_writer = PdfFileWriter()
>>> pdf_writer.addPage(first_page)
>» with Path("cropped_page.pdf").open(mode="wb") as output_file:
pdf_writer.write(output_file)
»>
Открыв обрезанный файл
cropped_yag-e.pdf в
текущем рабочем каталоге, вы
увидите, что верхняя часть стран11цы удале11а. Как обре:~ап, страницу так,
чтобы оставался видимым только текст в лсвоii части страницы? Необходимо
уменьшить горизонтальный размер страницы вдвое. Для этого достаточно
изменить координату
. upperRight
объекта
. mediaBox.
Давайте посмотрим,
как это делается.
Прежде всего необходимо создать новые объекты
PdfFileReader и PdfFileWriter,
pdf_reader, а затем до­
потому что вы только что измениm1 нервую страшщу в
бавили ее в pdf_wri ter:
»> pdf_reader
» > pdf_wri ter
PdfFileReader(str(pdf_path))
PdfFileWriter()
Теперь получим первую страницу
PDF:
>>> first_page = pdf_reader.getPage(0)
На этот раз будем работать с кo1111cii псрвоii стра111щы. чтобы только что из­
влеченная страница не изменялас1" Для :пого мож110 импортировать модуль
сору из стандартной библиотеки
Pytl1011 11
созлап, копию страницы вызовом
deepcopy ():
»> import сору
>>> left_side = copy.deepcopy(first_page)
Теперь скорректируем left_side
Gc:J 11:шс11с1111я каюrх-л11бо своiiств first_page.
first_page ;~ля r1:шлечения текста
Это позволяет нам позднее исно.•1ь:ювап,
правой части страницы.
Пришло время немного посчитат~" Мы уж<' выяс1111ли, •по 11равый верхний
угол
. mediaBox
необходимо перещ·r·пп1, в нr•11тр верхнего края странины. Для
14.5. Поворот и обрезка страниц PDF
347
этого мы создали новый кортеж, первый компонент которого равен половине
исходного значения, и присвоили его свойству
. upperRight.
Начнем с получения текущих координат правого верхнего угла
>>> current_coords
=
.mediaBox:
left_side.mediaBox.upperRight
Затем создадим новый кортеж, первая координата в котором равна половине
значения текущей координаты, а вторая равна исходной:
>>> new_coords
=
(current_coords(0] / 2, current_coords[l])
Наконец, кортеж с новыми координатами присваивается атрибуту
>>> left_side.mediaBox.upperRight
=
. upperRight:
new_coords
Исходная страница была успешно обрезана
-
теперь она содержит только текст
в левой части! Перейдем к определению правого края страницы.
Сначала получим новую копию
>>> right_side
=
first_page:
copy.deepcopy(first_page)
На этот раз вместо угла
. upperRight
>>> right_side.mediaBox.upperleft
перемещаем угол
. upperleft:
= new_coords
Левый верхний угол перемещаем в ту же точку, в которую был перемещен
правый верхний угол при извлечении левой части страницы. Таким образом,
right_side. mediaBox теперь представляет прямоугольник, левый
верхний угол
которого находится в середине верхнего края страницы, а правый верхний угол
совпадает с правым верхним углом страницы.
Наконец, добавим страницы left_side и right_side в pdf_wri ter и запишем
их в новый файл
PDF:
>>> pdf_writer.addPage(left_side)
>>> pdf_writer.addPage(right_side)
>>> with Path("cropped_pages.pdf").open(mode="wb") as output_file:
pdf_writer.write(output_file)
»>
Откройте файл
croppedyages.pdf в
программе просмотра
PDF.
Вы увидите
файл с двумя страницами: первая содержит текст из левой половины исходной
первой страницы, а вторая
-
текст из правой половины.
ГЛАВА 14
348
Создание и изменение файлов PDF
Упражнение
1.
В папке practice_files главы 14 находится файл PDF с именем split_and_
rotate.pdf. Создайте в своем домашнем каталоге новый файл rotated.pdf,
который содержит страницы split_and_rotate.pdf, повернутые на 90°
против часовой стрелки.
2.
Используя файл rotated.pdf, созданный в упражнении
1, разбейте каждую
PDF по вертикали по центру страницы. Создайте в домашнем
PDF split.pdf со всеми страницами, полученными
в результате разбиения. Файл split.pdf должен содержать четыре страницы
с номерами 1, 2, 3 и 4 в указанном порядке.
страницу
каталоге новый файл
14.6.
ШИФРОВАНИЕ И ДЕШИФРОВАНИЕ
ФАЙЛОВРDF
PDF защищаются паролями. Пакет PyPDF2 позволяет работать
с зашифрованными файлами PDF, а также добавлять парольную защиту к су­
ществующим файлам PDF.
Иногда файлы
Шифрование файлов
PDF
Для добавления парольной защиты к файлам
PDF
используется метод
. encrypt () экземпляра PdfFileWri ter(). Он получает два основных параметра.
1. user_pwd задает пароль пользователя, позволяющий открыть и прочитать
файл PDF.
2. owner_pwd задает пароль владельца, позволяющий открыть файл PDF без
каких-либо ограничений, включая редактирование.
Воспользуемся .encrypt() для добавления пароля к файлу
кроем файл
newsletter.pdf из
каталога
practice_files главы 14:
>>> from pathlib import Path
>>> from PyPDF2 import PdfFileReader, PdfFileWriter
»> pdf_path = (
Path. home() /
"python-basics-exercises" /
"ch14-interact-with-pdf-files" /
"practice_files" /
"newsletter.pdf"
>>> pdf_reader
= PdfFileReader(str(pdf_path))
PDF.
Сначала от­
14.6. Шифрование и дешифрование файлов PDF
Создадим новый экземпляр
349
PdfFileWriter и добавим в него страницы из
pdf_reader:
>>> pdf_writer = PdfFileWriter()
>>> pdf_writer.appendPagesFromReader(pdf_reader)
Затем добавим пароль
"SuperSecret" вызовом pdf_writer.encrypt():
»> pdf_writer.encrypt(user_pwd="SuperSecret")
Если задается только пароль пользователя
user_pwd,
аргументу
owner _pwd
по
умолчанию присваивается та же строка. Таким образом, приведенная выше
строка кода задает пароли как пользователя, так и владельца.
Наконец, запишем зашифрованные данные
protected. pdf
PDF в выходной файл newsletter _
в вашем домашнем каталоге:
>» output_path = Path.home() / "newsletter_protected.pdf"
»> with output_path.open(mode="wb") as output_file:
pdf_writer.write(output_file)
Когда вы откроете файл в программе просмотра
ввести пароль. Введите строку
PDF,
вам будет предложено
"SuperSecret", чтобы открыть PDF.
Если вам понадобится назначить файлу
передайте вторую строку в параметре
PDF
отдельный пароль владельца,
owner _pwd:
»> user_pwd = "SuperSecret"
»> owner_pwd = "ReallySuperSecret"
>>> pdf_writer.encrypt(user_pwd=user_pwd, owner_pwd=owner_pwd)
В этом примере используется пароль пользователя
владельца
Если файл
"SuperSecret"
и пароль
"ReallySuperSecret ".
PDF зашифрован
паролем, то при попытке открыть его вы должны
ввести пароль для просмотра его содержимого. Защита распространяется и на
чтение из
PDF в программе Python.
средствами модуля
А теперь посмотрим, как дешифровать
Дешифрование файлов
PDF
Чтобы дешифровать зашифрованный файл
.decrypt()
Метод
экземпляра
. decrypt ()
PDF
PyPDF2.
PDF,
воспользуйтесь методом
PdfFileReader.
получает один параметр
password,
в котором передается па­
роль для дешифрования. Уровень доступа, который вы получаете при открытии
PDF,
зависит от аргумента, передаваемого в параметре
password.
ГЛАВА 14
350
Создание и изменение файлов PDF
Сейчас мы откроем зашифрованный файл
newsletteryrotected.pdf,
в предыдущем разделе, и воспользуемся средствами
PyPDF2
созданный
для его дешифро­
вания.
Сначала создайте новый экз емпляр
файлу
PdfFileReader с путем к защищенному
PDF:
>>> from pathlib import Path
>>> from PyPDF2 import PdfFileReader, PdfFileWriter
»> pdf_path = Path.home() / "newsletter_protected.pdf"
>>> pdf_reader = PdfFileReader(str(pdf_path))
Прежде чем дешифровать
PDF,
проверьте, что произойдет при попытке полу­
чить первую страницу:
>>> pdf_reader.getPage(0)
Traceback (most recent call last):
File "c:\realpython\venv\lib\site-packages\PyPDF2\pdf.py",
line 1617, in getObject
raise utils.PdfReadError("file has not been decrypted")
PyPDF2.utils.PdfReadError: file has not been decrypted
Выданное исключение PdfReadError сообщает, что файл
PDF
не был дешиф­
рован.
Приведенная выше трассировка была сокращена, чтобы показать самое
главное. Вывод, который вы получите на своем компьютере, будет намного
длиннее.
Чтобы дешифровать файл, создайте новый экземпляр PdfFileReader:
>>> pdf_reader = PdfFileReader(str(pdf_path))
Затем дешифруйте файл :
>>> pdf_reader.decrypt(password="SuperSecret")
1
Метод
. decrypt()
возвращает целое число
-
пароль указан неверно;
•
0 -
•
1 - правильный пароль пользователя;
•
2 - правильный пароль владельца.
признак успеха дешифрования:
14.7. Задача : восстановление порядка страниц
351
После того как файл был дешифрован, вы можете обратиться к содержимому
файла
PDF:
>>> pdf_reader.getPage(0)
{'/Contents': Indirect0bject(7, 0), '/CropBox': [0, 0, 612, 792],
'/MediaBox': [0, 0, 612, 792], '/Parent': IndirectObject(l, 0),
'/Resources ' : Indirect0bject(8, 0), '/Rotate' : 0, ' /Туре': '/Page'}
Итак, вы научились цзвлекать текст, а также обрезать и поворачивать страницы
файлов так, как сч11таете нужным .
Упражнения
1. Папка practice_files главы 14 содержит файл PDF с именем top_secret.pdf.
Зашифруйте файл с паролем пользователя UnguessaЫe, используя метод
PdfFileWriter.encrypt().
Сохран11те за шифрованны й файл с именем
top_secret_encrypted.pdf
в своем дома шнем каталоге.
2.
Откройте файл
top _secгet_enc1ypted.pdf,
создан ный в упражнении
1,
расшифруйте его и выведите текст, содержащийся на первой странице
PDF.
14.7. ЗАДАЧА:
ВОССТАНОВЛЕНИЕ
ПОРЯДКА СТРАНИЦ
В папке
practice_files главы 14 содержится файл PDF с именем scramhled.pdf,
состоя щий из семи страниц. На каждой странице находится число от 1до7 , но
порядок чис ел н;:~ру шен.
Кром е того, некоторы е стра ниц "' п овер нуты на
90, 180
или
270
градусов по
часовой стрелке ил и против нее.
Напи шите програм ,,1у, которая восста 11 авл11вает файл
PDF.
Для этого она со­
ртирует страницы по номеру в тексте и 11ри необходимости поворачивает их
в вертикальное положе н ие.
Предполагается, что каждый объект PageObject, прочитанный из scramЫed.pdf,
содержит ключ
«/Rotate».
ГЛАВА 14
352
Создание и изменение файлов PDF
PDF в файле с именем unscramhled.pdf в до­
Сохраните восстановленный файл
машнем каталоге.
Решение задачи, а также многие другие дополнительные ресурсы доступны
в интернете по адресу
realpython.com/python-basics/resources.
14.8. СОЗДАНИЕ ФАЙЛА PDF С НУЛЯ
Пакет
PDF,
PyPDF2 отлично подходит для
чтения и изменения существующих файлов
но у него есть принципиальное ограничение: он не может использоваться
PDF. В этом разделе мы воспользуемся
ReportLab Toolkit для генерирования файлов PDF с нуля.
для создания новых файлов
тарием
инструмен­
ReportLab представляет собой полнофункциональное решение для создания
PDF. Существует платная коммерческая версия, но также доступна версия
с открытым исходным кодом и ограниченной функциональностью.
Установка repor11ab
Прежде всего следует установить
reportlab
при помощи
pip:
$ python3 -m pip install reportlab
Проверьте установку командой
pip show:
$ python3 -m pip show reportlab
Name: reportlab
Version: 3.5.34
Summary: The Reportlab Toolkit
Home-page: http://www.reportlab.com/
Author: Andy Robinson, Robin Becker, the Reportlab team
and the community
Author-email: reportlab-users@lists2.reportlab.com
License: BSD license (see license.txt for details),
Copyright (с) 2000-2018, Reportlab Inc.
Location: c:\realpython\venv\lib\site-packages
Requires: pillow
Required-by:
На момент написания книги последней версией
Если у вас открыта среда
IDLE,
сможете использовать пакет
reportlab была версия 3.5.34.
ее необходимо перезапустить, прежде чем вы
reportlab.
14.8. Создание файла PDF с нуля
353
Использование класса
Canvas
Основной интерфейс для создания
PD F в reportlab предоставляет класс Canvas
из модуля
reportlab. pdfgen. canvas.
Откройте новое интерактивное окно в
чтобы импортировать класс
IDLE
и введите следующую команду,
Canvas:
>>> from reportlab.pdfgen.canvas import Canvas
При создании нового экземпляра
емого файла
Canvas
передается строка с именем создава­
PDF.
Создайте новый экземпляр Canvas для файла
hello.pdf:
>>> canvas - Canvas("hello.pdf")
Теперь у вас имеется экземпляр
Canvas, который был присвоен переменной
canvas; он связан с файлом с именем hello.pdf в текущем рабочем каталоге.
Впрочем, файл
Добавим в
hello.pdf еще не существует.
PDF текст.
Эта задача решается методом
. drawString():
>>> canvas.draw5tring(72, 72, "Hello, World")
. drawString( ), определяют позицию вы­
canvas. Первый аргумент задает расстояние от левого
Первые два аргумента, передаваемые
вода текста на объекте
края, а второй
-
расстояние от нижнего края.
Значения, передаваемые
равен
. drawString(), задаются в пунктах. Так как один пункт
1/72 дюйма, .drawString(72, 72, "Hello, World")
World"
выводит строку
"Hello,
на расстоянии в один дюйм от левого и от нижнего края страницы.
Для сохранения данных
PDF в
файле используется метод
. save( ):
>>> canvas.save()
В текущем рабочем каталоге появился файл
его в программе просмотра
PDF -
PDF с именем hello.pdf.
Откройте
в нижней части страницы появится текст
Hello, World!
В только что созданном файле
тельства.
PDF
стоит обратить внимание на два обстоя­
ГЛАВА 14
354
Создание и изменение файлов PDF
По умолчанию используется размер страницы А4, отличный от стандарт­
1.
ного для США размера
Letter.
По умолчанию используется шрифт
2.
Helvetica, 12
пунктов.
Ваши возможности не ограничены этими параметрами.
Настройка размера страницы
При создании объекта Canvas можно изменить размер страницы необязательным
параметром
pagesize.
Этот параметр получает кортеж значений с плавающей
точкой, задающих ширину и высоту страницы в пунктах.
Например, чтобы задать для страницы стандартный размер
8,5
х
11
дюймов,
создайте следующий объект Canvas:
canvas = Canvas("hello.pdf", pagesize=(612.0, 792.0))
Кортеж (612.0, 792.0) представляет лист размера
равно
612, а 11
х
Letter,
потому что
8,5
х
72
72 равно 792.
Если математические вычисления для преобразования пунктов в дюймы или
сантиметры нагоняют на вас тоску, используйте модуль
reportlab. lib. units для
упрощения преобразований. Модуль. units содержит несколько вспомогатель­
ных объектов (таких, как
inch и cm), которые помогут вам с преобразованиями.
Импортируйте объекты inch и cm из модуля reportlab. lib. uni ts:
>>> from reportlab.lib.units import inch, cm
Теперь проверьте значения обоих объектов:
»> cm
28.346456692913385
»> inch
72.0
И
cm,
и
inch
являются значениями с плавающей точкой. Они представляют
количество пунктов в каждой единице измерения. Сантиметр
(cm)
составляет
28. 346456692913385 пунктов, а дюйм (inch) - 72. 0 пункта.
Чтобы использовать единицы, умножьте название единицы на количество
единиц, которые нужно преобразовать в пункты. Например, в следующей ко­
манде
и
11
inch используется для
назначения странице размера
8,5 дюйма в ширину
дюймов в высоту:
>>> canvas = Canvas("hello.pdf", pagesize=(8.5
* inch, 11 * inch))
14.8. Создание файла PDF с нуля
355
Передавая кортеж
pagesize, можно создать страницу любого размера. Впрочем,
reportlab определены стандартные встроенные размеры страниц,
в пакете
с которыми проще работать.
Размеры страниц определяются в модуле
reportlab. lib. pagesizes. Например,
Letter, импортируйте объект LЕТТЕR из модуля
pagesize и передайте его в параметре pagesize при создании экземпляра Canvas:
чтобы выбрать размер страницы
>>> from reportlab.lib.pagesizes import LETTER
>>> canvas = Canvas("hello.pdf", pagesize=LETTER)
Проверив объект LЕТТЕR, вы увидите, что он содержит кортеж чисел с плава­
ющей точкой:
»>
LЕТТЕR
(612.0, 792.0)
Модуль
reportlab. lib. pagesize
определяет много стандартных размеров
страниц. В таблице перечислены некоторые варианты с указанием размеров.
РАЗМЕР СТРАНИЦЫ
РАЗМЕРЫ
А4
210
LETTER
мм х
297
мм
8,5
дюймов х
11
LEGAL
8,5
дюймов х
14 дюймов
TABLOID
11
дюймов х
дюймов
17 дюймов
Кроме этих размеров модуль содержит определения всех стандартных размеров
листов
ISO 216 (https:j/ru.wikipedia.org/ wiki//S0_216).
Настройка свойств шрифта
Также при выводе текста на объект
Canvas можно изменить шрифт, его размер
и цвет.
Для изменения шрифта и его размера используется метод. setFont( ). Сначала
создайте новый экземпляр Canvas с именем файла /опt-ехатрlе.рd/ и размером
листа
Letter:
>>> canvas = Canvas("font-example.pdf", pagesize=LETTER)
Затем выберите шрифт
Times New Roman, 18 пунктов:
>>> canvas . setFont("Times -Roman" , 18)
356
ГЛАВА 14
Создание и изменение файлов PDF
Наконец, выведите строку
"Times New Roman (18 pt)"
на объект
Canvas
и со­
храните его:
>>> canvas.drawString(l
>>> canvas.save()
* inch, 10 * inch, "Times New Roman (18 pt)")
С этими настройками текст будет выводиться на расстоянии
края страницы и
10 дюймов от нижнего
1 дюйм от левого
края. Откройте фaйлfont-example.pdf
в текущем рабочем каталоге и удостоверьтесь в этом!
По умолчанию доступны три шрифта:
1. "Courier"
2. "Helvetica"
3. "Times-Roman"
Каждый шрифт существует в полужирном и курсивном начертании. Список
всех начертаний шрифтов, доступных в
•
"Courier"
•
"Courier-Bold
•
"Courier-BoldOЫique"
•
"Courier-OЬlique"
•
"Helvetica"
•
"Helvetica-Bold"
•
"Helvetica-BoldOЬlique"
•
"Helvetica-OЬlique"
•
"Times-Bold"
•
"Times-Bolditalic
•
"Times-Italic"
•
"Times-Roman"
reportlab:
. setFillColor( ). В следующем
font-colors.pdf, содержащий текст
Также можно задать цвет шрифта методом
примере мы создадим файл
PDF
с именем
синего цвета:
from reportlab.lib.colors import Ыuе
from reportlab.lib.pagesizes import LETTER
from reportlab.lib.units import inch
14.9. Итоги и дополнительные ресурсы
357
from reportlab.pdfgen.canvas import Canvas
canvas = Canvas("font-colors.pdf", pagesize=LETTER)
Назначить шрифт Times New Roman, 12
canvas.setFont("Times-Roman", 12)
#
#
#
Вывести текст
и
10
синего
цвета
дюймов от нижнего
на
пунктов
1
расстоянии
дюйма от левого края
края
canvas.setFillColor("Ыue")
canvas.drawString(l*inch, 10*inch, "Blue text")
#
Сохранить файл
PDF
canvas.save()
Ыuе - объект, импортированный из модуля reportlab. lib. colors module. Этот
модуль содержит несколько распространенных цветов. Полный список цветов
хранится в исходном коде
reportlab.
Примерами этого раздела мы хотели показать, как работает объект Canvas.
Но это знакомство
-
поверхностное. С модулем
reportlab вы можете создавать
таблицы, формы и даже высококачественную графику с нуля!
В руководстве пользователя
построения документов
Reportlab
PDF.
вы найдете многочисленные примеры
Руководство может стать отличной отправной
точкой, если вы захотите больше узнать о создании
14.9.
PDF в Python.
ИТОГИ И ДОПОЛНИТЕЛЬНЫЕ РЕСУРСЫ
В этой главе вы научились создавать и изменять файлы
PyPDF2
и
reportlab.
Вы узнали, как средствами пакета
•
PDF средствами пакетов
читать файлы
PDF
PyPDF2:
и извлекать из них текст при помощи класса
PdfF ileReader;
•
выполнять конкатенацию и слияние файлов
PDF
при помощи класса
PdfF ileMerger;
•
поворачивать и обрезать страницы файлов
•
шифровать и дешифровать файлы
PDF с
PDF;
паролем.
Также в этой главе я кратко познакомил вас с тем, как создавать файлы
с нуля средствами пакета
reportlab.
Вы научились:
PDF
ГЛАВА 14
358
Создание и изменение файлов PDF
•
пользоваться классом
•
записывать текст в
•
задавать гарнитуру и размер шрифта методом
•
изменять цвет шрифта вызовом
Пакет
reportlab -
Canvas;
Canvas
вызовом
.drawString();
. setFont();
. setFillColor().
мощный инструмент для создания файлов
PDF,
и вы полу­
чили лишь первое представление о его возможностях.
ИНТЕРАКТИВНЫЙ ТЕСТ
К этой главе прилагается бесплатный интерактивный тест для проверки усво­
енных вами знаний . Тест доступен на телефоне или компьютере:
realpython.com!quizzes!pybasics-pdf
Дополнительные ресурсы
За дополнительной информацией о работе с файлами
PDF в Python обращай­
тесь к следующим ресурсам:
•
«How toWoгk With а PDF in Python» (https.j/realpython.com/pdf-python/)
•
«ReportLab PDF Library User Guide» (https.j/www.reportlab.com/docs/
reportlab-userguide .pdf)
ГЛАВА
15
Базы данных
В главе 12 мы показали, как читать и сохранять данные из файлов средствами
Python. Также для хранения информации часто используются базы данных (БД).
База данных представляет собой структурированную систему хранения данных.
Иногда она состоит из нескольких файлов
CSV,
упорядоченных в каталогах,
а иногда оказывается гораздо сложнее. Система управления базами данных
(СУБД)
-
программа, управляющая доступом к БД и взаимодействием с ней.
В комплект поставки
ми данных
Python включена упрощенная система управления база­
SQLite. Она идеально подходит для изучения азов работы с базами
данных.
В этой главе вы научитесь:
•
создавать базу данных
•
сохранять и читать данные из базы
•
использовать пакеты для работы с другими базами данных.
SQLite;
SQLite;
ВАЖНО!
SQLite использует язык SQL (Structured Ouery Language -
язык структурирован­
ных запросов) для взаимодействия со своей базой данных. Некоторый опыт
работы с
SQL пригодится
вам при изучении материала этой главы.
Итак, за дело!
15.1. ЗНАКОМСТВО
С
SQLITE
Существует много разных ядер баз данных
SQL,
и некоторые из них лучше
подходят для каких-то конкретных целей, чем другие. Одно из самых простых
и облегченных ядер баз данных
новку
SQL - SQLite - входит в стандартную
Python, а значит, уже работает на вашем компьютере.
уста­
ГЛАВА 15
360
Базы данных
В этом разделе вы научитесь пользоваться пакетом
баз данных
Основы
SQLite, а также для
sqli tеЗ для создания
новых
хранения и чтения данных.
SQLite
Вот основные этапы работы с
SQLite.
1.
Импортирование пакета sqliteЗ.
2.
Подключение к существующей БД или создание новой.
3.
Выполнение команд
4.
Закрытие подключения к БД.
SQL.
Начнем знакомство с ними в интерактивном окне
IDLE. Откройте IDLE и вве­
дите следующие команды:
>>> import sqliteЗ
»> connection = sqliteЗ.connect("test_database.db")
Функция sqliteЗ.
connect()
используется для подключения или создания
новой базы данных.
При выполнении команды
ществующую БД с именем
. connect( "test_database. db") Python ищет су­
"test_database.db". Если БД с таким именем не
найдена, в текущем рабочем каталоге создается новая.
Чтобы создать БД в другом каталоге, необходимо указать полный путь в аргу­
менте
. connect( ).
ПРИМЕЧАНИЕ
Также возможно создать базу данных в памяти, передав
. conпect ()
строку
" : memory: ":
connection
= sqliteЗ.connect(":memory:")
Этот способ хорошо подходит для хранения данных, которые должны суще­
ствовать только во время работы программы.
Аргумент
. conпect ()
возвращает объект
убедиться при помощи
type( ):
>>> type(connection)
<class 'sqliteЗ.Connection'>
sqli tеЗ. Connection. В этом можно
15.1. Знакомство с SQLite
Объект
361
Connection осуществляет соединение между программой и базой дан­
ных. Он содержит набор атрибутов и методов, которые могут использоваться
для взаимодействия с БД.
Для хранения данных понадобится объект
вызовом
Cursor,
который можно получить
connection. cursor( ):
>>> cursor = connection.cursor()
>» type(cursor)
<class 'sqliteЗ.Cursor'>
Объект sqliteЗ.Cursor становится ~окном~ для взаимодействия с базой дан­
ных. При помощи
команды
SQL и
Cursor
можно создавать таблицы базы данных, выполнять
получать результаты запроса.
ПРИМЕЧАНИЕ
В терминологии баз данных курсором называется объект, предназначенный
для выборки результатов запроса к базе данных - по одной строке данных
за раз.
Воспользуемся функцией
SQLite datetime () для получения значения текущего
местного времени:
»> query = "SELECT datetime( 'now ', 'localtime');"
>>> results = cursor.execute(query)
»> results
<sqliteЗ.Cursor object at 0х000001А27ЕВ85Е30>
"SELECТ
datetime( 'now', 'localtime' ); " -
команда
SQL, возвращающая дату
query
и время в настоящий момент. Текст запроса присваивается переменной
и передается
cursor. execute (). Команда применяет запрос к базе данных и воз­
Cursor, который присваивается переменной results.
вращает объект
Возможно, вас интересует, где увидеть время, возвраЩенное datetime (). Чтобы
получить результаты запроса, используйте метод resul ts. fetchone (),который
возвращает кортеж с первой строкой результатов:
>>> row = results.fetchone()
>» row
('2018-11-20 23:07:21',)
Так как
. fetchone ()
возвращает кортеж, необходимо обратиться к первому
элементу для получения строки с информацией о дате и времени:
362
ГЛАВА 15
Базы данных
>>> time = row[0]
»> time
'2018-11-20 23:09:45'
Наконец, вызовите
connection. close() для закрытия подключения к базе
данных:
>>> connection.close()
Важно всегда закрывать подключение к БД после завершения работы с ней,
чтобы системные ресурсы не оставались занятыми после того, как ваша про­
грамма прекратит работу.
Использование with для управления
подключением к базе данных
Вспомните, о чем мы говорили в главе
с
open ( )
12: команда with
может использоваться
для открытия файла и его автоматического закрытия после выполне­
ния блока wi th. Та же схема используется с подключениями баз данных
SQLite,
и этот способ открытия подключений считается предпочтительным.
Пример использования
datetime()
из предыдущего примера с командой
with
для управления подключением к БД:
»> with sqlite3.connect("test_database.db") as connection:
cursor = connection.cursor()
query = "SELECT datetime('now', 'localtime');"
results = cursor.execute(query)
row = results.fetchone()
time = row[0]
»> time
'2018-11-20 23:14:37'
Connection, возвращенный sqli tеЗ. connect(), присва­
connection в команде with.
В этом примере объект
ивается переменной
Код в блоке with создает новый объект Cursor методом connection. cursor( ), а за­
тем получает текущее время методами
. execute()
и
. fetchone()
объекта
Управление подключениями к базе данных с помощью команды
Cursor.
wi th обладает
множеством преимуществ. Полученный код часто оказывается более чистым
и комнактным, чем код без использования
wi th. Более того, как будет показано
в следующем примере, любые изменения, вносимые в базу данных, автомати­
чески сохраняются.
15.1. Знакомство с SQLite
363
Работа с таблицами базы данных
Обычно создавать целую базу данных только для получения текущего времени
не стоит. Базы данных, как правило, используются для сохранения и чтения
информации. Чтобы сохранить информацию в базе, следует создать таблицу
и записать в нее набор значений.
Создадим таблицу People с тремя столбцами: FirstName, LastName и Age. Запрос
SQL для
создания этой таблицы выглядит так:
CREATE TABLE People(FirstName
ТЕХТ,
LastName
ТЕХТ,
Age INT);
Обратите внимание: после FirstName и LastName следует слово ТЕХТ, а после Age
следует слово INT. Оно сообщает
SQLite,
что значения в столбцах FirstName
и LastName являются текстовыми , тогда как значения в столбце Age являются
целыми числами.
После того как таблица будет создана, ее можно заполнить данными коман­
дой INSERT INTO SQL. Следующий запрос вставляет значения Ron, Obvious и 42
в столбцы FirstName, LastName и Age соответственно:
INSERT INTO People VALUES( 'Ron', 'Obvious', 42);
Обратите внимание: строки 'Ron' и 'Obvious' заключены в одинарные кавычки.
При этом они остаются валидными строками в
Python, но, что важнее, в SQLite
валидны только строки в одинарных кавычках.
ВАЖНО!
Когда вы записываете запросы в SQL в виде строк на языке Python, проследите,
чтобы они заключались в двойные кавычки. Это позволит вам использовать
одинарные кавычки внутри них как ограничители строк в
SQLite.
SQLite - не единственная СУБД SQL, где действует соглашение об одинарных
кавычках . Постоянно помните об этом , работая с базами данных SQL.
А теперь посмотрим, как выполнить эти команды и сохранить изменения в базе
данных. Сначала это будет сделано без команды with.
В новом окне редактора введите следующую программу :
import
sqliteЗ
create_taЫe
CREATE TABLE People(
364
ГЛАВА 15
Базы данных
FirstName ТЕХТ,
LastName ТЕХТ,
Age INT
) ; '""'
insert_values
INSERT INTO People VALUES(
'Ron',
'Obvious',
42
) ; """
connection = sqliteЗ.connect("test_database.db")
cursor = connection.cursor()
cursor.execute(crate_taЫe)
cursor.execute(insert_values)
connection.commit()
connection.close()
Сначала создаются две строки с командами
People
SQL,
которые создают таблицу
и вставляют в нее данные. Эти строки присваиваются переменным
create_taЫe и
insert_values.
Обе команды записываются в синтаксисе с утроенными кавычками, чтобы мы
могли отформатировать код.
SQL игнорирует отступы,
что позволяет исполь­
зовать пробелы в строке для улучшения удобочитаемости кода
Python.
Затем мы создаем объект
Connection вызовом sqliteЗ. connect () и присваи­
connection. Также можно создать объект Cursor вызовом
connection. cursor() и использовать его для выполнения двух команд SQL.
ваем его переменной
Наконец, метод
connection. commit ()
сохраняет информацию в базе данных.
Этот метод сохраняет внесенные изменения. Если не выполнить
commi t (), то
таблица
People
Сохраните файл и нажмите
database.db
connection.
создана не будет.
FS,
содержит таблицу
чтобы запустить программу. База данных
People
test_
с одной строкой данных. В этом можно
убедиться в интерактивном окне:
>» connection = sqliteЗ.connect("test_database.db")
>>> cursor = connection.cursor()
>>> query = "SELECT * FROM People;"
>>> results = cursor.execute(query)
>>> results.fetchone()
( 'Ron', 'Obvious', 42)
А теперь перепишем программу с использованием команды wi th для управления
подключением к базе данных.
15.1. Знакомство с SQLite
Прежде чем что-либо делать, необходимо удалить таблицу
365
People, чтобы со­
здать ее заново. Введите следующий код в интерактивном окне, чтобы удалить
таблицу
People из базы данных:
>>> cursor.execute("DROP TABLE People;")
object at 0x000001F739DB6650>
>>> connection.commit()
>>> connection.close()
<sqliteЗ.Cursor
Вернитесь к окну редактора и измените программу следующим образом:
import
sqliteЗ
create_taЫe
=
CREATE TABLE People(
FirstName ТЕХТ,
LastName ТЕХТ,
Age INT
) ; """
insert_values
INSERT INTO People VALUES(
'Ron',
'Obvious',
42
) ; """
with
sqliteЗ.connect("test_database.db")
cursor
=
as connection:
connection.cursor()
cursor.execute(create_taЫe)
cursor.execute(insert_values)
Ни вызов
connection. close(), ни вызов connection. commit() не обязательны.
Любые изменения, вносимые в базу данных, будут автоматически сохранены
при завершении выполнения блока wi th. Это еще одно преимущество исполь­
зования команды
wi th для управления подключением к БД.
Выполнение нескольких команд
SQL
Сценарий SQL представляет собой набор разделенных точкой с запятой команд
SQL,
которые могут выполняться одновременно. Объекты
метод
. executescript ()
для выполнения сценариев
Следующая программа выполняет сценарий
People
и вставляет в нее несколько значений:
import
sqliteЗ
sql = """
DROP TABLE IF EXISTS People;
SQL,
Cursor содержат
SQL.
который создает таблицу
ГЛАВА 15
366
Базы данных
CREATE TABLE People(
FirstName ТЕХТ,
LastName ТЕХТ,
Age INT
) j
INSERT INTO People VALUES(
'Ron',
'Obvious',
'42'
) j """
with
sqliteЗ.connect("test_database.db")
as connection:
cursor = connection.cursor()
cursor.executescript(sql)
Также возможно выполнить несколько сходных команд, вызвав метод
. executemany ()
и передав кортеж кортежей, в котором каждый внутренний
кортеж предоставляет информацию для одной команды.
Например, если у вас имеется большой набор записей о людях, которые нужно
вставить в таблицу
People, вы можете сохранить эту информацию в следующем
кортеже кортежей:
people_values = (
("Ron", "Obvious", 42),
("Luigi", "Vercotti", 43),
("Arthur", "Belling", 28)
После этого всю информацию можно вставить всего одной строкой кода:
cursor.executemany("INSERT INTO People VALUES(?, ?, ?)", people_values)
Вопросительные знаки обозначают место для подстановки элементов кортежей,
содержащихся в
people_values. Это называется параметризованной командой.
Каждый знак
?
представляет параметр, который заменяется значением из
people_values
при выполнении метода. Параметры заменяются по порядку.
Иначе говоря, первый знак
второй знак
? заменяется
? заменяется первым значением в people_values,
вторым значением и т. д.
Проблемы безопасности с параметризованными
командами
По соображениям безопасности
ми
SQL,
-
особенно при взаимодействиях с таблица­
основанными на данных, введенных пользователем,
применять параметризованные команды
SQL.
-
всегда следует
Дело в том, что пользователь
теоретически может ввести данные, которые выглядят как код
SQL и вызывают
15.1. Знакомство с SQLite
неожиданное поведение команд
SQL.
Это называется атакой внедрения
367
SQL,
причем, возможно, у пользователя нет вредоносных намерений и это проис­
ходит абсолютно случайно.
Допустим, вы хотите вставить запись в таблицу People на основании информа­
ции, введенной пользователем. Первая попытка может выглядеть примерно так:
import
#
sqliteЗ
Получить данные людей от пользователя
first_name = input("Enter your first name: ")
last_name = input("Enter your last name: ")
age = int(input("Enter your age: "))
#
Выполнить
команды вставки для
введенных данных
query = (
"INSERT INTO People Values"
f"('{first_name}', '{last_name}', {age});"
with
sqliteЗ.connect("test_database.db")
as connection:
cursor = coпnection.cursor()
cursor.execute(query)
А если имя пользователя содержит апостроф? Попробуйте добавить в таблицу
имя Flannery О' Connor - и вы увидите, что программа перестает работать. Дело
в том, что апостроф
-
то же самое, что одинарная кавычка, и для программы
все выглядит так, словно код
SQL завершается
раньше, чем вы предполагали.
В данном случае код только порождает ошибку, что уже достаточно плохо. Од­
нако в некоторых случаях некорректный ввод может привести к повреждению
всей таблицы. Многие другие трудно прогнозируемые случаи могут нарушить
структуру таблицы
SQL
и даже удалить части базы данных. Чтобы этого не
произошло, всегда используйте параметризованные команды.
В следующем коде параметризованная команда используется для безопасной
вставки пользовательского ввода в базу данных:
import
sqliteЗ
first_name = input("Enter your first name: ")
last_name = input("Enter your last name: ")
age = int(input("Enter your age: "))
data = (first_пame, last_name, age)
with
sqliteЗ.connect("test_database.db") as connection:
cursor = connection.cursor()
cursor.execute("INSERT INTO People VALUES(?, ?, ?);", data)
ГЛАВА 15
368
Базы данных
Параметризация также пригодится для обновления строки в базе данных
командой
SQL UPDAТE:
cursor.execute(
"UPDATE People SET Age=? WHERE FirstName=? AND LastName=?;",
( 45, 'Luigi', 'Vercotti')
Этот код обновляет значение столбца
поле
FirstName
содержит
'Luigi ',а
Age значением 45 для строки, в которой
LastName содержит 'Vercotti '.
поле
Чтение данных
Вставка и обновление информации в базе данных вряд ли принесут пользу, если
вам не удается прочитать информацию из этой базы данных.
Для чтения информации из базы данных можно воспользоваться методами
курсора
. fetchone ()
и
. fetchall ().
Метод
данных из результатов запроса, тогда как
. fetchone () возвращает одну строку
. fetchall () читает сразу все резуль­
таты запроса.
Следующая программа демонстрирует использование
. fetchall ():
import sqlite3
values = (
("Ron", "Obvious", 42),
("Luigi", "Vercotti", 43),
("Arthur", "Belling", 28),
with sqlite3.connect("test_database.db") as connection:
cursor = connection.cursor()
cursor.execute("DROP TABLE IF EXISTS People")
cursor.execute("""
CREATE TABLE People(
FirstName ТЕХТ,
LastName ТЕХТ,
Age INT
) ; """
cursor. executemany( "INSERT INTO People VALUES (?, ?, ? ) ; ", values)
# Выбрать все имена и фамилии людей, возраст которых
# превышает 30 лет
cursor.execute(
"SELECT FirstName, LastName FROM People WHERE Age > 30;"
for row in cursor.fetchall():
print(row)
15.2. Библиотеки для работы с другими базами данных SQL
369
В этой программе мы сначала удаляем таблицу People, чтобы уничтожить из­
менения, внесенные в предыдущих примерах этого раздела. Затем мы заново
создаем таблицу People и вставляем в нее несколько значений. Далее вызовом
. execute() выполняется команда SELECT, которая возвращает имена и фамилии
30.
всех людей, возраст которых превышает
Наконец,
. fetchall ()
возвращает результаты запроса в виде списка кортежей,
в котором каждый кортеж содержит одну строку данных из результатов запроса.
Если ввести программу в новом окне редактора, а затем сохранить и запустить
файл, в интерактивном окне появится следующий вывод:
( 'Ron', 'Obvious' )
( ' Luigi', 'Vercotti' )
Действительно, это единственные люди в базе данных, чей возраст более 30 лет.
Упражнения
1. Создайте новую базу данных, содержащую таблицу Roster. Таблица со­
стоит из трех полей: Name, Species и Age. Столбцы Name и Species должны
быть текстовыми, а столбец Age должен быть целочисленным полем.
2.
Заполните созданную таблицу следующими значениями:
NАМЕ
3.
SPECIES
Benjamin Sisko
Human
40
Jadzia Dax
Tri ll
300
Kira Nerys
Bajoran
29
Обновите поле Name записи Jadzia Dax, чтобы оно содержало значение
Ezri Dax.
4.
Выведите значения
Species
Name и Age для всех строк данных, у которых поле
Bajoran.
содержит значение
15.2. БИБЛИОТЕКИ ДЛЯ
БАЗАМИ ДАННЫХ SQL
РАБОТЫ С ДРУГИМИ
Если у вас имеется другая база данных
из кода
только
SQL, с которой вы бы хотели работать
Python, синтаксис в основном будет идентичен тому, с которым мы вас
что познакомили для SQLite. Тем не менее вам придется установить
ГЛАВА
370
Базы данных
15
дополнительные пакеты для взаимодействия с БД, потому что
SQLite -
един­
ственное встроенное решение.
Существует много разновидностей
Несколько наиболее
SQL с соответствующими пакетами Python.
распространенных и надежных альтернатив SQLite с от­
крытым исходным кодом:
•
pyodbc для работы с базами данных ODBC (Open Database Connectivity),
такими как
Microsoft SQL Server;
•
psycopg2 для работы с базами данных PostgreSQL;
•
PyMySQL для работы с базами данных MySQL.
Одно из отличий SQLite от других ядер баз данных (не считая синтаксиса кода
SQL, который незначительно различается в разных «диалектах» SQL) заключа­
ется в том, что многие ядра баз данных требуют имени пользователя и пароля
для подключения. За информацией о синтаксисе подключения к базе данных
обращайтесь к документации конкретного пакета.
Пакет SQLAlchemy -
еще один популярный инструмент для работы с базами
данных. SQLAlchemy является системой объектно-реляционного отображения
(Object-Relational Mapper - ORM); такие системы используют объектно-ори­
ентированную парадигму для построения запросов к базам данных. Их можно
настроить для подключения к разным базам данных. Объектно-ориентиро­
ванный подход позволяет создавать запросы без написания низкоуровневых
команд
SQL.
15.З. ИТОГИ И ДОПОЛНИТЕЛЬНЫЕ РЕСУРСЫ
В этой главе я показал, как взаимодействовать с базами данных ядра
SQLite,
Python. SQLite - компактная и облегченная система
управления базами данных SQL, которая предназначена для хранения и вы­
борки данных в программах Python. Для взаимодействия с SQLite в Python
которое входит в поставку
необходимо импортировать модуль sqliteЗ.
Чтобы работать с базой данных
SQLite, следует сначала подключиться
к суще­
ствующей БД или создать новую БД функцией
sqli tеЗ. connect (),которая воз­
вращает объект Connection. После этого можно использовать метод connection.
cursor() для получения нового объекта Cursor.
Объекты
Cursor предназначены для выполнения команд SQL и получения
cursor. execute() и cursor. executescript ()
результатов запросов. Например,
15.3. Итоги и дополнительные ресурсы
используются для выполнения запросов
просов можно применять методы
SQL. Для
371
получения результатов за­
cursor. fetchone ()
и
cursor. fetchall ().
Кроме того, мы рассказали о нескольких сторонних пакетах для подключения
к другим базам данных
зам данных
SQL,
включая пакет psycopg2 для подключения к ба­
и pyodbc для Microsoft SQL Server. Вы также узнали
SQLAlchemy, предоставляющей стандартный интерфейс для под­
PostgreSQL
о библиотеке
ключения к различным базам данных
,
SQL.
ИНТЕРАКТИВНЫЙ ТЕСТ .
К этой главе прилагается бесплатный интерактивный тест для проверки усво­
енных вами знаний. Тест доступен на телефоне или компьютере:
realpython.com/quizzes!pybasics-databases
Дополнительные ресурсы
За дополнительной информацией о работе с базами данных обращайтесь к сле­
дующим ресурсам:
•
«lntroduction to Python SQL Libraries» (https.j/realpython.com/pythonsql-libranes/)
•
«Preventing SQL Injection Attacks With Python» (https.j/realpython.com/
prevent-python -sql-injection/)
ГЛАВА
16
Веб-программирование
Пожалуй , интернет стал самым большим источником информации (и дезин­
формации) на планете.
Многие дисциплины: теория обработки данных, бизнес-аналитика, рассле­
довательская журналистика - могут извлечь колоссальную пользу из сбора
и анализа данных с веб-сайтов.
Веб-скрапинrом
(web scraping) называется процесс сбора и обработки низко­
Python были разработаны до­
уровневых данных с веб-сайтов. В сообществе
статочно мощные средства извлечения веб-данных.
В этой главе вы научитесь:
•
парсить данные с веб-сайтов с помощью строковых методов и регулярных
выражений;
•
парсить данные с веб-сайтов с помощью парсера
•
взаимодействовать с формами и другими компонентами веб-сайтов.
HTML;
При чтении этой главы желательно иметь некоторый опыт работы с
HTML.
Итак, за дело!
16.1. СКРАПИНГ И ПАРСИНГТЕКСТА
С ВЕБ-САЙТОВ
Автоматизированный сбор данных с веб-сайтов называется веб-скрапингом.
Некоторые веб-сайты явно запрещают пользователям извлекать данные с при­
менением автоматизированных средств вроде тех, которые мы будем создавать
в этой главе. На то есть две возможные причины:
·
16.1. Скрапинг и парсинг текста с веб-сайтов
1.
У сайта есть веские основания для защиты данных. Например,
Maps
373
Google
не позволяет слишком быстро запрашивать слишком много ре­
зультатов.
2.
Лавина повторных запросов к веб-серверу может забивать полосу про­
пускания, замедляя работу сайта для других пользователей, возможно
-
с созданием избыточной нагрузки на сервер, так что сайт полностью
перестает реагировать на запросы.
ВАЖНО!
Прежде чем заниматься скрапингом, всегда проверяйте правила и условия ис­
пользования веб-сайта, чтобы узнать, не нарушает ли этих правил обращение
к сайту с помощью автоматизированных инструментов. С юридической точки
зрения веб-скрапинг не имеет однозначной трактовки. Но, пожалуйста, учтите,
что описанные ниже методы могут оказаться незаконными применительно
к веб-сайтам, запрещающим скрапинг.
Попробуем извлечь НТМL-код с одной веб-страницы. Мы будем использовать
страницу на сайте
Real Python, созданную специально для этой главы.
Ваша первая программа для веб-скрапинга
В стандартную библиотеку
Python
входит пакет
ства для работы с URL-адресами. В частности,
держит функцию
ur lopen (),
urllib, содержащий сред­
urllib. requestmodule со­
которая служит для открытия URL-aдpeca
в программе.
В интерактивном окне
вать
IDLE введите следующую
команду, чтобы импортиро­
urlopen():
>>> from urllib.request import urlopen
Веб-страница, которую мы будем открывать, доступна по URL-aдpecy:
>>> url
"http://olympus.realpython.org/profiles/aphrodite"
=
Чтобы открыть веб-страницу, передайте
>>> page
=
urlopen()
url функции urlopen():
urlopen(url)
возвращает объект HПPResponse:
» > page
<http.client.HTTPResponse object at 0xl05fef820>
374
ГЛАВА 16
Веб-программирование
Чтобы извлечь НТМ L-код страницы, сначала вызовите метод . read () объекта
HTTPResponse, который возвращает последовательность байтов. Затем вызовите
метод . decode () для декодирования байтов в строку в кодировке
UTF-8:
>>> html_bytes = page.read()
»> html = html_bytes.decode("utf-8")
Далее выведите НТМL-код, чтобы просмотреть содержимое неб-страницы:
>» print(html)
<html>
<head>
<title>Profile: Aphrodite</title>
</head>
<body bgcolor="yellow">
<center>
<br><br>
<img src="/static/aphrodite.gif" />
<h2>Name: Aphrodite</h2>
<br><br>
Favorite animal: Dove
<br><br>
Favorite color: Red
<br><br>
Hometown: Mount Olympus
</center>
</body>
</html>
После получения НТМL-кода в текстовом виде вы сможете извлечь из него
информацию несколькими способами.
Извлечение текста из
HTML строковыми
методами
Один из способов получения информации из НТМL-кода неб-страницы основан
на использовании строковых методов. Например, можно при помощи метода
.find()
провести поиск тегов
<title>
по тексту
HTML
и извлечь заголовок
неб-страницы.
Извлечем заголовок неб-страницы, запрошенной в предыдущем примере.
Если вам известны индексы первого символа заголовка и первого символа
закрывающего тега
</title>,
вы можете воспользоваться срезом строки для
извлечения заголовка.
Так как
. find ()
возвращает индекс первого вхождения подстроки, вы можете
получить индекс открывающего тега
вызове
. find ():
<title>,
передавая строку
"<title>"
при
16.1. Скрапинг и парсинг текста с веб-сайтов
>>> title_index
» > ti tle_index
375
html.find("<title>")
14
Впрочем, нам нужен не индекс тега
<title>, а индекс самого заголовка.
Чтобы
получить индекс первой буквы в заголовке, следует прибавить длину строки
"<title>"
к
title_index:
>>> start index
» > start_index
title_index + len("<title>")
21
Теперь получим индекс закрывающего тега
title>"
методу
</title>,
передавая строку"</
.find():
>>> end_index = html.find("</title>")
»> end_index
39
Наконец, текст заголовка извлекается с помощью среза строки
html:
>>> title = html[start_index:end_index]
»> title
'Profile: Aphrodite'
Реальная разметка
HTML может быть куда более сложной и куда менее предска­
Aphrodi te. Другая страница профиля
с менее тривиальной разметкой HTML доступна для ваших экспериментов по
адресу http.j/ olympus.realpython.org/profiles/poseidon.
зуемой, чем разметка страницы профиля
Попробуйте извлечь заголовок по этому новому URL-aдpecy таким же способом,
как в предыдущем примере:
>>> url = "http://olympus.realpython.org/profiles/poseidon"
>>> page = urlopen(url)
>>> html = page.read().decode("utf-8")
>>> start_index = html.find("<title>") + len("ctitle>")
>>> end_index = html.find("</title>")
>>> title = html[start_index:end_index]
»> title
'\n<head>\n<title >Profile: Poseidon'
Сюрприз! С заголовком выдается некоторое количество НТМL-кода. Почему?
НТМL-разметка страницы
/profiles/poseidon
похожа на
но есть небольшое различие. В открывающем теге
угловой скобкой
бражается в виде
> стоит дополнительный
<title >.
/profiles/aphrodite,
<ti tle> перед закрывающей
пробел, в результате чего тег ото­
ГЛАВА 16
376
Веб-nрограммирование
html.find("<title>")
возвращает
-1,
равен
"<title>"
len( "<title>" ), который
потому что точная подстрока
не существует. Когда -1 прибавляется к результату
7, переменной start_index присваивается значение 6.
Символом с индексом 6 строки
html
\n, непо­
тега <head>. Это
является символ новой строки
средственно предшествующей открывающей угловой скобке<
означает, что выражение
html [ start_index: end_index]
HTML от символа новой
строки до позиции, предшествующей тегу
При обработке
вернет всю разметку
</title>.
HTML могут возникать бесчисленные и весьма непредсказуемые
проблемы такого рода. Нам понадобится более надежный механизм извлечения
текста из
HTML.
Знакомство с регулярными выражениями
Реrулярные выражения
-
шаблоны, которые применяются для поиска текста
в строках. Поддержка регулярных выражений в
модуль
re
Python осуществляется
через
стандартной библиотеки.
ПРИМЕЧАНИЕ
Регулярные выражения поддерживаются не только в Pythoп. Это общая кон­
цепция программирования, которая может использоваться в любом языке .
Чтобы работать с регулярными выражениями, прежде всего необходимо им­
портировать модуль
re:
import re
В регулярных выражениях для обозначения элементов шаблона используются
специальные символы, называемые метасимволами. Например, звездочка
*
обозначает нуль или более вхождений символа, находящегося непосредственно
перед звездочкой.
В следующем примере функция
findall()
используется для поиска в строке
любого текста, соответствующего заданному регулярному выражению:
>>> re.findall("ab*c",
"ас")
['ас']
В первом аргументе
re. findall ()
передается регулярное выражение, для ко­
торого ищется совпадение, а во втором
-
проверяемая строка. В приведенном
примере ищется совпадение для шаблона "аЬ*с" в строке "ас".
16.1. Скрапинг и парсинг текста с веб-сайтов
377
Регулярное выражение "аЬ*с" совпадает с любой частью строки, которая на­
чинается с "а" и завершается "с", а между ними содержится ну ль или несколько
вхождений "Ь".
re. findall()
возвращает список всех совпадений. Строка "ас"
совпадает с этим шаблоном, поэтому она возвращается в списке.
Несколько примеров применения того же шаблона к разным строкам:
>>> re.findall("ab*c", "abcd")
[ 'аЬс']
>>> re.findall("ab*c",
"асс")
['ас']
>>> re.findall("ab*c",
[ 'аЬс',
'ас'
"аЬсас")
]
>>> re.findall("ab*c", "abdc")
[]
Обратите внимание: если ни одного совпадения не найдено, то
findall() воз­
вращает пустой список.
Поиск по шаблону производится с учетом регистра. Если вы хотите искать
совпадение независимо от регистра, передайте третий аргумент со значением
re. IGNORECASE:
>>> re.findall("ab*c",
[]
"АВС")
>>> re.findall("ab*c",
"АВС",
re.IGNORECASE)
[ 'АВС']
Точка обозначает один произвольный символ в регулярном выражении. Напри­
мер, поиск всех строк, которые содержат буквы "а" и "с", разделенные одним
символом, выполняется так:
>>> re.findall("a.c",
"аЬс")
['аЬс']
>>> re.findall("a.c",
[]
"аЬЬс")
»> re.findall("a.c",
"ас")
[]
>>> re.findall("a.c",
"асс")
['асс']
Шаблон
. * в регулярном выражении обозначает произвольный символ, повто­
ренный любое количество раз. Например," а. *с" можно использовать для поиска
378
ГЛАВА 16
Веб-программирование
любой подстроки, начинающейся с "а" и заканчивающейся "с", независимо от
того, какая буква - или какие буквы - находятся между ними:
>>> re.findall("a.*c",
[
>>> re.findall("a.*c",
[
"аЬс")
'аЬс']
"аЬЬс")
'аЬЬс']
>>> re.findall("a.*c",
"ас")
['ас']
>>> re.findall("a.*c",
"асс")
['асс']
Обычно для поиска совпадения конкретного шаблона в строке используется
re.search(). Она несколько сложнее re.findall(), потому что воз­
MatchObject, в котором хранятся разные группы данных. Дело
в том, что совпадения могут быть найдены внутри других совпадений, и re.
search() возвращает все возможные результаты.
функция
вращает объект
MatchObject сейчас несущественны. Пока вам доста­
. group() для MatchObj ect возвращает первый результат
Подробности строения
точно знать, что вызов
с наибольшим охватом; в большинстве случаев это именно то, что вам нужно:
»> match_results = re.search("ab*c",
>>> match_results.group()
"АВС",
re.IGNORECASE)
'АВС'
В модуле
re реализована еще одна функция, которая пригодится для парсинга
re. sub () (сокращение от substitute - подстановка) позволяет
текста. Функция
заменить текст в строке, совпадающий с регулярным выражением, новым тек­
стом. Своим поведением она напоминает строковый метод
шла речь в главе
4.
В аргументах
re. sub()
. replace( ), о котором
передается регулярное выражение,
затем текст замены и исходная строка. Пример:
>» string = "Everything is <replaced> i f it's in <tags>."
>>> string = re.sub("<.*>", "ELEPHANTS", string)
»> string
'Everything is ELEPHANTS.'
Вероятно, не совсем то, что вы ожидали?
re. sub()
использует регулярное выражение"<.*>" для поиска и замены всего
текста от первого символа< до последнего символа>, то есть всего текста от нача­
ла <replaced> до конца
<tags>. Это объясняется тем, что регулярные выражения
16.1. Скрапинг и парсинг текста с веб-сайтов
Python
работают по максимальному принципу
символов, как
*,
-
при использовании таких
они пытаются найти самое длинное возможное совпадение.
Также можно воспользоваться минимальным шаблоном
так же, как
*,
379
*?. Он работает точно
не считая того, что он находит самую короткую из возможных
строк текста:
"Everything is creplaced> if it's in ctags>."
>» string
re.sub("<.*?>", "ELEPHANTS", string)
»> string
»> string
"Everything is ELEPHANTS i f it's in ELEPHANTS."
Извлечение текста из HTML с использованием
регулярных выражений
Вооружившись этой информацией, попробуем извлечь заголовок из разметки
http.j/olympus.realpython.org/profiles/dionysus,
записанную строку HTML:
которая включает неаккуратно
<TITLE >Profile: Dionysus</title / >
У метода . find () здесь возникли бы сложности, но при умном использовании
регулярных выражений можно обработать такую разметку быстро и эффективно:
import re
from urllib.request import urlopen
url = "http://olympus.realpython.org/profiles/dionysus"
page = urlopen(url)
html = page.read().decode("utf-8")
pattern = "ctitle.*?>.*?</title.*?>"
match_results = re.search(pattern, html, re.IGNORECASE)
title = match_results.group()
title = re.sub("<.*?>", "", title) #Удалить теги HTML
print (title)
Давайте детальнее рассмотрим первое регулярное выражение в строке шаблона
и разобьем его на три части.
<ПТLЕ >в html. Часть <ti tle
<TITLE, потому что re. search () вызывается со значением
re. IGNORECASE, а *? > совпадает со всем текстом после <ПТLЕ вплоть до
1. <ti tle. *?>совпадает с открывающим тегом
совпадает с
первого вхождения>.
2. . *?
минимально совпадает со всем текстом после открывающего тега
<ПТLЕ >,останавливаясь на первом совпадении
</title. * ?>.
ГЛАВА 16
380
Веб-программирование
3. </title. *?>отличается от первого шаблона только использованием
символа/, так что он совпадает с закрывающим тегом </title />в html.
"< • *? >", также использует минимальное
выражение . *? для поиска всех тегов HTML в строке заголовка. Заменяя любые
совпадения пустой строкой "", re. sub () удаляет теги и возвращает только текст.
Второе регулярное выражение, строка
При правильном использовании регулярные выражения становятся очень
мощным инструментом . Та информация , с которой мы познакомили вас сейчас,
едва ли дает даже первое представление об их возможностях. За дополнитель­
ными сведениями о регулярных выражениях и об их использовании обращай­
Expressions: Regexes in Python~ (https:j/
realpython.com/regex-python/) на сайте Real Python.
тесь к курсу из двух частей «Regulaг
ПРИМЕЧАНИЕ
Веб-скрапинг может оказаться утомительной и монотонной работой . Каждый
сайт не похож на другие, и разметка
HTML часто
неидеальна. Более того, сайты
изменяются со времене,.;. Если программа веб-скрапинга работает сегодня,
это еще не значит, что она будет работать через год
-
или даже через неделю,
если на то пошло!
Упражнения
1.
HTML веб-страницы
по адресу http:j/olympus.realpython.org/profiles/dionysus.
2.
Используйте строковый метод . find () для вывода текста, следующего за
Name: и Favorite Color: (не включая начальные пробелы или завершающие
теги HTML, которые могут присутствовать в той же строке).
3.
Напишите программу, которая извлекает весь код
Повторите предыдущее упражнение с регулярными выражениями . В конце
каждого шаблона следует поставить знак < (начало тега
HTML) или символ
новой строки, а из полученного текста надо убрать все лишние пробелы
и символы новой строки при помощи строкового метода
16.2.
ИСПОЛЬЗОВАНИЕ ПАРСЕРА
. strip().
HTML
ДЛЯ ИЗВЛЕЧЕНИЯ ВЕБ-ДАННЫХ
Хотя регулярные выражения отлично подходят для поиска по шаблону, иногда
проще использовать парсер
HTML, специально разработанный для разбора
16.2. Использование парсера HTML для извлечения веб-данных
страниц
381
HTML. Для этой цели было написано много инструментов Python, но
Beautiful Soup.
начать стоит с библиотеки
Установка библиотеки Beautiful Soup
Чтобы установить
Beautiful Soup,
выполните следующую команду в своем
терминале:
$
pythonЗ
-m pip install beautifulsoup4
Выполните команду
pip show, чтобы просмотреть подробную информацию
о только что установленном пакете:
$ pythonЗ -m pip show beautifulsoup4
Name: beautifulsoup4
Version: 4.9.1
Summary: Screen-scraping library
Home-page: http://www.crummy.com/software/Beautifu1Soup/bs4/
Author: Leonard Richardson
Author-email: leonardr@segfault.org
license: МП
Location: c:\realpython\venv\lib\site-packages
Requires:
Required-by:
В частности, обратите внимание, что последней версией на момент написания
книги была версия
4.9.1.
Создание объекта BeautifulSoup
Введите следующую программу в новом окне редактора:
from bs4 import BeautifulSoup
from urllib.request import urlopen
url =
page
html
soup
"http://olympus.realpython.org/profiles/dionysus"
urlopen(url)
page.read().decode("utf-8")
BeautifulSoup(html, "html.parser")
Программа выполняет три операции.
1.
Открывает
URL http.j/olympus.realpython.org/profiles/dionysus
мощи функции
2.
urlopen()
из модуля
при по­
urllib. request.
Читает НТМL-код страницы в виде строки и присваивает ее переменной
html.
3.
Создает объект
Beauti fulSoup и присваивает его переменной soup.
ГЛАВА 16
382
Веб-программирование
Объект Beauti fulSoup, присвоенный переменной soup, создается с двумя аргу­
HTML, а второй,
ментами. В первом аргументе передается разбираемая разметка
строка
"html. parser", сообщает объекту, какой парсер должен использоваться
"html. parser" - это встроенный парсер HTML
во внутренней реализации.
языка
Python.
Использование объекта
BeautifulSoup
Сохраните и запустите приведенную выше программу. Когда она завершит
работу, вы сможете использовать переменную
парсинга содержимого
Например, объекты
soup в интерактивном окне для
html.
Beauti fulSoup содержат метод . get_text (), который мы
используем для извлечения всего текста из документа и автоматического уда­
ления всех тегов
HTML.
Введите следующий код в интерактивном окне
IDLE:
>>> print(soup.get_text())
Profile: Dionysus
Name: Dionysus
Hometown: Mount Olympus
Favorite animal: Leopard
Favorite Color: Wine
В выводе много пустых строк! Они появились из-за символов новой строки
в тексте документа
вым методом
HTML.
При необходимости вы можете удалить их строко­
. replace ().
HTML требуется получить только конкретный текст.
Beautiful Soup для извлечения текста и последующий вызов
метода . find() иногда оказываются проще работы с регулярными
Часто из документа
Использование
строкового
выражениями.
Тем не менее иногда сами теги
HTML
являются элементами, указывающими
на данные, которые требуется извлечь. Допустим, вы хотите получить
URL-
aдpeca всех изображений на странице. Эти ссылки содержатся в атрибуте
тегов
HTML <img>.
src
16.2. Использование парсера HTML для извлечения веб-данных
В таком случае можно воспользоваться функцией
find_all (),
383
возвращающей
список всех экземпляров этого конкретного тега:
>» soup.find_all("img")
[<img src="/static/dionysus.jpg"/>, <img src="/static/grapes.png"/>)
Функция возвращает список всех тегов <img> в НТМL-документе. Может по­
казаться, что объекты в списке являются строками, представляющими теги, но
в действительности это экземпляры класса Tag из библиотеки Beautiful Soup.
Объекты Tag предлагают простой интерфейс для работы с содержащейся в них
информацией.
Чтобы немного исследовать этот способ, для начала распакуем объекты
Tag из
списка:
»> imagel, image2 = soup.find_all("img")
Каждый объект
пом тега
Tag содержит свойство . name,
HTML:
которое возвращает строку с ти­
»> imagel.name
'img'
Чтобы обратиться к атрибутам
HTML объекта Tag, укажите имя атрибута в ква­
дратных скобках (так, как если бы атрибуты были ключами словаря).
Например, тег
<img src="/static/dionysus .jpg"/> содержит единственный атри­
src со значением" /static/dionysus. jpg". А тег ссылки <а href="https: //
realpython. com" target="_Ыank" >содержит два атрибута, href и target.
бут
Чтобы получить источники изображений на странице профиля
обращаетесь к атрибуту
src
Dionysus,
вы
в синтаксисе словарей, упоминавшемся выше:
>>> imagel["src"]
'/static/dionysus.jpg'
>>> image2["src")
'/static/grapes.png'
К некоторым тегам в документах
объектов
Tag.
HTML можно обращаться по именам свойств
<title> в документе можно вос­
Например, для получения тега
пользоваться свойством
. title:
»> soup.title
ctitle>Profile: Dionysusc/title>
384
ГЛАВА 16
Веб-nрограммирование
Обратившись к исходному коду профиля Dionysus (перейдите по адресу
olympus.realpython.org/profiles/dionysus,
http.j/
щелкните правой кнопкой мыши на
странице и выберите команду просмотра исходного кода страницы), вы увидите,
что тег
<ti tle>
записан в документе в следующем виде:
<title >Profile: Dionysus</title/>
Beautiful Soup автоматически чистит теги, удаляя лишние пробелы в открыва­
/ в закрывающем теге.
ющем теге и лишнюю косую черту
Также можно получить только строку, заключенную между тегами заголовка,
из свойства
. string объекта Tag:
>>> soup.title.string
'Profile: Dionysus'
Одна из самых полезных возможностей
Beautiful Soup -
поиск конкретных
разновидностей тегов, атрибуты которых соответствуют определенным значе­
ниям. Например, если вы хотите найти все теги <img>, у которых атрибут src
равен значению /static/dionysusjpg, передайте
. find_all ()
дополнительный
аргумент:
>>> soup.find_all("img", src="/static/dionysus.jpg")
[<img src="/static/dionysus.jpg"/>]
Пример выглядит искусственно, и, возможно, полезность этого приема сразу
не очевидна. Если вы потратите некоторое время, чтобы помониторить веб­
сайты и их исходный код, вы заметите, что многие из них имеют чрезвычайно
сложную структуру
HTML.
При веб-скрапинге нас часто интересуют конкретные фрагменты страницы.
Потратив немного времени на просмотр документа
HTML, вы сможете иденти­
фицировать теги с уникальными атрибутами, которые годятся для извлечения
нужных данных.
И тогда вместо того, чтобы полагаться на сложные регулярные выражения или
проводить поиск по документу с
. find (),
вы сможете напрямую обращаться
к конкретному интересующему вас тегу и извлекать нужные данные.
В некоторых случаях
Beautiful Soup
ности. Библиотекой lxml
не предоставляет нужной функциональ­
(https.j/lxml.de)
труднее пользоваться на первых по­
рах, но она обладает большей гибкостью при разборе документов
Beautiful Soup.
HTML,
чем
Возможно, вам стоит ознакомиться с ее возможностями, когда
вы будете уверенно чувствовать себя с
Beautiful Soup.
16.3. Работа с НТМL-формами
385
ПРИМЕЧАНИЕ
Парсеры HTML - такие, как Beautiful Soup, - сэкономят вам немало времени
и усилий при поиске конкретных данных на веб-страницах. Тем не менее ино­
гда НТМL-код написан настолько плохо, что даже сложный парсер (например,
Beautiful Soup) не может правильно интерпретировать теги HTML.
В таких случаях обычно приходится извлекать необходимую информацию
самостоятельно (то есть с использованием
.find()
и регулярных выражений).
Упражнения
1.
Напишите программу, которая извлекает весь НТМ L-код веб-страницы
http.j/olympus.realpython.org/profiles.
2.
Используя
Beautiful Soup, выделите список всех ссылок на странице HTML с именем а и получите значение атрибута
проведите поиск тегов
href для
3.
каждого тега.
Извлеките НТМL-код каждой страницы в списке
- добавьте полный
HTML) на каждой странице,
. get_text () из библиотеки Beautiful Soup.
путь к файлу и выведите текст (без тегов
используя метод
16.З. РАБОТА С НТМL-ФОРМАМИ
Модуль urllib, с которым вы работали ранее в этой главе, хорош для запроса со­
держимого веб-страницы. Однако в некоторых случаях получение нужного кон­
тента требует взаимодействия с веб-страницей. Например, пользователь должен
отправить форму или щелкнуть на кнопке, чтобы отобразить скрытое содержимое.
В стандартной библиотеке
Python нет встроенных средств для интерактивной
PyPI доступны многочисленные сторонние паке­
ты . К их числу принадлежит пакет MechanicalSoup (https.j/github.com/hickford/
Mechanica/Soup) - популярный и достаточно простой в использовании.
работы с веб-страницами, но в
В сущности, MechaпicalSoup устанавливает консольный браузер, то есть браузер
без графического интерфейса. Работой браузера можно управлять на программ­
ном уровне из программы
Установка
Python.
MechanicalSoup
MechanicalSoup можно установить при помощи pip в терминале:
$
pythonЗ
-m pip install MechanicalSoup
386
ГЛАВА 16
Веб-программирование
Команда pip show выводит более подробную информацию о пакете:
pythonЗ -m pip show mechanicalsoup
Name: MechanicalSoup
Version: 0.12.0
Summary: А Python library for automating interaction with websites
Home-page: https://mechanicalsoup.readthedocs.io/
Author: UNKNOWN
Author-email: UNKNOWN
License: МП
Location: c:\realpython\venv\lib\site-packages
Requires: requests, beautifulsoup4, six, lxml
Required-by:
$
В частности, обратите внимание, что на момент написания книги последняя
- 0.12.0. Чтобы библиотека Mechanica!Soup была загружена и опознана
после установки, необходимо закрыть и перезапустить сеанс IDLE.
версия
Создание объекта Browser
Введите следующий фрагмент в интерактивном окне
IDLE:
>>> import mechanicalsoup
>>> browser = mechanicalsoup.Browser()
Объекты Browser представляют консольный браузер. Их используют для за­
проса страницы из интернета, для чего
U RL-aдpec
передается методу
. get ( ) :
>>> url = "http://olympus.realpython.org/login"
>>> page = browser.get(url)
Переменной
page присваивается объект Response, в котором хранится ответ,
полученный от браузера на запрос URL-aдpeca:
»> page
<Response [200]>
Число 200 представляет код состояния, возвращенный запросом. Код состояния
200 означает, что запрос был успешным. Неуспешный запрос может вернуть
код состояния 404, если URL не существует, или код состояния 500, если при
обработке запроса произошла ошибка на сервере.
Mechanica!Soup
использует
проса. Переменная
Beauti fu lSoup:
Beautiful Soup
для парсинга НТМL-кода из за­
page содержит атрибут . soup, представляющий объект
16.3. Работа с НТМL-формами
387
>>> type(page.soup)
<class 'bs4.Beautifu1Soup'>
Чтобы просмотреть НТМL-код, проверьте атрибут
. soup:
»> page.soup
<html>
<head>
<title>Log In</title>
</head>
<body bgcolor="yellow">
<center>
<br/><br/>
<h2>Please log in to access Mount Olympus:</h2>
<br/><br/>
<form action="/login" method="post" name="login">
Username: <input name="user" type="text"/><br/>
Password: <input name="pwd" type="password"/><br/><br/>
<input type="submit" value="Submit"/>
</form>
</center>
</body>
</html>
Обратите внимание: страница содержит тег <form> с элементами <input> для
ввода имени пользователя и пароля.
Отправка данных формы
с помощью MechanicalSoup
Откройте страницу http.j/olympus.realpython.org/login в браузере и просмотрите
ее, прежде чем двигаться дальше. Попробуйте ввести случайное имя пользовате­
ля и пароль. Если данные были указаны неправильно, в нижней части страницы
выводится сообщение «Wгong
username or password!».
Но если задать правильные данные (имя пользователя
zе us
и пароль
ThunderDude), то вы будете перенаправлены на страницу /profiles.
Следующий пример показывает, как использовать
нения и отправки этой формы средствами
MechanicalSoup для запол­
Python.
Важной частью НТМL-кода является форма ввода регистрационных дан­
ных
-
то есть все, что находится внутри тегов
<form>. Тег <form> на странице
содержит атрибут name, которому присвоено значение login. Форма содержит
два элемента
<input> с именами user и pwd. Третий элемент <input> определяет
кнопку отправки данных
Submit.
388
ГЛАВА 16
Веб-программирование
Итак, вы знаете структуру формы авторизации, а также данные, которые в нее
следует ввести. Теперь рассмотрим программу, которая заполняет форму и от­
правляет ее.
Введите в новом окне редактора следующий код:
import mechanicalsoup
# 1
browser = mechanicalsoup.Browser()
url = "http://olympus.realpython.org/login"
browser.get(url)
login_page
login_html = login_page.soup
# 2
form = login_html.select("form")[0]
"zeus"
form.select("input")[0]["value"]
form.select("input")[l]["value"] = "ThunderDude"
# 3
profiles_page
=
browser.submit(form, login_page.url)
Сохраните файл и нажмите
FS, чтобы запустить его. Чтобы убедиться в том, что
данные были успешно приняты, введите следующую команду в интерактивном
окне:
>>> profiles_page.url
'http://olympus.realpython.org/profiles'
Разобьем приведенный выше пример на несколько этапов.
1.
Создается экземпляр
страницы
Browser,
который далее используется для запроса
http.jJolympus.realpython.org!login.
сваивается переменной
login_html
2. login_html. select ( "form")
НТМL-код страницы при­
с использованием свойства
. soup.
<form> на
<form>, к форме
возвращает список всех элементов
странице. Так как страница содержит только один элемент
можно обратиться через элемент списка с индексом 0. Следующие две
строки выбирают поля для имени пользователя и пароля, а также задают
им значения
3.
"zeus"
и
"ThunderDude"
соответственно.
browser. submi t ().
Методу передаются два
аргумента: объект формы и URL-aдpec страницы
login_page, к которому
Форма отправляется вызовом
вы обращаетесь через
login_page. url.
В интерактивном окне убедитесь, что при отправке данных успешно происходит
/profiles. Если что-то пошло не так, то значение
profiles_page. url останется прежним - "http://olympus.realpython.org/login".
перенаправление на страницу
16.3. Работа с НТМL-формами
389
ПРИМЕЧАНИЕ
Хакеры могут использовать подобные автоматизированные программы для
подбора паролей методом полного перебора {брутфорса, brute force): про ­
грамма быстро проверяет разные комбинации имени и пароля, пока не найдет
работающий вариант.
Имейте в виду, что это считается серьезным правонарушением
- обнаружив,
что от вас поступает слишком много запросов, почти все современные сайты
блокируют вам доступ и сообщают ваш IР-адрес надзорным органам. Даже
не пытайтесь!
profiles_page задано. Посмотрим, как на про­
URL-aдpec каждой ссылки на странице /profiles.
Итак, значение переменной
граммном уровне получить
Для этого снова используем метод
. select( ), но
на этот раз передадим строку
"а" для выбора всех якорных элементов <а> на странице:
>>> links = profiles_page.soup.select("a")
Далее можно перебрать все ссылки и вывести их атрибут
href:
>>> for link in links:
address = link["href"]
text = link.text
print(f"{text}: {address}")
Aphrodite: /profiles/aphrodite
Poseidon: /profiles/poseidon
Dionysus: /profiles/dionysus
URL-aдpeca, содержащиеся в атрибутах href, являются относительными; от
них не будет пользы, если вы захотите перейти к ним позднее при помощи
MechanicalSoup.
Если вам известен полный URL-aдpec , то из него можно выделить часть,
необходимую для построения абсолютного адреса. В нашем случае базо­
вый URL-aдpec
- http:j/olympus.realpython.org.
После этого можно выпол­
нить конкатенацию базового URL-aдpeca с относительным
href:
>» base_url = "http://olympus.realpython.org"
>>> for link in links:
address = base_url + link["href"]
text = link.text
print(f"{text}: {address}")
URL из атрибута
390
ГЛАВА 16
Веб-программирование
Aphrodite: http://olympus.realpython.org /profiles/aphrodite
Poseidon: http://olympus.realpython.org/ profiles/poseidon
Dionysus: http://olympus.realpython.org/ profiles/dionysus
. get (), . select () и . submi t (), можно сделать много по­
MechanicalSoup способна на гораздо большее. За до­
полнительной информацией о MechanicalSoup обращайтесь к официальной
документации ( https.j/mechanicalsoup.readthedocs.io/en/staЬle/).
Используя только
лезного. Но библиотека
Упражнения
1.
Используйте
пользователя
MechanicalSoup для предоставления правильного имени
("zeus") и пароля ("ThunderDude") форме авторизации,
находящейся по адресу
2.
http.j/olympus.realpython.org/login.
Выведите заголовок текущей страницы, чтобы убедиться, что вы были
перенаправлены на страницу
3.
Используйте
/profiles.
MechanicalSoup для возвращения к странице входа (верни­
тесь к предыдущей странице).
4.
Введите в форме авторизации неправильное имя пользователя и пароль,
а затем найдите в НТМL-коде возвращенной веб-страницы текст «Wгong
username or password!~, чтобы убедиться в том, что попытка входа за­
вершилась неудачей.
16.4. ВЗАИМОДЕЙСТВИЕ С ВЕБ-САЙТАМИ
В РЕАЛЬНОМ ВРЕМЕНИ
Иногда возникает необходимость получить данные в реальном времени с сайта,
где информация постоянно обновляется.
В те времена, когда вы еще не начинали изучать программирование на языке
Python, вам пришлось бы сидеть перед браузером и постоянно обновлять стра­
ницу, чтобы узнать, не появился ли обновленный контент. Но теперь процесс
можно автоматизировать методом .get() объекта MechanicalSoup Browser.
Откройте браузер и перейдите на
http.j/olympus.realpython.org/dice.
Страница
моделирует бросок шестигранного кубика, результат обновляется при каждом
обновлении браузера. Мы напишем программу, которая непрерывно занимается
скрапингом страницы, чтобы извлечь новый результат.
16.4. Взаимодействие с веб-сайтами в реальном времени
391
Прежде всего необходимо определить, какой элемент страницы содержит
результат броска. Щелкните правой кнопкой мыши в любой точке страницы
и выберите команду просмотра исходного кода страницы. [де-то в середине
НТМL-кода располагается тег
<h2>, который выглядит так:
<h2 id="result">4</h2>
У вас текст тега <h2> может быть другим, и все же именно этот элемент страницы
понадобится для извлечения результата.
ПРИМЕЧАНИЕ
В этом примере можно легко убедиться, что на странице существует всего
id="result" . Хотя предполагается, что атрибут id уникален, на
один элемент с
практике всегда следует проверять, что интересующий вас элемент иденти­
фицируется однозначно .
Для начала напишем простую программу, которая открывает страницу
/dice,
извлекает результат и выводит его на консоль:
import mechanicalsoup
browser = mechanicalsoup.Browser()
page = browser . get("http://olympus.realpython.org/dice")
tag = page.soup.select("#result")[0]
result = tag.text
print(f"The result of your dice roll is: {result}")
Beauti fulSoup используется для поиска
элемента с id=result. Строка "#result", передаваемая .select(), использует
селектор CSS 10 для обозначения того, что resul t является значением id.
В этом примере метод
. select ()
объекта
Чтобы периодически получать новый результат, необходимо создать цикл,
загружающий страницу при каждой итерации. Таким образом, фрагмент под
строкой
browser = mechanicalsoup. Browser ()
в приведенном выше коде должен
перейти в тело цикла.
Наш пример будет получать результаты четырех бросков с десятисекундными
интервалами. Для этого последняя строка кода должна дать команду
приостановить выполнение на
из модуля
Python time.
10
секунд. Задача решается функцией
Python
sleep()
Она получает один аргумент, представляющий про­
должительность паузы в секундах.
ГЛАВА
392
16
Веб-программирование
Следующий пример показывает, как работает sleep( ):
import time
print("I'm about to wait for five seconds ... ")
time.sleep(5)
print("Done waiting!")
При выполнении этого кода сообщение "Done waiting!" появится только через
пять секунд после выполнения первой функции
print ().
В примере с броском кубика необходимо передать sleep() число 10. Обновлен­
ная версия программы:
import time
import mechanicalsoup
browser = mechanicalsoup.Browser()
for i in range(4):
page = browser.get("http://olympus.realpython.org/dice")
tag = page.soup.select("#result")(0]
result = tag.text
print(f"The result of your dice roll is: {result}")
time.sleep(l0)
При запуске программы на консоли немедленно появляется первый результат.
Через
10 секунд выводится второй результат, потом третий и, наконец, четвер­
тый. Что произойдет после вывода четвертого результата?
Программа продолжит работать еще
Конечно, так и должно быть
-
10 секунд до того,
как завершить работу!
ведь вы сами ей это приказали! Тем не менее лиш­
няя пауза только расходует время. Чтобы этого не происходило, воспользуйтесь
командой
if
для выполнения
time. sleep()
только для первых трех запросов:
import time
import mechanicalsoup
browser
=
mechanicalsoup.Browser()
for i in range(4):
page = browser.get("http://olympus.realpython.org/dice")
tag = page.soup.select("#result")(0]
result = tag.text
print(f"The result of your dice roll is: {result}")
#
Подождать
if i <
10
секунд,
З:
time.sleep(10)
если
это не последний запрос
16.5. Итоги и дополнительные ресурсы
393
Применяя подобные приемы, можно извлекать информацию с веб-сайтов, пери­
одически обновляющих свои данные. Тем не менее следует помнить, что запрос
страницы несколько раз подряд за короткий промежуток времени может рас­
сматриваться как подозрительное и даже злонамеренное использование сайта.
ВАЖНО!
Многие сайты публикуют документ с условиями использования {Тerms
of Use).
Часто ссылка на него размещается в подвале главной страницы сайта.
Всегда читайте этот документ, прежде чем пытаться извлекать данные. Если вы
не нашли условий использования, попробуйте связаться с владельцем сайта
и узнать, нет ли у него правил, касающихся частых запросов.
Несоблюдение условий использования может привести к блокировке вашего
IР-адреса, так что будьте внимательны и уважайте права других!
Чрезмерное количество запросов иногда вызывает даже сбой сервера. Понятно,
почему так много сайтов обращает внимание на массовые запросы к своему
серверу! Всегда проверяйте условия использования и уважительно относитесь
к ним, если решите бомбардировать сайт запросами.
Упражнение
Повторите пример из этого раздела с извлечением результата броска, но также
включите текущее время его получения с веб-страницы. Это время можно по­
лучить из части строки внутри тега <р>, следующего после результата броска
в НТМL-коде страницы.
16.5. ИТОГИ И ДОПОЛНИТЕЛЬНЫЕ РЕСУРСЫ
Хотя данные с веб-серверов можно парсить средствами стандартной библиотеки
Python, в Ру PI существует множество инструментов, упрощающих этот процесс.
В этой главе я показал, как:
•
запрашивать веб-страницы при помощи встроенного модуля
Python
urllib;
•
парсить
HTML средствами Beautiful Soup;
•
взаимодействовать с веб-формами с использованием MechanicalSoup;
•
многократно запрашивать данные с сайта для проверки обновлений.
394
ГЛАВА 16
Веб-программирование
Писать программы автоматизированного извле'-lения веб-данных интересно,
и в интернете хватает разных любопытных проектов.
Но помните: не все хотят, '-lтобы вы извлекали данные с их неб-серверов. Всегда
проверяйте условия использования
(Terms of Use) сайта, прежде чем браться
за такую задачу, и сознательно выбирайте частоту неб-запросов, чтобы не пере­
гружать сервер лишним трафиком.
ИНТЕРАКТИВНЫЙ ТЕСТ
К этой главе прилагается бесплатный интерактивный тест для проверки усво­
енных вами знаний. Тест доступен на телефоне или компьютере:
realpythoп.com!quizzes/pybasics-web
Дополнительные ресурсы
За дополнительной информацией о взаимодействии с веб-сайтами средствами
Python обращайтесь
•
к следующим ресурсам :
«Beautiful Soup: Build а Web ScraperWith Python» (https:j/realpython.com/
beautiful-soup-web-scraper-python/)
•
«API Integration in Python» (https:j/realpython.com/api-integration-inpython/)
ГЛАВА
17
Научные вычисления
и построение графиков
Python -
один из ведущих языков программирования для научных вычислений
и теории обработки данных
Популярность
(data science).
Python в этой области частично обусловлена обилием сторонних
пакетов для обработки и визуализации данных, доступных в Ру PI.
В экосистеме
Python можно найти инструменты на любой случай - от работы
с большими массивами данных до их визуализации на графиках и диаграммах.
В этой главе вы научитесь:
•
работать с массивами данных, используя
•
строить диаграммы и графики с помощью
NumPy;
Matplotlib.
ПРИМЕЧАНИЕ
Предполагается, что у вас есть некоторый опыт работы с матрицами. Если вы
не знакомы с матрицами или не интересуетесь научными вычислениями, эту
главу можно смело пропустить.
Итак, за дело!
17.1. ИСПОЛЬЗОВАНИЕ NUMPY ДЛЯ
ВЫЧИСЛЕНИЙ
МАТРИЧНЫХ
В этом разделе вы научитесь хранить и обрабатывать данные в матричном виде
при помощи пакета NumPy
чего нужен
NumPy.
(http.j/www.numpy.org/).
Но сначала выясним, для
396
Научные вычисления и построение графиков
ГЛАВА 17
Если вы изучали курс линейной алгебры, то, возможно, вспомните, что матри­
ца
-
это прямоугольная таблица, которая представляет собой совокупность
строк и столбцов, на пересечении которых находятся ее элементы
На «чистом» языке
Python
-
числа.
матрицу можно создать в виде списка списков:
>» matrix = [[1, 2, 3), [4, 5, 6), [7, 8, 9))
На первый взгляд, такое решение неплохо работает. Вы можете обращаться к от­
дельным элементам матрицы по индексам. Например, обращение ко второму
элементу первой строки матрицы выглядит так:
»> matrix[0] [1]
2
Допустим, вы хотите умножить каждый элемент матрицы на
2. Для этого надо
написать вложенный цикл for, который перебирает все элементы одной строки
матрицы:
>>> for row in matrix:
for i in range(len(row)):
row[i] = row[i] * 2
>» matrix
[[2, 4, 6), [8, 10, 12), [14, 16, 18))
Вроде бы несложно, но суть в том, что на «чистом» языке
Python
вам придется
проделать значительную работу с нуля для реализации даже очень простых
операций линейной алгебры. Для работы с многомерными массивами
NumPy
предоставляет практически всю необходимую функциональность. Такое реше­
ние получается более эффективным, чем реализация на «чистом» Python. Пакет
NumPy написан на языке С и использует сложные алгоритмы для эффективного
выполнения вычислений.
ПРИМЕЧАНИЕ
Область применения
NumPy не ограничивается научными вычислениями.
Допустим, вы проектируете игру и вам нужны простые средства для мани ­
пуляций со строками и столбцами. Массивы
NumPy идеально подходят для
хранения двумерных данных.
Установка
NumPy
Работу с пакетом
$
pythonЗ
NumPy
начнем с его установки при помощи
-m pip install numpy
pip:
17.1. Использование NumPy для матричных вычислений
397
После того как установка NumPy будет завершена, можно вывести подробную
информацию о пакете командой
pip
show:
$ python3 -m pip show numpy
Name: numpy
Version: 1.18 . 5
Summary: NumPy : array processing for numbers, strings,
records, and objects.
Home-page: http://www.numpy.org
Author: Travis Е. Oliphant et al.
Author-email: None
License: BSD
Location: c:\realpython\venv\lib\site-packages
Requires:
Required-by:
В частности, из вывода можно узнать, что новейшей версией на момент напи­
сания книги была версия
1.18.5.
Создание массива
NumPy
После того как пакет NumPy будет установлен, мы создадим в нем матрицу из
первого примера этого раздела. Матрицы в
объекта
NumPy являются экземплярами
ndarray (сокращение от n-dimensional array - п-мерный массив).
ПРИМЕЧАНИЕ
N-мерным массивом называется массив с п измерениями . Например, одно­
мерный массив представляет собой список, а двумерный
- матрицу. Массивы
могут иметь три, четыре и более измерений.
В этом разделе мы рассмотрим массивы с одним или двумя измерениями .
Для создания объекта ndarray можно воспользоваться псевдонимом array. Объ­
екты
array инициализируются списками списков, так что для создания матрицы
NumPy можно поступить так:
из первого примера в виде массива
>>> import numpy as пр
»> matrix = np.array([[l, 2, 3], [4, 5,
»> matrix
б],
[7, 8, 9]])
array([[l, 2, 3],
[ 4, 5, 6]'
[7, 8, 9]])
Обратите внимание:
NumPy выводит матрицу в удобочитаемом формате. Это
print () :
происходит даже при выводе матрицы вызовом
ГЛАВА 17
398
Научные вычисления и построение графиков
>>> print(matrix)
[[1 2 3]
[4 5 6]
[7 8 9]]
К отдельным элементам массива можно обращаться так же, как и к элементам
списка списков:
»> matrix[0] [1]
2
Кроме того, можно обращаться к элементам всего с одной парой квадратных
скобок, с разделением индексов запятой:
»> matrix[0, 1]
2
Чем же массивы
NumPy
Прежде всего массивы
принципиально отличаются от списков
Python?
NumPy могут хранить только объекты одного типа - на­
Python
пример, числа в матрице из рассмотренного примера, тогда как списки
могут хранить объекты смешанных типов.
Посмотрим, что произойдет при попытке создания массива с разнотипными
элементами:
>» np.array([[l, 2, 3],
array([['l', '2', '3'],
['а',
'Ь',
'с']],
["а",
"Ь",
"с"]])
dtype=' <Ull' )
NumPy не выдает ошибки. Вместо этого каждый элемент преобразуется в строку.
Выражение
dtype=' <Ull' в этом выводе означает, что массив позволяет хранить
только строки в формате Юникод длиной не более
11
байт.
Автоматические преобразования типов данных иногда помогают, но они также
могут стать потенциальным источником проблем
-
типы данных преобразуются
не так, как вы ожидали.
Обычно рекомендуется выполнять любые преобразования типов до инициа­
лизации объекта
array.
Так вы можете быть уверены в том, что тип данных,
хранящихся в массиве, соответствует ожиданиям.
В
NumPy
каждое измерение массива называется осью
(axis).
Матрицы, о ко­
торых мы говорили ранее, имели две оси. Массивы с двумя осями называются
двумерными массивами.
17.1. Использование NumPy для матричных вычислений
399
Пример трехмерного массива:
>>> matrix = np.array([
[[1, 2, З], [4, 5, 6]],
[[7, 8, 9], [10, 11, 12]],
[[13, 14, 15], [16, 17, 18]]
])
Чтобы обратиться к элементу этого массива, необходимо указать три индекса:
>>> matrix[0][1][2]
6
>>> matrix[0, 1, 2]
6
Если вы считаете, что создание такого трехмерного массива выглядит странно,
далее в этом разделе я покажу более правильный способ создания массивов
высокой размерности.
Операции с массивами
После того как объект
NumPy
array будет создан, вы
сможете пустить в ход всю мощь
и выполнять с массивом различные операции.
Вспомните, как в предыдущем примере мы написали вложенный цикл
умножения каждого элемента матрицы на
простым умножением
2.
объекта array на 2:
»>А= np.array([[l, 2,
»> 2 * А
array([[ 2, 4, 6],
[ 8, 10, 12],
[14, 16, 18]])
З],
В
for для
NumPy эта операция выполняется
[4, 5, 6], [7, 8, 9]])
Операции между двумя матрицами выполняются поэлементно, то есть оператор
применяется к соответствующим элементам матрицы:
np.array( [ [5, 4,
>» в
В - А
»> с
»> с
array( [ [ 4, 2, 0],
[ З, 1, -1],
[ 2, 0, -2]])
З],
[7, 6, 5] J [9, 8, 7]])
Обратите внимание: С[0] [0] равно В[0] [0] - А[0] [0]. То же относится ко всем
остальным парам индексов. Все базовые арифметические операторы
/ -
в отношении массивов выполняются поэлементно.
- +, -, *,
400
ГЛАВА
17
Научные вычисления и построение графиков
Например, при умножении двух массивов оператором
* произведение двух
матриц не вычисляется:
»>
»>
np.array([[l, 1, 1], [1, 1, 1], [1, 1, 1]])
А=
*
А
А
array( [ [ 1, 1, 1],
[1, 1, 1],
[1, 1, 1]])
Для вычисления настоящего произведения матриц
(https.j/rn.wikipedia.org/
wiki/Умножение_матриц) используется оператор@:
»>
А
@А
array( [ [З,
З,
З],
[З,
З,
З],
[З,
З,
З]])
Оператор @появился в
Python,
Python 3.5, и, если
вы используете более старую версию
матрицы придется умножать другим способом.
NumPy предоставляет
функцию matmul () для умножения двух матриц:
>>> np.matmul(matrix, matrix)
array([[З,
З,
З],
[З,
З,
З],
[З,
З,
З]])
Оператор@ использует функцию np.matmul() во внутренней реализации, по­
этому принципиальных различий между этими двумя способами нет.
Ниже перечислены другие распространенные операции с массивами:
»> matrix
>>> #
=
np.array([[l, 2,
З],
[4, 5,
б],
[7, 8, 9]])
Получение кортежа длин осей
»> matrix.shape
( 3, 3)
>>> # Получение массива
>>> matrix.diagonal()
array( [1, 5, 9])
диагональных
>>> # Получение одномерного массива
>>> matrix.flatten()
array( [1, 2, З, 4, 5, 6, 7, 8, 9])
>>> # Транспонирование
>>> matrix.transpose()
array([[l, 4, 7],
элементов
всех записей
17.1. Использование NumPy для матричных вычислений
401
[2, 5, 8],
[З, б, 9]])
»> # Вычисление
»> matrix.min()
минимума
1
»> # Вычисление
»> matrix.max()
максимума
9
»>
#
Вычисление
среднего значения
всех элементов
>>> matrix.mean()
5.0
»> # Вычисление
»> matrix.sum()
суммы всех элементов
45
А теперь рассмотрим некоторые способы создания новых массивов на базе
старых.
Стыковка и изменение формы массивов
Если размеры осей совпадают, два массива можно состыковать по вертикали
методом пр. vstack() или по горизонтали методом пр.
»>А
»>
В
hstack():
np.array([[l, 2, З], [4, 5, 6]])
np.array([[7, 8, 9], [10, 11, 12]])
>» np.vstack([A, В])
array([[ 1, 2, З],
[4,5,б],
[7,8,9],
[10, 11, 12]])
>>> np.hstack([A, В])
array([[ 1, 2, 3, 7, 8, 9],
[ 4, 5, б, 10, 11, 12]])
Также можно изменить форму массивов методом пр.
>>> A.reshape(б, 1)
array( [ [1],
[2],
[З],
[ 4],
[5],
[ б]])
reshape():
402
ГЛАВА 17
Научные вычисления и построение графиков
Следует заметить, что
. reshape()
возвращает новый массив, а не изменяет ис­
ходный массив на месте.
Общий размер массива после изменения формы должен соответствовать размеру
исходного массива. Например, выполнить
matrix. reshape( 2, 5)
невозможно:
>>> A.reshape(2, 5)
Traceback (most recent call last):
File "cstdin>", line 1, in cmodule>
ValueError: cannot reshape array of size 6 into shape (2, 5)
В этом случае мы пытаемся изменить форму массива с девятью элементами
и превратить его в массив с двумя столбцами и пятью строками. Такой массив
содержит десять элементов.
np. reshape() особенно полезен в сочетании с np. arange() - эквивален­
Python range() в NumPy. Главное различие заключается в том,
что np. arange() возвращает объект array:
Метод
том функции
>>> nums = np.arange(l, 10)
»> nums
array([l, 2, 3, 4, 5, 6, 7, 8, 9])
Диапазон
np. arange()
начинается с первого аргумента и завершается непосред­
ственно перед вторым аргументом. Таким образом,
np. а range ( 1, 10) возвращает
массив, содержащий числа от 1до9.
Совместно
np. arange() и np. reshape() предоставляют полезный способ со­
здания матрицы:
>>> matrix = nums.reshape(3, 3)
»> matrix
array([[l, 2, 3],
[4, 5, 6],
[7, 8, 9]])
Это даже можно сделать в одной строке, объединив вызовы
и
np. reshape()
np. arange ()
в цепочку:
>>> np.arange(l, 10).reshape(3, 3)
array([[l, 2, 3],
[4, 5, 6],
[7, 8, 9]])
Такой способ создания матриц особенно полезен для создания массивов высо­
кой размерности. Пример построения трехмерного массива с использованием
np.array()
и
np.reshape():
17 .1. Использование NumPy для матричных вычислений
403
>>> np.arange(l, 13).reshape(3, 2, 2)
array([[[ 1, 2],
[ з, 4]],
[ [ 5, 6],
[ 7, 8]],
[[ 9, 10],
[11, 12]]])
Конечно, не каждый многомерный массив может быть построен из последова­
тельного списка чисел. В таких случаях часто проще создать одномерный список
элементов, а затем привести массив к нужной форме вызовом пр.
reshape ():
»> arr = np.array([l, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23])
>>> arr.reshape(3, 2, 2)
array([[[ 1, 3],
[ 5, 7]],
[[ 9, 11],
[13, 15]],
[[17, 19],
[21, 23]]])
В списке, передаваемом пр. array() в приведенном примере, разность в любой
паре равна
2.
Для упрощения создания массивов такого рода можно передать
пр. araпge () необязательный третий аргумент с именем
stride:
>>> np.arange(l, 24, 2)
array([ 1, З, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23])
Перепишем предыдущий пример так, чтобы пр. araпge() передавался аргумент
stride:
>>> np.arange(l, 24, 2).reshape(3, 2, 2)
array([[[ 1, 3],
[ 5, 7]],
[[ 9, 11],
[13, 15]],
[[17, 19],
[21, 23]]])
В этом разделе я познакомил вас с несколькими способами создания и об­
работки многомерных массивов с использованием структуры данных
array.
NumPy
Однако вы получили лишь очень поверхностное представление о том,
ГЛАВА
404
17
Научные вычисления и построение графиков
что можно сделать с
NumPy!
В конце главы вы найдете ссылки на материалы,
которые помогут вам углубить ваши знания о
NumPy.
Упражнения
1.
Используйте np.arange() и np.reshape() для создания массива
3
2.
х
3 с именем А,
содержащего числа от
NumPy
3 до 11.
Выведите минимальное, максимальное и среднее значение по всем эле­
ментам А .
3.
Возведите в квадрат каждый элемент А, используя оператор
**. Сохраните
результаты в массиве с именем В.
4.
Пристыкуйте А к В вызовом np. vstack( ), после чего сохраните результаты
в массиве с именем С.
5.
Используйте оператор @для вычисления произведения матриц С и А.
6.
Измените форму с до размеров
17 .2.
3
х
3
х
2.
ПОСТРОЕНИЕ ГРАФИКОВ
С ПОМОЩЬЮ
MATPLOTLIB
В предыдущем разделе я показал, как работать с массивами данных средствами
NumPy.
Хотя пакет
NumPy
удобен для работы с данными, большие массивы
чисел обычно плохо воспринимаются пользователями . Лучше наглядно пред­
ставлять такие данные в виде графиков или диаграмм .
В этом разделе я кратко расскажу о
Matplotlib -
одном из популярных пакетов
для быстрого создания двумерных графиков.
ПРИМЕЧАНИЕ
Если вам доводилось создавать графики в
MATLAB, вы обнаружите, что
Matplotlib очень на него похож.
Сходство между MATLAB и Matplotlib неслучайно. Интерфейс создания диа­
грамм
MATLAB напрямую происходит от Matplotlib. Даже если вы еще не
Matplotlib, то, скорее всего, создание диаграмм в Matplotlib
пользовались
покажется вам простым и понятным.
Итак, за дело!
17.2. Построение графиков с помощью Matplotlib
Установка
Установим
405
Matplotlib
Matplotlib из терминала следующей
командой:
$ python3 -m pip install matplotlib
После этого вы можете просмотреть информацию о пакете командой
pip show:
$ python3 -m pip show matplotlib
Name: matplotlib
Version: 3.2.1
Summary: Python plotting package
Home-page: http://matplotlib.org
Author: John D. Hunter, Michael Droettboom
Author-email: matplotlib-users@python.org
License: BSD
Location: c:\realpython\venv\lib\site-packages
Requires: python-dateutil, pytz, kiwisolver, numpy,
cycler, six, pyparsing
Required-by:
В частности, обратите внимание на то, что новейшей версией на момент на­
писания книги была версия
3.2.1.
Построение простейших графиков
с использованием pyplot
Пакет
Matplotlib предоставляет два разных способа создания
Первый и самый простой способ использует интерфейс
интерфейс сразу кажется пользователям
Второй способ построения графиков в
тированном
API.
графиков.
pyplot. Именно этот
MATLAB знакомым.
Matplotlib основан на объектно-ориен­
Объектно-ориентированный подход предоставляет больше
возможностей для управления графиками, чем интерфейс
pyplot. Тем не менее
его концепции обычно более абстрактны.
В этом разделе вы освоите интерфейс
pyplot
и научитесь строить эффектные
графики с минимальными затратами времени.
Начнем с создания простого графика. Введите следующий фрагмент в новом
окне редактора
IDLE:
from matplotlib import pyplot as plt
plt.plot( [1, 2, 3, 4, 5))
plt.show()
Научные вычисления и построение графиков
ГЛАВА 17
406
Сохраните программу и запустите ее клавишей
FS.
На экране появится новое
окно со следующим графиком:
5.0
4.5
4.0
3.5
3.0
2.5
2.0
1.5
1.0
о.о
Вызов
0.5
1.0
1.5
2.0
plt. plot ( [ 1, 2, 3, 4, 5]) строит
1), (1, 2), (2, 3), (3, 4) и (4, 5).
2..5
3.0
3.5
4.0
график с линией, проходящей через
точки (О,
Список
[1, 2, З, 4, 5 ], переданный plt. plot(), представляет координаты точек
на графике. Так как значения хне заданы,
индексы элементов списка
-
Matplotlib автоматически использует
1, 2, 3 и 4, так как в Python
в данном случае О,
нумерация индексов начинается с О.
Функция
pl t. plot ()
строит график, но на экране при этом никакая информа­
ция не появляется. Чтобы отобразить график на экране, необходимо вызвать
plt.show().
Также можно задать значения х для точек вашего графика, для чего следует
передать
plt. plot ()
два списка. Когда
plt. plot () передаются
- значения у.
первый список задает значения х, а второй
В окне редактора приведите программу к следующему виду:
from matplotlib import pyplot as plt
XS =
[1, 2, 3,
4,
5)
два аргумента,
17.2. Построение графиков с помощью Matplotlib
407
ys = [2, 4, 6, 8, 10]
plt.plot(xs, ys)
plt. show()
Сохраните обновленную программу и нажмите
FS.
На экране должен появиться
следующий график:
10
9
8
7
б
s
4
3
2
1.0
l.S
2.0
2.S
3.0
3.S
4.0
4.S
s.o
Обратите внимание: метки на осях теперь представляют новые координаты
точек графика.
Возможности
plot ()
не ограничиваются рисованием линий. На приведен­
ных выше графиках получилось, что все точки находятся на прямой линии.
По умолчанию при нанесении точек вызовом
plot ()
каждая пара соседних
точек соединяется отрезком прямой.
Обновите значения х и у так, чтобы точки не располагались на прямой:
from matplotlib import pyplot as plt
xs
ys
[1, 2, 3, 4, 5]
[3, -1, 4, 0, 6]
plt.plot(xs, ys)
plt. show()
Научные вычисления и построение графиков
ГЛАВА 17
408
Сохраните файл и нажмите клавишу
1.0
У
1.5
2.0
FS. На экране появится следующий график.
2.5
3.0
3.5
4.0
4.5
5.0
plot () имеется необязательный параметр форматирования, который исполь­
зуется для задания цвета и стиля линий или точек.
Например, следующая программа передает в параметре форматирования строку
"g-o":
from matplotlib import pyplot as plt
plt.plot([2, 4, 6, 8, 10], "g-o")
plt. show()
Буква
g
в
а буква о
-
"g-o"
задает зеленый цвет
(green),
знак
-
задает сплошную линию,
пометку точек на линии кругами.
о.о
0.5
1.0
1.5
2.0
2.5
3.0
3.5
4.0
17.2. Построение графиков с помощью Matplotlib
409
Полный список всех комбинаций форматирования вы найдете в документации
Matplotlib ( https.j/matplotlib.org/2 .0.2/api/pyplot_ api.html).
Рисование нескольких графиков в одном окне
Если вам нужно вывести несколько графиков в одном окне, вызовите
plot ()
несколько раз.
Например, следующая программа рисует два графика:
from matplotlib import pyplot as plt
plt.plot([l, 2, 3, 4, 5])
plt.plot([l, 2, 4, 8, 16])
plt.show()
Сохраните программу в новом окне редактора и нажмите
FS,
чтобы увидеть
результат.
lб
14
12
10
в
б
4
2
о.о
o.s
1.0
l.S
2.0
2.S
3.0
3.S
4.0
Обратите внимание: графики выводятся разными цветами. Если вы хотите
управлять внешним видом каждого графика, передайте строки форматирования
plot ()
в дополнение к значениям х и у:
from matplotlib import pyplot as plt
plt.plot([l, 2, 3, 4, 5], "g-o")
plt.plot([l, 2, 4, 8, 16), "Ь-л")
plt. show()
ГЛАВА 17
41 О
Научные вычисления и построение графиков
Теперь графики выводятся синим и зеленым цветом с выделением точек.
lб
14
12
10
8
6
4
2
о.о
o.s
1.0
L5
2.0
2.S
3.0
3.5
4.0
А теперь рассмотрим некоторые возможности построения графиков по другим
типам источников данных.
Вывод на графике данных из массивов NumPy
До настоящего момента мы хранили данные для графика в классических спис­
В реальном мире для хранения данных, вероятно, будет исполь­
зоваться что-то вроде массива NumPy. К счастью, Matplotlib хорошо работает
ках
Python.
с объектами
array.
Например, вместо списка можно воспользоваться функцией
NumPy arange( ),
array
чтобы определить данные для графика и передать полученный объект
при вызове
plot():
from matplotlib import pyplot as plt
import numpy as np
array
=
np.arange(l, 6)
plt.plot(array)
plt.show()
17.2. Построение графиков с помощью Matplotlib
411
Этот фрагмент строит следующий график:
5.0
4.5
4.0
3.5
3.0
2.5
2.0
1.5
1.0
о.о
0.5
1.0
1.5
2.0
2.5
3.0
3.5
4.0
При передаче двумерного массива каждый столбец массива интерпретирует­
ся как значения оси ординат у. Например, следующий фрагмент кода рисует
четыре линии:
from matplotlib import pyplot as plt
import numpy as np
data = np.arange(l, 21).reshape(S, 4)
#
#
#
#
#
#
data содержит следующий массив:
array([[ 1, 2, 3' 4]'
[ 5' 6, 7' 8]'
[ 9, 10, 11, 12],
[13, 14, 15, 16]'
[17, 18, 19, 20]])
plt.plot(data)
plt.show()
Вот результат:
20.О
17.5
15.О
12.5
10.О
7.5
5.0
2.5
о.о
LO
0.5
1.5
2.0
2.5
3.0
3.5
4.0
Если же вы хотите нанести на график матрицу по строкам, то выводить следует
результат транспонирования массива:
from matplotlib import pyplot as plt
import numpy as пр
data
=
np.arange(l, 21).reshape(S, 4)
plt.plot(data.transpose())
plt.show()
На графике, сгенерированном этим фрагментом кода, линии построены по
строкам матрицы (вместо столбцов).
20.О
17.S
15.О
12.5
10.О
7.5
5.0
2.5
о.о
0.5
LO
L5
2.0
2.5
3.0
17 .2. Построение графиков с помощью Matplotlib
41 З
До настоящего момента на графиках, которые мы строили, не было никакой
информации о том, что же этот график показывает. Сейчас мы расскажем, как
отформатировать график и добавить текст.
Форматирование графиков
Python было изу­
20 дней в результате чтения Real Python по сравнению с другим
Попробуем показать на графике, сколько материала о языке
чено за первые
веб-сайтом:
from matplotlib import pyplot as plt
import numpy as пр
days = np.arange(0, 21)
other_site, real_python
=
days, days
**
2
plt.plot(days, other_site)
plt.plot(days, real_python)
plt.show()
График, выводимый этим кодом, выглядит так:
400
350
300
250
200
150
100
so
о
о.о
2.5
5.0
7.5
10.О
U.5
15.О
17.5
20.0
График далеко не идеален. На оси х отображаются половины дней, у графика
нет заголовка и названия осей.
Начнем с настройки оси х. Для определения местонахождения меток на осях
используется функция
plt. xticks ():
414
ГЛАВА 17
Научные вычисления и построение графиков
from matplotlib import pyplot as plt
import numpy as пр
days = np.arange(0, 21)
other_site, real_python = days, days
** 2
plt.plot(days, other_site)
plt.plot(days, real_python)
plt.xticks([0, 5, 10, 15, 20])
plt.show()
Теперь на графике появляются метки дней: О,
5, 10, 15
и
20.
400
350
300
250
200
150
100
50
о
о
5
10
15
20
Такой график лучше воспринимается, но и сейчас неясно, что же показывает
каждая ось. Для добавления названия осей используются функции
и
pl t. xlabel ()
plt. ylabel (). Обе функции имеют один параметр, в котором должна пере­
даваться строка с названием оси.
Для добавления заголовка к графику исполюуется функция pl t. title( ). Как
и
plt. xlabel() и plt .ylabel(), функция plt. title() получает один строковый
аргумент с заголовком графика.
Обновите приведенный выше код, чтобы к оси х добавлялось название "Days of
Reading" (Время чтения в днях), к оси у - "Amount of Python Learned" (Объем
изученного Python), а сам график выводился под заголовком "Python Learned
Reading Real Python vs Other Site" (Python, изученный на Real Python vs на
других сайтах):
17.2. Построение графиков с помощью Matplotlib
415
from matplotlib import pyplot as plt
import numpy as np
days = np.arange(0, 21)
other_site, real_python
=
days, days
** 2
plt.plot(days, other_site)
plt.plot(days, real_python)
plt.xticks([0, 5, 10, 15, 20])
plt.xlabel("Days of Reading")
plt.ylabel("Amount of Python Learned")
plt.title("Python Learned Reading Real Python vs Other Site")
plt. show()
График с заголовком и названиями осей показан ниже.
Python Learned Reading Real Python vs. Other Site
400
350
~ 300
,,,Е
~ 250
с
о
.&:
~
Q.
200
"о
~ 150
::;J
о
Е
< 100
50
о
о
5
10
15
20
Days of Reading
Становится на что-то похоже!
Но проблемы все еще остались: непонятно, какой график относится к
Python,
а какой
-
Real
к другому веб-сайту. Чтобы уточнить, какая линия чему
соответствует, можно добавить условные обозначения вызовом pl t. legend ().
pl t. legend () имеет один обязательный позиционный параметр. Функции пере­
дается список строк с названиями всех графиков на рисунке. Строки должны
следовать в том же порядке, в котором они добавлялись на рисунок.
416
ГЛАВА 17
Научные вычисления и построение графиков
Например, следующая обновленная версия добавляет условные обозначения,
которые поясняют, какой график представляет
сайт»
Real Python, а какой -
«другой
( «other site» ).
Так как первым был добавлен график с данными другого сайта other _site,
первой строкой в списке, передаваемом
plt. legend( ),
становится
"Other Site":
from matplotlib import pyplot as plt
import numpy as пр
days = np.arange(0, 21)
other_site, real_python
=
days, days ** 2
plt.plot(days, other_site)
plt.plot(days, real_python)
plt.xticks([0, 5, 10, 15, 20])
plt.xlabel("Days of Reading")
plt.ylabel("Amount of Python Learned")
plt.title("Python Learned Reading Real Python vs Other Site")
pl t. legend ( [ "Other Si te'', "Real Python"])
plt.show()
Вот как выглядит итоговый вариант:
Python Learned Reading Real Python vs. Other Site
400
-
Other Site
Real Python
350
so
о
о
5
10
Days of Reading
1S
20
pl t. legend () также поддерживает ряд необязательных параметров, предназна­
ченных для настройки условных обозначений. За дополнительной информацией
17 .2.
Построение графиков с помощью
Matplotlib
обращайтесь к описанию условных обозначений в документации
417
Matplotlib
( https.j/matplotlib.orglusers/legend_guide.html).
Другие разновидности графиков
До сих пор вы строили только линейные графики, но
Matplotlib позволяет строить
многие другие разновидности, включая гистограммы и столбчатые диаграммы.
Столбчатые диаграммы
Столбчатые диаграммы строятся функцией plt.bar(), которая получает два
обязательных параметра:
1)
список значений х для центральной точки каждого столбца;
2)
список значений у для высоты каждого столбца.
Например, следующий код строит столбчатую диаграмму, у которой центры
столбцов находятся в точках
1, 2, 3, 4 и 5 по
оси х с высотами
ответственно:
from matplotlib import pyplot as plt
centers = (1, 2, 3, 4, 5]
tops = [2, 4, 6, 8, 10]
plt.bar(centers, tops)
plt. show()
Столбчатая диаграмма выглядит так:
10
8
б
4
2
о
1
2
3
4
s
2, 4, 6, 8 и 10 со­
ГЛАВА 17
418
Научные вычисления и построение графиков
Для определения центральных точек и высот столбцов вместо списка можно
использовать массив
NumPy.
Следующий фрагмент строит диаграмму, анало­
гичную предыдущей, но вместо списков используются массивы
NumPy:
from matplotlib import pyplot as plt
import numpy as np
centers = np.arange(l, 6)
tops = np.arange(2, 12, 2)
plt.bar(centers, tops)
plt.show()
Функция plt.bar() весьма гибкая. Первый аргумент не обязан быть списком
чисел
-
это может быть список строк, представляющих категории данных.
Допустим, вы хотите построить столбчатую диаграмму, на которой выводятся
данные из словаря:
fruits = {
"apples": 10,
"oranges": 16,
"bananas": 9,
"pears": 4,
}
Список названий фруктов можно получить функцией
ветствующие значения
-
функцией
frui ts. keys (),
а соот­
fruits. values( ):
>>> fruits.keys()
dict_keys( [ 'apples', 'oranges', 'bananas', 'pears'])
>>> fruits.values()
dict_values([10, 16, 9, 4))
Списки
fruits. keys()
и
fruits. values ()
можно передать функции
plt. bar(),
чтобы на столбчатой диаграмме были показаны значения, соответствующие
названиям фруктов:
from matplotlib import pyplot as plt
fruits = {
"apples": 10,
"oranges": 16,
"bananas": 9,
"pears": 4,
}
plt.bar(fruits.keys(), fruits.values())
plt. show()
17.2. Построение графиков с помощью Matplotlib
419
Столбцы разделены одинаковыми расстояниями, а названия фруктов обозна­
чены метками на оси х.
16
14
и
10
8
б
4
2
о
apples
oranges
Ьananas
pears
Гистограммы
Другая популярная разновидность диаграмм
-
гистограмма
распределение данных. Для создания гистограмм в
функция
- показывает
Matplotlib используется
plt.hist().
Функция pl t. hist () получает два обязательных параметра:
1)
список (или массив
2)
количество категорий на гистограмме.
Функция
NumPy)
значений;
pl t. hist () может вычислить частоту каждого значения в списке зна­
чений, а также рассчитать категории за вас. Это сэкономит вам массу усилий
при построении гистограмм.
Рассмотрим пример, который строит гистограмму десяти тысяч случайных
чисел с нормальным распределением, сгруппированных по
генерирования случайных чисел используется функция
NumPy random.
20 категориям. Для
randn() из модуля
Она возвращает массив случайных чисел с плавающей точкой,
многие из которых близки к нулю.
420
ГЛАВА 17
Научные вычисления и построение графиков
Код построения гистограммы:
from matplotlib import pyplot as plt
from numpy import random
plt.hist(random.randn(10000), 20)
plt.show()
Matplotlib автоматически создает 20 категорий одинаковой
ширины
0,5.
1400
иоо
1000
800
600
400
200
о
--4
-3
-2
-1
о
1
2
3
4
Гистограммы легко настраиваются. Подробнее о построении гистограмм на
Python рассказано в статье <1Python Histogram Plotting: NumPy, Matplotlib,
Pandas & Seaborn~ (https://realpython.com/python-histogramsj) на сайте Real
Python.
языке
Сохранение рисунков в графических файлах
Возможно, вы заметили, что в нижней части окна с графиками расположена
панель инструментов. Она позволяет сохранить построенную диаграмму в гра­
фическом файле.
Как правило, никому не хочется сидеть у компьютера и нажимать кнопку
для сохранения каждого графика. К счастью,
Save
Matplotlib позволяет леrко сделать
это программными средствами.
Для сохранения графиков используется функция
pl t. savefig().
Путь к фай­
лу, в котором вы хотите сохранить свой график, передается в виде строки.
17.2. Построение графиков с помощью Matplotlib
421
Следующие примеры сохраняют простую столбчатую диаграмму с именем
bar.png в текущем
рабочем каталоге.
Если вы решите сохранить его в другом месте, передайте при вызове абсолют­
ный путь.
from matplotlib import pyplot as plt
import numpy as np
xs = np.arange(l, 6)
tops = np.arange(2, 12, 2)
plt.bar(xs, tops)
plt.savefig("bar.png")
ПРИМЕЧАНИЕ
Чтобы одновременно и сохранить диаграмму, и вывести ее на экране, обяза­
тельно сохраните ее перед выводом!
Функция show() приостанавливает выполнение кода, а при закрытии окна
график уничтожается, так что при попытке сохранения диаграммы после вы­
зова
show() вы получите пустой файл.
Работа с графиками в интерактивном режиме
Когда мы начинаем форматировать график, хочется настраивать его внешний
вид и оценивать результат, не тратя время на перезапуск программы.
Проще всего сделать это при помощи оболочки Jupyteг
jupyter.org/),
Notebook (https.j/
Python,
которая создает интерактивный сеанс интерпретатора
выполняемого в браузере.
Блокноты Jupyteг стали главным средством взаимодействия с данными и их
анализа, и они отлично работают как с
NumPy, так и с Matplotlib. Интерактив­
Notebook представлен в руководстве
«IPython in Depth>.> (https.j/realpython.com/pybasics-ipython-in-depth) на сайте
Real Python.
ный учебник по использованию Jupyteг
Упражнения
1.
Постройте как можно больше графиков, приведенных в этом разде­
ле,
-
напишите собственные программы, не подглядывая в приведен­
ный код.
422
2.
ГЛАВА
17
Научные вычисления и построение графиков
Способствуют ли пираты глобальному потеплению? В папке
главы
practice_files
17 находится файл CSV с данными о количестве пиратов и глобаль­
ной температуре. Напишите программу для анализа связи между ними;
программа должна читать файл
pirates.csv и
строить график, на котором
количество пиратов указано на оси х и температура
-
на оси у. Добавьте
заголовок и названия осей графиков, затем сохраните полученный график
в виде файла в формате
PNG.
17.З. ИТОГИ И ДОПОЛНИТЕЛЬНЫЕ РЕСУРСЫ
В этой главе вы узнали о научных вычислениях и визуализации данных в
Python.
В частности, вы научились:
•
работать с массивами и матрицами средствами
•
строить графики при помощи
NumPy;
Matplotlib.
Чтобы на должном уровне рассказать про научные вычисления, анализ и ви­
зуализацию данных, не хватит и отдельной книги. Однако понимания базовых
понятий, с которыми вы познакомились в этой главе, достаточно для начала
самостоятельной работы.
ИНТЕРАКТИВНЫЙ ТЕСТ
К этой главе прилагается бесплатный интерактивный тест для проверки усво­
енных вами знаний. Тест доступен на телефоне или компьютере:
realpython.com/quizzes/pybasics-scientific-computing
Дополнительные ресурсы
За дополнительной информацией обращайтесь к следующим ресурсам:
•
«Look Ма, No For-Loops: Array ProgrammingWith NumPy>.> (https.j/
realpython.com/numpy-array-programming/)
•
«Data ScienceWith Python Core Skills (Learning Path)» (https.j/realpython.
com/leaming-paths/data -science-python -core-skills/)
ГЛАВА
18
Графические интерфейсы
До сих пор в этой книге я рассказывал о приложениях командной строки
-
программах, которые запускаются и выводят результаты в окне терминала.
Приложения командной строки хорошо подходят для создания инструментов,
которыми пользуются разработчики, но большинство пользователей программ
никогда не открывают окно терминала!
Графический интерфейс пользователя, или
GUI (Graphical User Interface), -
это
окна с элементами (кнопками, текстовыми полями и т. д. ), которые предоставля­
ют пользователю знакомый и наглядный способ взаимодействия с программой.
В этой главе вы научитесь:
•
добавлять простой графический интерфейс к приложениям командной
строки с помощью
•
EasyGUI;
создавать полнофункциональные GUI-приложения с использованием
Tkinter.
Итак, за дело!
18.1. ДОБАВЛЕНИЕ ЭЛЕМЕНТОВ GUI
С ПОМОЩЬЮ EASYGUI
Библиотека
EasyGUI
позволяет быстро добавить в программу графический
интерфейс. Возможности
EasyGUI
несколько ограниченны, но библиотеку
удобно применять для создания простых инструментов, которым нужен не­
большой объем входных данных от пользователя.
В этом разделе мы используем
EasyGUI для создания короткой программы,
PDF на жестком диске и повернуть его стра­
которая позволяет выбрать файл
ницы на заданный угол.
424
ГЛАВА 18
Установка
Графические интерфейсы
EasyGUI
Прежде всего необходимо установить
$
pythonЗ
EasyGUI
при помощи
pip:
-m pip install easygui
Когда библиотека
EasyGUI
будет установлена, можно вывести расширенную
информацию о пакете командой
pip show:
$ pythonЗ -m pip show easygui
Name: easygui
Version: 0.98.1
Summary: EasyGUI is а module for very simple, very easy GUI
programming in Python. EasyGUI is different from other
GUI generators in that EasyGUI is NOT event-driven.
Instead, all GUI interactions are invoked Ьу simple
function calls.
Home-page: https://github.com/robertlugg/easygui
Author: easygui developers and Stephen Ferg
Author-email: robert.lugg@gmail.com
License: BSD
Location: c:\realpython\venv\lib\site-packages
Requires:
Required-by:
Код этой главы написан для
EasyGUI 0.98.1 -
той же версии, которую вы видите
во фрагменте кода выше.
Первое приложение
EasyGUI
EasyGUI
хорошо подходит для создания диалоговых окон, предназначенных
для получения пользовательского ввода, и вывода результатов. Библиотека не
особенно годится для создания больших приложений с несколькими окнами,
меню и панелями инструментов.
EasyG UI можно представить как своего рода замену функций input ()
и
print (),
которые ранее мы использовали для ввода и вывода.
Логика ЕаsуGUl-программ обычно работает по следующей схеме.
1.
В какой-то момент на экране пользователя появляется визуальный
элемент.
2.
Выполнение кода приостанавливается, пока пользователь не введет
данные в визуальном элементе.
3.
Пользовательский ввод возвращается в ви;1е объекта, а вы1ю1шение кода
возобновляется.
18.1. Добавление элементов GUI с помощью EasyGUI
Чтобы получить представление о том, как работает
интерактивное окно в
IDLE и
откройте новое
выполните следующие команды:
>>> import easygui as gui
»> gui.msgbox(msg="Hello!", title="My first message
При выполнении кода в
EasyGUI,
425
Ьох")
Windows на экране появляется окно, которое выглядит
примерно так:
1 Му first message Ьох
о
х
Rello!
Внешний вид окна зависит от операционной системы, в которой выполняется
код. В
macOS окно
выглядит так:
• 1"'18
Му
first message
Му
flrst message
Ьох
Hellol
То же окно в
Ubuntu:
Hellot
Ьох
~
-
~
426
ГЛАВА
18
Графические интерфейсы
В примерах этого раздела мы используем скриншоты для
Windows.
ВАЖНО!
Как
EasyGUI, так и IDLE написаны с использованием библиотеки Tkiпter, о кото­
рой речь пойдет в следующем разделе. Такое использование одного ресурса
иногда создает проблемы с выполнением
-
например, диалоговые окна могут
застывать на месте или блокироваться.
Если вам кажется, что вы тоже с этим столкнулись, попробуйте выполнить свой
код в окне терминала. Интерактивный сеанс
командой
Python запускается из терминала
python в Windows или командой руthопЗ в macOS/Ubuntu.
Вот что вы увидите в диалоговом окне, сгенерированном приведенным кодом:
1.
Строка
"Hello! ",переданная
в параметре
msg
функции
msgbox(),
выво­
дится как текст в окне сообщения.
2.
Строка "Му
first message
Ьох", переданная в параметре
ti tle,
выводится
в заголовке окна сообщения.
3.
Окно сообщения содержит кнопку с надписью "ОК".
Закройте диалоговое окно кнопкой ОК и загляните в интерактивное окно
IDLE.
Строка "ОК" выводится под последней введенной вами строкой кода:
»> gui.msgbox(msg="Hello! ", title="My first message
Ьох")
'ОК'
При закрытии диалогового окна
msgbox()
возвращает название кнопки. Если
пользователь закрывает диалоговое окно без нажатия кнопки ОК, то
msgbox()
возвращает значение Nопе.
Название кнопки можно настроить при помощи третьего необязательного
параметра ok_buttoп. Например, следующая команда создает окно сообщения
с кнопкой
"Click me":
»> gui.msgbox(msg="Hello!", title="Greeting", ok_button="Click me")
Функция
msgbox() хорошо подходит для вывода сообщений, но она не предо­
ставляет пользователю достаточно средств для взаимодействия с программой.
EasyG UI содержит несколько функций для вывода различных типов диалоговых
окон. Давайте рассмотрим некоторые из них!
18.1. Добавление элементов GUI с помощью EasyGUI
Элементы графического интерфейса в
Кроме
msgbox() EasyGUI
427
EasyGUI
содержит еще несколько функций для вывода других
видов диалоговых окон. Описание этих функций приведено в следующей таблице.
ФУНКЦИЯ
ОПИСАНИЕ
msgbox()
Выводит сообщение с одной кнопкой и возвращает название
кнопки
buttonbox()
Выводит сообщение с несколькими кнопками и возвращает на­
звание выбранной пользователем кнопки
indexbox()
Выводит сообщение с несколькими кнопками и возвращает ин­
декс выбранной кнопки
enterbox()
Предоставляет пользователю поле для ввода текста и возвра­
щает текст, введенный пользователем
fileopenbox()
Предлагает пользователю выбрать файл, который должен быть
открыт, и возвращает абсолютный путь к выбранному файлу
diropenbox()
Предлагает пользователю выбрать каталог, который должен
быть открыт, и возвращает абсолютный путь к выбранному
каталогу
filesavebox()
Предлагает пользователю выбрать место для сохранения фай­
ла и возвращает абсолютный путь к месту сохранения
Рассмотрим каждую из этих функций по отдельности.
buttonbox()
Функция
EasyGUI buttonbox()
отображает диалоговое окно с сообщением
и несколькими кнопками, которые пользователь может нажимать. Название
кнопки, которую нажал пользователь, возвращается вашей программе.
Как и
msg и title, которые определя­
ют выводимое сообщение и заголовок диалогового окна. Функция buttonbox ()
также поддерживает третий параметр с именем choices, который используется
msgbox( ), buttonbox()
содержит параметры
для настройки кнопок.
Например, следующий фрагмент выводит диалоговое окно с тремя кнопками,
с метками
"Red", "Yellow"
и
"Blue":
>>> gui.buttonbox(
msg:"What is your favorite color?",
title:"Choose wisely ... ",
choices:("Red", "Yellow", "Blue"),
ГЛАВА 18
428
Графические интерфейсы
Диалоговое окно выглядит так:
1 Choose wisely-.
ИЬ.аt
iз
о
х
your :favori te color?
Когда вы нажимаете одну из кнопок, ее метка возвращается в виде строки. На­
пример, нажатие кнопки
Yellow заставляет buttonbox()
вернуть строку
"Yellow":
>>> gui.buttonbox(
msg="What is your favorite color?",
title="Choose wisely ... ",
choices=("Red", "Yellow", "Blue"),
'Yellow'
Как и msgbox(), buttonbox() возвращает значение None, если пользователь за­
крывает диалоговое окно без нажатия одной из кнопок.
indexbox()
indexbox () выводит диалоговое окно , идентичное тому, которое выводит
buttonbox( ) . Собственно, indexbox() создается точно так же, как и buttonbox( ):
>>> gui.indexbox(
msg="What's your favorite color?",
title="Choose wisely ... ",
choices=("Red", "Yellow", "Blue"),
А вот как выглядит диалоговое окно:
1 Choose wisely...
What
iз
your ravorite color?
о
х
18.1. Добавление элементов GUI с помощью EasyGUI
Различие между
indexbox()
и
buttonbox()
заключается в том, что
429
indexbox()
возвращает индекс названия кнопки (вместо самого названия) в списке или
кортеже, передаваемом
choices.
Например, при щелчке на кнопке
Yellow
возвращается целое число
1:
>>> gui.indexbox(
msg="What's your favorite color?",
title="Favorite color",
choices= ( "Red", "Yellow", "Blue"),
1
Так как
для
indexbox() возвращает индекс, а не строку, удобно определить кортеж
choices за пределами функции, чтобы позже вы могли обращаться к на­
званию по индексу:
>>> colors = ("Red", "Yellow", "Blue")
>>> choice = gui.indexbox(
msg="What's your favorite color?",
title="Favorite color",
choices=colors,
»> choice
1
>>> colors[choice]
'Yellow'
Функции
buttonbox() и indexbox() отлично подходят для получения ввода от
пользователя, когда пользователь выбирает из заранее определенного набора
вариантов. Эти функции плохо подходят для получения такой информации,
как имя пользователя или адрес электронной почты. В таких случаях лучше
применять функцию
enterbox( ).
enterbox()
Функция
enterbox() предназначена для получения текстового ввода от поль­
зователя:
>>> gui.enterbox(
msg="What is your favorite color?",
title="Favorite color",
Диалоговое окно, создаваемое
enterbox (),содержит текстовое поле,
пользователь может ввести ответ.
в котором
ГЛАВА 18
430
f
Графические интерфейсы
D
Favorite color
х
Wha.t is your fa.vorite color?
Введите название цвета (например,
Yellow)
и нажмите кнопку ОК. Введенный
вами текст возвращается в виде строки:
>>> gui.enterbox(
msg="What is your favorite color?",
title="Favorite color",
'Yellow'
Диалоговые окна очень часто применяются для выбора пользователем файла
или папки. В
существуют специальные функции, разработанные
EasyGUI
именно для таких операций.
fileopenbox()
Функция
fileopenbox()
выводит диалоговое окно для выбора открываемого
файла:
>>> gui.fileopenbox(title="Select
а
file")
Это диалоговое окно очень похоже на стандартное системное диалоговое окно,
в котором пользователь открывает файл (см . рис. на с.
439).
Выберите файл и щелкните на кнопке Open. Функция возвращает строку с пол­
ным путем к выбранному файлу.
ВАЖНО!
fileopenbox() не открывает файл , а только предоставляет воз ­
можность выбрать его! Чтобы открыть файл, необходимо воспользоваться
Функция
встроенной функцией
Как и
msgbox()
и
open (),
buttonbox(), fileopenbox()
пользователь нажмет кнопку
файл.
о которой я рассказывал в главе
Cancel
12.
возвращает значение
None,
если
или закроет диалоговое окно, не выбрав
18.1. Добавление элементов GUI с помощью EasyGUI
--=- _VJ
1 Select а file
v
1'
>
1
2!1is РС
Х
~~ ·
Organize •
k
v
Р j
~i 3'~ch This РС
>
v
Quick access
Desktop
;
~ Downloads
;
lfil
;
Documents
431
[1
8
"
Folders (7)
ЗD
Objects
1
Desktop
;
11!;1 Pictures
Movies
•
~
}' Music
Documents
~
>Q
This PC
>
Network
Downloads
Movies
v
File na me:
v
1All files
j
~~~~~~~~~~
Ореп
j
1
Cancel
j
diropenbox() и filesavebox()
EasyGUI
содержит две другие функции для создания диалоговых окон, рабо­
тающие практически так же, как функция
1.
Функция
fileopenbox().
diropenbox() открывает диалоговое окно, которое может ис­
пользоваться для выбора папки вместо файла. Когда пользователь на­
жимает
2.
Open,
Функция
возвращается полный путь к каталогу.
filesavebox() открывает диалоговое окно для выбора места
сохранения файла; если выбранное имя уже существует, функция пред­
лагает пользователю подтвердить, что он хочет перезаписать файл. Как
и
fileopenbox (), filesavebox () возвращает путь к файлу, если
Save. Сам файл при этом не сохраняется.
пользова­
тель нажал кнопку
ВАЖНО! .
Функции
diropenbox()
·
и
filesavebox()
не открывают каталог и не сохраняют
файл. Они только возвращают абсолютный путь к открываемому каталогу или
сохраняемому файлу.
Чтобы открыть каталог или сохрани т ь файл , вам придется написать соответ­
ствующий код самостоятельно .
432
ГЛАВА
Обе функции
18
Графические интерфейсы
- diropenbox()
и
filesavebox() - возвращают None, если поль­
зователь закрыл диалоговое окно, не нажав кнопку
Open
или
Save.
Это может
привести к аварийному завершению вашей программы, если вы будете недо­
статочно осторожны.
Например, следующий фрагмент выдает ошибку
TypeError, если пользователь
закроет диалоговое окно без выбора:
>>> path = gui.fileopenbox(title="Select а file")
»> open_file = open(path, "r")
Traceback (most recent call last):
File "cstdin>", line 2, in <module>
TypeError: expected str, bytes or os.Pathlike object, not NoneType
То, как вы обрабатываете подобные ситуации, очень сильно влияет на впечат­
ление пользователя от вашей программы.
Корректное завершение программы
Допустим, вы пишете программу для извлечения страниц из файла
что может делать такая программа,
мог выбрать файл
PDF для
-
PDF. Первое,
вызвать fileopenbox( ), чтобы пользователь
открытия.
Что делать, если пользователь решил, что он не хочет запускать программу,
и нажал
Cancel?
Вы должны позаботиться о том, чтобы ваша программа корректно обрабатывала
такие ситуации. Она не должна аварийно завершаться или выдавать неожидан­
ные результаты. В описанной выше ситуации программа, скорее всего, должна
просто завершить работу.
Один из способов завершения программы подразумевает использование встро­
енной функции exi t () языка
Python.
Например, следующая программа вызывает функцию exi t (), чтобы прервать вы­
полнение, когда пользователь нажимает Cancel в диалоговом окне выбора файла:
import easygui as gui
path = gui.fileopenbox(title="Select
а
file")
if path is None:
exit()
Если пользователь закрывает диалоговое окно, не нажав кнопку ОК, программа
закроется, а выполнение будет остановлено, потому что переменная path со­
держит
None, а программа вызывает exi t () в блоке i f.
18.1. Добавление элементов GUI с помощью EasyGUI
433
ПРИМЕЧАНИЕ
Если программа выполняется в
IDLE,
функция
exit{}
также з.акроет текущее
интерактивное окно .
Ключевое слово
is из предыдущего примера вам еще не встречалось.
Оно срав­
нивает два объекта и определяет, не являются ли они одним и тем же объектом,
проверяя, имеют ли они одинаковые адреса памяти.
Как мы говорили ранее, адрес памяти объекта можно получить функцией id ( ) .
Таким образом, если значения id (а) и id ( Ь) для двух объектов одинаковы, то
выражение а
is
Ь дает результат True. В этом случае а и Ь не являются разными
объектами. Они представляют один объект с двумя разными именами.
Почему же is используется для сравнения значения с None? Потому что объект
None может быть только один. Каждый раз, когда функция возвращает None, она
возвращает тот же объект в памяти, на который указывает ключевое слово None.
Теперь вы умеете создавать диалоговые окна вызовом
EasyGUI,
и мы можем
применить все ваши приобретенные навыки в реальном приложении.
Упражнение
1.
Создайте следующее диалоговое окно.
о
, Watch out!
Warning!
2.
Создайте следующее диалоговое окно.
о
Whe.t is your ne.me?
х
х
Графические интерфейсы
434
ГЛАВА 18
18.2.
ПРИМЕР: ПРОГРАММА ДЛЯ ПОВОРОТА
СТРАНИЦ
EasyGUI
PDF
очень хорошо подходит для вспомогательных приложений, авто­
матизирующих простые, но повторяющиеся операции. Если вам частенько
приходится выполнять рутинные операции, например в офисе, то программы
EasyGUI,
упрощающие выполнение повседневных задач, могут значительно
повысить производительность вашей работы .
В этом разделе мы воспользуемся некоторыми диалоговыми окнами
EasyGUI,
о которых вы узнали в прошлом разделе, чтобы разработать приложение для
поворота страниц
PDF.
Дизайн приложения
Прежде чем браться за написание кода, давайте немного поразмыслим над тем,
как должна работать программа.
Она должна сначала спросить у пользователя, какой файл
PD F следует открыть,
на сколько градусов должна быть повернута каждая страница и где пользователь
хотел бы сохранить новый файл
PDF.
Затем программа должна открыть файл,
повернуть страницы и сохранить новый файл.
.
ПРИМЕЧАНИЕ
На стадии проектирования приложения следует спланировать каждый шаг до
того, как вы начнете программировать. При разработке крупного приложения
желательно нарисовать диаграмму, описывающую логику программы,
-
это
поможет упорядочить ваши будущие действия.
Итак, опишем последовательность действий, которые затем будем кодировать.
Это упростит нашу задачу.
1.
Вывести диалоговое окно выбора файла для открытия документа
2.
Если пользователь отменяет диалоговое окно, завершить работу про­
PDF.
граммы.
3.
Предложить пользователю выбрать угол поворота в градусах
значений
4.
-
одно из
90, 180 или 270 градусов .
Открыть диалоговое окно для выбора файла, чтобы сохранить документ
PDF с повернутыми
страницами.
18.2. Пример: программа для поворота страниц PDF
5.
435
Если пользователь пытается сохранить файл с таким же именем, как
у входного файла:
6.
•
уведомить пользователя, что операция недопустима;
•
вернуться к шагу
4.
Если пользователь отменит диалоговое окно для сохранения файла, за­
вершить работу программы.
7.
Вьшолнить поворот страниц:
• открыть выбранный файл PDF;
•
новернуть все страницы;
•
сохранить документ
PD F с повернутыми страницами в выбранном файле.
Реализация дизайна приложения
Итак, мы составили план и теперь можем последовательно реализовать каждый
шаг. Откройте новое окно редактора в
Начните с импорта
EasyGUI
и
IDLE.
PyPDF2:
import easygui as gui
from PyPDF2 import PdfFileReader, PdfFileWriter
На шаге
PDF.
1 выводится
диалоговое окно выбора файла для открытия документа
Это можно сделать функцией
fileopenbox():
1. Вывести диалоговое окно для выбора
input_path = gui.fileopenbox(
title="Select а PDF to rotate ... ·,
default="*.pdf"
#
файла,
чтобы открыть документ
Здесь параметру по умолчанию задается значение"*.
PDF.
pdf", которое настраивает
диалоговое окно так, чтобы в нем выводились только файлы с расширением
.pdf.
Тем самым мы предотвращаем случайный выбор пользователем файла,
формат которого отличается от
PDF.
Путь, выбранный rюль:ювателем, присваивается переменной iпput_path. Если
нользователь закрыл диалоговое окно, не выбрав файл (шаг
input_path
сrщержит
2), переменная
None.
Чтобы завершить программу, если пользователь закрыл диалоговое окно, не
выбрав значение, проверьте, равна ли None переменная input_path, и если рав­
на
-
вызовите
exi t ():
ГЛАВА 18
436
#
if
2.
Графические интерфейсы
Если пользователь отменяет диалоговое окно,
iпput_path
завершить работу программы.
is None:
exit()
На третьем шаге следует спросить пользователя, на какой угол он хотел бы по­
вернуть страницы
PDF.
Пользователь может выбрать
90, 180 или 270 градусов.
Для получения этой информации можно воспользоваться функцией
#
#
3.
Предложить пользователю выбрать угол поворота
90, 180 или 270 градусов.
choices = ("90", "180", "270")
degrees = gui.buttonbox(
msg="Rotate the PDF clockwise
title="Choose rotation ... ",
choices=choices,
Ьу
buttonbox ():
-
how many degrees?",
Созданное диалоговое окно содержит три кнопки с метками "90", "180" и "270".
Когда пользователь щелкает на одной из этих кнопок, метка присваивается
переменной degrees в виде строки. Чтобы повернуть страницы в файле PDF
на выбранный угол, необходимо иметь это значение в виде целого числа, а не
строки. Преобразуйте его в целое число:
degrees = int(degrees)
Затем запросите у пользователя путь к выходному файлу функцией
filesa-
vebox( ):
#
4.
с
повернутыми
Открыть диалоговое окно для выбора файла,
PDF
чтобы открыть документ
страницами.
save_title = "Save the rotated PDF as ... "
file_type = "*.pdf"
output_path = gui.filesavebox(title=save_title, default=file_type)
Как и в случае с
fileopenbox( ), параметру default присваивается значение*. pdf.
Это гарантирует, что файл будет автоматически сохранен с расширением
Пользователю следует запретить перезапись исходного файла (шаг
жете использовать цикл
while для вывода предупреждения,
5).
.pdf.
Вы мо­
пока пользователь
не выберет путь, отличный от пути входного файла:
#
#
5.
Если пользователь пытается сохранить файл с таким же именем,
как у ВХОДНОГО файла:
while input_path == output_path:
# - Уведомить пользователя окном сообщения о том, что операция недопустима.
gui.msgbox(msg="Cannot overwrite original file!")
Вернуться к шагу 4.
output_path = gui.filesavebox(title=save_title, default=file_type)
# -
18.2.
Цикл
while
Пример: программа для поворота страниц
проверяет, совпадает ли путь
PDF
437
input_path с output_path. Если пути
input_path и output_path совпа­
различны, то тело цикла игнорируется. Если
дают, то
msbox()
выводит предупреждение о том, что перезапись исходного
файла запрещена.
После вывода предупреждения
filesavebox()
выводит другое диалоговое окно
для сохранения файла с таким же заголовком и типом файла по умолчанию,
как и прежде. Эта часть возвращает пользователя к шагу
4.
И хотя программа
фактически не возвращается к строке кода, в которой функция
filesavebox()
была вызвана в первый раз, эффект остается тем же.
Если пользователь закрывает диалоговое окно для сохранения файла, не нажав
Save, программа должна завершить работу (шаг 6):
#
#
6.
Если пользователь отменяет диалоговое окно сохранения файла,
завершить работу программы.
if output_path is None:
exit ()
Теперь у вас есть все необходимое для реализации последнего шага программы:
#
#
7.
Выполнить
поворот
страниц:
PDF.
input_file = PdfFileReader(input_path)
output_pdf = PdfFileWriter()
- Открыть выбранный файл
# -
Повернуть все страницы.
for page in input_file.pages:
page = page.rotateClockwise(degrees)
output_pdf.addPage(page)
PDF с повернутыми страницами
with open(output_path, "wb") as output_file:
output_pdf.write(output_file)
# - Сохранить файл
Опробуйте новое приложение
в
Windows, macOS
и
PDF
Ubuntu Linux.
в выбранном файле.
в деле! Оно одинаково хорошо работает
Ниже для удобства просмотра приведен полный код приложения:
import easygui as gui
from PyPDF2 import PdfFileReader, PdfFileWriter
1. Вывести диалоговое окно для выбора
input_path = gui.fileopenbox(
title="Select а PDF to rotate ... ",
default="*.pdf"
#
файла,
чтобы открыть документ
PDF.
438
ГЛАВА 18
Графические интерфейсы
2. Если пользователь
if input_path is None:
exit()
#
#
#
3.
отменяет диалоговое окно,
Предложить пользователю выбрать угол поворота
90, 180 или 270 градусов.
choices = ("90", "180", "270")
degrees = gui.buttonbox(
msg="Rotate the PDF clockwise
title="Choose rotation ... ",
choices=choices,
#
#
4.
#
#
5.
завершить работу программы.
Открыть диалоговое окно для
Ьу
-
how many degrees?",
выбора файла,
чтобы сохранить
PDF с повернутыми страницами.
save_title = "Save the rotated PDF as ... "
file_type = "*.pdf"
output_path = gui.filesavebox(title=save_title, default=file_type)
документ
Если пользователь пытается сохранить файл
с таким же именем,
как у ВХОДНОГО файла:
while input_path == output_path:
# - Alert the user with а message Ьох that this is not allowed.
gui.msgbox(msg="Caпnot overwrite original file!")
# - Returп to step 4.
output_path = gui.filesavebox(title=save_title, default=file_type)
#
#
6.
Если пользователь отменяет диалоговое окно для
сохранения файла,
завершить работу программы.
if output_path is None:
exi t ()
# 7. Выполнить поворот страниц:
# - Открыть выбранный файл PDF.
PdfFileReader(input_path)
input_file
output_pdf = PdfFileWriter()
# -
Повернуть
все
страницы.
for page in input_file.pages:
page = page.rotateClockwise(degrees)
output_pdf.addPage(page)
PDF с повернутыми страницами
with open(output_path, "wb") as output_file:
output_pdf.write(output_file)
# - Сохранить документ
в выбранном файле.
EasyGUI отлично шщходит для быстрого создания пользовательских шперфей­
сов небольших приложений и утилит. Для более крупных проектов возможно­
стей
EasyGUI
может быть недостаточно. В таких случаях на помощь приходит
встроенная библиотека
Tkinter -
Python Tkinter ( https//wiki.python.org/moin/Tklnter).
GUl-фреймворк, работающий на более низком уровне, чем
EasyGUI.
Это означает, что у вас больше возможностей для унравления виэуальными
18.3. Задача: приложение для извлечения страницы PDF
аспектами
439
G U 1: размером окна, размером шрифта, цветом шрифта и элементами
графического интерфейса, присутствующими в диалоговом или обычном окне.
Далее в этой главе я расскажу о разработке GUI-приложений со встроенной
библиотекой
Python Tkinter.
Упражнение
В GUI-приложении для поворота страниц файла
PDF существует проблема.
В программе происходит сбой, если пользователь закрывает окно buttonbox (),
используемое для выбора угла, не выбрав значение.
Исправьте этот недостаток: в цикле
while продолжайте выводить диалоговое
окно выбора, пока переменная degrees остается равной None.
18.З. ЗАДАЧА: ПРИЛОЖЕНИЕ ДЛЯ ИЗВЛЕЧЕНИЯ
СТРАНИЦЫ
PDF
EasyGUI для написания GUI-приложения, извлекающего
PDF.
Сейчас вы примените
страницы из файла
Подробный план:
1.
Предложить пользователю выбрать файл
2.
Если файл
3.
Запросить начальный номер страницы.
4.
PDF
PDF.
не выбран, завершить программу.
Если пользователь не ввел начальный номер страницы, завершить про­
грамму.
5.
Допустимыми номерами страниц являются положительные целые числа.
Если пользователь ввел недопустимый номер страницы:
6.
7.
•
предупредить пользователя о том, что введенное значение недопустимо;
•
вернуться к шагу
3.
Запросить конечный номер страницы.
Если пользователь не ввел конечный номер страницы, завершить про­
грамму.
8.
Если пользователь ввел недопустимый номер страницы:
•
предупредить пользователя о том, что введенное значение недопустимо;
•
вернуться к шагу
6.
ГЛАВА 18
440
Графические интерфейсы
Запросить место (путь) для сохранения извлеченных страниц.
9.
Если пользователь не выбрал место для сохранения, :Jавершить про­
10.
грамму.
Если выбранное место совпадает с нутем входного файла:
11.
•
предупредить пользователя о том, что перезапись входного файла
недопустима;
•
вернуться к шагу
9.
Выполнить извлечение страниц:
12.
•
открыть входной файл
PDF;
•
записать новый файл
PDF,
который содержит только страницы из
выбранного диапазона.
Решение задачи, а также многие другие дополнительные ресурсы доступны
в интернете по адресу
realpython.com/python-basics/resources.
18.4. ЗНАКОМСТВО
Для
Python
С
TKINTER
существует много GUI-фреймворков, 110
фреймворк, встроенный в стандартную библиотеку
Tkinter Python.
единственный
У Tkinter несколько сильных сторон. Прежде всего это кросс-платформенность,
то есть один и тот же код будет работать в Windows, macOS и Linux. Визуаль­
ные элементы строятся с использованием собственных элементов интерфейса
операционной системы, так что приложения, построенные на базе
Tkinter,
выглядят так, словно они были написаны для той конкретной платформы, H<J.
которой они выполняются.
Хотя
Tkinter
считается стандартным фреймворком
Python GUI,
он не лишен
недостатков. Один из наиболее заметных заключается в том, что графические
интерфейсы, построенные с использованием
Tkinter,
выглядят устаревшими.
Если вам нужен стильный, современный интерфейс, воэможно,
Tkinter -
не
то, что вам подойдет.
Тем не менее
Tkinter
относительно компактен и прост в исполь:ювании 110
сравнению с другими фреймворками. Это делает его привлекательным для
построения GUl-приложений на языке
Python,
особенно если современный
лоск вас не привлекает, а на первый план выходит быстрое построение функ­
ционального и кросс-платформенного решения.
Посмотрим, как строятся приложения на базе
Tkinter.
18.4. Знакомство с Tkinter
441
ПРИМЕЧАНИЕ
Как я говорил в предыдущем разделе, среда IDLE построена на базе Tkiпter.
Из-за этого у вас могут возникнуть трудности при запуске ваших собственных
GUl-программ в
IDLE.
Если вы обнаружите, что окно
зависает или
GUI, которое вы пытаетесь создать, внезапно
IDLE начинает вести себя непредсказуемо, попробуйте запустить
программу из командной строки или терминала.
Ваше первое приложение на базе Tkinter
Основополагающим элементом графического интерфейса
окно. Окна
-
Tkinter является
это контейнеры, в которых размещаются все остальные элементы
GUI. Другие элементы -
текстовые поля, надписи, кнопки и т. д.
-
называются
виджетами. Виджеты размещаются внутри окон.
Создадим окно, содержащее один виджет. Откройте новое интерактивное окно
в
IDLE.
Первое, что необходимо сделать,
- импортировать модуль Tkinter:
>>> import tkinter as tk
Окно является экземпляром класса Tkinter Tk. Создайте новое окно и присвойте
его переменной
window:
>>> window = tk.Tk()
При выполнении этого кода на экране появится новое окно. Его внешний вид
зависит от операционной системы.
1 tk
о
(а) Windows
tk
х
(b)macOS
Далее в этой главе я буду использовать скриншоты для
(с)
Ubuntu
Windows.
""""'0
442
ГЛАВА 18
Графические интерфейсы
Теперь, когда у вас имеется окно, добавим в него виджет. Применим класс
tk. Label для добавления в окно текста.
Label с текстом "Hello, Tkinter"
greeting:
Создайте виджет
с именем
>>> greeting
=
и присвойте его переменной
tk.Label(text="Hello, Tkinter")
Окно, созданное ранее, не изменяется. Вы только что создали виджет
Label,
но
он еще не был добавлен в окно.
Есть несколько способов добавления виджетов в окно. Сейчас мы воспользуемся
методом
. pack ()
виджета
Label:
>>> greeting.pack()
Окно принимает следующий вид:
о
х
Hell:o, Тklnter
Когда вы вызываете
. pack (), Tkinter выбирает для окна минимальный размер,
при котором виджет все еще полностью помещается в окне.
Теперь выполните следующие команды:
>>> window.mainloop()
На первый взгляд, ничего не происходит, но обратите внимание : новое при­
глашение в оболочке не появляется.
· ВАЖНО!
.
·
Когда вы работаете с Tkinter из оболочки REPL (например, интерактивного окна
IDLE), обновления в окнах происходят при выполнении каждой строки кода.
При выполнении программы
Если вы не включите вызов
Tkinter из файла Python этого не происходит.
window.mainloop()
в конец программы в файле
Python, то приложение Tkinter выполнено не будет и на экран ничего не вы­
ведется .
Вызов
window.mainloop() приказывает Python запустить приложение Tkinter
и блокирует выполнение всего последующего кода, пока не будет закрыто окно,
18.5. Работа с виджетами
443
для которого был сделан вызов. Закройте созданное окно, и в командной обо­
лочке появится новое приглашение.
Чтобы создать окно средствами
Tkinter,
достаточно пары строк кода. Однако
от пустого окна пользы немного! В следующем разделе я познакомлю вас с не­
которыми виджетами, доступными в
Tkinter,
и возможностями их настройки
в соответствии с потребностями приложений.
Упражнения
1.
Используя Tkinter в интерактивном окне
Label,
в котором выводится текст
IDLE, создайте окно с виджетом
"GUis are great ! ".
2.
Повторите упражнение
1 с текстом "Python rocks ! ".
3.
Повторите упражнение
1с
18.5.
текстом
"Engage ! ".
РАБОТА С ВИДЖЕТАМИ
Виджеты
-
подлинная суть
Tkinter.
Они создают элементы, посредством ко­
торых пользователь взаимодействует с вашей программой .
Каждый виджет в
Tkinter определяется классом.
Несколько примеров доступ­
ных виджетов показаны ниже.
КЛАСС ВИДЖЕТА
ОПИСАНИЕ
Label
Виджет для в ы вода текст а на экра н
Button
К н опка, которая может содержать текст и в ы п ол ня е т дей­
ствие п ри нажатии
Entry
В иджет для в вода одн ой строки текста
Text
Виджет для ввода м н огост р оч н ого текста
Frame
П рямоугольная область, ис п ользуемая для гру ппир о в к и
взаимосвязан н ых в и джетов или для соз д а ния о т сту п о в
между виджетами
. ПРИМЕЧАНИЕ
·
·
.
Tkinter содержит намного больше виджетов, чем перечислено в таблице. Пол­
ный список вы найдете в статьях «Basic Widgets» (https://tkdocs.com/tutorial/
widgets.html) и «More Widgets» (https://tkdocs.com/tutorial/morewidgets.html)
в учебном руководстве TkDocs.
444
ГЛАВА
Графические интерфейсы
18
О работе со всеми этими виджетами я расскажу в следующих разделах. Поближе
познакомимся с виджетом
Виджеты
Виджеты
Label.
Label
Label
используются для вывода текста или графики . Те кст, выво­
димый в виджет
Label ,
пользователь редактировать не может. Этот виджет
предназначен только для вывода.
Как было показано в примере в начале этой главы, чтобы создать виджет
можно создать экземпляр класса
Label
и передать строку в параметре
Label,
text:
label = tk.Label(text="Hello, Tkinter")
При выводе текста виджеты
Label
используют системный цвет текста и цвет
фона по умолчанию. Обычно это черный и белый цвета соответственно, но они
могут быть и другими, если вы изменили соответствующие настройки в своей
операционной системе.
Для управления цветами текста и фона для виджета
метры
foreground
и
background = "Ьlack"
белый цвет текста
# Выбрать черный цвет фона
Tkinter поддерживает много разных названий
"red"
•
"orange"
•
"yellow"
•
"green"
•
"Ыuе"
•
"purple"
пара­
background:
label = tk . Label(
text="Hello, Tkinter",
foreground="white", #Выбрать
•
Label используются
цветов, в том числе:
ПРИМЕЧАНИЕ
Полный список цветов, включая системные цвета macOS и Windows, опреде­
ляемые текущей темой ОС, доступен на сайте TkDocs (https://www.tcl.tk/man/
tcl8.6/ТkCmd/colors.html).
18.5. Работа с виджетами
Многие названия цветов
подходят для
445
HTML (https.j/htmlcolorcodes.com/color-names/) также
Tkinter.
Цвета также возможно задавать шестнадцатеричными кодами
RGB:
label = tk.Label(text="Hello, Tkinter", background="#34A2FE")
Эта команда назначает
label
Шестнадцатеричные коды
приятный светло-синий цвет фона.
RGB
менее понятны, чем текстовые названия
цветов, но открывают доступ к более широкой цветовой палитре. К счастью,
существуют средства
(https.j/htmlcolorcodes.com/),
значительно упрощающие
работу с шестнадцатеричными кодами цветов.
Если вы не хотите постоянно вводить названия
пользуйте сокращенные имена
fg
и
bg для
foreground
и
background,
ис­
назначения цветов текста и фона:
label = tk.Label(text="Hello, Tkinter", fg="white",
bg="Ьlack")
Также можно управлять шириной и высотой надписи при помощи параметров
width
и
height:
label = tk.Label(
text="Hello, Tkinter",
fg="white",
bg="Ьlack" ,
width=10,
height=10
Вот как виджет
Label будет выглядеть в окне:
_,
D
х
ГЛАВА 18
446
Графические интерфейсы
Может показаться странным, что виджет в окне не квадратный, хотя и
width,
и height присвоено значение 10. Это объясняется тем, что значения height
и
width
измеряются в текстовых единицах .
Одна горизонтальная текстовая единица определяется шириной символа 0
(цифра ноль) системного шрифта по умолчанию. Аналогичным образом одна
вертикальная текстовая единица определяется высотой этого же символа
0.
ПРИМЕЧАНИЕ
Для обеспечения последовательного поведения приложения на разных плат­
формах Tkiпter измеряет высоту и ширину в текстовых единицах вместо дюй­
мов, сантиметров или пикселей.
Определение единиц по ширине символа означает, что размер виджета опре­
деляется шрифтом по умолчанию на машине пользователя. Это гарантирует,
что текст будет правильно размещаться в надписях и на кнопках независимо
от того, где выполняется приложение.
Виджеты
Label
отлично подходят для вывода текста, но они не годятся для
получения ввода от пользователя. Следующие три виджета, которые мы рас­
смотрим, предназначены для решения именно этой задачи.
Виджеты
Button
Виджеты Buttoп применяют для отображе ния кнопок, которые пользователь
может нажимать. Их можно настроить так, чтобы при щелчке на кнопке вызы­
валась заданная функция . О том, как вы з ывать функции при нажатии кнопки,
я расскажу в следующем разд еле. А пока я покажу, как создать виджет
Button
и применить к нему стилевое оформление .
Между виджетами
Button и Label существует определенное сходство. Во многих
Button - это всего лишь виджет Label, на котором можно
отношениях виджет
щелкать мышью! Ключевые аргументы, используемые при создании и стилевом
оформлении
Label, работают и с виджетами Button. Например , следующий
Button с синим фоном, желтым текстом и шириной
фрагмент создает виджет
и высотой
25
и
5 текстовых
button = tk.Button(
text="Click me!",
width=25,
height=S,
bg="Ьlue",
fg="yellow",
единиц соответственно:
18.5. Работа с виджетами
А вот как виджет
Button
447
выглядит в окне:
Неплохо!
ПРИМЕЧАНИЕ
Фоны кнопок не работают в
а не ошибка в
macOS. Это
ограничение операционной системы,
Tkinter.
Следующие два виджета, о которых речь пойдет ниже, предназначены для
получения текстового ввода от пользователя.
Виджеты
Entry
Если вам нужно получить от пользователя небольшой фрагмент текста (напри­
мер, имя или адрес электронной почты), используйте виджет
Entry. Он выводит
поле, в котором пользователь может печатать текст.
Виджеты
Entry создаются
практически так же, как
Label
и
Button.
Например,
следующая команда создает виджет с синим фоном, желтым текстом и шириной
50 текстовых единиц:
entry = tk.Entry(fg="yellow",
Впрочем, в виджетах
Entry
bg="Ьlue",
width=50)
представляет интерес вовсе не оформление, а воз ­
можность их использования для получения ввода от пользователя. С виджетами
Entry
можно выполнять три основные операции.
1.
Получение текста методом
2.
Удаление текста методом
3.
Вставка текста м етодом
. get () .
. de lete ().
. insert () .
Если вы хотите освоить виджеты
Entry,
лучше всего создать их и поэкспери­
ментировать с ними. Откройте интерактивное окно
приведенные в этом разделе.
IDLE и повторите пример ы,
448
ГЛАВА 18
Графические интерфейсы
Для начала импортируйте
tkinter
и создайте новое окно:
>>> import tkinter as tk
>>> window = tk.Tk()
Затем создайте виджеты
Label
и
Entry:
tk.Label(text="Name")
>>> label
>>> entry = tk.Entry()
Виджет Label описывает, какой текст должен быть введен в виджете Entry. Он
не устанавливает никаких требований, а лишь сообщает пользователю, какие
данные нужно здесь ввести с точки зрения вашей программы.
Далее необходимо вызвать
. pack ()
для виджетов в окне, чтобы они стали ви­
димыми:
»> label. pack()
»> entry.pack()
Результат выглядит так:
D
х
Name
Tkinter автоматически выравнивает Label по центру над виджетом Entry в окне.
Это особенность метода . pack( ), о которой вы узнаете позднее.
Щелкните внутри виджета
Entry и
введите
D
"Real Python".
х
Name
Real Python
В виджете Entry введен текст, но он еще не был передан программе.
Используйте метод
.get() виджета Entry, чтобы получить текст и присвоить
name:
его переменной с именем
>>> name = entry.get()
»> name
'Real Python'
18.5. Работа с виджетами
449
Текст можно удалить методом
мент, передаваемый
Например,
. delete () виджета Entry. Целочисленный аргу­
. delete( ), сообщает методу, какой символ следует удалить.
.delete(0)
удаляет из
Entry
первый символ:
>>> entry.delete(0)
В виджете остается текст
"eal Python" .
о
х
Name
eal Python
ПРИМЕЧАНИЕ
Символы текста в виджетах Entry, как и символы строковых объектов Python,
индексируются с нуля.
Если потребуется удалить из
. delete ()
Entry сразу несколько символов, передайте
второй целочисленный аргумент с индексом символа, на котором
удаление должно остановиться.
Например, следующая команда удаляет первые четыре буквы измененного
текста в виджете
Entry:
>>> entry.delete(0, 4)
Команда удаляет символы "е", "а" и "1" с последующим пробелом. В виджете
остается текст
"Python ".
о
х
N<3me
Python
ПРИМЕЧАНИЕ
Entry.delete() работает так же, как и срезы строк. Первый аргумент определяет
начальный индекс, а символы удаляются до индекса, переданного во втором
аргументе,
-
не включая его.
450
ГЛАВА 18
Графические интерфейсы
Entry, передайте специальную константу tk. END во
. delete ():
Чтобы удалить весь текст в
втором аргументе
>>> entry.delete(0, tk.END)
Текстовое поле остается пустым:
о
х
Name
Вставка текста в виджет
Entry выполняется
методом
.insert():
>>> entry.insert(0, "Python")
Окно выглядит так:
о
х
Name
Python
. insert (), в какой позиции должен быть вставлен
Entry нет текста, новый текст всегда вставляется в начале
Первый аргумент сообщает
текст. Если в виджете
виджета независимо от того, какое значение передается во втором аргументе.
Например, если в приведенном выше вызове
гументе 100 вместо 0,
Если виджет
. insert()
передать в первом ар­
результат окажется тем же самым .
Entry уже содержит текст, . insert ()
вставляет новый текст в за­
данной позиции и сдвигает весь существующий текст вправо:
>>> entry.insert(0, "Real ")
Теперь виджет содержит текст
"Real Python".
о
Name
Real Python
х
18.5. Работа с виджетами
Виджеты
451
Entry хорошо подходят для получения небольших фрагментов текста
от пользователя, но так как этот текст всегда отображается в одной строке, они
хуже подходят для получения больших объемов текста. На помощь приходят
виджеты
Text.
Виджеты
Text
Виджеты
используются для ввода текста, как и виджеты
Text
Entry.
Различия
в том, что первые могут содержать несколько строк текста. С виджетом
пользователь может вводить целые абзацы
Виджеты
Text,
как и виджеты
Entry,
1)
получение текста методом
2)
удаление текста методом
3)
вставка текста методом
-
Text
и даже целые страницы!
поддерживают три основные операции:
. get ( ) ;
.delete();
. insert( ).
И хотя имена методов совпадают с именами методов Entry, работают они не­
много иначе. Проверим их на практике
-
создадим виджет
Text
и посмотрим,
на что он способен.
ПРИМЕЧАНИЕ
Если окно из предыдущего раздела все еще открыто, вы можете закрыть его
следующей командой в интерактивном окне
IDLE:
>>> window.destroy()
Также окно можно закрыть вручную, щелкнув на кнопке Х в заголовке окна.
В интерактивном окне
виджет
Text
вызовом
IDLE создайте
. pack():
новое пустое окно и включите в него
>>> window = tk.Tk()
>>> text_box = tk.Text()
>>> text_box.pack()
На экране должно появиться окно с текстовым полем. Щелкните в любой точке
окна, чтобы активизировать текстовое поле. Введите слово
Enter и введите во второй строке слово "Wor ld".
Окно должно выглядеть примерно так:
"Hello", нажмите
452
ГЛАВА 18
Графические интерфейсы
о
х
Hello
1iforld
Text, как и текст виджета Entry, можно получить вызовом . get ().
. get () без аргументов не возвращает весь текст в текстовом поле,
это делается для виджетов Entry. Он выдает исключение:
Текст виджета
Однако вызов
как
>>> text_box.get()
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
text_box.get()
TypeError: get() missing 1 required positional argument: 'indexl'
Метод Text. get ( ) получает как минимум один аргумент. Вызов
. get ()
с одним
индексом возвращает один символ. Для получения нескольких символов не­
обходимо передать два аргумента: начальный и конечный индекс.
Индексы виджетов
жеты
Text
Text отличаются
от индексов виджетов
Entry. Так как
вид­
могут содержать многострочный текст, индекс должен содержать
два значения:
1)
номер строки;
2)
позицию символа в этой строке.
Номера строк начинаются с
1, а позиции символов - с 0.
Индекс строится в виде строки вида "<строка>. <символ>" (<строка> заменяется
номером строки, а <символ>
- номером символа).
Например, индекс "1. 0" представляет первый символ первой строки, а "2. з"
четвертый символ второй строки.
-
18.5. Работа с виджетами
453
Используем индекс "1.0" для получения первой буквы первой строки тексто­
вого поля, созданного ранее:
>>> text_box.get("l.0")
'Н'
Так как индексы символов начинаются с О, а слово
"Hello"
начинается в первой
позиции текстового поля, индекс буквы о равен 4. Как и в случае со срезами
строк
Python, для
извлечения всего слова Hello из текстового поля конечный
индекс должен быть на
1 больше индекса последнего читаемого символа.
Таким образом, чтобы получить из текстового поля все слово "Hello", следует
использовать первый индекс "1.0" и второй индекс "1. 5":
>>> text_box.get("l.0", "1.5")
'Hello'
Чтобы получить слово
"World" во второй строке текстового поля, измените
номер строки в каждом индексе на
2:
>>> text_box.get("2.0", "2.5")
'World'
Чтобы получить весь текст из текстового поля, передайте начальный индекс
"1. 0"
и специальную константу
tk. END
вместо второго индекса:
>>> text_box.get("l.0", tk.END)
'Hello\nWorld\n'
Обратите внимание: текст, возвращаемый
. get (),
включает символы новой
строки. Пример также показывает, что каждая строка в виджете
Text,
включая
последнюю, завершается символом новой строки.
Метод
. delete ()
используется для удаления символов из текстового поля. Он
работает так же, как и метод
Метод
. delete()
. delete ()
для виджетов Entry.
может использоваться двумя способами:
1)
с одним аргументом;
2)
с двумя аргументами.
В версии с одним аргументом
. delete()
передается индекс одного удаляемого
символа. Например, следующая команда удаляет из текстового поля первый
СИМВОЛ Н:
>>> text_box.delete("l.0")
454
ГЛАВА 18
Графические интерфейсы
В результате в первой строке остаются символы
"ello":
о
х
ello
lioi::ld
В версии с двумя аргументами передаются два индекса для удаления диапазона
символов, от символа с первым индексом и до символа со вторым индексом
(не включая последний).
Например, чтобы удалить оставшиеся символы
"ello"
из первой строки тек­
стового поля, используйте индексы "1.0" и "1.4":
>>> text_box.delete("l.0", "1.4")
Текст в первой строке исчезает. На его месте остается пустая строка, а за ней
следует слово "World" во второй строке:
о
lioi::ld
х
18.5. Работа с виджетами
При этом в первой строке остается символ, хотя он и не виден,
-
455
символ новой
строки.
В этом можно убедиться при помощи метода
. get ():
>>> text_box.get("l.0")
'\n'
Если удалить этот символ, то остальное содержимое текстового поля сдвигается
вверх на одну строку:
>>> text_box.delete("l.0")
Теперь слово
"World"
выводится в первой строке текстового поля:
о
, tk
х
iiorld
Сотрем остальной текст из текстового поля. В первом аргументе передается
значение
"1. 0",
а во втором
- tk. END:
>>> text_box.delete("l.0", tk.END)
Текстовое поле становится пустым.
Для вставки текста в текстовое поле используется метод
>>> text_box.insert("l.0", "Hello")
. insert( ):
456
ГЛАВА
18
Графические интерфейсы
В начале текстового поля появляется слово
,
tk
"Hello":
о
х
BeJ.J.o
Посмотрим, что произойдет при попытке вставить слово "World" во вторую
строку:
»> text_box.insert("2.0", "World")
Однако текст вставляется не во второй строке, а в конец первой строки:
,
tk
о
х
BeJ.J.oWorJ.d
Если вы хотите вставить текст в новую строку, придется вручную добавить
символ новой строки во вставляемый текст:
»> text_box.insert("2.0", "\nWorld")
18.5. Работа с виджетами
Теперь слово
"World"
457
оказывается во второй строке текстового поля:
о
х
Hello
World
Таким образом,
. insert()
либо вставляет текст в заданной позиции, если в ней
уже есть текст, либо присоединяет его к заданной строке, если количество сим­
волов больше индекса последнего символа в текстовом поле.
Отслеживать индекс последнего символа обычно неудобно. Лучший способ
вставки текста в конец виджета
Text -
передача
tk. END
в первом параметре
.insert():
text_box.insert(tk.END, "Put me at the end!")
Не забудьте включить символ новой строки
\n
в начало текста, если вы хотите
вывести его в новой строке:
text_box.insert(tk.END, "\nPut me
Label, Button, Entry
в
Tkinter.
и
Text -
оп а
new line!")
всего лишь малая часть виджетов, доступных
Также существуют виджеты для флажков, переключателей, полос
прокрутки и индикаторов прогресса. Дополнительную информацию о других
доступных виджетах вы найдете в учебнике на сайте
TkDocs.com ( https:j/tkdocs.
com/tutorial/widgets.html).
- с четырьмя вы уже
- Frame. Виджеты Frame игра­
В этой главе мы будем работать только с пятью виджетами
познакомились, а сейчас пришло время для пятого
ют важную роль в организации расположения виджетов в вашем приложении.
Прежде чем рассказывать о формировании визуального представления вид­
жетов, я 11окажу, как работают ви11.жеты
виджетами.
F rame и как связать их с другими
458
ГЛАВА
18
Графические интерфейсы
Связывание виджетов с
Frame
Следующая программа создает пустой виджет
Frame
и связывает его с главным
окном приложения:
import tkinter as tk
window = tk. Tk ()
frame = tk.Frame()
frame. pack()
window.mainloop()
Вызов frame. pack() упаковывает фрейм в окно так, чтобы оно имело мини­
мально возможный размер, но при этом вмещало фрейм.
При выполнении приведенного выше кода вы получите абсолютно неинтерес­
ный результат:
о
х
Пустой виджет Frame практически невидим. Фреймы лучше всего рассматри­
вать как контейнеры для других виджетов. Чтобы связать виджет с фреймом,
задайте значение атрибута master виджета:
frame
label
tk. Frame()
tk.Label(master=frame)
Чтобы вы получили представление о том, как это работает, напишем программу,
Frame с именами frame_a и frame_b. Фрейм frame_a
Label с текстом "I 'm in Frame А", а frame_b - виджет
Label с текстом "I 'm in Frame В". Одно из возможных решений выглядит так:
которая создает два виджета
должен содержать виджет
import tkinter as tk
window = tk. Tk()
tk.Frame()
frame_a
frame_b = tk.Frame()
label_a = tk.Label(master=frame_a, text="I'm in Frame
label_a.pack()
А")
label_b = tk.Label(master=frame_b, text="I'm in Frame
label_b.pack()
В")
frame_a.pack()
frame_b.pack()
window.mainloop()
18.5. Работа с виджетами
459
Обратите внимание: frame_a упаковывается в окне до frame_b. В открывшемся
окне виджет
Label
в
frame_a
располагается над виджетом
-1
D
Label
в
frame_b.
х
l'm in Frame А
l'm in Frame В
Посмотрим, что произойдет, если поменять местами вызовы
и
frame_a. pack()
frame_b. pack( ):
import tkinter as tk
window = tk.Tk()
frame_a = tk.Frame()
label_a = tk.Label(master=frame_a, text="I'm in Frame
label_a.pack()
А")
frame_b = tk.Frame()
label_b = tk.Label(master=frame_b, text="I'm in Frame
label_b. pack()
В")
'frame_a' и 'frame_ьframe_b. pack ()
frame_a.pack()
#
меняются местами
window.mainloop()
Результат выглядит так:
-1
D
х
l'm in Fram.e В
l'm in. Fram.eA
Теперь виджет label_b располагается сверху. Так как виджет label_b был связан
с
frame_b,
он перемещается каждый раз, когда вы перемещаете
Все четыре типа виджетов, о которых вы узнали,
frame_b.
- Label, Button, Entry и Text -
поддерживают атрибут master, который задается при создании экземпляра. Он
позволяет управлять тем, с каким фреймом связывается виджет.
Виджеты Frame очень удобны для логического упорядочения других виджетов.
Взаимосвязанные виджеты можно связать с одним фреймом, чтобы при пере­
мещении фрейма в окне все эти виджеты оставались в одной группе.
ГЛАВА 18
460
Графические интерфейсы
Кроме логической группировки виджетов, Frame могут добавить немного лоска
внешнему облику вашего приложения. Далее вы узнаете, как создавать различ­
ные визуальные эффекты при отображении виджетов Frame.
Изменение внешнего вида фреймов
с использованием атрибута relief
Frame можно настроить атрибутом relief, который создает визу­
эффект вокруг фрейма. Атрибуту relief можно присвоить любое из
Виджеты
альный
следующих значений:
•
tk. FLAT - визуальный эффект отсутствует (значение по умолчанию);
•
tk. SUNKEN - эффект углубления;
•
tk.RAISED- эффект возвышения;
•
tk. GROOVE - эффект выемки;
•
tk. RIDGE - эффект гребня.
Чтобы применить визуальный эффект, необходимо присвоить атрибуту
borderwidth значение больше 1. Этот атрибут задает ширину обрамления
в пикселях.
Чтобы понять, что делает каждый эффект relief, проще всего увидеть их
в действии. Следующая программа упаковывает в окне пять виджетов
с разными значениями аргумента
relief:
import tkinter as tk
border_effects = {
"flat": tk.FLAT,
"sunken": tk.SUNKEN,
"raised": tk.RAISED,
"groove": tk.GROOVE,
"ridge": tk.RIDGE,
}
window = tk. Tk()
for relief_name, relief in border_effects.items():
# 1
frame = tk.Frame(master=window, relief=relief, borderwidth=5)
# 2
frame.pack(side=tk.LEFT)
#
з
label = tk.Label(master=frame, text=relief_name)
label.pack()
window.mainloop()
Frame
18.5. Работа с виджетами
461
Выполним этот код в несколько этапов.
Сначала мы создаем словарь и присваиваем его переменной border_effects.
Ключами словаря являются названия разных эффектов, доступных в Tkinter,
а значениями
-
соответствующие объекты
Tkinter.
После создания объекта window цикл for используется для перебора всех эле­
ментов словаря
border _effects. При каждой итерации цикла выполняются
три операции.
1.
Создается новый виджет Frame, который связывается с объектом window.
Атрибуту relief присваивается соответствующий визуальный эффект
из словаря border _effects, а атрибуту border присваивается значение 5,
чтобы эффект был заметен.
2.
3.
Frame размещается в окне вызовом . pack( ) . Ключевой аргумент
side сообщает Tkinter, в каком направлении должны упаковываться объ­
екты frame. О том, как это происходит, мы расскажем в следующем разделе.
Виджет
Создается виджет
Label для вывода названия эффекта и упаковывается
в только что созданный объект frame.
Окно, которое создается при выполнении приведенного выше кода, выглядит
примерно так:
о
flat
F
х
raised l groove l @
На иллюстрации показаны примеры всех рельефных эффектов:
•
tk. FLAT создает плоское обрамление (отсутствие визуального эффекта);
•
tk. SUNKEN добавляет эффект «погружения» фрейма в окно;
•
tk. RAISED добавляет эффект, когда фрейм кажется выступающим из
экрана;
•
tk.GROOVE создает эффект углубленной борозды, окружающей плоский
фрейм;
•
tk. RIDGE создает эффект приподнятой кромки, окружающей фрейм.
Правила именования виджетов
При создании виджету можно присвоить любое имя, если оно является
Python. Тем не менее на практике имя класса
валидным идентификатором
ГЛАВА 18
462
Графические интерфейсы
виджета часто включается в имя переменной, которой присваивается экземп­
ляр виджета.
Например, если виджет Label используется для вывода имени пользователя,
ему можно присвоить имя
label_user _name. А виджет Entry, предназначенный
entry_user_age.
для ввода возраста пользователя, можно называть
Включая имя класса виджета в имя переменной, вы помогаете понять любому
читателю вашего кода, к какому типу виджета относится имя переменной.
Включение полного имени класса виджета может привести к появлению длин­
ных имен переменных, поэтому для типов виджетов стоит ввести сокращенные
обозначения. Далее в этой главе в именах виджетов мы будем использовать
следующие префиксы.
КЛАСС ВИДЖЕТА
ПРЕФИКС
ПРИМЕР
lЫ_name
Label
lЫ
Button
btn
btn_ submit
Entry
ent
ent_age
Text
txt
txt_notes
Frame
frm
frm_address
В этом разделе я показал, как создавать окна, использовать виджеты и работать
с фреймами. На данный момент вы умеете создавать простые окна, в которых
выводятся сообщения, но до полноценных приложений дело еще не дошло.
В следующем разделе вы научитесь управлять макетом ваших приложений при
помощи топологических менеджеров Tkinteг.
Упражнения
1.
Попробуйте воссоздать скриншоты из этого раздела, не подглядывая в ис­
ходный код. Если вы зайдете в тупик, загляните в код и завершите упраж­
нение. Затем подождите
10-15
минут и попробуйте снова. Повторяйте,
пока не сможете проделать все это самостоятельно. Сосредоточьтесь на
выводе. Если ваш код будет слегка отличаться от приведенного в книге,
это абсолютно нормально.
2.
Напишите программу, которая выводит виджет
стовых единиц и высотой
25
Button шириной 50 тек­
текстовых единиц. Виджет должен иметь
белый фон с синим текстом "Click here".
3.
Напишите программу для вывода виджета
Entry шириной 40 текстовых
единиц, с белым фоном и черным текстом. Используйте метод . insert()
для вставки в виджет
Entry
текста
"What is your name?".
18.6. Управление макетом при помощи менеджеров геометрии
18.6. УПРАВЛЕНИЕ
463
МАКЕТОМ ПРИ ПОМОЩИ
МЕНЕДЖЕРОВ ГЕОМЕТРИИ
До настоящего момента мы добавляли виджеты в окна и виджеты Frame вызовом
. pack( ), но вы пока не знаете, что именно делает этот метод. Давайте разберемся!
В
Tkinter
разработчик управляет размещением виджетов в окне приложения
при помощи менеджеров геометрии. Метод
. pack()
является примером менед­
жера геометрии, но это не единственный представитель этого типа. Кроме него,
в
Tkinter также есть два других метода: . place ()
Каждое окно и каждый виджет
и
. grid ().
Frame в приложении может использовать только
один менеджер геометрии. Однако разные фреймы могут применять разные
менеджеры, даже если они связываются с фреймом или окном с использованием
другого менеджера геометрии.
Для начала поближе познакомимся с
Менеджер геометрии
Метод
. pack().
.pack()
использует специальный алгоритм для размещения виджетов
.pack()
во фрейме или окне в определенном порядке. Алгоритм упаковки состоит из
двух основных этапов.
1.
Алгоритм вычисляет прямоугольную область с минимальной высотой
(или шириной), достаточной для размещения виджета, и заполняет
оставшуюся ширину (или высоту) окна пустым пространством.
2.
Если вы не задали другой способ размещения, виджет выравнивается
по центру области.
Метод. pack() очень мощный, но его трудно представить себе наглядно. Понять,
как он работает, лучше всего на примерах.
Посмотрим, что происходит при размещении трех виджетов
Frame
вызовами
import
tkiпter
wiпdow
= tk.Tk()
Label
. pack( ):
as tk
framel = tk.Frame(master=window, width=100, height=100, bg="red")
framel. pack()
frame2 = tk.Frame(master=window, width=50, height=50, bg="yellow")
frame2.pack()
в виджете
ГЛАВА 18
464
frameЗ
frameЗ.
Графические интерфейсы
= tk.Frame(master=window, width=25, height=25,
pack()
bg="Ьlue")
window.mainloop()
По умолчанию
. pack()
размещает каждый виджет Frame под предыдущим в том
порядке, в котором они связывались с окном.
о
х
11
Каждый виджет Frame размещается в высшей доступной позиции. Красный
виджет Frame размещается у верхнего края окна. Желтый виджет Frame раз­
мещается непосредственно под красным, а синий
-
под желтым .
Существуют три невидимые области, каждая из которых содержит один из трех
виджетов Frame. Каждая область имеет ширину окна и высоту виджета Frame,
который в ней содержится. Так как при вызове .pack() для каждого виджета
Frame
якорная точка не указывалась, все они выравниваются по центру своих
областей, а следовательно, по центру окна.
Метод
. pack()
получает ключевые аргументы, позволяющие точнее настро­
ить расположение виджетов. Например, ключевой аргумент
fill позволяет
указать, в каком направлении должны заполняться фреймы. Поддерживаются
три значения:
1) tk.X -
заполнение в горизонтальном направлении;
2) tk. У -
заполнение в вертикальном направлении;
3) tk. вотн -
заполнение в обоих направлениях.
В следующем примере три фрейма размещаются так, чтобы каждый заполнял
все окно по горизонтали:
18.6. Управление макетом при помощи менеджеров геометрии
465
import tkinter as tk
window = tk. Tk()
framel = tk.Frame(master=window, height=100, bg="red")
framel.pack(fill=tk.X)
frame2 = tk.Frame(master=window, height=50, bg="yellow")
frame2.pack(fill=tk.X)
frameЗ
= tk.Frame(master=window, height=25,
bg="Ьlue")
frameЗ.pack(fill=tk.X)
window.mainloop()
Обратите внимание: для виджетов Frame значение width более не задается. Оно
не нужно, потому что
. pack ()
настраивается на горизонтальное заполнение
в каждом фрейме, с переопределением любой заданной ширины.
Окно, созданное этим фрагментом, выглядит так:
Одно из преимуществ заполнения окна методом
. pack ()
заключается в том, что
заполнение реагирует на изменение размеров окна. Чтобы понять, как это про­
исходит, попробуйте увеличить ширину окна, сгенерированного приведенным
выше кодом.
При увеличении ширины окна ширина трех виджетов
Frame увеличивается для
Frame не расширяются
заполнения окна. Однако следует заметить, что виджеты
в вертикальном направлении .
side метода . pack() указывает, с какой стороны окна дол­
- tk. ТОР, tk. воттом, tk. LEFT
и tk. RIGHT. Если сторона не задана, . pack() автоматически использует tk. ТОР
Ключевой аргумент
жен размещаться виджет . Допустимые варианты
и размещает новые виджеты у верхнего края окна или в самой верхней части
окна, которая еще не занята виджетом.
466
ГЛАВА
18
Графические интерфейсы
Например, следующая программа размещает три фрейма рядом друг с другом
слева направо и расширяет каждый фрейм, чтобы он заполнял окно по верти­
кали:
import tkinter as tk
window = tk.Tk()
мframel = tk.Frame(master=window, width=200, height=100, bg="red"}
framel.pack(fill=tk.Y, side=tk.LEFT}
frame2 = tk.Frame(master=window, width=100, bg="yellow")
frame2.pack(fill=tk.Y, side=tk.LEFT}
frameЗ
= tk.Frame(master=window, width=50,
side=tk.LEFT}
bg="Ьlue")
frameЗ.pack(fill=tk.Y,
window.mainloop()
На этот раз нам приходится задать ключевой аргумент
height по крайней мере
для одного из фреймов, чтобы задать высоту окна.
Полученное окно выглядит так:
о
Подобно тому как присваивание fill=tk.X меняло ширину фреймов при из­
менении ширины окна, присваивание
fill=tk. У
меняет высоту фреймов при
изменении размеров окна по вертикали. Убедитесь сами!
Чтобы макет полноценно реагировал на корректировку размеров, можно за­
дать исходный размер ваших фреймов при помощи атрибутов width и height.
fill метода . pack () значение tk. ВОТН ,
expand - значение True:
Затем задайте ключевому аргументу
а ключевому аргументу
import tkinter as tk
window = tk. Tk ()
framel = tk.Frame(master=window, width=200, height=100, bg="red")
framel.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)
frame2 = tk.Frame(master=window, width=100, bg="yellow")
frame2.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)
18.6. Управление макетом при помощи менеджеров геометрии
frameЗ
467
= tk.Frame(master=window, width=50, bg="Ьlue")
side=tk.LEFT, expand=True)
frameЗ.pack(fill=tk.BOTH,
window.mainloop()
При выполнении этого кода появляется окно, которое изначально похоже на
созданное в предыдущем примере. Различие в том, что теперь при изменении
размеров окна фреймы будут расширяться и заполнять окно соответствующим
образом. Впечатляет!
Менеджер геометрии
Метод
.place()
.place() позволяет управлять точным расположением виджета в окне
или фрейме. При вызове метода должны передаваться два ключевых аргумен­
тах и у, определяющих координаты левого верхнего угла виджета. Значениях и у
задаются в пикселях, а не в текстовых единицах.
Помните, что начало координат (точка, для которой координаты х и у равны
0)
располагается в левом верхнем углу фрейма или окна. Аргумент у метода
. place()
можно рассматривать как расстояние в пикселях от верхнего края
окна, а аргумент х
-
как расстояние в пикселях от левого края.
Пример использования менеджера геометрии
. place ():
import tkinter as tk
window = tk. Tk ()
# 1
frame = tk.Frame(master=window, width=150, height=150)
frame. pack()
# 2
labell = tk.Label(master=frame, text="I'm at (0, 0)", bg="red")
labell.place(x=0, у=0)
# 3
label2 = tk.Label(master=frame, text="I'm at (75, 75)", bg="yellow")
label2.place(x=75, у=75)
window.mainloop()
Сначала создается новый виджет
в ширину и
Frame с именем frame, имеющий 150 пикселей
150 пикселей в высоту, который размещается в окне вызовом . pack( ).
Затем создается элемент Label с красным фоном, которому присваивается имя
labell; он размещается в framel в позиции (О, О). Наконец, создается второй
виджет Label с желтым фоном, которому присваивается имя label2, и он раз­
мещается в framel в позиции (75, 75).
ГЛАВА
468
18
Графические интерфейсы
Окно, созданное этим кодом, показано ниже:
о
х
l'm at (75, 75)
Метод
. place()
1. . place()
используется не так часто. Он имеет два основных недостатка:
усложняет управление макетами, особенно если в вашем при­
ложении используется множество виджетов.
2.
Макеты, созданные вызовом
. place (),не динамичны: они не изменяются
при изменении размеров окна.
Одна из основных трудностей разработки кросс-платформенных графических
интерфейсов связана с созданием макетов, которые выглядят хорошо независимо
от платформы. В большинстве случаев
. place() плохо подходит для создания
динамичных кросс-платформенных макетов.
Это не означает, что
. place()
вообще не стоит использовать. В каких-то случаях
это именно то, что вам нужно. Например, когда вы создаете графический интер­
фейс для интерактивной карты,
. place ()
может оказаться идеальным вариантом
для размещения виджетов на правильном расстоянии друг от друга на карте.
Метод . pack() обычно работает лучше. place(), но даже у. pack() есть свои не­
достатки. Например, размещение виджетов зависит от порядка вызова
. pack (),
что затрудняет изменение существующих приложений без полного понимания
кода, управляющего макетом.
Как будет показано в следующем разделе, менеджер геометрии . grid () решает
многие из этих проблем.
Менеджер геометрии
.grid()
Вероятно, чаще всего вы будете использовать менеджер геометрии
Он предоставляет всю мощь
и сопровождения.
. grid ().
. pack() в формате, более простом для понимания
18.6. Управление макетом при помощи менеджеров геометрии
469
Работа . grid () основана на разбиении окна или фрейма на строки и столбцы.
Вы указываете местоположение виджета, вызывая
. grid (), и передаете индексы
строки и столбца в ключевых аргументах row и column соответственно. Индексы
строк и столбцов начинаются с 0, поэтому индекс строки 1 и индекс столбца
2 приказывают .grid() разместить виджет в третьем столбце второй строки.
Например, следующий код создает сетку ЗхЗ из фреймов, в которых упакованы
виджеты
Label:
import tkinter as tk
window = tk. Tk()
for
1 ln range(З):
for j in range(З):
frame = tk.Frame(
master=window,
relief=tk.RAISED,
borderwidth=l
frame.grid(row=i, column=j)
label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}")
label.pack()
window.mainloop()
Построенное окно выглядит так.
1 tk
-
D
х
RowO
RowO
RowO
ColumnO Column 1 Column2.
Row1
Row1
Row1
ColumnO Column 1 Column2.
Row2.
Row2.
Row2.
ColumnO Column 1 Column2.
В этом примере используются два менеджера геометрии. Каждый виджет
связывается с окном при помощи менеджера
.grid(),
связывается со своим фреймом-контейнером вызовом
а каждый виджет
Frame
Label
. pack( ).
Здесь важно понимать, что, хотя .grid() вызывается для объектов Frame, ме­
неджер геометрии применяется к объекту window. Аналогичным образом рас­
положением каждого объекта
frame управляет менеджер геометрии .pack().
470
ГЛАВА 18
Графические интерфейсы
Фреймы в предыдущем примере размещаются вплотную друг к другу. Чтобы
добавить немного свободного места вокруг каждого виджета Frame, можно на­
значить отступы для каждой ячейки сетки. Отступ представляет собой пустое
место вокруг виджета, визуально отделяющее его от содержимого.
Существуют два типа отступов: внешние и внутренние. Внешние отступы до­
бавляют свободное место вокруг внешнего контура ячейки. Для управления
ими используются два ключевых аргумента
. grid () :
1) padx добавляет отступы
в горизонтальном направлении;
2) pady добавляет отступы
в вертикальном направлении.
Значения padx и pady измеряются в пикселях, а не в текстовых единицах, по­
этому одинаковые значения создают одинаковые отступы в обоих направлениях.
Добавим внешние отступы вокруг фреймов из предыдущего примера:
import tkinter as tk
window = tk.Tk()
for i in range(З):
for j in range(З):
frame = tk.Frame(
master=window,
relief=tk.RAISED,
borderwidth=l
frame.grid(row=i, column=j, padx=S, pady=S)
label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}")
label. pack()
window.mainloop()
Получаем окно следующего вида:
1 tk
D
Х
RowO
Column О
RowO
Column 1
RowO
Column 2
Row 1
CofumnO
Row 1
Cofumn 1
Row 1
Col.umn2
Row2
Co!umn О
Row2
Column 1
Row2
Cofumn 2
18.6. Управление макетом при помощи менеджеров геометрии
Метод
. pack()
тоже содержит параметры
471
padx и pady. Следующий фрагмент
Label
почти идентичен предыдущему, не считая того, что вокруг каждого виджета
создаются дополнительные отступы размером
5 пикселей в направлениях х и у:
import tkinter as tk
window = tk. Tk()
for
1 in range(З):
for j in range(З):
frame = tk.Frame(
master=window,
relief=tk.RAISED,
borderwidth=l
frame.grid(row=i, column=j, padx=S, pady=S)
label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}")
label.pack(padx=S, pady=S)
window.mainloop()
Дополнительные отступы вокруг виджетов
Label создают в каждой ячейке сетки
немного свободного места между границей Frame и текстом Label.
1 tk
о
-
х
RowO
ColumnO
RowO
Column 1
RowO
Column2
Row1
ColumnO
Row1
Columл
1
Row1
Column2
Row2
ColumnO
Row2
Column 1
Row2
Column2
Симпатично выглядит! Но если вы попробуете расширить окно в каком-либо
направлении, то увидите, что макет плохо реагирует на изменения. При увели­
чении окна вся сетка остается в левом верхнем углу.
Чтобы управлять размерами строк и столбцов сетки при изменении размеров
окна, используйте методы
. columnconfigure()
и
. rowconfigure() окна window.
. grid () вызывается
Помните, что сетка связана с окном несмотря на то, что
для каждого виджета
Frame.
ГЛАВА 18
472
Как
Графические интерфейсы
. columnconfigure(),
так и
. rowconfigure()
получают три необходимых
аргумента:
1)
индекс столбца или строки, который вы хотите настроить (или список
индексов для настройки нескольких строк или столбцов);
2)
ключевой аргумент weight, определяющий реакцию столбца или строки
на изменение размеров окна относительно других столбцов и строк;
3)
ключевой аргумент
minsize, задающий минимальную высоту строки или
ширину столбца в пикселях.
Ключевой аргумент
weight
(вес) по умолчанию задает значение
0,
которое
означает, что столбец или строка не расширяется при изменении размеров
окна. Если каждому столбцу или строке назначен вес
1, все они увеличиваются
- вес
в одинаковых пропорциях. Если одному столбцу назначен вес 1, а другому
2, то второй столбец будет увеличиваться вдвое быстрее первого.
Откорректируем предыдущий код, чтобы он лучше обрабатывал изменение
размеров окна:
import tkinter as tk
window = tk. Tk()
for i in range(З):
window.columnconfigure(i, weight=l, minsize=75)
window.rowconfigure(i, weight=l, minsize=50)
for j in range(0, З):
frame = tk.Frame(
master=window,
relief=tk.RAISED,
borderwidth=l
frame.grid(row=i, column=j, padx=S, pady=S)
label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}")
label.pack(padx=S, pady=S)
window.mainloop()
Методы
. columnconfigure()
и
. rowconfigure()
размещаются в теле внешнего
цикла for. Каждый столбец и каждую строку можно настроить явно за преде­
лами цикла for, но для этого потребуется написать шесть лишних строк кода.
При каждой итерации цикла i-e столбец и строка настраиваются с весом
равным
1.
weight,
Это гарантирует, что каждая строка и каждый столбец расширяются
с одинаковой скоростью при изменении размеров окна.
18.6. Управление макетом при помощи менеджеров геометрии
473
Аргументу minsize присваивается значение 75 для каждого столбца и 50 для
каждой строки. Это гарантирует, что виджет Label всегда выводит текст без от­
сечения символов даже при очень малом размере окна. Попробуйте выполнить
код, чтобы получить представление о том, как он работает. Поэксперименти­
руйте с параметрами
weight
и
minsize
и посмотрите, как они влияют на сетку.
По умолчанию виджеты выравниваются по центру своих ячеек. Например,
следующий код создает два виджета
Label и размещает их в сетке с одним
столбцом и двумя строками:
import tkinter as tk
window = tk. Tk()
window.columnconfigure(0, minsize=250)
window.rowconfigure([0, 1], minsize=100)
labell = tk.Label(text="A")
labell.grid(row=0, column=0)
label2 = tk.Label(text="B")
label2.grid(row=l, column=0)
window.mainloop()
Каждая ячейка таблицы
Виджеты
- 250
пикселей в ширину и
100
пикселей в высоту.
Label размещаются в центре каждой ячейки, как видно из следующей
иллюстрации:
1 tk
D
х
А
в
Вы можете изменить расположение каждого виджета
при помощи параметра
sticky метода . grid ().
Label внутри ячейки сетки
Параметру
строка, содержащая одну или несколько букв:
sticky присваивается
474
ГЛАВА
18
Графические интерфейсы
•
"n"
или "N" для выравнивания по центру верхней стороны ячейки;
•
"s"
или
•
"е" или "Е" для выравнивания по центру правой стороны ячейки;
•
"w" или "W" для выравнивания по центру левой стороны ячейки.
"5"* для выравнивания по центру нижней стороны ячейки;
Буквы "n", "s", "е" и "w" происходят от названий
East и West - север, юг, восток и запад).
сторон света
Например, если присвоить sticky значение
для обоих виджетов Label
"n"
(North, South,
в предыдущем фрагменте, каждый виджет Label будет расположен в середине
верхней стороны ячейки:
import tkinter as tk
window = tk. Tk ()
window.columnconfigure(0, minsize=250)
window.rowconfigure([0, 1], minsize=100)
labell = tk.Label(text="A")
labell.grid(row=0, column=0, sticky="n")
label2 = tk.Label(text="B")
label2.grid(row=l, column=0, sticky="n")
window.mainloop()
Результат показан ниже:
1 tk
о
А
в
х
18.6. Управление макетом при помощи менеджеров геометрии
Чтобы разместить каждый виджет
475
Label в углу ячейки, объедините несколько
букв в одной строке:
import tkinter as tk
window = tk.Tk()
window.columnconfigure(0, minsize=250)
window.rowconfigure([0, 1], minsize=100)
labell = tk.Label(text="A")
labell.grid(row=0, column=0, sticky="ne")
label2 = tk.Label(text="B")
label2.grid(row=l, column=0, sticky="sw")
window.mainloop()
В этом примере параметру
sticky
виджета
labell
присваивается значение
"пе", при котором виджет размещается в правом верхнем углу ячейки.
размещается в левом нижнем углу, для чего
sticky присваивается
label2
строка" sw".
Вот как это выглядит в окне:
х
D
А
в
При позиционировании виджета с использованием
sticky задается минималь­
ный размер виджета, которого достаточно, чтобы он вместил текст и любой
другой контент, находящийся внутри него. Виджет не заполняет всю ячейку.
Чтобы виджет заполнил ячейку, укажите значение
"ns" ( виджет заполнит ячей­
ку по вертикали) или "ew" (виджет заполнит ячейку по горизонтали). Чтобы
заполнить всю ячейку, присвойте
sticky значение "nsew".
ГЛАВА
476
18
Графические интерфейсы
Все эти возможности продемонстрированы в следующем примере:
import tkinter as tk
window = tk. Tk()
window.rowconfigure(0, minsize=50)
window.columnconfigure([0, 1, 2, 3), minsize=50)
labell = tk.Label(text="l",
label2 = tk.Label(text="2",
labelЗ = tk.Label(text="З",
label4 = tk.Label(text="4",
bg="Ыack",
bg="Ыack",
bg="Ыack",
bg="Ыack",
fg="white")
fg="white")
fg="white")
fg="white")
labell.grid(row=0, column=0)
label2.grid(row=0, column=l, sticky="ew")
label3.grid(row=0, column=2, sticky="ns")
label4.grid(row=0, column=З, sticky="nsew")
window.mainloop()
Результат выглядит так:
1 tk
1Этот пример показывает, что при помощи параметра
метрии
. grid ()
sticky
менеджера гео­
можно достичь того же эффекта, что и с параметром
неджера геометрии
fill ме­
. рас k ( ) .
Соответствие между параметрами sticky и fill показано в следующей таблице .
.GRID()
•РАСК()
sticky="ns"
fill=tk. у
sticky="ew"
fill=tk.X
sticky="nsew"
fill =tk.BOTH
. grid () - очень мощный менеджер геометрии. Обычно он более понятен, чем
.pack(), и намного более гибок, чем .place(). При создании новых приложе­
ний
Tkinter в
именно
качестве основного менеджера геометрии стоит рассматривать
.grid() .
18.6. Управление макетом при помощи менеджеров геометрии
477
ПРИМЕЧАНИЕ
Менеджер геометрии
.grid() обладает гораздо более гибкими возможностями,
чем мне удалось показать здесь. Например, вы можете настроить ячейки так,
чтобы они охватывали несколько строк и столбцов .
Дополнительную информацию вы найдете в разделе «Grid Geometry Maпager»
(https://tkdocs.com/tutorial/grid.html} учебного руководства TkDocs (https://
tkdocs.com/tuto ria l/i ndex. htm 1}.
Теперь, когда вы познакомились с азами использования менеджеров геометрии
Tkinter, я предлагаю вам сделать следующий шаг -
оживите интерфейс вашего
приложения, связывая с кнопками выполняемые действия .
Упражнения
1.
Попробуйте воссоздать скриншоты из этого раздела, не подглядывая
в исходный код. Если вы зайдете в тупик, просмотрите код и завершите
упражнение. Затем подождите
10-15 минут и попробуйте снова. Повто­
ряйте, пока не сможете проделать все это самостоятельно. Сосредоточь­
тесь на выводе. Если ваш код будет слегка отличаться от приведенного
в книге, это абсолютно нормально.
2.
Ниже показано окно, созданное в Tkinter. Попробуйте воссоздать его
средствами, о которых вы узнали в этой главе. Используйте любой ме­
неджер геометрии на свое усмотрение .
1 Address Entry Form
-
First Name: 1
Last Name: 1
о
х
...
--
- --
..
-
Address l ine 1: 1
-~ ·- · -- ··
-
Address l ine 2: 1
~.
-
-
-
-
City: 1
-
State/Province: 1
-
·-
Post:a/ Code: 1
Countr:y: 1
-
-
Г
Clear
1
1
Submit
1
Графические интерфейсы
478
ГЛАВА 18
18.7.
ИНТЕРАКТИВНОСТЬ В ПРИЛОЖЕНИЯХ
К настоящему моменту вы уже достаточно хорошо представляете, как создать
окно в
Tkinter,
добавить виджеты и управлять макетом приложения. И это
прекрасно! Однако приложения должны не только хорошо выглядеть
-
они
должны что-то делать.
Сейчас мы покажем, как оживить ваши приложения, чтобы они выполняли
определенные действия при возникновении тех или иных событий.
События и обработчики событий
Создавая приложение
window.mainloop().
Tkinter,
вы должны запустить цикл событий вызовом
В цикле ваше приложение проверяет, произошло ли это
событие. Если оно произошло, приложение может среагировать на него вы­
полнением некоторого кода.
Цикл событий предоставляет
Tkinter,
так что вам не придется писать код для
проверки возникновения событий. Тем не менее вам придется написать код,
выполняющий некоторое действие в ответ на событие. В
Tkinter для событий,
используемых в вашем приложении, предназначены функции, называемые
обработчиками событий.
Что же такое событие и что происходит при его возникновении?
Событие представляет собой любое действие, которое происходит во время
цикла событий (например, нажатие пользователем клавиши или кнопки мыши)
и активизирует некую реакцию в приложении.
При возникновении события выдается объект события; это означает, что
создается экземпляр класса, представляющего данное событие. Вам не нужно
беспокоиться об этих экземплярах
- Tkinter создает их за вас
Чтобы лучше понять, как работает цикл событий
автоматически.
Tkinter, можно написать соб­
Tkinter интегриру­
ственный цикл событий. Это покажет вам, как цикл событий
ется в ваше приложение и какие его части вы должны написать самостоятельно.
Допустим, список
events_list содержит объекты событий. Каждый раз, когда
events_list присоединяется новый объ­
в программе происходит событие, к
ект события. Вам не нужно реализовать этот механизм обновлений
-
в нашем
примере он работает как по волшебству.
В бесконечном цикле вы можете непрерывно проверять, присутствуют ли
в
events_list какие-либо объекты событий:
18.7.
#
Предполагается,
Интерактивность в приложениях
479
что список обновляется автоматически.
events_list = []
# Запустить цикл событий.
while True:
#
#
Если список
events_list
и можно переходить
к
значит,
пуст,
события
итерации
следующей
не происходили
цикла.
if events_list == []:
continue
#
#
Если выполнение достигает этой точки,
значит,
в
events_list
содержится как минимум один объект события
event
=
events_list[0]
Пока созданный нами цикл событий ничего не делает с событием. Исправим
этот недочет.
Допустим, ваше приложение должно реагировать на нажатия клавиш. Необхо­
димо проверять, было ли сгенерировано событие (нажал ли пользователь кла­
вишу на клавиатуре), и, если было,
-
передать событие функции-обработчику
события «нажатие клавиши».
- это нажатие клавиши, то его объект event
type, которому присвоена строка "keypress", и атрибут .char,
Будем считать, что если событие
имеет атрибут
который содержит символ, соответствующий нажатой клавише.
Добавим функцию
handle_keypress() и обновим код цикла событий:
ever1ts_list = []
#
Создать обработчик события
def handle_keypress(event):
"""Вывести символ,
связанный с
нажатой
клавишей"""
print(event.char)
while True:
if events_list == []:
continue
event = events_list[0]
Если event является объектом
if event.type == "keypress":
#
события нажатия клавиши
# Вызвать обработчик события нажатия клавиши
handle_keypress(event)
Когда вы вызываете метод
window. mainloop() из библиотеки Tkinter, выполня­
.mainloop() берет на себя
ется некое подобие этого цикла. А конкретно метод
две части этого цикла:
1.
Он поддерживает список генерируемых событий.
ГЛАВА 18
480
2.
Графические интерфейсы
Он выполняет обработчик события при добавлении нового события
в список.
Вы можете обновить свой цикл событий, чтобы вместо вашего собственного
цикла событий использовался метод window.mainloop( ):
import tkinter as tk
#
Создать объект окна
window
#
=
tk. Tk()
Создать обработчик события
def handle_keypress(event):
"""Вывести символ,
связанный с нажатой клавишей"""
print(event.char)
# Запустить цикл событий
window.mainloop()
. mainloop()
многое делает за вас, но в этом коде чего-то не хватает. Откуда
Tkinter узнает,
когда следует вызывать handle_keypress ()?
Оказывается, у виджетов
Метод
Tkinter есть метод . bind (),который
помогает в этом.
.blnd()
Чтобы обработчик события вызывался каждый раз, когда в виджете проис­
ходит событие, можно воспользоваться методом виджета . bind (). Говорят, что
обработчик события связывается с событием, потому что он вызывается при
каждом возникновении события.
Продолжим пример с нажатием клавиши, приведенный в предыдущем раз­
деле, и воспользуемся
. bind ()
для связывания handle_keypress () с событием
нажатия клавиши:
import tkinter as tk
window = tk. Tk()
def handle_keypress(event):
"""Вывести символ,
связанный с нажатой клавишей"""
print(event.char)
Связать событие нажатия клавиши с handle_keypress()
window.bind("<Key>", handle_keypress)
#
window.mainloop()
18.7. Интерактивность в приложениях
481
Здесь обработчик события handle_keypress () связывается с событием "<Кеу>"
вызовом window. bind( ). Каждый раз, когда во время выполнения приложения
пользователь нажимает клавишу, выводится символ, соот.ветствующий этой
клавише.
Метод. bind() всегда получает два аргумента:
1)
событие, представленное строкой в форме "<имя_события>", где им.я_со­
бытия может быть любым из событий
2)
обработчик события
-
Tkinter;
имя функции, которая должна вызываться при
возникновении события.
Обработчик события связывается с виджетом, для которого вызывается . Ьind( ).
При вызове обработчика события функции-обработчику передается объект
события.
В предыдущем примере обработчик события связывается с самим окном, но его
также можно связать с любым виджетом в вашем приложении. Например, об­
работчик события можно связать с виджетом Button, который будет выполнять
некоторое действие при щелчке на кнопке:
def handle_click(event):
print("The button was clicked!")
button = tk.Button(text="Click me!")
button.bind("<Button-1>", handle_click)
В этом примере событие "<Button-1>" виджета button связывается с обработ­
чиком события handle_click. Событие "<Button-1>" происходит при щелчке
левой кнопкой мыши, когда указатель мыши находится над виджетом.
Существуют и другие события в ответ на щелчки мыши, включая "<Button-2>"
для средней кнопки (если она существует) и
"<Button-3>" для правой кнопки .
ПРИМЕЧАНИЕ
Список часто используемых событий вы найдете в разделе «Eveпt types» спра­
вочника
Tkinter 8.5 (https://realpythoп.com/pybasics-event-types).
Обработчик событий можно связать с любой разновидностью виджетов методом
. Ьind( ), но существует и более простой способ связывания обработчиков со­
бытий со щелчками на кнопке - он реализуется при помощи атрибута command
виджета
Button.
482
Графические интерфейсы
ГЛАВА 18
Атрибут command
Каждый виджет Button содержит атрибут command, который можно присвоить
функции. Она выполняется при нажатии кнопки.
Label, содержащим
Label располагаются кнопки.
Левая кнопка будет уменьшать значение в Label, а правая - увеличивать его.
Рассмотрим пример. Сначала создадим окно с виджетом
числовое значение. Слева и справа от виджета
Ниже приведен код для создания этого окна:
import tkinter as tk
window = tk. Tk ()
window.rowconfigure(0, minsize=50, weight=l)
window.columnconfigure([0, 1, 2], minsize=50, weight=l)
btn_decrease = tk.Button(master=window, text="-")
btn_decrease.grid(row=0, column=0, sticky="nsew")
lЫ_value
= tk.Label(master=window, text= "0")
column=l)
1Ы_value.grid(row=0,
btn_increase = tk.Button(master=window, text="+")
btn_increase.grid(row=0, column=2, sticky="nsew")
window . mainloop()
Окно выглядит так:
,
о
о
х
_J
Мы определили макет приложения, теперь оживим его, назначив кнопкам
команды.
Начнем с левой кнопки. При ее нажатии значение в виджете
уменьшаться на
1. Чтобы
Label будет
реализовать эту возможность, необходимо знать, как
выполняются две операции: получение текста Label и обновление текста Label.
У виджетов
Label,
в отличие от виджетов
Entry
и
Text,
нет метода
. get ().Од­
нако текст метки можно получить, обратившись к атрибуту text в синтаксисе,
сходном с синтаксисом индексирования словарей:
18.7. Интерактивность
в приложениях
483
label = Tk.Label(text="Hello")
Получить текст Label
text = label["text"]
#
#
Назначить новый текст
label["text"] = "Good
Ьуе"
Итак, вы научились получать и назначать текст
щ1ю, которая увеличивает значение
Label
на
Label
и можете написать функ­
1:
def increase():
value = int(lЫ_value["text"])
lЫ_value["text"] = f"{value + 1}"
increase() получает текст из lЫ_value и преобразует его в целое
int (). Затем она увеличивает полученное значение на 1 и при­
сваивает результат атрибуту text виджета Label.
Функция
число вызовом
Также напишем функцию
на
decrease( ),
которая уменьшает значение lЫ_value
1:
def decrease():
value = int(lЫ_value["text"])
lЫ_value["text"] = f"{value - 1}"
Разместите функции
increase()
и
decrease()
в коде сразу же после команды
import.
Чтобы связать кнопки с функциями, присвойте функцию атрибуту
command
кнонки. Это можно сделать при создании экземпляра кнопки. Например, чтобы
присвоить
increase()
переменной
btn_increase,
приведите строку, в которой
сшдается экземпляр кнопки, к следующему виду:
btn_increase = tk.Button(master=window, text="+", command=increase)
Теперь 11рисвойте
decrease()
переменной Ьtn_decrease:
btn_decrease = tk.Button(master=window, text="-", command=decrease)
Вот и вес, что следует сделать для связывания кнопок с функциями
и
decrease()
increase()
и наделения нрограммы полезной функциональностью. Попро­
буйте сохранить изменения и эапустить приложение.
Ниже для удобства мы приводим полный код приложения:
import tkinter as tk
def increase():
484
ГЛАВА 18
value •
Графические интерфейсы
int(lЫ_value["text"])
lЫ_value["text"]
• f"{value + 1}"
def decrease():
value • int(lЫ_value["text"])
lЫ_value["text"] • f"{value - 1}"
window • tk. Tk()
window.rowconfigure(0, minsize•50, weight•l)
window.columnconfigure([0, 1, 2], minsize-50, weight•l)
btn_decrease • tk.Button(master•window, text•"-", command•decrease)
btn_decrease.grid(row•0, column•0, sticky•"nsew")
lЫ_value
• tk.Label(master•window, text•"0")
column•l)
1Ы_value.grid(row•0,
btn_increase • tk.Button(master•window, text•"+", command•increase)
btn_increase.grid(row-0, column•2, sticky•"nsew")
window.mainloop()
Полученное приложение не особенно полезно, но приобретенные сейчас навыки
пригодятся вам при создании любого другого приложения:
•
используйте виджеты для создания компонентов пользовательского
интерфейса;
•
используйте менеджеры геометрии для управления макетом приложе­
ния;
•
создавайте функции, которые взаимодействуют с различными компо­
нентами для получения и преобразования пользовательского ввода.
В следующих двух разделах я покажу, как строить приложения, которые делают
что-то полезное. Сначала мы построим программу преобразования температур,
введенных по шкале Фаренгейта, к температурам по шкале Цельсия. Затем мы
создадим текстовый редактор, который может открывать, редактировать и со­
хранять текстовые файлы.
Упражнения
1.
Напишите программу, которая выводит одну кнопку с цветом фона по
умолчанию и черным текстом
"Click me". Когда пользователь щелкнет
на кнопке, ее фон должен окрашиваться в цвет, случайным образом вы­
бранный из следующего списка:
["red", "orange", "yellow",
"Ыuе",
"green", "indigo", "violet"]
18.8. Пример приложения: конвертер температур
2.
485
Напишите программу, моделирующую бросок игрального кубика. В про­
грамме должна быть одна кнопка с текстом "Roll". Когда пользователь
щелкает на кнопке, должно выводиться случайное цел.:ое число от
1 до 6.
Окно приложения должно выглядеть примерно так:
о
х
Roll
4
18.8. ПРИМЕР
ПРИЛОЖЕНИЯ:
КОНВЕРТЕР ТЕМПЕРАТУР
Сейчас мы создадим программу преобразования температур: пользователь
вводит температуру в градусах по шкале Фаренгейта и щелчком кнопки пре­
образует ее к значению по Цельсию.
Рассмотрим код шаг за шагом. Полный код приложения мы приводим в конце
этого раздела.
Чтобы извлечь максимум пользы из этого раздела, откройте окно редактора
IDLE и воспроизводите приведенный
код.
Прежде чем браться за кодирование, давайте уделим немного времени дизайну
приложения. Нам понадобятся три основных элемента:
1)
виджет Eпtry с именем eпt_temperature для ввода значения по шкале
Фаренгейта;
2)
виджет
Label
с именем lЫ_resul t для вывода результата по шкале
Цельсия;
3)
виджет Buttoп с именем btп_convert, который читает значение из вид­
жета Eпtry, преобразует его из значений по Фаренгейту к значениям
по Цельсию и присваивает результат тексту виджета
Label
при щелчке.
Эти виджеты можно разместить в сетке с одной строкой, где для каждого
виджета выделяется один столбец. Вы получите работающее приложение, но
486
ГЛАВА 18
Графические интерфейсы
вряд ли его можно назвать дружественным для пользователя. Все виджеты
желательно снабдить полезными надписями. Разместите виджет Label со
знаком °F справа от виджета ent_temperature, чтобы пользователь знал, что
значение ent_temperature должно задаваться в градусах по Фаренгейту. Для
этого присвойте тексту виджета
Label значение "\N{DEGREE FAHRENHEIТ} ",
использующее поддержку именованных символов Юникода в
Python
для
вывода символа.
Чтобы виджет Ьtn_convert выглядел чуть более стильно, присвойте его тексту
значение
"\N{RIGHTWARDS BLACK ARROW}",
которое ВЫВОДИТ черную стрелку, на­
правленную вправо. Также можно сделать так, чтобы за полем lЫ_resul t всегда
следовал знак 0 С; для этого добавьте в конец значение "\N{DEGREE CELSIUS} ",
показывающее, что результат выводится в градусах по шкале Цельсия.
Итоговое окно должно выглядеть так:
1 Те ...
х
о
•F
_:J
100.04<:
Теперь, когда вы знаете, какие виджеты вам понадобятся и как будет выгля­
деть окно, можно переходить к кодированию! Сначала импортируйте
tkinter
и создайте новое окно:
import tkinter as tk
window = tk. Tk ()
window.title("Temperature Converter")
Вызов window. ti tle () задает заголовок существующего окна. Если вы запустите
приложение, в заголовке окна будет выводиться текст "Temperature Converter".
Затем создайте виджет ent_temperature с виджетом lЫ_temp и свяжите оба
виджета с виджетом
Frame
с именем
frm_entry:
frm_entry = tk.Frame(master=window)
ent_temperature = tk.Entry(master=frm_entry, width=10)
lЫ_temp = tk.Label(master=frm_entry, text="\N{DEGREE FAHRENHEIТ}")
ent_temperature пользователь вводит значение по шкале Фаренгейта,
°F. frm_entry представляет собой
контейнер для группировки ent_temperature и lЫ_temp.
В виджете
а lЫ_temp помечает ent_temperature знаком
18.8.
Пример приложения: конвертер температур
Надпись lЫ_temp должна размещаться непосредственно справа от
temperature,
поэтому для размещения виджетов в
зоваться менеджером геометрии
. grid ()
frm_entry
487
ent_
можно восполь­
с одной строкой и двумя столбцами:
ent_temperature.grid(row=0, column=0, sticky="e")
1Ы_temp.grid(row=0, column=l, sticky="w")
sticky виджета ent_temperature задаем значение "е", чтобы виджет
всегда закреплялся у правого края ячейки. Параметру sticky виджета lЫ_temp
задаем значение "w", чтобы он закреплялся у левого края ячейки. Эти действия
Параметру
гарантируют, что виджет lЫ_temp всегда будет находиться непосредственно
ent_temperature.
справа от
btn_convert и lЫ_resul t для преобразо­
ent_temperature, и вывода результата:
Теперь можно переходить к созданию
вания температуры, введенной в
btn_convert = tk.Button(
master=window,
text="\N{RIGHTWARDS BLACK ARROW}"
lЫ_result
Как и
= tk.Label(master=window, text="\N{DEGREE CELSIUS}")
frm_entry,
виджеты
btn_convert и
lЫ_result связаны с окном. Вместе эти
три виджета образуют три ячейки основной сетки приложения. Воспользуемся
вызовом
. grid ()
для их размещения:
frm_entry.grid(row=0, column=0, padx=10)
btn_convert.grid(row=0, column=l, pady=10)
1Ы_result.grid(row=0, column=2, padx=10)
Наконец, запустите приложение:
window.mainloop()
Выглядит превосходно, но кнопка еще ничего не делает. В начале файла с кодом,
import, добавьте функцию с именем fahrenheit_
to_celsius (). Эта функция читает значение, введенное пользователем, из
ent_temperature, преобразует его из значения по Фаренгейту к значению по
непосредственно под строкой
Цельсию, после чего выводит результат в lЫ_result:
def fahrenheit_to_celsius():
"""Преобразовать значение
к шкале Цельсия и вставить
no
шкале Фаренгейта
результат в
lЫ_result.
fahrenheit = ent_temperature.get()
celsius = (5/9) * (float(fahrenheit) - 32)
lЫ_result["text"] = f"{round(celsius, 2)} \N{DEGREE CEL5IUS}"
ГЛАВА 18
488
Графические интерфейсы
Перейдите к строке, в которой определяется
раметру
command
=
btп_coпvert
значение
btn_convert,
fahrenheit_to_celsius:
и присвойте его па­
tk.Buttoп(
master=wiпdow,
text="\N{RIGHTWARDS BLACK ARROW}",
commaпd=fahreпheit_to_celsius
# <--- Добавьте эту строку
Вот и все! Вы создали полностью работоспособное приложение для преобразо­
вания температур всего в
26 строках кода!
Впечатляет, не правда ли?
Ниже для удобства приведен полный код приложения:
import
def
as tk
tkiпter
fahreпheit_to_celsius():
"""Преобразовать значение по шкале Фаренгейта
к шкале Цельсия и вставить результат в lЫ_result.
fahreпheit = eпt_temperature.get()
celsius = (5/9) * (float(fahreпheit) - 32)
lЫ_result["text"] = f"{rouпd(celsius, 2)} \N{DEGREE CELSIUS}"
#
Set up the wiпdow
= tk.Tk()
wiпdow
wiпdow.title("Temperature Coпverter")
wiпdow.resizaЫe(width=False,
#
#
Создать фрейм для ввода
и
height=False)
значения по шкале Фаренгейта,
содержащий виджет Eпtry
Label
frm_eпtry
=
tk.Frame(master=wiпdow)
eпt_temperature
lЫ_temp
=
=
width=10)
text="\N{DEGREE FAHRENHEП}")
tk.Eпtry(master=frm_eпtry,
tk.Label(master=frm_eпtry,
Label в frm_eпtry
.grid()
eпt_temperature.grid(row=0, columп=0, sticky="e")
1Ы_temp.grid(row=0, columп=l, sticky="w")
#
#
Разместить виджет Eпtry для температуры и
#
Создать кнопку преобразования
с использованием топологического менеджера
btп_coпvert
=
и виджет
Label
для
вывода результата
tk.Buttoп(
master=wiпdow,
text="\N{RIGHTWARDS BLACK ARROW}",
commaпd=fahreпheit_to_celsius
)
lЫ
#
#
result =
tk.Label(master=wiпdow,
Определить макет с
maпager
text="\N{DEGREE CELSIUS}")
использованием топологического менеджера
.grid() geometry
18.9. Пример приложения: текстовый редактор
489
frm_entry.grid(row=0, column=0, padx=10)
btn_convert.grid(row=0, column=l, pady=10)
1Ы_result.grid(row=0, column=2, padx=10)
#
Запустить приложение
window.mainloop()
А теперь замахнемся на решение более амбициозной задачи и построим про­
стой текстовый редактор.
Упражнение
Попробуйте воссоздать приложение-конвертер температур из этого раздела,
не подглядывая в исходный код. Если вы зайдете в тупик, просмотрите код
и завершите упражнение. Затем подождите
10-15 минут
и попробуйте снова.
Повторяйте, пока не сможете проделать все это самостоятельно. Сосредоточьтесь
на результате. Если ваш код будет слегка отличаться от приведенного в книге,
это абсолютно нормально.
18.9.
ПРИМЕР ПРИЛОЖЕНИЯ:
ТЕКСТОВЫЙ РЕДАКТОР
Сейчас мы построим текстовый редактор, который должен уметь создавать,
открывать, редактировать и сохранять текстовые файлы.
Нам понадобятся три основных элемента:
1)
виджет
с именем
Button
btn_open -
открывает файл для редактиро­
вания;
с именем Ьtn_save
2)
виджет
Button
3)
виджет
TextBox
с именем
-
сохраняет файл;
txt_edit -
создает и редактирует текстовый
файл.
При помощи виджетов две кнопки разместим в левой части окна, а текстовое
поле
-
в правой.
Окно должно иметь минимальную высоту
минимальную ширину
800
пикселей, а поле
txt_edi t -
800 пикселей. Макет сделаем динамичным, чтобы
при изменении размеров окна также корректировались размеры виджета
txt_edi t.
При этом ширина виджета
не /\ОЛЖНа.
Frame,
содержащего кнопки, меняться
490
Графические интерфейсы
ГЛАВА 18
Вот скетч будущего окна:
"f"i;;:".,-
6.o11'"1:tf't..
-
о х.
ёр~
~
~.-;з
~
~
~
Для построения нужного макета воспользуемся менеджером геометрии
.grid( ).
Макет состоит из одной строки и двух столбцов: узкий столбец слева 11ред11а­
значен для кнопок, а более широкий столбец справа
-
для текстового поля.
Чтобы задать минимальные размеры для окна и txt_edit, присвоим параметрам
minsize методов . rowconfigure() и . columnconfigure() объекта окна значение
800. Для изменения размеров можно присвоить параметрам weight этих методов
значение
1.
Чтобы обе кнопки размещались в одном столбце, необходимо создать виджет
Frame,
которому присвоим имя
fr _buttons.
Согласно скетчу, две кнопки должны
быть выстроены по вертикали внутри фрейма, кнопка btn_open должна нахо­
диться наверху. Воспользуемся менеджерами геометрии
лучше использовать
. grid (),
.grid()
или
.pack():
потому что с ним чуть нроще работать.
План готов, можно переходить к кодированию приложения. Начнем с создания
всех необходимых виджетов:
import tkinter as tk
# 1
window = tk. Tk ()
window.title("Simple Text Editor")
# 2
window.rowconfigure(0, minsize=800, weight=l)
window.columnconfigure(l, minsize=800, weight=l)
# 3
txt_edit = tk.Text(window)
fr_buttons = tk.Frame(window)
tk . Button(fr_buttons, text="Open")
btn_open
tk.Button(fr_buttons, text="Save As ... ")
Ьtn save
18.9. Пример приложения: текстовый редактор
491
(#1) импортируем пакет tkinter и создадим новое окно с заголовком
"Simple Text Editor". Затем (#2) настроим конфигурации строк и столбцов.
В завершение (#3) создадим четыре виджета: текстовое поле txt_edit, фрейм
fr _buttons, а также кнопки btn_open и btn_save.
Сначала
minsize метода
800, а параметру weight - значение 1.
Присмотримся повнимательнее к части #2. Параметру
. rowconfigure()
присваивается значение
Первый аргумент равен 0, поэтому эта команда назначает первой строке высо­
ту
800 пикселей
и обеспечивает изменение размера строки, пропорциональное
изменению высоты окна. Макет состоит только из одной строки, поэтому эти
настройки применяются ко всему окну.
. columnconfigure() используется для назначения
width и weight столбца с индексом 1 значений 800 и 1 соответственно.
В следующей строке метод
атрибутам
Помните: индексы строк и столбцов начинаются с нуля, поэтому эти настройки
применяются ко второму столбцу.
Настраивая только второй столбец, мы гарантируем, что текстовое поле будет
естественно расширяться и сужаться при изменении размеров окна, тогда как
столбец с кнопками сохранит фиксированную ширину.
Далее возьмемся за макет приложения. Сначала две кнопки связываем с фрей­
мом
fr _buttons
с использованием менеджера геометрии
. grid( ):
btn_open.grid(row=0, column=0, sticky="ew", padx=S, pady=S)
btn_save.grid(row=l, column=0, sticky="ew", padx=S)
Эти две строки кода соадают сетку с двумя строками и одним столбцом во фрей­
fr _buttons, так как и у btn_open, и у btn_save атрибуту master присвоено
fr _buttons. btn_open размещается в первой строке, а btn_save - во
второй, так что btn_open отображается в макете над btn_save, как и было по­
ме
значение
казано на скетче.
Как у
btn_open, так и у btn_save атрибуту sticky присвоено значение "ew", ко­
торое заставляет кнопки расширяться по горизонтали в обоих направлениях
и заполнять весь фрейм. Тем самым гарантируется, что обе кнопки будут иметь
одинаковые размеры.
Вокруг каждой кнопки создаются отступы величиной
параметрам
padx
и
pady
присвоим значение
5.
5
пикселей, для чего
Вертикальные отступы есть
только у Ьtn_open. Так как Ьtn_open располагается сверху, вертикальный от­
стун немного смещает кнопку вниз от верха окна и создает небольшой отступ
между Ьtn_open и Ьtn_save.
492
ГЛАВА 18
Графические интерфейсы
Кнопка fr _buttons размещена и готова к работе, можно переходить к настройке
макета сетки для остальной части окна:
fr_buttons.grid(row=0, column=0, sticky="ns")
txt_edit.grid(row=0, column=l, sticky="nsew")
Эти две строки кода создают в окне сетку с одной строкой и двумя столбцами.
fr _buttons располагается в первом столбце, а txt_edit fr _buttons располагается слева от txt_edit в макете окна.
Фрейм
что
Параметру
во втором, так
sticky фрейма fr _buttons задаем значение "ns", которое обеспе­
чивает вертикальное расширение всего фрейма и заполнение всей высоты
txt_edi t заполняет всю ячейку сетки, потому что его параметру
sticky присвоено значение "nsew", заставляющее его расширяться во всех
столбца. Поле
направлениях.
Макет приложения готов. Добавьте вызов window. mainloop() в конец программы,
сохраните и запустите файл. На экране появляется вот такое окно:
# Simple Text Editor
о
х
Смотрится отлично! Но программа пока ничего не делает, поэтому следует на­
писать команды для кнопок.
Кнопка Ьtn_open открывает диалоговое окно для выбора файла. Затем нужно
открыть этот файл и заполнить
txt_edi t
содержимым файла.
18.9. Пример приложения: текстовый редактор
Это делает функция
493
open_file( ):
def open_file():
"""Open а file for editing."""
# 1
filepath = askopenfilename(
filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")]
# 2
if not filepath:
return
# 3
txt_edit.delete("l.0", tk.END)
# 4
with open(filepath, "r") as input_file:
text = input_file.read()
txt_edit.insert(tk.END, text)
# 5
window.title(f"Simple Text Editor - {filepath}")
Сначала
( #1)
диалоговое окно
askopenfilename
из модуля
tkinter. filedialog
используется для вывода диалогового окна открытия файла, а выбранный путь
к файлу сохраняется в переменной
filepath.
Если пользователь закрывает
Cancel (#2), то, поскольку переменная
filepath содержит None, функция возвращает управление без выполнения кода,
который читает файл и заполняет текст txt_edi t.
диалоговое окно или щелкает на кнопке
Если пользователь выбрал файл
вызовом
. delete ().
читается вызовом
Строка
text
Наконец
Затем
. read()
включается в
(#5),
( #4)
(#3), то текущее содержимое txt_edit стирается
выбранный файл открывается, его содержимое
и сохраняется в виде строки в переменной
txt_edit
вызовом
text.
. insert().
в заголовок окна включается путь к открытому файлу.
Теперь можно обновить программу так, чтобы виджет btn_open вызывал open_
file() при щелчке. Для этого необходимо выполнить три действия:
1.
Импортировать
askopenfilename ()
tkinter. filedialog, для
import в начало программы:
из модуля
чего следует добавить следующую команду
from tkinter.filedialog import askopenfilename
2.
Добавить определение
import.
open_ file () непосредственно под командами
ГЛАВА 18
494
Графические интерфейсы
Присвоить атрибуту
3.
command виджета btn_open значение open_file.
btn_open = tk.Button(fr_buttons, text="Open", command=open_file)
Сохраните файл, запустите его и убедитесь в том, что все работает. Попробуйте
открыть текстовый файл!
ПРИМЕЧАНИЕ
Если после обновления программа перестанет работать, то посмотрите пол­
ный код редактора, который приведен в конце раздела.
btn_open заработает, займемся функцией для btn_save. Она должна от­
Когда
крыть диалоговое окно для сохранения файла, чтобы пользователь мог выбрать,
где он хочет сохранить файл. Для этого можно воспользоваться диалоговым
окном
asksaveasfilename из модуля tkinter. filedialogmodule. Функция также
txt_edi t, и записать
должна извлечь текст, в настоящее время содержащийся в
его в файл в выбранном месте.
Следующая функция делает все это:
def save_file():
"""Save the current file as
а
new file . """
# 1
filepath = asksaveasfilename(
defaultextension="txt",
filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")],
# 2
if not filepath :
return
# 3
with open(filepath, "w") as output_file:
text = txt_edit.get("l.0", tk.END)
output_file . write(text)
#
4
window.title(f"Simple Text Editor - {filepath}")
Сначала
(#1)
диалоговое окно
asksaveasfilename
получает от пользователя
путь для сохранения информации. Выбранный путь сохраняется в переменной
filepath. Если пользователь закрывает диалоговое окно или щелкает на кнопке
Cancel (#2), то, поскольку переменная filepath содержит None, функция воз­
вращает управление без выполнения кода, который сохраняет текст в файле.
18.9. Пример приложения: текстовый редактор
495
(#3), функция создает новый файл. Текст из
.get(), присваивается переменной text и за­
Если пользователь выбрал файл
txt_edit
извлекается методом
писывается в выходной файл.
Наконец
(#4),
в заголовок окна включается путь к новому файлу.
Теперь можно обновить программу так, чтобы виджет Ьtn_save вызывал
save_
file () при щелчке. Для этого необходимо выполнить три действия:
1.
Импортировать
asksaveasfilename() из модуля tkinter. filedialog, для
import в начале программы к следующему
чего следует привести команду
виду:
from tkinter.filedialog import askopenfilename, asksaveasfilename
2.
Добавить определение
save_file() непосредственно под определением
open_file().
3.
Присвоить атрибуту
command виджета Ьtn_save значение save_file:
btn_save = tk.Button(
fr_buttons, text="Save As ... ", command=save_file
Сохраните файл и запустите его. У нас получился простейший, но полностью
функциоюtльный текстовый редактор!
Ниже для удобства мы приводим 110лный код приложения:
import tkinter as tk
from tkinter.filedialog import askopenfilename, asksaveasfilename
def open_file():
"""Открыть файл для
редактирования."""
filepath = askopenfilename(
filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")]
if not filepath:
return
txt_edit.delete(l.0, tk.END)
with open(filepath, "r") as input_file:
text = input_file.read()
txt_edit.insert(tk.END, text)
window.title(f"Simple Text Editor - {filepath}")
def save_file():
"""Сохранить текущий файл как новый."""
filepath = asksaveasfilename(
defaultextension="txt",
496
ГЛАВА
18
Графические интерфейсы
filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")],
if not filepath:
return
with open(filepath, "w") as output_file:
text = txt_edit.get(l.0, tk.END)
output_file.write(text)
window.title(f"Simple Text Editor - {filepath}")
window = tk. Tk ()
window.title("Simple Text Editor")
window.rowconfigure(0, minsize=800, weight=l)
window.columnconfigure(l, minsize=800, weight=l)
txt_edit =
fr_buttons
btn_open
btn_save =
tk.Text(window)
= tk.Frame(window, relief=tk.RAISED, bd=2)
tk.Button(fr_buttons, text="Open", command=open_file)
tk.Button(fr_buttons, text="Save As ... ", command=save_file)
btn_open.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
btn_save.grid(row=l, column=0, sticky="ew", padx=5)
fr_buttons.grid(row=0, column=0, sticky="ns")
txt_edit.grid(row=0, column=l, sticky="nsew")
window.mainloop()
Вы создали в
Python
два приложения с графическим интерфейсом. При этом
применили многие концепции, о которых мы рассказывали в книге. Это немалое
достижение, и вы можете заслуженно гордиться тем, что сделали.
Теперь вы готовы к тому, чтобы взяться за самостоятельное программирование!
Упражнение
Попробуйте воссоздать текстовый редактор, не подглядывая в исходный код.
Если вы зайдете в тупик, просмотрите код и завершите упражнение. Затем подо­
ждите
10-15 минут и попробуйте снова.
Повторяйте, пока нс сможете построить
приложение с нуля самостоятельно. Сосредоточьтесь на результате. Если ваш
код будет слегка отличаться от приведенного в книге, это абсолютно нормально.
18.1 О. ЗАДАЧА:
ВОЗВРАЩЕНИЕ ПОЭТА
В этом упражнении мы напишем
G UI-приложение, генерирующее стихи. В ос­
9. Окно приложения должно
нову приложения заложен генератор из главы
выглядеть примерно так:
18.1 О . Задача: возвращение поэта
1
-
Make your own poeml
о
497
х
Enter your morite words, separated Ьу commas.
Nouns: lprogrammer,laptop,code
Verbs: ltyped,napped,cheered
Adjectives: lgreat,smelly,robust
Prepositions: lto.from,on.like
AdverЬs: lgracefully
,G;;,;,;-t; 1
Yourpoem:
А
А
great programmer
great programmer cheered from the robust code
gracefully, the programmer typed
the code napped on а smelly laptop
Savetofile 1
Вы можете использовать любой менеджер геометрии на свое усмотрение,
но приложение должно удовлетворять всем требованиям из следующего
списка.
1.
Пользователь должен ввести правильное количество слов в каждом
виджете
Entry:
•
не менее трех существительных;
•
не менее трех глаголов;
•
не менее трех прилагательных;
•
не менее трех предлогов;
•
хотя бы одно наречие.
Если в каком-либо из виджетов Entry введено слишком мало слов, в той
области, где выводится сгенерированное стихотворение, должно выво­
диться сообщение об ошибке.
2.
Программа должна случайным образом выбрать из пользовательского
ввода три существительных, три глагола, три прилагательных и три пред­
лога, а также одно наречие.
3.
Программа должна сгенерировать стихотворение по следующему ша­
блону:
ГЛАВА 18
498
{A/An}
{nрилl} {сущl}
{A/An}
{nрилl} {сущl} {глl} {npeдl}
the
{наречl},
the
4.
Графические интерфейсы
{сущ2}
the
{nрил2} {сущ2}
{сущl} {гл2}
{глЗ} {nред2} а {nрилЗ} {сущЗ}
Приложение должно предоставить пользователю возможность экспорта
стихотворения в файл.
5.
Дополнительное задание: удостоверьтесь, что пользователь вводит
в виджетах
Entry уникальные слова.
Например, если пользователь введет
одно и то же существительное в виджете
Entry дважды,
то при попытке
сгенерировать стихотворение приложение должно вывести сообщение
об ошибке.
Решение этой задачи, а также многие другие дополнительные ресурсы доступны
в интернете по адресу
18.11. ИТОГИ
realpython.com/python-basics/resources.
И ДОПОЛНИТЕЛЬНЫЕ РЕСУРСЫ
В этой главе я рассказал, как строить несложные графические интерфейсы
(GUI).
Сначала вы узнали, как использовать пакет
EasyGUI для создания диалоговых
окон, которые выводят сообщения и получают пользовательский ввод, а также
позволяют пользователю выбрать файл для чтения и записи. Затем мы рассмо­
трели Tkinter - встроенный GUI-фреймворк
Python. Tkinter сложнее EasyGUI,
но зато он обладает большей гибкостью.
Вы научились работать с виджетами в Tkinter - Frame, Label, Button, Entry
Text. Виджеты можно настраивать, присваивая значения их атрибутам. На­
и
пример, присваивание значения атрибуту text виджета
Label заполняет виджет
текстом.
Затем я показал, как при помощи менеджеров геометрии
. place ()
и
. grid ()
Tkinter . рас k (),
выстраивать макет GUI-приложений. Вы научились управ­
лять различными параметрами макета, например внутренними и внешними от­
ступами, а также создавать динамичные макеты с использованием менеджеров
.pack()
и
.grid().
Наконец, мы закрепили полученные навыки, создав два завершенных
приложения
-
конвертер температур и простой текстовый редактор.
GUI-
18.11. Итоги и дополнительные ресурсы
ИНТЕРАКТИВНЫЙ ТЕСТ
499
.
К этой главе прилагается бесплатный интерактивный тест для проверки усво­
енных вами знаний . Тест доступен на телефоне или компьютере:
realpython.comlquizzes!pybasics-gui
Дополнительные ресурсы
За дополнительной информацией обращайтесь к следующим ресурсам:
•
«Tkinter tutorial» (https.j/tkdocs.com/tutonal/index.html)
ГЛАВА
19
Мысли напоследок
и следующие шаги
Поздравляю! Вы дочитали книгу до конца. Вы уже знаете, как сделать много
полезных вещей на языке
Python, но сейчас начинается самое интересное: при­
шло время самостоятельных исследований!
Учиться лучше всего на реальных задачах, с которыми вы сталкиваетесь в по­
вседневной жизни. Конечно, на первых порах ваш код будет не самым краси­
вым или эффективным, но он будет полезным. Нужен источник вдохновения?
В статье «13 Project Ideas fог Intermediate Python Developers» (https.j/realpython.
com/intermediate-python-project-ideas/) вы найдете некоторые идеи, которые
помогут вам начать!
Есть еще один факт, который делает программирование на
влекательным,
-
Python
таким при­
сообщество питонистов. Знаете других людей, изучающих
Python? Помогите им!
Если вы хотите по-настоящему глубоко понять какую-то
концепцию, попробуйте объяснить ее другим.
Затем обратитесь к более сложному материалу
-
на сайте
realpython.com - или
PyCoder's Weekly
к статьям и учебникам, рекомендуемым в новостной рассылке
( https:j/pycoders.com/).
А когда почувствуете, что готовы, подумайте об участии в проекте с открытым
исходным кодом на
GitHub (https:j/github.com/topics/python).
Если же вы
предпочитаете решать головоломки, попробуйте заняться математическими
задачами на сайте
Project Euler
(http.j/projecteuler.net/proЬlems).
Если в какой-то момент вы зайдете в тупик, знайте: почти наверняка кто-то
уже сталкивался с точно такой же проблемой (и, возможно, успешно решил
ее!). Поищите ответы в интернете, особенно на сайте Stack Overflow (https.j/
stackoverflow.com/questions/tag,ged/python), или найдите сообщество питонистов
(например, https:j/realpython.com/community) - они всегда придут на помощь.
501
19.2. Книга «Чистый Python»
Если все попытки окажутся безуспешными, выполните import this и помеди­
тируйте над тем, что есть
Р.
S.
Python.
Посетите нас в интернете и продолжите свое путешествие в мир
на сайте
19.1.
realpython.com
и в аккаунте
Python
Twitter @realpython.
ЕЖЕНЕДЕЛЬНЫЕ БЕСПЛАТНЫЕ СОВЕТЫ
для питонистов
Хотите еженедельно получать подборку советов от разработчиков
Python
о том, как повысить производительность и оптимизировать рабочие процессы?
Хорошие новости - у нас есть бесплатная рассылка по электронной почте для
таких же разработчиков
Python, как
и вы.
Наша рассылка не относится к материалам типа «вот вам список популярных
статей». Мы стараемся поделиться хотя бы одной оригинальной мыслью в не­
делю в формате (короткого) очерка.
Если вам захотелось узнать, о чем идет речь, переходите на
newsletter и
realpython.com/
вводите свой адрес электронной почты в форму. С нетерпением
ожидаем встречи!
19.2. КНИГА «ЧИСТЫЙ РУТНОN»
Итак, вы овладели азами
Python.
Настало время изучить язык поглубже, до­
полнить и расширить ваши знания.
В книге
Buffet of Awesome Python Features» 1 вы найдете
программирования на Python, а также ощутите всю
«Python Tricks:
эффективные приемы
А
мощь красивого питонического кода с простыми примерами и пошаговыми
описаниями.
Вы еще на шаг приблизитесь к профессиональному владению
писать чистый идиоматический код
Изучить все хитросплетения
-
Python и сможете
легко и непринужденно.
Python достаточно сложно. Эта книга
Python на следующий уровень.
поможет
вам поднять ваши навыки работы с
1
Бейдер Д. Чистый
Python.
Тонкости программирования для профи.
-
СПб.: Питер.
502
ГЛАВА 19
Мысли напоследок и следующие шаги
Найдите тайные сокровища, скрытые в стандартной библиотеке
Python.
и на­
чинайте писать чистый питонический код прямо сегодня. Ознакомительную
главу книги можно загрузить бесплатно с
realpython.com/pytricks-book 1•
19.З. БИБЛИОТЕКА ВИДЕОКУРСОВ
REALPYТHON
Большая (и постоянно растущая) подборка учебников
Python
и учебных мате­
риалов поможет вам достичь уровня всесторонне развитого питониста. Новые
материалы публикуются еженедельно, и вы всегда найдете что-то интересное
для повышения вашей квалификации.
•
Осваивайте практически значимые навыки программирования на языке
Python: созданием, отбором
и контролем наших учебных материалов ;~а­
нимается целое сообщество опытных разработчиков. На сайте
Real Python
вы найдете ресурсы, заслуживающие доверия, которые щшгодятся вам
при совершенствовании мастерства программирования на Pytlюn.
•
Знакомьтесь с другими питонистами: присоединяйтесь к чату сообще­
Real Python и еженедельным Q&А-сессиям, чтобы 1юзнакомиться
Real Python и с другими неофитами. Получайте ответы на
свои вопросы, относящиеся к Python, обсуждайте вопросы программиро­
ства
с командой
вания и построения карьеры или просто проводите с нами время у этого
виртуального кофейного автомата.
•
Пройдите интерактивные тесты и траектории обучения: реальные задач 11
по кодированию, интерактивные тесты и 11рограммы обучения, ориен­
тированные на получение необходимых навыков, 110зволят вам оценип,
ваш текущий уровень и потренироваться в применении новых л1аний.
•
Отслеживайте ход обучения: помечайте уроки как завершенные или
«в процессе», занимайтесь в наиболее подходящем для вас темпе. Созда­
вайте закладки на самых интересных уроках и просматривайте их позднее,
чтобы запомнить надолго.
•
Получайте сертификаты об окончании курсов: по окончании каждот
курса вы получаете сертификат, который можно предъявить (в электрон­
ном или печатном виде). Включайте сертификаты в свой проектный
портфель, резюме на
Linkedln
или на других веб-сайтах, чтобы показать
миру, что вы продвинутый питонист.
1
Или rюлистап, главу на русском я:Jыкс на сайте издательства «ilитср» 1oш1!''.fliler.com. -
Примеч. ред.
19.4. Благодарности
•
503
Идите в ноrу со временем: поддерживайте свои рабочие навыки на долж­
ном уровне и не отставайте от технологических новинок. Мы постоянно
выпускаем новые учебные материалы только для зарегистрированных
участников, а также регулярно обновляем контент.
За информацией о доступных курсах обращайтесь на
19.4.
realpython.com/courses.
БЛАГОДАРНОСТИ
Эта книга появилась на свет только благодаря помощи и поддержке многих
друзей и коллег. Мы хотим поблагодарить всех вас за содействие.
Нашим семьям: спасибо за то, что вы мирились с нашими авралами, когда мы
работали днем и ночью, чтобы книга вовремя попала в руки читателей.
Команде
CPython: спасибо за то, что вы создали замечательный язык програм­
мирования и инструменты, которые мы обожаем и используем в повседневной
работе.
Сообществу
Python
Python:
спасибо за вашу усердную работу над тем, чтобы сделать
самым гостеприимным и доброжелательным языком программирова­
ния в мире, за проведение конференций и поддержание такой критической
инфраструктуры, как
Читателям
PyPI.
realpython.com -
таким, как вы: спасибо за то, что читаете наши
материалы и купили эту книгу. Без вашей поддержки и внимания вся наша
работа была бы напрасной!
Надеемся, вы продолжите активно участвовать в работе сообщества, задавая
вонросы и делясь советами. Читательские отклики сформировали эту книгу
и нродолжают помогать нам вносить улучшения в будущих изданиях, поэтому
мы надеемся услышать ваше мнение.
Наша искренняя благодарность всем, кто поддержал нас на
кто поверил в будущее этого проекта в
2012
Kickstarter, -
тем,
году. Мы никогда не рассчитывали
собрать такую большую группу отзывчивых, доброжелательных людей.
Наконец, мы хотим поблагодарить читателей ранней версии этой книги за пре­
восходную обратную связь; вот их имена: Зохеб Айнапор
(Zoheb Ainapore),
(Luther Reed), Роб Сандаски (Rob Sandusky), Лютер (Luther), Марк
(Маге), Рики Митчелл (Ricky Mitchell), Роберт Ливингстон (Robert Livingston),
Уэйн (Wayne), Том Моэнс (Tom Moens), Меир Гуттман (Meir Guttman), Ларри
Айзенберг (Larry Eisenberg), Рики (Ricky), Фу Ле (Phu Le), Джеффри Хансен
Лютер Рид
504
ГЛАВА 19
Мысли напоследок и следующие шаги
(Jeffrey Hansen), Албрехт (Albrecht), Марк Пали (Mark Palie), Питер Аронофф
(Peter Aronoff), Килимандарос (Kilimandaros), Патрицио Уррутиа (Patricio
Urrutia), Джоанна Яблонски (JoannajaЬlonski), Миrель Алвес (Miguel Alves),
Мурсалин Симпсон (Mursalin Simpson), Сю Чуньян (Xu Chunyang), Лукас
(Lucas), Уорд Уокер (Ward Walker), W., Влад (Vlad), Джим Андерсон (Jim
Anderson), Мохамед Альшихани (Mohamed Alshishani), Мелвин (Melvin), Ал­
брехт Кадауке (Albrecht Kadauke), Патрик Старренбурr (Patrick Starrenburg),
Вивек (Vivek), Шринивасан Самуэль (Srinivasan Samuel), Сампат (Sampath),
Сиджей Сервантес (Ceejay Cervantes), Лиам (Liam), TyWait, Marp, Хорхе Алберк
(Jorge Alberch), Эдит (Edythe), Мигель Галан (Miguel Galan), Том Карневаль
(Tom Carnevale), Флорент (Florent), Питер (Peter), Джон Рэдью (Jon Radue),
Мэтт Гарднер (Matt Gardner), Роберт (Robert), Шон Янг (Sean Yang), Дэвид С.
(David S.), Хане ван Нилен (Hans van Nielen), Юрий Торчальский (Youri
Torchalski), Гэвин (Gavin), Карен Калхаун MD (Karen Н Calhoun MD), Роман
(Roman), Роберт Робб Ливингстон (Robert Robb Livingston), Теренс Филипе
(Terence Phillips), Нико (Nico), Дэниел (Daniel), W, Кейро Деголар (Cairo
DeGaillard), Лукас дас Дорес (Lucas das Dores), Дэвид (David), Дэйв (Dave),
Тони Деннинг (Tony Denning), Шон (Sean), Питер Кронфельд (Peter Kronfeld),
Марк (Mark), Деннис Миллер (Dennis Miller), Джозеф Аранета мл. (Joseph
Araneta jr.), Натан Эгер (Nathan Eger), Кумаран Раджендиран (Kumaran
Rajendhiran), Дэвид Фуллертон (David Fullerton), Никлас (Nicklas), Джейкоб
Андерсен (Jacob Andersen), Марио (Mario), Алехандро Рамос (Alejandro Ramos),
Beni_begin, Aj, Дон Эдварде (Don Edwards), Джон (Jon), Ридван Мизан (Ridwan
Mizan), Грэхем Нин (Graham Kneen), Илиян (Iliyan), Хельмут (Helmut), Айзек
Зикер (lzak Zycer), Майк (Mike), Норман Гринвуд (Norman Greenwood), Фор­
рест (Forrest), Патрицио (Patricio), Рене (Rene), Ричард Мерц (Richard Mertz),
Крис Робинсон (Chris RoЬinson), Пит Сторер (Pete Storer), Расе Гарсайд (Russ
Garside), Мэтт (Matt), Ричард (Richard), Тиаго Мендес (Tiago Mendes), Майкл
(Michael), Дэниел Алвес Мертинс (Daniel Alves Mertins), Марко Умек (Marko
Umek), Крис Дженкс (Chrisjenks), Эдди (Eddy), Дмитрий (Dmitry), Келсанг
Шераб (Kelsang Sherab), Томас (Thomas), Дом Дженнингс (Domjennings),
Мартин (Martin), Энтони Шеффилд (Anthony Sheffield), S F, Белу В (Velu V),
Питер Кавалларо (Peter Cavallaro), Чарли Браунинг 3 (Charlie Browning 3),
Милинд Махаджани (Milind Mahajani), Джейсон Барнс (Jason Barnes), Люсьен
Баланд (Lucien Boland), Адам Бретел (Adam Bretel), Уильям (William), Велтейн
(Veltaine), Джерри Петри (Jerry Petrey), Джеймс (James), Рэймонд Е. Роджерс
(Raymond Е Rogers), Тай Уэйт (Ту Wait), Бимперн Уэн (Bimperng Uen), Си­
Джей Хван (CJ Hwang), Гвидо (Guido), Эван (Evan), Мигель Галан (Miguel
Galan), Хан Ци (Han Qi), Джим Бремнер (Jim Bremner), Мэтт Чан (Matt Chang),
Дэниел Дразан (Daniel Drazan), Коул (Cole), Боб (ВоЬ), Риб Ховальд (Reed
Howald), Эдвард Дуарте (Edward Duarte), Майк Паркер (Mike Parker), Аарт
Клейнендорст (Aart Кleinendorst), Рок (Rock), Джонни (Johnny), Рок Ли (Rock
19.4. Благодарности
505
(Dusan Ranisavljev), Грант (Grant), Джек (Jack),
(Reinhard), Вивек Вашист (Vivek Vashist), Дэн (Dan), Гаретт (Garett),
Джун Ли (Jun Lee), Джеймс Силк (James Silk), Ник Сингал (Nik Singhal), Чарльз
( Charles ), Аллард Шмидт ( Allard Schmidt ), Джефф Десаль (Jeff Desalle ), Мигель
(Miguel), Стив По (Steve Рое), Джонатан Сейберт (Jonathan Seubert), Марк
Пулен (Marc Poulin), Ли Джордан (Lee Jordan), Мэтью Чин (Matthew Chin),
Джеймс Митчелл (James Mitchell), Уэйн (Wayne), Зарата (Zarata), Лиза (Lisa),
Райан Отеро (Ryan Otero), Ли (Lee), Рафаэль Байтбир (Raphael ByteЬier), Грэм
Эдварде (Graeme Edwards), Джефф Скиппер (Jeff Skipper), Боб Д (ВоЬ D),
Андерсон Томазели (Anderson Tomazeli), Селемани Саид Джава (Selemani Said
jawa), Мью Картер (Meow Carter), Расе Гарсайд (Russ Garside), Луис Шелдон
(Louis Sheldon), Джеймс Рэдфорд (James Radford), Николай Джоне (Nikkolai
Jones), Джордж Загас (George Zagas), Лен Гульд (Len Gould), Дэниел Капитан
(Daniel Kapitan), Крис (Chris), Шен Джун (Shengjun), Уолт Бюссе (Walt Busse),
Мелисса Грегуар (Melissa Gregoire), Мохаммад Нассар (Mohammad Nassar),
Карлес Касадемун (Carles Casademunt), Форрест Смит (Forrest Smith), Орел
Вайссванге (Aurel Weisswange), Расе (Russ), Вольфрам Блехнер (Wolfram
Blechner), Тони Деннинг (Tony Denning), Рон Фенимор (Ron Fenimore), Эдвард
Райт (Edward Wright), Джастин (Justin), Даррен Олив (Darren Olive), Чарли
Клеммер (Charlie Clemmer), Дуэйн Рейд (Dwayne Reid), Уэйман Яу (Waiman
Yau), Скотт Киппен (С. Scott Kippen), Джимми (Jimmy), Вольфрам Блехнер
(Wolfram Blechner), Марк Мэтьюсон (Mark Mathewson), Франко Брюне (Fraщ:os
Brunet), Джефф Кабрал (Jeff Cabral), Бьорн (Bjorn), Джейсон Уильямс (Jason
Williams), Скотт Пейдж (Scott Page), Мэрилин Гартли (Marilyn Gartley), Лиф
Рутцебек (Lief Rutzebeck), Мустафа Адаоглу (Mustafa Adaoglu), Thejan, Теян
Ратнаяк (Thejan Rathnayake), Синди Анкрам (Cindy Ancrum), Тати Карвало
(Tati Carvalho), Марек Ратиборски (Marek Ratiborsky), Бен (Ben), Фрэнсис
Адеподжу (Francis Adepoju), Нир (Nir), Прабху (Prabhu), Стив Фишер (Steve
Fisher), Карлос (Carlos), Аарон (Aaron), Дэвид Майетта (David Maietta), Майкл
Хаклберри (Michael Huckleberry), Павел (Pawel), Хулио Сезар Зебадуа (Julio
Cesar Zebadua), Венцислав Шойков (Vencislav Shoykov), Майкл Кленгель
(Michael Кlengel), Ксрри Альфред (Kerry Alfred), Афиз Попула (Afeez Popoola),
Синди А. (Cindy А.), LC, tfig, Тиаго (Tiago), Софи Ван (Sophie Wang), Тосико
(Toshiko), Фами (Fahmi), Пол Пеннингтон (Paul Pennington), Wer, Джефф
Джонсон (Jeff Johnson), Dutchy, Сезар (Cesar), Албрехт Кадауке (Albrecht
Kadauke), Джим Браун (Jim Brown), Эрик (Eric), Кристофер Эванс (Christopher
Evans), МЕЛВИН (MELVIN), Идрис (Idris), Джон Чирико (John Chirico),
Уинстт Эспиноса (Wynette Espinosa), J.P., Грегори (Gregory), Марк Эдгеллер
(Mark Edgeller), Дэвид Мелансон (David Melanson), Рауль Пена (Raul Pena),
Даррелл (Darrell), Шрирам (Shriram), Том Флинн (Тош Flynn), Белу (Velu),
Майкл Линдси (Michael Lindscy), Суло Колемайнен (Sulo Kolehmainen), Джей
(Jay), Майлос «Оа:шкс» Косик (Milos "Ozzyx" Kosik), Хане де Кок (Hans de
Lee),
Душан Ранисавлиев
Рейнхард
506
ГЛАВА 19
Мысли напоследок и следующие шаги
Cocq), Гленн Мьюлз (Glen Mules), Натан Лунднер (Nathan Lundner), Фил (Phil),
Шуб (Shubh), Пувэй Ван (Puwei Wang), Алекс Мак (Alex Mtick), Алекс (Alex),
Хитоси (Нitoshi), Бруно Ф. Де Лима (Bruno F. De Lima), Дарио Дэвид (Dario
David), Раджеш (Rajesh), Харольдас Вальчукас (Haroldas Valciukas), GVeltaine,
Сьюзен Фаул (Susan Fowle), Джаред Симмс Qared Simms), Нейтан Коллинз
(Nathan Collins), Дилан (Dylan), Лес Черчмэн (Les Churchman), Стефан Ли­
Тяо-Тэ (Stephane Li-Thiao-Te), Фрэнк П. (Frank Р), Пол (Paul), Дэмьен Муртах
(Damien Murtagh), Джейсон Qason), Тхан ле Куан (Th~ng Le Quang), Нейл
(Neill), Леле (Lele), Чарльз Уилсон (Charles Wilson), Дэмьен (Damien), Кри­
стиан (Christian), Андреас Крейзиг (Andreas Kreisig), Марко (Marco), Марио
Панагиотопулос (Mario Panagiotopoulos), Нерино (Nerino), Мариуш (Mariusz),
Михаил (Mihhail), Микениr (Mikonig), Фабио (FaЬio), Скотт (Scott), А, Педро
Торрес (Pedro Torres), Матиас Йоханссон (Mathias Johansson), Джошуа С.
Qoshua S.), Матиас (Mathias), Скотт (Scott), Дэвид (David Корру), Рохит
Бхарти (Rohit Bharti), Филип Дуглас (Phillip Douglas), Джон Стивенсон Qohn
Stephenson), Джефф Джоне Qeffjones), Джордж Мает (George Mast), Аллардс
(Allards), Палак (Palak), Никола Н. (Nikola N.), Палак Калси (Palak Kalsi),
Аннекатрин (Annekathrin), Цун-Ю Ян (Tsung-Ju Yang), Ник Хантингтон (Nick
Huntington), Сай (Sai), Джордан Qordan), Вим Алсемгеест (Wiш Alsemgeest),
Ди Джей (DJ), Боб Харрис (ВоЬ Harris), Эндрю (Andrew), Регги Смит (Re~ie
Sшith), Стив Санти (Steve Santy), Мохи Джарада (Moheejarada), Марк Арза­
га (Mark Arzaga), Пулос Маттен (Poulose Matthen), Брент Гордон (Brent
Gordon), Гэри Батлер (Gary Butler), Брайан (Bryaвt), Дана (Dana), Коджак
(Koajck), Perrи (Reggie), Луис Браво (Luis Bravo ), Элайджа (Elijah), Николай
(Nikolay), Эрик Льетч (Eric Lietsch), Фред Янссен (Fredjanssen), Дон Стиллу­
элл (Don Stillwell), Гаурав Шарма (Gaurav Sharma), Майк Маккенна (Mike
McKenna), Картик Бабу (Karthik Babu), Булат Мансуров (Bulat Mansurov),
Август Трилланес (August Trillanes), Дэррен Со (Daпen Saw), Джагадиш
Qagadish). Кайл (Kyle), Техас Шетти (Tejas Shetty), Баба Сариффодин (ВаЬа
Sariffodeen ), Дон ( Don ), Ян (lan ), Ян Барбур (Ian Barbour ), Редуан ( Redhouane ),
Уэйн Розинr (Wayne Rosing), Эмануэль ( Eшanuel ), Тойгонгонбай (Toigongonbai ),
Джейсон Кастильо (Jasoп Castillo), Кришна Чайтанья Свами Кесаварупу
(Krishna Chaitanya Swamy Kesavarapu), Кори Хагалей (Corey Huguley), Ник
(Nick), Сючуньян (Xuchuпyang), Дэниел Буис (Danicl Buis), Кеннет (Kenneth),
Леоданис Позо Рамос (Leodanis Pozo Raшos). Джон Феникс (John Pheпix),
Линда Моран (Linda Moran), Лало (W Laleau), Трой Флинн (Troy Flynп), Эбер
Нильсен (Heber Nielseп), Рок (Rock), Майк Лерой (Mike LeRoy), Томас Дэвис
(Thomas Davis), Джейкоб (Jacob), Шаболч Синка (Szabolcs Sinka), Калайселван
(Kalaiselvan), Леанна Кусе (Leanпe Kuss), Андрей (Andrey), Омар (Ошаr),
Джейсон Уоден (Jason Woden), Дэвид Себало (David Cebalo), Джон Миллер
(John Miller), Дэвид Буи (Davi<l Bui), Нико Занферрари (Nico Zanferrari), Ари­
эль (Ariel), Борис (Boris), Борис Эндер (Boris Ender), ЧарлиЗ (CharlieЗ), Осси
19.4. Благодарности
507
(Matthias Kuehl), Скотт Кох (Scott Koch), Хесус Авина
(Charlie), Авадеш (Awadhesh), Энди (Aпdie), Крис Джон­
сон ( Chris J оhпsоп ), Мал ан (Ма\ап ), Киро ( Ciro ), Тамижсслван (Tha111izhselva11),
Ilexa (Nclы), Кристи;ш Лангпап (Christiaп Laпgpap), Иван (lvan), доктор Крейг
Леви (Dr. Craig Lcvy), Х. Б. Робинсон (Н. В. RoЬinsoп), Стефан (Stephane),
Стив Макилри (Stcve Mcllrce), Ив (Yves), Тереза (Teresa), Аллард (Allard), Том
Коун мл. (Tom ConcJr.), Дирк (Dirk), Иоахим ван дер Вейден Uoachi111 van der
Weijden), Джим Ву;щорд Ui111 Woodward), Кристоф Липка (Christoph Lipka),
Джон Всргслли Uohп Vergel\i), Джерри (Gerry), Лу (Lu), Роберт Р. (Robert R.),
Влад (Vlacl), Ричард Хитвол (Richard Heatwole), Гэбриел (Gabriel), Кшиштоф
Суровсцки ( Krzysztot" Surowiecki ), Александра Дэвис ( Alexandra Davis ), Джсй­
сон Волл Uason Voll) и Дуэйн Девер (Dwayпe Dever).
(Ossy),
Маттиас Кюль
Ucsнs Aviпa), Чарли
Если мы ;1абыли у11омянуть ваше имя, пожалуйста, знайте, что мы в высшей
степени благодарны вам за помощь. Спасибо всем!
Дэн Бейдер,
Дэвид Эймос, Джоанна Яблонски, Флетчер Хейслер
Знакомство с
Python
Перевел с английского Е. Матвеев
Ю. Сергuенко
Руководитель дивизиона
Е. Строганова
Ведущий редактор
Ю. Леонова
Литературный редактор
В. Мостuпан
Художественный редактор
С Беляева, Л. Галаганова
Корректоры
Л. Егорова
Верстка
Изготовлено в России. Изготовитель: ООО «Прогресс книга}).
Место нахождения и фактический адрес:
194044, Россия, г. Санкт-Петербург,
52. Тел.: +78127037373.
Б. Сампсониевский пр" д. 29А, пом.
Дата изготовления:
Налоговая льгота
09.2022.
-
Наименование: книжная продукция. Срок годности: не ограничен.
общероссийский классификатор продукции ОК
034-2014, 58.11.12 -
Книги печатные профессиональные, технические и научные.
V!мпортер в Беларусь: ООО «ПИТЕР М»,
Подписано в печать
28.07.22.
220020,
РБ, г. Минск, ул. Тимирязева, д.
Формат ?Ох 100/16. Бумага офсетная. Усл. п. л.
121 /3,
41,280.
к.
214,
Тираж
тел./ факс:
1500.
Отпечатано в типографии филиала АО «ТАТМЕДИА» «ПИК «Идел-Пресс».
420066,
Россия, г. Казань, ул. Декабристов,
2.
Заказ
208
80О1
0-2358.
Download