Эл свейгарт автоматизация рутинных задач с помощью python практическое руководство для начинающих

Название книги: Автоматизация рутинных задач с помощью Python: практическое руководство для начинающих 
Год: 2017 
Автор: Эл Свейгарт
Страниц: 573
Язык: Русский
Формат: pdf, rtf, epub, fb2
Размер: 19.9 Мб

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

Книга “Автоматизация рутинных задач с помощью Python: практическое руководство для начинающих” научит вас тому, как использовать Python для написания программ, способных в считанные минуты сделать то, на что раньше у вас уходили часы работы вручную.

Как только вы овладеете основами программирования, вы сможете создавать на языке Python программы, которые будут без труда выполнять в автоматическом режиме такие задачи, как:

– поиск определенного текста в файле или во множестве файлов;
– создание, обновление, перемещение и переименование файлов и папок;
– выполнение поиска и загрузка содержимого из Интернета;
– обновление и форматирование данных в электронных таблицах Excel любого размера;
– разбиение, слияние, разметка водяными знаками и шифрование PDF-документов;
– рассылка напоминаний в виде сообщений электронной почты или текстовых уведомлений;
– заполнение форм в режиме онлайн.

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

Не тратьте свое драгоценное время на выполнение чисто механической работы.

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

Оглавление:

Введение

Часть I. Основы программирования на языке Python

  1. Основные понятия языка Python
  2. Поток управления
  3. Функции
  4. Списки
  5. Словари и структурирование данных
  6. Манипулирование строками

Часть II. Автоматизация задач

  1. Поиск по шаблону с помощью регулярных выражений
  2. Чтение и запись файлов
  3. Управление файлами
  4. Отладка
  5. Автоматический сбор данных в Интернете
  6. Работа с электронными таблицами Excel
  7. Работа с документами в форматах PDF и Word
  8. Работа с CSV-файлами и данными в формате JSON
  9. Обработка значений даты и времени, планировщик заданий и запуск программ
  10. Отправка сообщений электронной почты и текстовых сообщений
  11. Работа с изображениями
  12. Управление клавиатурой и мышью с помощью средств автоматизации графического интерфейса пользователя

Приложение А. Установка модулей сторонних разработчиков

Приложение Б. Запуск программ

Приложение В. Ответы на контрольные вопросы

Предметный указатель

Скачать книгу “Автоматизация рутинных задач с помощью Python: практическое руководство для начинающих”

Читать книгу «Автоматизация рутинных задач с помощью Python» онлайн



#статьи

  • 20 окт 2020

  • 11

Эта книга поможет освоить Python. А если повезёт, раз и навсегда покончить с рутиной.

 vlada_maestro / shutterstock

Цокто Жигмытов

Кандидат философских наук, специалист по математическому моделированию. Пишет про Data Science, AI и программирование на Python.

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

Подробно объясняется каждый шаг, и есть много информации для “чайников”: как установить, что сделать и где спросить, если что-то не работает. Объём материала почти 600 страниц, освоение займёт приблизительно 35–40 часов.

Это не учебник для будущих кодеров, которые планируют заниматься коммерческим программированием, а руководство по решению конкретных задач и автоматизации рутины. Поэтому в примерах кода есть отступления от правил и best practice в пользу понятности и простоты.

Книга полезна тем, кто:

  • хочет облегчить себе жизнь с помощью Python;
  • не имеет опыта в программировании.

Что понадобится для работы:

  • Компьютер с интернетом.
  • Школьные знания арифметики.
  • Умение пользоваться поиском, скачивать и устанавливать программы.
  • Упорство и вдохновение (шутка — упорства хватит за глаза).

Что в итоге получит читатель:

  • Знание базового Python.
  • Несколько программ для портфолио.
  • Понимание, нравится ли вам программировать.
  • Уважение коллег и домочадцев.
  • Вы увидите профессию целиком. Книга предназначена для непрограммистов, но проведёт читателя по настоящему пайплайну разработчика: постановка задачи → поиск решения → написание кода → запуск. Именно так, в общих чертах, работает вся индустрия разработки софта.
  • Вы ощутите себя программистом. Русский перевод сделан с издания 2016 года, поэтому некоторые ссылки уже не работают. Ответы на часть вопросов придётся искать самостоятельно, как настоящему программисту.
  • В книге есть контрольные вопросы. Если вам понравится программировать, они помогут закрепить знания. И вполне вероятно, что попадутся на собеседовании — эйчары не всегда выдумывают их сами. Как в школе: “Решайте обязательно, это будет на контрольной”.

Кстати, ответы на вопросы тоже есть — в конце книги, как и полагается. (Приложение В).

Заведите тетрадку, документ на компьютере или в облаке — для конспекта. Когда будете вспоминать что-то из материала книги, старайтесь опираться именно на него.

Прежде чем начать работать:

  • Посмотрите оглавления. Их два: краткое и подробное. Краткое даст полную карту книги, а по подробному удобно искать конкретную тему или вопрос.
  • Отметьте заинтересовавшие вас главы. Любопытно ведь, как автор предлагает решить важную для вас задачу. Например, отправку sms и email, работу с Excel или что-то ещё.
  • Прочитайте введение, оно прекрасно: “Эта книга предназначена не для них (высокооплачиваемых программистов). Она предназначена для всех остальных”.
  • Пролистайте отмеченные главы, а также приложения А и Б — про установку модулей и запуск программ.

Приложение В (ответы на вопросы) смотреть нельзя — ни под каким предлогом! Мы вас предупредили.

Не старайтесь сразу глубоко вникать в материал. Инструкции по установке тоже пока что выполнять не надо. Цель первого этапа — присмотреться, войти в процесс постепенно, понять тон и темп книги. После этого можно начинать работу.

Чтобы понять, годится ли вам эта книга, рассмотрим подробнее восьмую главу, которая называется “Чтение и запись файлов”.

Читатель к этому времени уже прошёл шесть глав первой части, посвящённой основам программирования: установил Python, умеет работать в IDLE, искать по шаблону, а также создавать, редактировать и запускать файлы с расширением .py. Теперь пришло время узнать, как использовать язык для создания, чтения и хранения других файлов на жёстком диске, а также написать несколько полезных программ.

Книга написана в 2016 году, поэтому автор приводит примеры для Windows 7 и Python 3.4. У меня всё прекрасно работало и на более свежих версиях системы и интерпретатора.

Глава начинается с рассказа про модули os и os.path, обратную косую черту, создание новых папок и учит определять размеры файлов и содержимое папок с помощью os.listdir ():

>>> import os
>>> os.listdir('C:\Users')
['All Users', 'Default', 'Default User', 'Default.migrated', 'desktop.ini', 'Public', 'tsokto', 'Все пользователи']

Дальше — чуть сложнее. Теперь нам предстоит с помощью функции “красивой печати” pprint.pformat () создать свои собственные модули, которые можно будет вызывать командой import.

Но это было только вступление, а теперь мы готовы кодить проект.

Представьте, что вы — преподаватель, которому нужно провести контрольную на знание столиц штатов в США. Билеты должны быть составлены так, чтобы вопросы в них располагались в случайном порядке. По идее, это помешает списывать.

Решаем задачу с помощью Python. Список будущих фич немного пугает: требуется создать и 35 билетов, и 50 вопросов со множественным выбором для каждого билета. Вместе с правильным ответом нужно дать три случайных неправильных и записать это всё в 35 текстовых файлов. А ещё ключи ответов, которые тоже будут в отдельных файлах.

Но оказывается, нам вполне по силам:

  • сохранять названия штатов в словаре — умеем;
  • вызывать методы open (), write (), close () для текстовых файлов — без проблем;
  • использовать функцию random.shuffle () — тоже не бином Ньютона;
  • записывать содержимое в файлы — легко!

Итак, за четыре шага мы напишем программу, которая всё это может делать.

Что ещё нас ждёт в этой главе:

  • работа с многоразрядным буфером;
  • поиск с помощью regexp (регулярных выражений);
  • создание программы Mad Lib, которая читает текстовые файлы и позволяет добавлять произвольный текст в нужные места (например, в тесты по грамматике).

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

Книга есть в “Лабиринте” и на “Алибе”. Если не торопитесь, можно дождаться выхода осенью этого года второго издания на русском языке. Чтобы не пропустить момент, подпишитесь на новости издательства. Как вариант, если скоро день рождения, намекните друзьям, чтобы они подарили вам книгу.

Если что-то всё-таки не взлетело: просто запишитесь на наш курс Python-разработчик и станьте востребованным профессионалом. А книгу потом можно будет передарить, разыграть или даже продать на аукционе с вашим автографом — потому что мы в вас верим!

Здесь будут храниться ваши отложенные товары.

Вы сможете собирать коллекции книг,
а мы предупредим, когда отсутствующие товары снова появятся в наличии!


Ваша корзина невероятно пуста.
Не знаете, что почитать?

Лабиринт.Сейчас


Здесь наша редакция собирает для вас
лучшие книги и важные события.

Главные книги


А тут читатели выбирают все самое любимое.


Сумма без скидки


0
р.


Вы экономите


0
р.


Итого
подарков:
со скидкой


0
р.

Оформить

Эл Свейгарт - Автоматизация рутинных задач с помощью Python. Практическое руководство для начинающих обложка книги

Автоматизация рутинных задач с помощью Python. Практическое руководство для начинающих

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

Никонов Yuri Strannik

Понравилось?
Да

|

Рейтинг:

+2

Книга очень похожа на справочник: минимум воды, максимум полезности. Разобраны многие варианты рутинных задач, много кода с примерами. Для начинающих даже не знаю, что добавить. Книга прекрасна, но портит её две вещи.
1.мягкая обложка. Эта книга имеет все шансы стать настольной, она должна выдержать многое.
2. Перевод. Я понимаю книги за 100 рублей. Но за 2 тысячи. В коде слово переводится одним русским словом, в пояснение другим. Иногда переведены служебные слова. А перенос адреса .com на другую строку вообще считается нормой.
-2 звезды только за перевод

Турсунов Иброхим

(рецензий 3 / оценок +17)

Понравилось?
Да

|

Рейтинг:

+2

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

Понравилось?
Да

|

Рейтинг:

+4

Отличная книга.
Хотел бы так же добавить, что помимо опечатки со схемами, есть еще опечатка на 95 странице — при вызове функции hello() отступов быть не должно.
Аналогичная проблема на 105 странице: из-за отсутсвия отсупов в примере программа не будет выполняться.

Don Serjio

(рецензий 320 / оценок +797)

Понравилось?
Да

|

Рейтинг:

+3

Возрастная аудитория:

Старше 11 лет

Одна из книг для начинающих программировать на Python.
При этом сам Автор делает акцент на том, что данная книга предназначена для быстрого освоения языка Python и регулярного применения его в на практике. Для более глубокого изучение Python Автор рекомендует обратить внимание на другие книги.
Между тем, данная книга действительно весьма полезна из-за свое практической (прикладной) направленности. Когда нужно за короткое время окунуться в новый язык, «попробовать его на вкус» и дальше двинуться расширяя и углубляя свои знания и нарабатывая навыки программирования (например для постижения Data Science, Machine Learning, etc). Такой вывод я могу сделать ознакомившись с данной книгой.
Чтобы не пересказывать содержимое книги, приведу ниже сканы Введения, где Автор подробно описывает о чём и для кого эта книга.
Замечу, что для закрепления материала, Автор в конце каждой главы приводит резюме по главе и контрольные вопросы.

По оформлению.
Книга в бумажной обложке, бумага белая, просвечивает.
Печать серая («экономия чернил»).

Вывод — есть желание быстро освоить навыки программирования на Python — покупайте книгу.

Для ознакомления Введение и Глава 14.

К Всеволод

(рецензий 6 / оценок +35)

Понравилось?
Да

|

Рейтинг:

+5

Шикарная книга для введения в программирование на Питоне. Обьяснения хорошие, и автор даёт достаточную базу для понимания реализации описанных в книге приложений.
Наверно можно читать с абсолютного нуля в программировании
Однако эта книга конечно же не охватывает весь синтаксис и конечно же это далеко не полный курс .

(рецензий 1 / оценок +11)

Понравилось?
Да

|

Рейтинг:

+11

Отличная книга для начинающих программистов на Python.

Внимание: в главе 2, рисунки 2.9 и 2.10 перепутаны местами и не соответствуют своим подписям (см фото сравнения оригинала и перевода).

В целом очень довольна возможностью ознакомиться с книгой Эла на великом и могучем.

Есть что добавить?

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

  • Доставка и оплата
  • Сертификаты
  • Рейтинги
  • Новинки
  • Скидки
  • 8 800 600-95-25
  • Контакты
  • Поддержка
  • 24 пункта самовывоза
  • Главное 2023
  • Все книги
  • Билингвы

    • Назад в «Книги»
    • Все книги в жанре «Билингвы»

    • Все книги жанра

    • Билингвы для детей

    • Билингвы. Английский язык

    • Билингвы. Другие языки

    • Билингвы. Испанский язык

    • Билингвы. Итальянский язык

    • Билингвы. Немецкий язык

    • Билингвы. Французский язык

  • Книги для детей

    • Назад в «Книги»
    • Все книги в жанре «Книги для детей»

    • Все книги жанра

    • Детская художественная литература

    • Детский досуг

    • Первые книги малыша. Развитие ребенка

    • Познавательная литература для детей

  • Книги на иностранных языках

    • Назад в «Книги»
    • Все книги в жанре «Книги на иностранных языках»

    • Все книги жанра

    • Книги на английском языке

    • Книги на других языках

    • Книги на испанском языке

    • Книги на итальянском языке

    • Книги на немецком языке

    • Книги на французском языке

  • Комиксы, Манга, Артбуки

    • Назад в «Книги»
    • Все книги в жанре «Комиксы, Манга, Артбуки»

    • Все книги жанра

    • Артбуки. Игровые миры. Вселенные

    • Комиксы

    • Комиксы для детей

    • Манга

    • Манга для детей

    • Новеллизации

    • Образовательные комиксы

    • Ранобэ

    • Фан-сувениры

  • Молодежная литература
  • Нехудожественная литература

    • Назад в «Книги»
    • Все книги в жанре «Нехудожественная литература»

    • Все книги жанра

    • Бизнес. Экономика

    • Государство и право. Юриспруденция

    • Домашние ремесла. Рукоделие

    • Домоводство

    • Естественные науки

    • Информационные технологии

    • История. Исторические науки

    • Книги для родителей

    • Коллекционирование

    • Красота. Этикет

    • Кулинария

    • Культура. Искусство

    • Медицина и здоровье

    • Охота. Рыбалка. Собирательство

    • Психология

    • Публицистика

    • Развлечения. Праздники

    • Растениеводство

    • Ремонт. Строительство. Интерьер

    • Секс. Камасутра

    • Технические науки

    • Туризм. Путеводители. Транспорт

    • Универсальные энциклопедии

    • Уход за животными

    • Филологические науки

    • Философские науки. Социология

    • Фитнес. Спорт. Самооборона

    • Эзотерика. Парапсихология

  • Периодические издания
  • Религия

    • Назад в «Книги»
    • Все книги в жанре «Религия»

    • Все книги жанра

    • Ислам

    • Религии мира

    • Религиоведение

    • Христианство

  • Учебная, методическая литература и словари

    • Назад в «Книги»
    • Все книги в жанре «Учебная, методическая литература и словари»

    • Все книги жанра

    • Вспомогательные материалы для студентов

    • Демонстрационные материалы

    • Дополнительное образование для детей

    • Дошкольное обучение

    • Иностранные языки: грамматика и учебники

    • Книги для школы

    • Педагогика

    • Подготовка в вуз

    • Пособия для детей с ограниченными возможностями

    • Словари и разговорники

  • Художественная литература

    • Назад в «Книги»
    • Все книги в жанре «Художественная литература»

    • Все книги жанра

    • Афоризмы

    • Басни

    • Детективы

    • Драматургия

    • Историческая проза

    • Классическая проза

    • Отечественный боевик

    • Поэзия

    • Приключения

    • Сентиментальная проза

    • Современная проза

    • Фантастика

    • Фэнтези

    • Эпос и фольклор


  • Скидки
    ·
    Обзоры
    ·
    Рецензии
    ·
    Подборки читателей
    ·
    Новинки
    ·
    Рейтинг
    ·
    Авторы
    ·
    Изд-ва
    ·
    Серии
  • Все книги на иностранном языке
  • Книги на английском языке

    • Назад в «Иностранные»
    • Все книги в жанре «Книги на английском языке»

    • Все книги жанра

    • Книги на английском языке для детей

    • Курсы изучения языка

    • Нехудожественная литература на английском языке

    • Художественная литература на английском языке

  • Книги на других языках

    • Назад в «Иностранные»
    • Все книги в жанре «Книги на других языках»

    • Все книги жанра

    • Литература на других языках

    • Литература на других языках для детей

  • Книги на испанском языке

    • Назад в «Иностранные»
    • Все книги в жанре «Книги на испанском языке»

    • Все книги жанра

    • Адаптированная литература на испанском языке

    • Курсы изучения испанского языка

    • Литература на испанском языке

    • Литература на испанском языке для детей

  • Книги на итальянском языке
  • Книги на немецком языке

    • Назад в «Иностранные»
    • Все книги в жанре «Книги на немецком языке»

    • Все книги жанра

    • Курсы изучения языка

    • Литература на немецком языке

    • Литература на немецком языке для детей

  • Книги на французском языке

    • Назад в «Иностранные»
    • Все книги в жанре «Книги на французском языке»

    • Все книги жанра

    • Курсы изучения языка

    • Литература на французском языке

    • Литература на французском языке для детей

  • Все игрушки
  • Детское творчество

    • Назад в «Игрушки»
    • Все товары в разделе «Детское творчество»

    • Все товары раздела

    • Алмазные мозаики

    • Витражная роспись

    • Гравюры

    • Другие виды творчества

    • Конструирование из бумаги и другого материала

    • Лепка

    • Наборы для рукоделия

    • Наклейки детские

    • Панч-дыроколы фигурные

    • Работаем с воском, гелем, мылом

    • Работаем с гипсом

    • Работаем с деревом

    • Скрапбук

    • Сопутствующие товары для детского творчества

    • Творческие наборы для раскрашивания

    • Фрески

  • Игры и Игрушки

    • Назад в «Игрушки»
    • Все товары в разделе «Игры и Игрушки»

    • Все товары раздела

    • Все для праздника

    • Головоломки

    • Детские сувениры

    • Детские часы

    • Другие виды игрушек

    • Игрушка-антистресс

    • Игрушки для самых маленьких

    • Игры для активного отдыха

    • Игры с мишенью

    • Книжки-игрушки

    • Конструкторы

    • Куклы и аксессуары для кукол

    • Кукольный театр

    • Магнитные буквы, цифры, игры

    • Машинки и Транспорт

    • Музыкальные инструменты

    • Мягкие игрушки

    • Наборы для тематических игр

    • Настольные игры

    • Научные игры для детей

    • Пазлы

    • Роботы и трансформеры

    • Ростомеры

    • Сборные модели

    • Слаймы

    • Фигурки

    • Электронные игры


  • Скидки
    ·
    Отзывы
    ·
    Новинки
    ·
    Рейтинг
    ·
    Производители
    ·
    Серии
  • Все канцтовары
  • Аксессуары для книг

    • Назад в «Канцтовары»
    • Все товары в разделе «Аксессуары для книг»

    • Все товары раздела

    • Закладки для книг

    • Обложки для книг

  • Глобусы
  • Обложки для документов

    • Назад в «Канцтовары»
    • Все товары в разделе «Обложки для документов»

    • Все товары раздела

    • Другие обложки

    • Конверты для путешествий

    • Обложки для автодокументов

    • Обложки для военных билетов

    • Обложки для зачетных книжек

    • Обложки для паспортов

    • Обложки для проездных билетов

    • Обложки для студенческих билетов

    • Чехлы для карт, обложки для пропусков

  • Офисная канцелярия

    • Назад в «Канцтовары»
    • Все товары в разделе «Офисная канцелярия»

    • Все товары раздела

    • Бумажная продукция для офиса

    • Мелко-офисная канцелярия

    • Офисные принадлежности

  • Папки, скоросшиватели, разделители

    • Назад в «Канцтовары»
    • Все товары в разделе «Папки, скоросшиватели, разделители»

    • Все товары раздела

    • Папки из картона

    • Папки из пластика

    • Папки из текстиля

    • Папки-портфели (с пластиковыми отделениями)

  • Письменные принадлежности

    • Назад в «Канцтовары»
    • Все товары в разделе «Письменные принадлежности»

    • Все товары раздела

    • Карандаши черногрифельные

    • Ручки

  • Принадлежности для черчения

    • Назад в «Канцтовары»
    • Все товары в разделе «Принадлежности для черчения»

    • Все товары раздела

    • Другие виды чертежных принадлежностей

    • Линейки

    • Наборы для черчения, готовальни

    • Транспортиры

    • Треугольники

    • Тубусы

    • Циркули

    • Шаблоны, трафареты, лекала

  • Рисование

    • Назад в «Канцтовары»
    • Все товары в разделе «Рисование»

    • Все товары раздела

    • Аксессуары для рисования

    • Инструменты и материалы для каллиграфии

    • Карандаши цветные

    • Кисти

    • Краски

    • Линеры для творчества

    • Мелки

    • Наборы для рисования

    • Палитры, стаканы-непроливайки

    • Папки для чертежей и рисунков

    • Пастель

    • Тушь, перья

    • Уголь художественный

    • Фломастеры

    • Холсты. Мольберты

  • Сумки
  • Товары для школы

    • Назад в «Канцтовары»
    • Все товары в разделе «Товары для школы»

    • Все товары раздела

    • Веера, счетный материал, счетные палочки

    • Другие виды школьной канцелярии

    • Канцелярские наборы

    • Косметички, кошельки

    • Ластики

    • Мешки для обуви

    • Ножницы школьные

    • Обложки для тетрадей и книг

    • Папки для школьных тетрадей. Папки для труда

    • Пеналы

    • Пластилин

    • Подставки для книг

    • Рюкзаки, портфели

    • Точилки

    • Фартуки. Клеенки для уроков труда

    • Школьная бумажно-беловая продукция

    • Школьные наборы, подставки, органайзеры


  • Для школы
    ·

    Скидки
    ·
    Отзывы
    ·
    Новинки
    ·
    Производители
    ·
    Серии

  • Все CD/DVD
  • Аудио

    • Назад в «CD/DVD»
    • Все товары в разделе «Аудио»

    • Все товары раздела

    • Аудиокниги

    • Музыка

    • Религия

  • Видео

    • Назад в «CD/DVD»
    • Все товары в разделе «Видео»

    • Все товары раздела

    • Документальные фильмы

    • Концерты. Постановки. Мюзиклы. Видеоклипы

    • Мультфильмы

    • Познавательные фильмы

    • Художественные фильмы

    • Эротика

    • Юмор

  • Софт

    • Назад в «CD/DVD»
    • Все товары в разделе «Софт»

    • Все товары раздела

    • Игры

    • Иностранные языки

    • Мультимедиа для школьников и студентов

    • Программное обеспечение и обучение работе на ПК

    • Руководства, справочники и энциклопедии


  • Скидки
    ·
    Отзывы
    ·
    Новинки
    ·
    Рейтинг
    ·
    Производители
    ·
    Серии
  • Каталог журналов
  • Новое в мире толстых литературных журналов
  • Все сувениры
  • Календари

    • Назад в «Сувениры»
    • Все товары в разделе «Календари»

    • Все товары раздела

    • Адвент-календари. Семейные календари-планеры

    • Календари на магните

    • Квартальные календари

    • Настенные календари

    • Настольные календари

  • Сувенирная продукция

    • Назад в «Сувениры»
    • Все товары в разделе «Сувенирная продукция»

    • Все товары раздела

    • Альбомы, рамки для фотографий

    • Воздушные шары

    • Детские сувениры

    • Значки и медали

    • Игрушки для животных

    • Конверты для денег

    • Магниты

    • Новогодние сувениры

    • Открытки

    • Пакеты подарочные

    • Подарочная упаковка

    • Подарочные сертификаты

    • Постеры

    • Праздничные аксессуары

    • Таблички и статусы для рабочего стола

    • Шкатулки

    • Другое


  • Скидки
    ·
    Отзывы
    ·
    Новинки
    ·
    Рейтинг
    ·
    Производители
    ·
    Серии
  • Все товары
  • Салфетки влажные

  • Скидки
    ·
    Отзывы
    ·
    Новинки
    ·
    Рейтинг
    ·
    Производители
    ·
    Серии
  • Весь клуб
  • Журнал

    • Назад в «Клуб»
    • Лабиринт. Сейчас

    • Детский навигатор

    • Новости Лабиринта

    • Книжные обзоры

    • Рецензии читателей

    • Подборки читателей

    • Литературные премии

  • Скидки и подарки

    • Назад в «Клуб»
    • Акции

    • Бонус за рецензию

  • Только у нас

    • Назад в «Клуб»
    • Главные книги

    • Подарочные сертификаты

    • Эксклюзивы

    • Предзаказы

  • Развлечения

    • Назад в «Клуб»
    • Литтесты

    • Конкурсы

    • Дома с детьми

  • Лабиринт — всем

    • Назад в «Клуб»
    • Партнерство

  • Приложения Лабиринта

    • Назад в «Клуб»
    • Apple App Store

    • Google Play

    • Huawei AppGallery

Журнал

  • Лабиринт. Сейчас

  • Детский навигатор

  • Новости Лабиринта

  • Книжные обзоры

  • Рецензии читателей

  • Подборки читателей

  • Литературные премии





  • Школа
  • Игрушки
  • Канцтовары
  • CD/DVD
  • Сувениры
  • Журналы
  • Товары для дома

Мы используем файлы cookie и другие средства сохранения предпочтений и анализа действий посетителей сайта.
Подробнее в пользовательском соглашении. Нажмите «Принять», если даете согласие на это.

Авторизируясь в Лабиринте, я подтверждаю, что я старше 18 лет, принимаю условия пользовательского соглашения и даю добровольное согласие на обработку своих персональных данных и получение E-mail / SMS и Viber рассылок с информацией об акциях и новых поступлениях Интернет-магазина. См. основные правила.

клуб ЖЖ

Введите Ваш логин в ЖЖ, и цена товаров пересчитается согласно величине Вашей скидки


Примем заказ, ответим на все вопросы

Укажите регион, чтобы мы точнее рассчитали условия доставки

Например: 
Москва,
Санкт-Петербург,
Новосибирск,
Екатеринбург,
Нижний Новгород,
Краснодар,
Челябинск,
Кемерово,
Тюмень,
Красноярск,
Казань,
Пермь,
Ростов-на-Дону,
Самара,
Омск

                    AUTOMATE ТНЕ
BORING STUFF
WITH PYTHON
Practica/ Prograттiпg for Tota/ Begiппers
Ьу Д[ SWEiGART
св
по starch
press
5а" Fra"cisco


АВТОМАТИЗАЦИЯ рvтинныx ЗАДАЧ С ПОМОЩЬЮ PYTHON Практическое руководство для начинающих Эл СВЕйrАрТ . Москва . СанктПетербурr. Киев 2017
ББК 32,973,26Ol8.2.75 С24 УДК 681.3.07 Издательский дом "Вильямс" lлавный рt.:дактор С.1/. ТрU2уб Зав. редакцией Е.Р. rU'n,..1буj)2 Перевод с анrлийскоrо и редакция канд. хим. наук А.т. Iу.1U1Севuча По общим вопросам обращайтесь в Издательский дом "Вильямс" по адресу: info@williamspublislIillg.com, httр://www.wiшашsрtlыihillg.соIIl Свейrарт, Эл. С24 Авroмаrnзшщя руrnнных задач с помощью PytJlOI1: практическое руководстве для начинающих.: Пер. санrл. М.: 000 "ИД Вильямс", 2017. 592 с.: ил. Парал. тит. aнrл. ISBN 97858459-20904 (рус.) ББК 32.973.26-018.2.75 Все Нilзвания про['раммных продуктов являются эареrистрированными торrовыми марками со- ответствующих tjэирм. Никакая часть наrтоящеrо издания ни в каких целях ие может быть воспроизведена в какой бы то ни было форме и какими бы то ни было средствами, будь то электронные или механические. включая фотокопироваиие и запись на маrнитный иоситель, если на это нет письмеииоro разреmе- ШIЯ издательства No Stal'ch Press, AutllOri7.ed Russiall tJ'allslatiol of the ЕllglЫI editiol1 01' Аlliота/I' /111' &/i1l1( S/lIff I/Ji/h Py/hOlI: Pтctical P1'ograrltmittgfor Тotal Вegi/l/IR/:5 (ISBN 978.1.593-27599.{) ,С'! 2/11:' IJ}' AI Sweig;l1'[. Tl1is lI'anslation is pubIislled and sold ьу pel'missiol1 01' No StЩ'1:11 PI'CSS, wllirll OWIlS 01' controls all l'igl1tS [о puhlisll ald sell the saще, AII riglJts I'esel''('d. No pal't of tlJis book тау ье reproduced 0[' tl'aIlsll1ittcd in апу fОl'Ш 01' ьу апу means, elect'onic or lI1ecl1anical, iJ1cluding photocopying, re('ording, 0[' Ьу ilВУ i1lfol'I1Jation sto['age 0[' l'etl'icv,,1 systel1l, witllOlJt tl1e РI;ОI'пittеll permissioJ1 of tl1c rOP}'light OW1CI' aJ1d the PubIishel'. КиШа отпечатаиа соrлаl'НО доroвору с 000 ПРИМСI IAБ, н а:учн,(}-nоnУЛЛРНое 'IlздаН:llе Эл Свейrарт Автоматизации рyrинных задач с помощью Python: практическое руководство Д1IJI начинающих Литературный редактор Верстка Художественный редактор Корректоры Л.Н Красн.оЖQ/I л'в. черн.(f/(.ОЗUНСКОJl в./: ПавлютU1/ л'А. /ордитко Подписаио в печать 05.08.2016. Формат 70х 100/ 16 Усл. печ,л. 47,7, Уч,'изд. л, 27.4. Тираж 400 ;K3. Заказ.Ni! 8141 Отпечатано в АО "Первая Образцовая ТИIIOI'рафия" Филиал "ЧехоIICКИЙ Печатный Двор' 14200, Московская область, 1: Чехов, ул, ПОЛИl'рафиеr()h, А,I ОО() "И, Д. Вильямс', 127055, r, Москва, ул, Леl:ная, д. 4;4, crp. 1 ISBN 978.5-8459--2090-4 (рус.) ISBN 978.1.593-27599--0 (аНl'Л.) (!) 2016 Издателы:киЙ дОМ "ВИЛЬЯМI:" @ 2015 Ьу А Sweigm't
оrпАвпЕНИЕ Введение 25 Часть 1. Основы проrраммирования на языке Python 39 fлава 1. Основные понятия языка Python 41 fлава 2. Поток управления 61 fлава 3. Функции 95 rлава 4. Списки 115 rлава 5. Словари и структурирование данных 145 rлава 6. !dанипулирование строками 165 Часть 11. Автоматизация задач 189 rлава 7. Поиск по шаблону с помощью реryлярных выражений 191 fлава 8. Чтение и запись файлов 223 fлава 9. Управление файлами 253 fлава 10. Отладка 275 fлава 11. Автоматический сбор данных вИнтернете 299 rлава 12. Работа с электронными таблицами Ехсеl 337 rлава 13. Работа с документами в форматах PDF и Word 373 rлава 14. Работа с СSV-файлами и данными в форматеJSОN 403 rлава 15. Обработка значений даты и времени, планировщик заданий и запуск проrрамм 423 lЛава 16. Отправка сообщений электронной почты и текстовых сообщений 457 fлава 17. Работа с изображениями 491 rлава 18. Управление клавиатурой и мышью с помощью средств автоматизации rрафическоrо интерфейса пользователя 525 Приложение А. Установка модулей сторонних разработчиков 559 Приложение Б. Запуск проrрамм 561 Приложение В. Ответы на контрольные вопросы 565 Предметный указатель 581
СОДЕРЖАНИЕ Об авторе О техническом рецензенте 23 23 Введение 25 Для KOro предназначена эта книra 25 Исходные предположения 26 Что такое проrpаммирование 27 Что означает название Pythoп 28 Проrраммисту вовсе не обязательно в совершенстве знать математику 28 Проrраммирование творческий вид деятельности 30 Структура книrи 30 Зarрузка и установка PytIЮll 32 Запуск IDLE 34 Интерактивная оболочка М Как получить справку 35 Правильно формулируйте вопросы, ответы на которые ищете 36 Резюме 38 Часть 1. Основы проrрамиирования на языке Python rлава 1. Основные понятия JlЗЫка Python Ввод выражений в интерактивной оболочке Типы данных: целые числа, вещественные числа, строки Конкатенация и репликация строк Сохранение значений впеременных Инструкции присваивания Имена переменных Ваша первая проrрамма Анализ проrpаммы Комментарии Функция print () Функция input () Вывод имени пользователя Функция len ( ) Функции str (), int () и float () Резюме Контрольные вопросы 39 41 41 45 46 47 47 49 51 52 53 53 54 54 54 55 59 59
Содержание 7 rлава 2. ПОТОК управления б 1 Булевы :шачения б2 Операторы сравнения б3 Булевы операторы б5 Бинарные булевы операторы 65 Оператор not б6 Сочетание операторов сравнения с булевыми операторами б7 Элементы потока управления б8 Условия б8 Блоки кода б8 Выполнение ПрOl'раммы б9 Управляющие инструкции б9 Инструкция if б9 Инструкция else 70 Инструкция elif 71 Цикл while 7б Инструкция break 80 Инструкция continиe 81 Цикл for и функция range ( ) 86 ИМIюртирование модулей 89 Инструкция from import 91 ПРСЖ;J;евременное прекращение выполнения ПрOl'раММЫ с помощью вызова sys. exit () 91 Рсэюме 92 Контрольные вопросы 92 rлава 3. Функции 95 Инструкции def с парамстрами 9б Инструкция retиrn и во:шращаемыс значения 97 Значсние None 99 Имснованные aprYMcHTbI и функция print () 100 Локальная и l'Лобальная области видимости 101 Локальные переменныс не MOryт исполь:юваться в l'Jюбальной области ВИi(ИМОСТИ 102 В локальных областях видимости не MOIyr использоваться нсременные из друтих локальных областей видимости 103 1Jюбальные персменные MOryт читаться иа локальной области видимости 104 Локальные и I'J!обальные неременные с одинаковыми именами 104 Инструкция g:obaJ 105
8 Содержание Обработка исключений 108 Короткая проrрамма: yraдай число 11 О Резюме 112 Контрольные вопросы 113 Учебные проекты 113 Последовательность Коллатца 114 Проверка корректности ввода 114 Thaвa 4. СПИСКИ 11 !) Что такое список 115 Доступ к отдельным элементам списка с помощью индексов 116 Orрицательныс индексы 118 Получение Ч<lСТИ списка с помощью среза 118 Получение длины списка с помощью функции len ( ) 119 Изменение значений в списках с помощью индексов 119 Конкатенация и репликация списков 120 Удаление значений из списка с помощью инструкции del 120 Работа со списками 121 Использование циклов for со списками ] 22 Операторы in и not in 124 Трюк с rрупповым присваиванием ] 24 Комбинированные операторы присваивания 125 Методы 126 Поиск значения в списке с помощью метода index ( ) 126 Добавление значений в список с ПОМОlЦью методов append () и ir:.sert () 127 Удаление значений из списка С помощью метода remove () 128 Сортировка значений в списке с помощью метода sort ( ) 129 Пример проrраммы: Magic 8 ВаН со списком 130 Типы данных, подобные спискам: строки и кортежи 132 Изменяемые и неизмсняемыс типы данных 132 Кортежи 135 Преобразование типов с помощью функций list () и tuple () 136 Ссылки 136 Передача ссылок 139 Функции сору () и deepcopy () модуля сору 140 Резюме 141 Контрольные вопросы 142 Учебные проекты 142 Запятая в качестве разделителя 143 Рисование символами 143
Содержание 9 fлава 5. Словари и структурирование дaJIIIых 145 Что такое словарь 145 Сравнение словарей и списков 146 Методы keys ( ) , values () и i tems ( ) 148 Проверка существования ключа или значения в словаре 149 Методgеt() 150 Метод setdefault () 150 Красивая печать 152 Использование струюур данных для моделирования реальных объектов 153 Поле для иrpы в "крестики-нолики" 154 Вложенные словари и списки 160 Резюме 162 Контрольные вопросы 162 Учебныепроеклы 163 Инвентарь приключенческой иrры 163 Функция преобраэования списка в словарь для приключенческой иrpы 164 fлава 6. Манипулирование строками 165 Работа со строками 165 Строковые литералы 165 Индексирование строк и извлечение срезов 168 Исполь:ювание операторов in и not in со строками 170 Полезные методы для работы со строками 170 Методы upper () , lower () , isupper () и islower ( ) 170 Строковые методы isX ( ) 172 Методы startswi th () и endswi th () 174 СтроковыеметодЫjоiп() иsрlit() 175 Выравнивание текста с помощью методов rjust (), ljust () И center ( ) 176 Удаление пробелов с помощью методов strip (), rstrip () и lstrip () 178 Копирование и вставка строк с помощью модуля pyperclip 179 Проект: парольная защита 180 Шаr 1. Проектирование проrpаммы и структур данных 180 Шаr 2. Обработка apryмeHТOB командной строки 181 Шar 3. Копирование пароля 182 Проект: добавление маркеров в разметку Wiki-документов 183 Шar 1. Копирование и вставка посредством буфера обмена 184 Шar 2. Разбивка текста на строки и добавление звездочек 184 Шar 3. Объединение измененных строк 185 Резюме 186 Контрольные вопросы 187
10 Содержание Учебный проект Табличный вывод данных 187 187 Часть 11. Автоматизация задач 189 IЛава 7. Поиск по шаблону с помощью реrуля:рных выражений 191 Поиск образцов текста без использования реryлярных выражений 192 Поиск образцов текста с помощью реryлярных выражений 194 Создание объектов Regex 195 Поиск соответствий объектам Regex 196 Пошаroвая процедура поиска соответствий реryлярному выражению 197 Друrnе возможные шаблоны реryлярных выражений 197 Создание rрупп с помощью крyrлых скобок 197 Выбор альтернативных I'РУПП с помощью канала 199 Указание необязательной rруппы символов с помощью вопросительноrо знака 200 Указание соответствия rpуппе символов, повторяющейся нуль или несколько раз, с помощью звездочки 201 Указание соответствия одному или нескольким повторениям rpуппы с ПОМОЩЬЮ плюса 201 Указание соответствия определенному количеству повторений rруппы с помощью фиrypных скобок 202 Жадный и нежадный виды поиска 203 Метод f indall ( ) 204 Символьные классы 205 Создание собственных символьных классов 20б Символ крышки и знак доллара 207 fрупповой символ 208 Указание соответствия любому тексту с помощью комбинации "точказвездочка" 208 Указание соответствия символам новой строки с помощью точки 209 Сводка символов реryлярных выражений 210 Иrнорирование реrистра при поиске соответствий 211 Замена строк с помощью метода sub () 211 Работа со сложными реryлярными выражениями 212 Комбинация констант re. IGNORECASE, re. DOTALL и re. VERBOSE 213 П роект: извлечение телефонных номеров и адресов электронной почты 214 Шаr 1. Создание реryлярноro выражения для поиска телефонных номеров 215 Шаr 2. Создание реryлярноrо выражения для поиска адресов электронной почты 216
Содержание 11 Шаr 3. Поиск всех (ОВlIадений в тексте, СКОlIированном в буфер обмена 217 Шar 4. Объединение совпадений в одну строку для копирования в буфер обмена 218 Выполнение проrpаммы 218 Идеи относительно создания аналоrичных проrрамм 219 Резюме 219 Контрольные вопросы 220 Учебные проекты 222 Обнаружение сильных паролей 222 Версия функции strip (), использующая реryлярные выражения 222 IЛава 8. Чтение и запись файлов 223 Файлы и пути доступа к ним 223 Использование обратной косой черты в Windows и косой черты в 08 Х и Linux 224 Текущий рабочий каталоr 225 Абсолютные и относительные пyrи доступа 226 Создание новых папок с помощью функции os . та kedi r s ( ) 227 Модуль os.path 227 Обработка абсолютных и относительных nyrей 228 Определение размеров файлов и содержимоro папок 230 Проверка существования пути 231 Чтение и запись файлов 232 Открытие файла с помощью функции open ( ) 233 Чтение содержимоro файла 234 Запись в файл 235 Сохранение переменных с помощью модуля shel ve 236 Сохранение переменных с помощью функции pprint .pformat () 238 Проект: rенерация файлов случайных экзаменационных билетов 240 Шаr 1. Сохранение данных билетов в словаре 240 Шаr 2. Создание файлов билетов и перемешивание вопросов 241 ШаI' 3. Создание вариантов ответов 243 Шаl' 4. Запись содержимоro в файлы билетов и ключей ответов 244 Проект: буфер обмена для работы с несколькими значениями 245 Шаr 1. Комментарии и настройка хранилища 246 Шаr 2. Создание содержимоro буфера обмена, ассоциируемоro с ключевым словом 247 Шаr 3. Список ключевых слов и заrpузка содержимоrо, асеоциированноro (: ключевым словом 248 Резюме 249
12 Содержание Контрольные вопросы 249 Учебные проекты 250 Расширение возможностей буфера обмена, рассчитанноro на работу с несколькими значениями 250 Проrрамма Mad Libs 250 Поиск с помощью реryлярнbIX выражений 251 rлава 9. Управление файлами 253 Модуль shutil 254 Копирование файлов и папок 254 Перемещение и переименование файлов и папок 255 Безвозвратное удаление файлов и папок 257 Сохраняйте резервные копии удаленных файлов и папок с помощью модуля send2trash 258 Обход дерева каталоrов 259 Сжатие файлов с помощью модуля zipfile 261 Чтение ZIР-файлов 261 Извлечение файлов из ZIР-архива 262 Со:щание ZIР-файлов и добавление в них новых файлов 263 П роект: переименование файлов с заменой американскоro формата дат европейским 2М Шаr 1. Создание реryлярноro выражения для поиска дат, указанных в американском формате 264 Шаr 2. Идентификация частей имен файлов, соответствующих датам 266 Шаr 3. Формирование HOВOro имени файла и переименование файлов 267 Идеи относительно создания аналоrичных проrрамм 268 Проект: создание резервной копии папки в виде ZIР-файла 269 Шаr 1. Определение имени, которое следует присвоить ZIР-файлу 269 Шаr 2. Создание HOBOro ZIР-файла 270 Шаr 3. Обход дерева каталоroв и добавление содержимоro в ZIP-фaйJI 271 Идеи относительно создания аналоrичных I1porpaмM 272 Резюме 272 Контрольные вопросы 273 Учебные проекты 274 Выборочное копирование 274 Удаление ненужных файлов 274 Заполнение пропусков в нумерации файлов 274 rлава 10. Отладка 275 Возбуждение исключений 276 Получение обратной трассировки стека вызовов в виде строки 278
Содержание 13 Утверждения 279 Использование yrверждений в проrрамме, имитирующей работу светофора 281 Отключение yrверждсний 282 Протоколирование 283 Иснользование модуля logging 283 Не выполняйте отладку с помощью инструкции print () 285 Уровень критичности сообщений 286 Отключение протоколирования 287 Запись сообщений нротоколирования в файл журнала 288 Отладчик IDLE 288 KHOH Stcp 290 Кноп Over 290 KHOH Out 290 Кноп Quit 290 Отладка проrраммы для сложения чисел 291 Точки останова 294 Резюме 295 Контрольные вопросы 296 Учебный проект 297 Отладка проrраммы, имитирующей подбрасывание монсты 297 IЛава 11. Автоматический сбор данных вИнтернете 299 Проект: проrрамма maPlt,py с модулем webbrowser 300 ШЮ" 1. Определение URLaдpeca 300 Шаr 2. Обработка apryMeHToB командной строки 301 Шаr 3, Обработка содержимоrо буфера обмена и запуск браузера 302 Идеи относительно создания аналОl'ИЧНЫХ проrрамм 303 Эаrрузка файлов из Интернета с номощью модуля Rcquests 303 3аl'РУЗ всб-страницы посредством функции requests. get () 304 Проверка ошибок 305 Сохранение заrpуженных файлов на жестком диске 306 HTML 308 Ресурсы для изучения HTMI. 308 Краткие сведения но llTML 308 Просмотр исходною HTMI.KOдa веб-страницы 309 Открытие окна инструментов разработчи в браузере 311 Использование инструментов разработчи для поис HTML элементов 312
14 Содержание Синтаксический анализ HTMI. с помощью Beautiful Soup 314 Создание объеКТd BeautifulSoup на основе HTMI. 314 Поиск элемента с помощью метода select ( ) 3] 5 Получение данных из атрибутов элемента 317 Проект: кнопка "Мне повезет" поисковика Google 318 Шаr 1. Получение apryмeHTOB командной строки и запрос поисковой страпицы 318 Шar 2. Поиск всех результатов 319 Шаr 3. Открьrrие браузера для каждоrо из результатов поиска 320 Идеи относ.ительно создания аналоrичных проrрамм 321 Проект: заI'рУЗка всех комиксов на сайте ХКСО 322 Шаr 1. Проектирование проrраммы 323 Шar 2. Заrрузка вe(kтраницы 324 Шаr 3. Поиск и заrрузка изображения комикса 325 Шаr 4. Сохранение изображения и поиск предыдущеrо комикса 326 Идеи относительно создания аналоrичных проrрамм 327 Управление браузером с помощью модуля Selепiuш 328 Запуск браузера, управляемоro модулем Selеlliuш 328 Поиск элементов на странице 329 Щелчок па страпице 33] Заполнение и отправка форм 332 Отправка кодов специальпых клавиш 332 Щелчки па КПОIIКах браузера 333 Получение дополнительной информации о модуле SelепiulП 334 Резюме 334 КОНТРОЛЫlые вопросы 334 Учебпые проекты 335 Проrрамма для отправки электронной почты из командной строки 335 Заrрузчик изображений из Интернета 336 "2048" 336 Верификация ссылок 336 rлава 12, Работа с электронными таблицами Excel 337 OКYMeHTЫ Ехсеl 338 Устаповка модуля openpyxl 338 Чтение документов Ехсеl 339 Открытие документов Ехсеl с помощью модуля OpenPyXL 339 Получение списка листов рабочей книrи 340 Получение ячеек рабочих листов 341 Выполнение прсобразований между буквенными и цифровыми обозначениями столбцов 342
Содержание 15 Получение строк и столбцов рабочих листов 343 Рабочие книrи, листы и ячейки 345 Проект: чтение данных электронной таблицы 345 Шаr 1. Чтение данных электронной таБлицыI 347 Шаr 2. Заполнение структуры данных 348 Шаr 3. Запись результатов в файл 349 Идеи относительно создания аналоrичных nporpaMM 351 Запись документов Ехсеl 351 Создание и сохранение документов Ехсеl 352 Создание и удаление рабочих листов 352 Запись значений в ячейки 353 Проект: обновление электронной таблицы 354 Шаr 1. Создание структуры, содержащей данные для обновления 355 Шаr 2. Проверка всех строк и обновление некорректных цен 356 Идеи относительно создания аналоrичных проrрамм 357 Настройка 'rипов шрифтов, используемых в ячейках таблицы 358 Объекты Fопt 359 Формулы 360 Настройка строк и столбцов 362 Настройка высоты строк и ширины столбцов 362 Слияние и отмена слияния ячеек 364 Закрепление областей 365 Диаrраммы 366 Резюме 368 Контрольные вопросы 369 Учебные проекты 370 lенератор таблиц умножения 370 ПрОJ'рамма для вставки пустых строк 370 Отражение электронной таблицы относительно диаrонали 371 Преобразование текстовых файлов в электронную таблицу 372 Преобразование электронной таблицы в текстовые файлы 372 IЛава 13. Работа с документами в форматах PDF и Word 373 РDF-документы 373 Извлечение текста из РDF-файлов 374 Дешифрование РDFдокументов 376 Создание РDF-документов 377 Проект: объединение выбранных страниц из мноrИХ PDI<'-документов 382 Шаr 1. Поиск всех РDF-файлов 383 Шаr 2. Открытие РDFфайлов 384
16 Содержание UПаr3.Добавлениестраниц 385 UПаr 4. Сохранение результатов 385 Идеи относителыlO создания аналоrичных IIporpaMM 386 ДOКYMeНТbI Word 386 Чтение документов Word 388 Получение полноro текста из файла .docx 389 Стилевое оформление абзаца и объекты Run 390 С..оздание документов Word с нестандартными стилями 391 Атрибуrы объекта Run 392 ЗаписьдокументовWоrd 394 Добавление заroловков 396 Добавление разрывов строк и страниц 397 Добавление изображений 397 Резюме 398 Контрольные вопросы 399 Учебные проекты 99 РDFпаранойя 399 Персонализированные ПРИIJlашения в виде документов Word 400 Взлом паролей PDF методом Iрубой силы 400 lЛава 14. Работа с СSV.файлами и данными в форматеJSОN 403 Модуль C5V 403 Объекты Reader 405 Чтение данных из объектов Reader в ци.кле for 406 Объекты Wr i ter 406 Именованные apryMeHTbI delimi ter и lineterminator 408 Проект: удаление заrоловков из СSV-файла 409 UПаr 1. Цикл по всем СSV-файлам 410 UПаr 2. Чтение CSV-фaйJIa 410 UПаr 3. Запись С5V-файла без первой строки 411 Идеи относительно создания аналоrичнbIX проrрамм 412 jSON и интерфейсы прикладноro проrраммирования 413 Модуль j 50n 414 Чтение данныхjSОN с помощью функции load5 () 414 Запись JSON-даннbIX с помощью функции dumps ( ) 415 Проект: получение текущеrо проrноза поroды 415 UПаr 1. Получение расположения из apryмeнтa командной строки 416 UПаr 2, 3аrрузкаjSОN-данных 417 UПаr 3. ЗаrрузкаjSОN-данных и вывод проrноза поrоДЫ 417 Идеи относительно создания аналоrичных проrрамм 419
Содержание 17 Резюме 420 Контрольные вопросы 420 Учебный проскr 420 Проrрамма для преобра.'ювания данных из формата Ехсеl в формат CSV 421 rлава 15. Обработка значений даты R времени, lVIавиРОВlЦик заданий и запуск проrpамм 423 Модуль time 423 ФУНКЦИЯ time. time ( ) 424 Функция time . sleep ( ) 425 Окруrление чисел 426 Проект: суперсекундомер с остановом 427 Шаr 1. Создание каркаса проrраммы для отслеживания времени 428 Шаr 2. Отслеживание и вывод длительности замеров 428 Идеи относительно создания аналоrичных проrрамм 430 Модуль datetime 430 Тип данных timedel ta 432 Орraнизация паузы до наступления определенной даты 434 Преобразование объектов da tetime в строки 434 Преобразование строк в объекrы datetime 436 Обзор функций Python для работы с датами и временем 436 Мноroпоточность 437 Передача apryмeHToB целевой функции 440 Проблемы параллелизма 441 Проект: мноrollОТОЧНЫЙ заl'РУЗЧИК файлов с сайта ХКСО 441 Шаr 1. Видоизмепение проrраммы пyrем вынесения ее кода в функцию 442 Шаr 2. Создание и запуск потоков ВbIПолнения 443 Шаr 3. Ожидание завершения всех потоков 444 Запуск друrих проrрамм изl'ylllOП 445 Передача арryментов командной строки функции Popen () 447 ПЛанировщик заданий Willdows, система инициализации launchd и демон-планировщик cron 448 Открытие ве&сайтов с помощью Python 448 Запуск друrих сценариев PyttlOn 448 Открытие файлов проrраммами по умолчанию 449 Проект: простая проrрамма обратноro отсчета времени 451 Шаr 1. Обратный отсчет 451 Шаr 2. Воспроизведение звуковом файла 452 Идеи относительно (:озд.шия аналОI'ИЧНЫХ проrрамм 453
18 Содержание Резюме Контрольные вопросы Учебные проекты Приукрашенный хронометр 3аrрузка веб.комиксов по расписанию rлава 16. Отправка сообщениii электронной почты и текстовых сообщений SMTP Отправка электронной почты Установление соединения с SMTP-сервером Orправка строки приветствия SMTP.cepBcpy Начало ТLS-шифрования Выполнение процедуры входа на SMTP-сервер Orправка почты Разрыв соединения с SMTP.cepBepoM IMAP 454 454 455 455 455 457 457 458 459 460 461 461 462 462 463 Извлечение и удаление сообщений электронной почты с помощью IМAP 463 Соединение с IMAP-сервером 464 Вход в учетную запись на IМAP-cepвepe 465 Поиск сообщений 466 Извлечение сообщений электронной почты и снабжение прочитанных писем специальной меткой 471 Получение адресов электронной почты из "сырых" сообщений 472 Получение тела письма из "сыроro" сообщения 473 Удаление сообщений 474 Разрыв соедипения с сервером IMAP 475 Проект: рассьтl<a по электронной почте напоминаний о необходимости уплаты членских взносов 475 Шаl" 1. Открытие файла Ехсеl 476 Шаr 2. Поиск всех членов клуба, не уплативших взнос 477 Шаr 3. Отправка персональнbIX напоминаний по электронной почте 478 Отправка текстовых сообщений с помощью Twilio 480 Создание учетной записи lwilio 481 Отправка текстовых сообщений 481 Получение текстовых сообщений с помощью Python 484 Проект: модуль "Черкни мне" 484 Резюме 485 Контрольные вопросы 486 Учебные проекты 486
Содержание 19 Распределение рyrинных задач пyrем рассылки по электронной почте 486 Напоминание о зонтике 487 Автоматический отказ от подписки 487 Дистанционное управление компьютером посредством электронной почты 488 rлава 17. Работа с изображениями 491 Основы компьютерной обработки изображений 491 Цвета и RGВА-значения 491 Кортежи координат и прЯМОУl'ОЛЬНИКОВ 494 Манипулирование изображениями с помощью библиотеки РШоw 495 Работа с типом данных Iшаgе 497 Обрезка изображений 499 Копирование и вставка изображений в друrие изображения 499 Изменение размеров изображения 504 Поворот и зеркальное отображение изображений 504 Изменение отдельных пикселей 507 П роект: добавление лоютипа 508 Шаr 1. Открытие изображения ЛОl'Oтипа 510 Шаr 2. Цикл по всем файлам и открытым изображениям 511 Шаr 3. Изменение размеров изображений 512 Шаr 4. Добавление лоrотипа и сохранение изменений 513 Идеи относительнО создания аналоrичных I1porpaMM 514 Рисование изображений 515 Рисование фиryр 516 Рисование текста 518 Резюме 520 Контрольные вопросы 521 Учебные I1роекты 522 Расширение и доработка проrpамм основною проекта этой 1:Jl3.ВbI 522 Обнаружение папок (: фотоrрафиями на жестком диске 523 Персональные приrлашения 524 rлава 18. Управление клавиатурой и МhlПlЫО с помощыо средств автоматизации rpафическоro интерфейса пользователя 525 Установка модуля pyaиtogиi 526 Сохранение контроля над клавиатурой и мышью 52б Прекращение выполнения всех задач пуreм выхода из учетной записи 527 Паузы и безопасный резервный выход 527
20 Содержание Управление перемещениями указателя мыши 528 Перемещение указателя мыши 529 Получение позиции указателя мыши 530 Проект "[де сейчас находится указатель мыши?" 530 Шаr 1. Импортирование модуля 531 Шаr 2. Код выхода из проrраммы и бесконечный цикл 531 Шаr 3. Получение и вывод координат указателя мыши 532 Управление взаимодействием с мышью 533 Щелчки мышью 533 Перетаскивание указателя мыши 534 Прокрутка 536 Работа с экраном 538 Получение снимка экрана 538 Анализ снимка экрана 539 Проект: расширение проrраммы mouseNow. ру 540 Распознавание образов 540 Управление клавиатурой 542 Orправка строки, набранной на виртуальной клавиатуре 542 Обозначения клавиш 543 Нажатие и отпускание клавиш 54!') IЬрячие клавиши 545 Обзор функций PyAutoGUI 546 Проект: автоматическое заполнение формы 547 Шаr 1. Составление плана действий 549 Шаr 2. Настройка координат 550 ШЮ' 3. Начало ввода данных 552 Шаr 4. Обработка списков выбора и переключателей 553 Шаr 5. Отправка формы и ожидание 555 Резюме 556 Контрольные вопросы 556 Учебные проекты 557 Как притвориться занятым 557 Бот для отправки MrH08eHHbIX сообщений 557 Руководство по созданию иrpовоro бота 558 Приложеиве А. Установка модулей стороавих разработчиков 559 УТИJlита р i р 559 Установка сторонних модулей 560
Содержание 21 Приложение Б. Запуск проrрамм "Мш'ичсская" строка Запуск проrрамм на Python в Willdows Запуск проrрамм на PythOIl в 08 Х и Ijllux Запуск проrрамм на Pytholl с отключенными утверждениями 561 561 561 563 563 Приложение В. Ответы на контрольные вопросы rлава 1 lлава 2 rлава 3 rлава 4 rлава 5 rлава 6 rлава 7 rлава 8 rлава 9 rлава 1 о lлава 11 rлава 12 rлава 13 rлава 14 rлава 15 rлава 16 rлава 17 rлава 18 565 565 566 567 568 569 570 570 572 572 572 574 575 576 576 577 577 578 578 581 Предметный указатель
06 авторе Эл Свейrарт разработчик ПО, автор киИl' по проrраммированию, жи вет в Сан-Франциско. Ero любимый язык проrраммирования PytllOll, для KOToporo он разработал несколько модулей с открытым исходным кодом. Друrие ero книrи доступны на условиях бесплатной лицензии Creative CommoIlS (http://www . inventwithpython. сот). о техническом рецензенте Ари Лаценски разрабатывает приложения для Android и проrраммное обеспечение на языке PythOIl. Живет в СанФранциско, rде публикует ста- тьи ПО проrраммированию для Android на сайте http://gradlewhy . ghost. io и занимается преподавательской деятельностью внекоммерческой орrани- зации "Wошеn Who Code", Ее хобби иrра на rитаре.
ВВЕДЕНИЕ "За каКИХJfО пару часов ты сделал то, на что у нас троих ушло бы целых три дня". В начале 200о..х rодов мой сосед по комнате в колледже работал в маrазине электроники. Время от времени они получали электронные таблицы с ценниками своих конкурентов, включающие тысячи наи менований. Распечатка одной таблицы представляла собой толстую стопку бумажных листов. Обработкой данных занимались три со-- трудника маrазина. Они сравнивали цены, указанные в таблице, с ценами в своем маrазИне и отмечали тот товар, который конкуренты продавали по более низкой цене. На эту работу у них уходило примерно два дня. "А знаете что? Если вы дадите мне исходный файл таблицы, то я напишу проrрамму, которая выполнит всю работу вместо вас", сказал мой TOBa рищ, увидев, как они копошатся среди rруды разбросанных на полу и ело-- женных в стопки бумажных листов. Через пару часов у Hero была rOToBa небольшая проrрамма, которая чи тала данные о ценах конкурентов из файла, находила для каждоrо продукта ана.тюr в базе данных маrазина и отмечала весь товар конкурентов, цена ко-- Toporo была ниже. Здесь уместно сказать, что мой товарищ был вcero лишь начинающим проrраммистом, и БШlЬшую чаСТЬ этоro времени он потратил ыа просмотр нужных разделов в книrе по проrраммированию. Собственно выполнение проrраммы заняло вcero лишь несколько секунд, что дало воз- можность моему товарищу и ero коллеrам по работе насладиться в тот день удлиненным обеденным переРЫ80М. Этот пример наrлядно демонстрирует всю мощь проrраммирования. Компьютер подобен армейскому ножу, который можно использовать в са- мых разных ситуациях. МНOI'Ие люди часами работают за клавиатурой, вво- дя данные для выполнения повторяющихся задач, и даже не доraдываются, что их компьютер, если снабдить ero соответствующими инструкциями, способен выполнить ту же работу за считанные секунды. Дп. Koro предназначена эта книrа в наши дни трудно найти сферу человеческой деятельности, в которой вообще не используется проrpаммное обеспечение (ПО). Почти каждый из нас общается в социальных сетях, телефоны мноrих из нас это по сyrи компьютеры, подключенные к Интернету, а бальшая часть офисноrо перСОllала для выполнения своих функциональных обязанностей нужда ется в компьютерной технике. Как следствие, это привело к необычайно высокому спросу на специалистов, способных писать проrраммный код.
26 Введение Бесчисленные КНИI'И по проrраммированию, интсраКТИВНblе онлайновые руководства, практические семинары для ра;раБОТI(ИКОВ все это lIаправ лено на превращение амбициозных новичков в специалистов проrрамм ной индустрии, заработная плата которых выражается шестизначными числами. Эта книrа предназнаtlена не для них. Она предназначена для всех ocтa.1IW ных. Про чтение одной только ЭТОЙ КНИI'и не сможет сделать из вас разработчикапрофессионала, точно так же как пяти уроков иrры на rита ре вряд ли будет Достаточно для Toro, чтобы стать рокзвездой. Но если вы офисный работник, администратор, преподаватель или вообще один из тех, кто использует компьютер для работы или развлечения, то изучения основ проrраммирования в том объеме, который предлaraeтся в данной книrе, вам хватит для автоматизации следующих простых задач: . перемещение и переименование тысяч файлов и их сортировка по папкам; . заполнение онлайновых форм без ввода данных вручную; . заrрузка файлов или копироваНI1:е текста с веб-сайта при ero обнов лении; . вывод компьютером заранее подrотовленных уведомлений; . оБНОR1Iение и форматирование электронных таблиц Excel; . проверка электронной почты и отправка заранее подroтовленных ()'I'-- ветных писем. Все эти задачи просты, но отнимают у человека массу времени. Кроме Toro, ачастую они настолько тривиальны или узкоспециальны, что поды <KaTЬ какую-то rотовую nporpaMМY для их выполнения не удается. Воору- жившись даже минимальными знаниями в области проrраммировапия, вы сможете заставить свой компьютер выполнять эти задачи вместо вас. ИсходныепредпопожеНИR Эта книra не справочник, а руковод<:тво для начинающих. ИСПОЛl>зу емый в ней стиль проrpаммирования иноrда идет вразрез с принцинами наилучшей практики (например, в некоторых проrраммах используют<:я rлобальные переменные), но это компромиссное решение, позволяющее сделать код более леrким для изучения. Книrа предназначена для тех, кому будет достаточно научиться писать простой одноразовый код, поэтому сти- лю оформления проrрамм и приданию им элеrантноro вида не уделяется особоrо внимания. Такие ПОНятия "продвинyrоrо" проrpаммирования, как "объектно-ориентированный подход", "списковые включения" и "reHe- раторы", не рассматриваются, дабы не усложнять излarаемый материал.
Введение 27 Возможно, опытные проrраммисты леrко укaжyr в книrе те места, rде код следовало бы изменить, чтобы сделать ero более эффективным, но в этой книre нас в основном заботит создание работоспособных проrрамм с мини- мальными усилиями. Что такое проrраммирование в телевизионных шоу и фильмах часто показывают проrpaмми<,:тов, при. с.тально Вrлядывающихся в потоки заrадочных нулей и единиц, беryщих по экрану, но современное проrраммирование далеко не столь таинственно. Проzpам.мированue Bcero лишь снабжение компьютера инструкциями, при- казывающими ему что-либо сделать. Это может быть оперирование числа- ми, изменение текста, поиск информации в файлах или обмен данными с друrими компьютерами через Интернет. Во всех проrраммах в качестве строительных блоков используются эле- ментарные инструкции. Вот как выrлядят некоторые из наиболее распро- страненных инструкций TaKoro рода, если перевести их на обычный чело- веческий язык. "Сделай зто; затем сделай то". "Если данное условие удовлетворяется, выполви такое-то дей- ствие; в противном случае выполни такое-то действие". "Выполни это действие такое-то количество раз". "Продолжай выполнять эти действИJI до тех пор. пока удовлетво- ряется данное условие". Эти строительные блоки можно комбинировать для реализации более сложных решений. В качестве примера ниже приведены инструкции (так называемый исходныЙ 'Код) простой проrраммы на языке PytI1OIl. Проrрамм- ное обеспечение последовательно выполняет каждую строку кода, начиная с первой (при этом некоторые строки выполняются лишь при соблюдении определенных условий, иначе выполняется друrая строка), пока не будет достиrнут конец nporpaMMbI. о passwordFile open('SecretPasswordFile.txt') . secretPassword passwordFile.read() . print ( 'Введите пароль . ' ) typedPassword inpиt() О if typedPassword == secretPassword: О print ( 'Доступ разрешен.') Ф if typedPassword == '12345': . рriпt('Рекомендуем установить более сложный пароль! ') else: . print ('В доступе отказано.')
28 Введение Даже если вы ничеrо не смыслите в проrраммировании, вы все равно сможете сделать разумные предположения относительно Toro, что дела ет этот код, просто читая ero. Сначала открывается файл SecretPasswordf'ile. ехе О, из KOToporo считывается секретный пароль .. Затем поль:ювателю предлаrается ввести свой пароль (с помощью клавиатуры).. Далее оба па роля сравниваются между собой О, и в случае их совпадения nporpaMMa выводит на экран текст Доступ разрешен.. Далее IIporpaMMa проверяет, является ли введенный пароль числом 12345 ., и, если это так, подсказыва- ет, что такой вариант выбора пароля не ЯR1lЯется оптимальным .. В случае несовпадения паролей nporpaмMa выводит на экран сообщение в доступе отказано .. Чrо о,но.,о,т но,.он., "'''оп Название Руеhoп относится как к языку проrраммирования Python (с ero собственным синтаксисом, определяющим правила написания корректно- ro кода), так и к интерпретатору Pyt:hon проrpамме, предназначеннойД1lЯ Чтения исходноrо кода (написанноrо на языке Python) и выполнения ero инструкций. Различные версии интерпретатора Python, ориентированные на платформы l.illux, OS Х и Windows, доступны для бесплатной зarpуэки по адресу http://python . org. Своим названием языК Python обязан вовсе не одноименному виду пре- смыкающихся (питон), а комедийной rруппе из Великобритании "Monty PytllOn"J, работавшей в жанре сюрреалистическоrо юмора. Проrраммистов на Python шутливо называют пUтOHucтa.мU (Pythonistas), а мноrочисленные руководства и документация по языку Python пестрят ссылками как на rpуп- пу "Monty PytllOll", так и на рептилию. nРО'РО..."1 .0." н, 06.'О"II6НО . ".ерш,н"" ,но" .0".0"'''1 По моим наблюдениям, наибольшую озабоченность у тех, кто собирает- ся учиться проrраммированию, вызывает то, что, по их мнению, для ЭтОro надо хорошо знать математику. На самом деле большинству проrраммистов не нужно знать ничеrо кроме элементарной арифметики. В этом смысле хорошему проrраммисту понадобится не HaMHoro больший объем матема- тических знаний по сравнению с тем, который требуется для решения ro- ловоломок судоку. I Читается как "Монти Пайl'ОН". fрамматически правильное звучание названия языка РуйЮl1 также [пай-тон), однако среди проrраммистов принято произно- сиl'ь cro как lпи-тон). Пpu.м.eч, ред.
Введение 29 Cyrb rоловоломки судоку заключается в заполнении цифрами от 1 до 9 каждOl'О из внyrренних квадратов размером 3х3, расположенных на иrpo-- вом поле размером 9х9, таким образом, чтобы ни одна cTpOl<a и ни один столбец большоrо квадрата не содержали повторяющихся цифр. Для Ha хождения решения необходимо использовать дедуктивный лоrический метод, исходя из заданной начальной конфиrypации цифр. Например, по-- скольку в rоловоломке, показанной на рис. 1, цифра 5 находится в левом верхнем уrлу иrpовоro поля, она не может появиться ни в верхней строке, ни в крайнем слева столбце, ни в левом верхнем квадрате размером 3х3. Последовательное применение подобной лоrики к строкам, столбцам и внyrренним квадратам будет предоставлять вам подсказки, позволяющие заполнять пустые клетки roловоломки. 5 3 7 б 1 9 5 9 8 6 8 6 3 4 8 3 1 7 2 6 б 2 8 4 1 9 5 8 7 9 5 3 4 б 7 8 9 1 2 6 7 2 1 9 5 3 4 8 1 9 8 3 4 2 r, 6 7 . 8 5 9 7 б 1 4 2 3 4 2 б 8 5 3 7 Q 1 --" 7 1 3 9 2 4 8 5 б 9 6 1 5 3 '7 2 8 4 2 8 7 4 1 9 б 3 5 3 4 5 2 8 б 1 7 9 Рис. 1. rоловоломка судоку (слева) и ее решение (справа). Несмотря на то что 8 rоло- воломке используются числа, никаких особых математических знаний для нахождения решения не требуется (изображения предоставлены компанией Wikimedia Commoпs) Из Toro факта, что в rоловоломке судоку используются числа, вовсе не следует, что для нахождения решения необходимо быть хорошим MaTeMa тиком. То же самое справедливо и в отношении проrраммирования. Как и в судоку, написание проrрамм предусматривает разбиение задачИ на ряд отдельных этапов. Аналоrичным образом при отладке проера.м.м (процесс обнаружения и предотвращения возможности возникновения ошибок) вы кропотливо анализируете результаты интересующих вас Действий, выпол ненных проrраммой, пытаясь выявить причину ОIlIибки. И, как это харак- терно для любоrо друrоrо вида деятельности, чем больше вы проrрамми- руете, тем лучше у вас это будет получаться.
30 Введение nроrpo"'.РО'"И.' 7IОР"'''.' '.III1'.".'ИО". Проrраммирование это вид творчества, несколько напоминающий возведение замков из элементов LEGO. Сначала вы формулируете для себя основные идеи ОТНОСИтельно Toro, что собой должна предстаwшть будущая nporpaмMa и какие строительные элементы имеются в вашем распоряж нии. После этоrо вы при ступаете к построению nporpaMMbI. Завершив по- строение ПрOl'раммы, вы наводите окончательный порядок в своем КОДе, аналоrично тому как по окончании строительства замка принялись бы за уборку еro территории. Различие между проrраммированием и дрyrими творческими видами д ятельности заключается в том, rro все необходимые исходные материалы находятся R вашем компьютере, и вам не нужно дополнительно закупать какие-либо холсты, краску, пленку, нитки, блоки I.EGO или электронные компоненты. Написав проrрамму, вы можете леrко поделиться ею через Интернет с целым миром. И даже если в процес(:е проrраммирования вы будете допускать неизбежные ошибки, это занятие доставит вам массу удо- вольствия. Структура книrи в части 1 книrи рассмотрены основы проrраммирования на языке PytllOn, тоrда как часть 11 ПОСВЯЩена различным задачам, решение которых можно автоматизировать. Каждая rлава части 11 включает nporpaMMHbIe проекты, с которыми вам предстоит работать. Ниже приведено краткое описание rлав. Часть 1. Основы проrраимироваиия на языке Python . rлава 1. Основные понятия языка Python. В этой rлаве рассматри ваются выражения базовый тип инструкций PythOIl, а также описы вается использование интерактивной проrраммной оболочки PythOIl для экспериментирования с кодом. . rлава 2. Поток управления. В этой rлаве речь идет о том, как заста вить проrрамму принимать решения, касающиеся последовательно сти выполнения инструкций, чтобы код MOI' самостоятельно реаrиро- вать на возникновение различных условий. . rлава 3. Функции. В этой rлаве показано, как использовать собствен ные функции для разбиения кода на отдельные части, С которыми проще работать. . fлава 4. Списки. Вводится понятие списка, одноrо из встроенных ти пов данных PytllOll, и рассказывается о том, как использовать списки для орrанизации данных.
Введение 31 . rлава 5. Словари и структурирование данных. Вводится понятие друrоrо BCTpoeHHoro типа данных PythOIl, словаря, и демонстрируются более совершенные способы орrанизации данных. . I'лава 6. Манипулирование строками. Описываются методы работы с текстовыми данными (которые в языке Python принято называть стрrжа.мu). Часть 11. Автоматизация задач . rлава 7. Поиск по шаблону с помощью реryлярных выражений. Обсуждаются приемы обработки строк и способы поиска образцов текста, соответствующих заданному шаблону, с помощью реryлярных выражений. . rлава 8. Чтение и запись файлов. В этой rлаве речь идет о том, как орrанизовать в проrрамме Чтение данных из текстОвых файлов и со-- хранить информацию на жестком диске. . rJlaBa 9. Управление файлами. Рассматриваются автоматизирован ные способы КОIlИРОвания, перемещения, пере именования и удале ния файлов, позволяющие выполнять данные операции быстрее, чем это можно сделать вручную. . I'лава 10. Отладка. Рассматриваются средства PythOIl, предназначен ные для обнаружения и устранения лоrических ошибок. . I'лава 11. Автоматический сбор данных в Интериете. Показано, как писать проrраммы, выполняющие автоматическую заrрузку веб-страниц и их синтаксический анализ с целью извлечения полез ной информации. Для этоrо проце<:са часто используют термин веб- С1(рапи'Не. Соответствующие про.'раммы называют uumepneт-бomaмu. . rлава 12. Работа с электронными таблицами Excel. Рассматрива ются методы манипулирования электронными таблицами Excel, ори ентированныс lIa обработку данных без их чтения человеком. Такая возможноет.) чрезвычайно полезна в тех случаях, коrда количество обрабатываемых документов исчисляется сотнями и даже тысячами. . rJlaBa 13. Работа с документами в форматах PDF и Word. Рассматри ваются проrраммные методы Чтения документов, подrотовленных в форматах PDJo' и Word. . rлава 14. Работа с СSV-файлами и даиными в формате jSON. Рассматриваются методы манипулирования СSVайлами и JSON данными. . ThaBa 15. Обработка значений даты и времени, планировщик за- даний и запуск проrpамм. Рассказывается о способах обработки ин формации, связанной с датой и временем, и выполнении задач по расписанию. Также показано, как запускать проrраммы, написанные на языках, отличных от P}11101l, из PytllOnI1poI'paMM.
32 Введение . rлава 16. Отправка сообщений электронной почты и текстовых сообщеиий. Обсуждается написание проrpамм, осуществляющих ав- томатическую рассылку сообщений электронной почты и текстовых сообщений. . rлава 17. Работа с изображенИJlМИ. Рассматриваются способы про- rpaMMHoro манипулирования изображениями, сохраненными в раз- личных форматах, таких как JPEG или PNG. . I'лава 18. Управление клавиатурой и мышью с помощью средств ав. томатизации rрафическоrо интерфейса пользователя. Речь идет о возможностях управления клавиатурой и мышью пуrем проrраммной эмуляции щелчков мышью и нажатий клавиш. Исходный код примеров доступен в виде архивноrо файла Automatethe Boring....Sttlffoпliпeтaterials.zip на сайте ht tps: / /www.nostarch.com/automa te stuff/. Там же содержатся друrие полезные ресурсы с примерами, предла- raCMble автором в дополнение к данной книrе, с указанием rлав, к которым они относятся, а также публикуется информация об ОlUибках И опечатках, обнаруженных в книre. 3arpY3Ka и установка Python Версии Python для Windows, 05 Х и Ubuntu доступны ДЛЯ бесплатной за- rpузки по адресу http://python.org/downloads/. Если вы заrpузите текущую версию для своей системы, то все при меры проrpамм, приведенные в кни- re, должны будyr работать. ПptUynpeжiJeнue ОбязателыtO зazpyзume версию Python 3 (иanpuм,ejJ, 3.4.5). Все npuмep'Ы nроерам.м в 'К:ниее наnисан'Ы с использованием Python 3, и если вы nоn'Ытаетесь заnускатъ их в версии Руеhon 2, mo они м.оеут в'Ыnолняться нenравил'Ьно Шlи вообще не ВЫ- nолнят'bfЯ. На указанной странице заrрузки ДЛЯ каждой операционной системы IIредлaraются отдельные установщики, рассчитанные на 6+ и 32-разрядные версии, поэтому предварительно определитесь, какой именно установщик вам нужен. Если вы приобрел и компьютер в 2007 roдy или позже, то, скорее Bcero, на нем установлена 6+разрядная операционная система. В против- ном случае можно полarать, что вы пользуетесь 32-разрядной версией, но лучше убедиться в этом непосредственно, выполнив следующие дсйствия.
Введение 33 . Если вы используете Windows, выберите пункты меню ПускПанель упраsленияСистемs и проверьте, какая система указана в качестве зна чения параметра Тип системы 6+ или 32разрядная. . Если вы используете 05 Х, перейдите в меню Apple, выберите пункты меню About This MacMore IпfoSystem ReportHardware, а затем проверь те значение поля Processor Name. Если в этом поле отображается текст "Core 5010" или "Intel Core Duo", то У вас 32разрядный компьютер. Если же в поле отображается какой-либо дрyroй текст (включая "Iпtеl Core 2 Duo"), то У вас 6+разРЯДIIЫЙ компьютер. . Если вы используете Ubuntu Ипих, откройте терминал и выполните команду unаше M. Orвeт iб8б означает 32-раэрядный компьютер, oт вет х8 б 64 6+разрядный. Для Windows заrpузите установщик Python (файл с расширением .msi) и дважды щслкните на нем. Чтобы установить Python, следуйте инструкциям, которые установщик отображает на экране. 1. Выбсрите вариант Install for AlI Users, а затем щелкните на кнопке Next. 2. Выполните установку в папку С:РуthoпЗ4, щелкнув на кнопке Next. 3. Вновь щелкните на кнопке Next, чтобы пропустить раздсл Customize Python. Для МАС 05 Х заrрузите файл с расширением .dтg, <:оответствующий аlIlей версии 05 Х, и дважды щелкните на нем. Чтобы установиТl) PytllOll, следуйте инструкциям, которые у<..тановщик отображае1' на экране. 1. Korдa 8 новом окнс откроется I1акет DMG, дважды щелкните на файле Python. тpkg. Возможно, вам придется ввести пароль администратора. 2. Щелкните на кнопке Сопtlпuе для прохождения раздела Welcome, а за- тем на кнопке Agree для принятия условий лицензии. 3. Выделите имя ЖеСТКОl'О диска, на который выполняется установка, и щелкните на кнопке Iпstall. В случас использования Ubuntu можете установить Python из окна Tep минала, выполнив следующие действия. 1. Orкройте окно Тermiпsl. 2. Введите команду sudo aptget install pythоnЗ. 3. Введите команду sudo aptget install idlеЗ. 4. Введите команду sudo aptget install руthоnЗрiр.
34 Введение Запуск IDLE Если uHmepnpemamopPython это проrpаммное обеспечение, предназна- ченное для выполнения проrрамм на Python, то uптера",тuвная среда разра- бom",u (IDLE) ;TO проrраммное обеспечение, с помощью KOТOpOl'O можно вводить текст проrрамм примерно так же, как это делается с помощью тек- cToBoro процессора. Приступим к запуску IDLE. . Если ваш компьютер работает под управлением операционной сист мы Windows 7 или более новой версии Windows, щелкните на кнопке Пуск в левом нижнем уrлу экрана, введите IDLE в строке поиска и вы- берите в раскрывшемся меню пункт IDLE (Python GUI). . Если ваш компьютер работает под управлением операционной систс' мы Windows Хр, щелкните на кнопке Пуск и выберите последовательно пункты меню programsqpython 3.4qIDLE (Python GUI). . Е(ли ваш компьютер работает под управлением операционной си- стемы МАе 05 Х, откройте окно Finder, выберите последовательно Applications и Python 3.4, а затем щелкните на значке IDLE. . Если ваш компьютер работает под управлением операционной систе- мы Ubuntu, выберите ApplicationsqAccessoriesqTerminal, а затем введи- те команду idlеЗ. (Вы также можете щелкнyrь на кнопке Applications в верхней части экрана, выбрать раздел Programming и щелкнyrь на пун- кте IDLE 3.) Инreр""'..н". 060.0'1"" Независимо от Toro, в какой операционной системе вы работаете, окно IDLE при ero первом открытии будет в ОСНОВIIОМ пустым, не считая текста, который будет выrлядеть примерно так. Python 3.4.0 (v3.4.0:04f714765c13, Mar 16 2014, 19:25:23) [MSC v.1600 64 bit (AМD64)] оп win32Type "copyright", "credits" or "license()" for more information. »> Это окно называется иHtnepa",тuвпoй оболuч",ой. Оболочка это проrрам- ма, которая позволяет вводить инструкции в компьютер во MHoroM анало- rично тому, как это делается в окне терминала или командной строки на компьютерах 08 Х и Windows соответственно. Команды, которые вы вво- дите в интерактивной оболочке, выполняются интерпретатором PythOIl. Компьютер (IИтает введенные инструкции (команды) и немедленно выпол- няет их.
Введение 35 Чтобы увидеть, как это работает на практике, введите в интерактивной оболочке сразу же за приrлашением к вводу (>>» следующую инструкцию: »> print('Hello world! ') После 1'01"0 как вы введете эту инструкцию и нажмете клавишу <Enter>, интерактивная оболочка должна отреarировать на это выводом следующей строки: »> print('Hello world! ') He1lo world! Какпопучитьсправку Самостоятельно находить решения проблем, возникающих 8 процес се проrраммирования, rораздо леrче, чем вы думаете. Чтобы убедить вас в этом, давайте намеренно вызовем ошибку при попытке выполнить ин струкцию. Введите в интерактивной оболочке инструкцию '42' + З. Вам необязательно знать сейчас, что она означает, но результат должен выrля деть так. >>> '42' + 3 О Traceback (most recent call last): File "<pyshe1l#O>", Нпе 1, in <module> '42' + 3 . TypeError: Can't convert 'int' object to str irnplicitly >>> Появление здесЬ сообщения об ошибке. обусловлено тем, что смысл введенной вами инструкции остался неПОНЯТIIЫМ для PytllOIl. В той части сообщения, которая касается текущеrо стека вызовов (Traceback) ., ото- бражаются конкретная инструкция и номер строки, в которой Python стол кнулся с проблемой. Если сообщение об ошибке ни о чем вам не rоворит, выполните поиск в Интернете по точному тексту сообщения. Введите текст "ТypeError: Can't convert 'int' object to str i:ш.рliсitlу" (включая кавычки) в своем поисковике, и вы увидите тысячи ссылок, по которым можно узнать о том, что означает данное сообщение и что породило ошиб- ку (рис. 2).
36 Введение ,,""' '...;.;o i-}ie TypeError: Can-I CCH)Vert 'irr 10 str ir.lpllcltly - 11 :., -".r!! .!;.;.,.:.....,: (.:')БР.'" r-c )'!(.'М, эапр::.-с' во. Мс.+.е'Те "':;Й1' c,; на р)'('<:ком я3ыfеe '/'зз.:rь rЧ',";;';:I'С'.j"':'.еr.ь!-ь.'" "'ЗЬ'У ;:i,,'''Ч1 pe-1'"rb:""':c.s net. f,'с..ню е ,3Д"?:-: I-;,)Ч::i"'н' pythO" - TypeError: Ca"'t CO"Vert 'int' obJect to str impllcitly - s... . ',:и,"'''''':':-:' c':.r'-,' tурееrrоr.СЗПI-СОПV'n-IП . Пt>р.;>е'='ПI! .;Т', ИР,-,"I'Ц:. .'::'-'.' :,., ..: ':':-,:il:,r';! .., '!I:::' '. :I Ji" if' .'.::i, :"',. . :: .: :", ,, I) ';'iri')', ',0 :'Л' ..;,. 1.,.1":'_ 11: " ".; C..:' ."1111.1rtjl?, : ':"T<): -:ry"" :(; Can't convert 'int' obJect to str impllcitly' Python 3+ - Stack Оуе.. :: :.:. "')';' "!"':.: ((J""].' ct1nt-conv.n.jnt.,:;..:e( .(: .. nee.I?( ТJ1 Зl''С' r. rр.ltШLf..- : j":-I: .:.' :: "':J r; :,:Cj. t (. I!'I '...r ; ..: ::j.' t r:. s ,: I; f:!.('iti .0;" ':: ,! :::! ; j.:, . ",:. 1;". ''!l::':JI!i ''''':i:>':jH' :. 'i":n:-';oI '.',У':'. p'I".:1te >t!.' I нН ':t'.If:( TypeError: Can't convert 'Int' obJect to str implicitly errcr python,. ,::,.".':"..,":";,, ,:(,",' .tур..rror.сOIпt-сопv.nlп . 'lе.,=,си Э.. С!,'i1НИЩ д.? ф,. ;:/: > 1?1..?::: .:' "ri .' ,,. .; ';'. '::a''! ':1C; ip.t -:!"!"' .; 50 '')!:!":':;: ";I .- "'r,:rL! "'I:, ! I';:',-:I'! ':.,:;11 ;;.."!<':': !':;" S'.::!"'' !!I'; .;', ..:;t;I=?, Проrрамма не работает. что делать? I Python 3 ДЛЯ начина... !r(I:;.', :J! (II tl ('-:':O(;v l':'(,}"Jr"1,,.,-r.е.:'JrjQ,)е I"ttln! ... 1 -: [1 ;'.' I'[I.: .", '.'1,: ,,,. ,!t,' ":'.:.' ..,(:.; ;.:"I: . ": !..t.:>"EH':'.( j'f:-'!,!:,,:I.;,';: ECJ ,," l'I":"f"',' 'If':("! ,',i:' r''llv !!1.'.).!t?.rt.::'"t H1-;p!::;H'.. L_ Рис. 2. Получение дополнительной информации о природе ошибки путем поиска в Goog/e с использованием текста сообщения об ошибке в качестве ключа Часто вы будете сталкиваться с тем, что у Коrо-то уже возникал тот же во- прос, что и у вас, и на этот вопрос уже был дан ответ. В проrраммировании никому не дано знать абсолютно ВСС, поэтому свыкнитесь с мыслью о том, Что поиск ответов на различные вопросы техническоrо характера Неотъе- млемая часть ежедневной деятельности I1роrраммиста-ра.зработчика. Правипьно формупируйте вопросы, ответы на которые ищете Если онлайновый поиск не позволил получить ответ на интересующий вас вопрос, то поспрашивайте людей на таких форумах, как Stack Overflow (http://stackoverflow.com), или Посетите учебный раздел сайт" Rcddit (http://reddit .com/r/learnprogramming). Однако имейте в виду, что при обращении за помощью очень важно правильно формулировать свои во- просы. Обязательно посетите разделы Frequently A'iked Questiol1s (часто
Введение 37 задаваемые вопросы) этих сайтов, rде вы сможете ознакомиться с форму лировками вопросов, которые послужат вам образцом для подражания. Задавая вопросы, касающие<:я проrраммирования, старайтесь придер- живаться следующих рекомендаций. . Объясните, что именно вы пъtтaemecь сделать, а не только то, что вы делали. Это позволит тому, кто хочет вам помочь, определить, находи тесь вы на верном или На неверном пyrи. . Укажите, Коrда именно возникает ошибка: сразу после запуска про- rpaMMbl или после Toro, как вы выполняете определенные действия. . Скопируйте и вставьте пОЛ1tъtй т{KCT сообщения об ошибке и ваш код в буферное хранилище по адресу http://pastebin.com/ или http:/ / gist.github.com/. Указанные веб-сайты упрощают обмен большими объемами кода че рез Интернет без риска потерять форматирование текста. Затем мо- жете перес.лать URL-aдpec размещенноrо в буферном хранилище кода нужному человеку по электронной почте или опубликовать ero на фо- руме. Чтобы увидеть, как это работает, просмотрите код, который я разместил в хранилищах по следующим адресам: http://pastebin . сom/ SzP2DbFx/ и https: / /gist. github. соm/аswеigаrt/б9121б8/. . Объясните, какие меры вы предпринимали для разрешения возник шей проблемы. Тем самым вы покажете, что уже приложили усилия со своей стороны, стараясь самостоятельно выяснить причину непо-- ладок. . Укажите версию Python, которую используете. (Между интерпретато-- рами, входящими в состав вер(:ий PytllOn 2 и 3, имеются важные раз личия.) Также укажите используемую вами операционную систему и ее версию. . Если ошибка появилась после Toro, как вы внесли изменения в код, детально опишите, какие именно измененИЯ были вами внесены. . Расскажите, воспроизводится ли ошибка всякий раз, коrда вы выпол няете IIРOl'рамму, или она возникает лишь после Toro, как вы совер- шаете определенные действия. В последнем случае опишите, в чем именно заключаются эти дей<:твия. Кроме Toro, cTporo соблюдайте правила ceTeBoro этикета. Например, размещая на форуме свои вопросы, не набирайте весь текст прописными буквами, пытаясь сделать ero более заметным, и не ВЫДВИI'айте необосно-- ванных требований к людям, которые пытаются вам помочь.
38 Введение РеЗlOме Для большинства людей компьютер это вcero лишь полезНое устрой СТВО, а не инструмент. Вместе с тем, научившись нроrраммировать, вы получите доступ к одному из наиболее мощных инструментов в современ- ном мире, работа с которым доставит вам, кроме Bcero прочеrо, orpoMHoe удовольствие. Проrраммирование вовсе не сродни нейрохирурrии оно предостаВ.IIЯет новичкам великолепную возможность экспериментировать и при этом не бояться, что допущенные ошибки MOryr быть чреваты ката- строфическими последствиями. Мне нравится помоrать людям открывать для себя Python. Я пишу руководства по проrраммированию на своем блоrе по адресу http: / / inventwi thpython. com/blog/, и вы можете связаться со мной и задать вопросы, отправив сообщение электронной почты по адресу al@inventwithpython. сот. Эта книrа лишь помоraет преодолеть начальный барьер в изучении про rраммировании, поэтому вы не всеrда найдете в ней ответы на все свои во- просы. Не забывайте о том, что умение правильно формулировать ВОПРОСJ)( и знание TOro, как находить ответы на них, окaжyr вам неоценимую помощь в вашем путешествии в мир проrpаммирования. Итак, приступим!
ЧАСТЬ I ОСНОВЫ проrРАММИРОВАНИЯ НА ЯЗЫКЕ PYTHON
ОСНОВНЫЕ ПОНЯТИЯ ЯЗblКА PYTHON Язык проrраммирования Pyt]lOn предлаrает боrатейший набор синтаКсических конструкций, функций стандартной библиотеки и средств интерактивной разработки. К счас- тью, без большинства этих средств можно спокойно обой- тись, ведь все, что вам нужно, это научиться писать ко-- роткие полезные проrраммы. Прежде чем вы сможете что-либо сделать. вам следует усвоить некото- рые базовые понятия проrраммирования. Поскольку на данном этапе вы еще только учитесь, рассматриваемый в этой rлаве материал поначалу мо-- жет показаться вам скучным и чересчур сложным. Но даже самых скром- НbIX знаний и небольшой практики, которыс вы приобретете, вам хватит ДЛЯ тoro, чтобы управлять компьютером подобно Mary, который вооружен волшебной палочкой и способен соверш"ть самые невероятные подвиrи. В этой rлаве мы разберем несколько примеров, которые пробудят в вас интерес к работе с интерактивной оболочкой. С ее помощью вы сможете выполнять по одной команде Python за один раз и сразу же видеть результа- ты. Интерактивная оболочка будет вашим надеЖНЫМ помощником в изуче- нии основных инструкций Python, поэтому отказываться от такой возмож- ности не стоит. Коrда делаешь что-то своими руками, а не просто читаешь книry, все запоминается rораздо лучше. Ввод выражений в интерактивной оБОllочке Для вызова интерактивной оболочки необходимо запустить ин- терактивную среду IIН.Е, процедура установки которой описана во введении. В Windows откройте меню Пуск и выберите пункты меню Все nporpaMMblPython 3.3, а затем IDLE (Python GUI). В 05 Х выберите пункты меню ApplicationsMacPython 3.3QIDLE. В Ubuntu откройте новое окно терми- нала и введите команду idlеЗ.
42 r лава 1 В результате должно открыться окно с ПрИlJlашением ко вводу (»»; это и есть интерактивная оболочка. Введите в командной строке инструкцию 2 + 2 в ответ на приrлamение, trrобы Python выполнил для вас простое Ma тематическое действие. »> 2 + 2 4 Теперь в окне IDLE должен отображаться примерно такой текст. Python 3.3.2 (v3.3.2:d047928ae3f6, Мау 16 2013, 00:06:53) [MSC v.1600 64 bit (AМD64)] оп win32 Туре "copyright", "credits" or "license()" formore information. >>> 2 + 2 4 »> В PytllOn запись 2 + 2 нааывается 8'ЫражeuueJК. Выражение это наи БОJlеt фундаментальная разновидность проrраммных инструкций языка. Выражения состоят из з'Н.а'Чf!1tuu (таких, как 2) и операторов (таких, как +) и всеrДа MOryr 8ъtчuс.п.ятъся (сводиться) до получения единственноrо знач ния. Отсюда следует, ЧТО в коде Python выражения MOryr использоваться везде, rде допускается ис.пользование значения. В предыдущем "римере выражение 2 + 2 вычисляется, сводясь к един ственному значению 4. Одиночное значение без операторов также счи тается выражением, результатом вычисления KOToporo является само зна- чение. Проrраммные ошибки не представляют уrрозы для оборудования компьютераl Если компьютеру встречается непонятнь,й для Hero проrраммный код, то про- rpaMMa завершоется аварийно, и Python выводит сообщение об ошибке. Эти сооб- щения не MOryт причинить вред вашему компьютеру, так что не бойтесь совершать ошибки. Аварийное завершение, или крох, nporpaMMbI это просто неожиданное прекращеиие ее выполнения. Если вы Хотите получить более подробную информацию о каком-то сообщении об ошибке, используйте точный текст сообщения для проведения СООТ8етствующе ro поиска вИнтернете. Кроме Toro, вы сможете воспользоваться ресурсами, но- ходящимися по адресу http://nostarch.com/autornatestuff/, для просмотра списка наиболее часто встречающихся сообщений об ошибках в Python вместе с их описаниями.
Основные понятия языка Python 43 >>> 2 2 Кроме оператора +, существует множество друrих операторов, допусти мых в выражениях Python. Полный список математическИХ операторов Python приведен в табл. 1.1. Т.ица 1.1. Математические операторы Python в ПОрllДКе умен"wения их приоритета Оператор Операц- "рим., Реау""тат ** Возведение в степень 2 ** 3 8 с Деление по модулю/ остаток 22 ::' 8 б , // Целочисленное деление с отбрасыванием дробной части 22 // 8 2 / Деление 22 / 8 2.75 * Умножение 3 * 5 15 Вычитание 5 2 3 + Сложение 2 + 2 4 Очередность (cTporoe название приopuтет) выполнения математичес ких операторов Python совпадает с очередностью, принятой в математИ- ке. Сначала выполняется оператор **; затем, в порядке следования слева направо, выполняются операторы *, /, / / и %; и наконец, последними вы- полняются операторы + и (также в порядке ('ледования слева направо). для изменения очередности выполнения операций используются круrлые скобки. Введите в интерактивной оболочке <:ледующие выражения. »> 2 + 3 * 6 20 »> (2 + 3) * 6 30 »> 48565878 * 578453 28093077826734 »> 2 ** 8 256 »> 23 1 7 3.2857142857142856 »> 23 11 7 3 »> 23 % 7 2 »> 2 + 2 4 »> (5 1) * «7 + 1) 1 (3 1» 16.0
44 rлава 1 Во всех этих случаях вы как проrраммист лишь вводите выражения, Tor- да как всю тяжелую работу по сведению выражения к единственному зна- чению берет на себя интерпретатор. Python последовательно вычисляет отдельные части выражения до тех пор, пока не преобразует ero в един- ственное значение (рис. 1.1). (5 1) * «7 + 1) / (3 1» + 4 * «7 + 1) / (3 1» + 4* « 8 ) / (3 1» + 4* ( 8 ) / ( 2 ) + 4* 4.0 + 16.0 Рис. 1. 1. Резупьтат вычисления выражения сводится к единственному значению Вышеперечисленные правила cOBMeCTHoro использования операторов и значений для формирования выражений являются фундаментальной ча- стью языка проrраммирования Python точно так же, как правила обычной rрамматики обеспечивают нам возможность общения на родном языке. Рассмотрим соответствующий пример. Это предложение на русском языке rpамматически корректно. Это корректно предложение не на языке rpамматически русском. Понять смысл второй строки практически невозможно, поскольку ОНа не следует правилам построения предложений, ПРИНЯТblМ в русском языке. Точно так же и Python, встретив неправильно составленную инструкцию, не сможет ее однозначно интерпретировать и выведет сообщение осин. таксической ошибке (SyntaxError). »> 5 + File "<stdiп>", line 1 5 + SyntaxError: invalid syntax »> 42 + 5 + * 2 File "<stdin>", line 1 42 + 5 + * 2 SyntaxError: invalid syntax
Основные понятия языка Руthоп 45 Вы всеrда можете проверить, работает ли та или иная инструкция, введя ее в интерактивной оболочке. Вам не о чем волноваться компьютер вы не сломаете. В самом худшем случае PytllOn отреаrирует на неправильную инструкцию выводом сообщения об ошибке. Даже профессиональные раз работчики проrраммноrо обеспечения постоянно получают подобные со-- общения в процессе написания кода. Тип... данных: цеllые чиспа, вещественные ЧИСllа, строки Вспомните о том. что выражения это просто сочетания значений и операторов и что результат их вычисления всеrда сводится к единственно-- му значению. Тип дшн:н:ых это определенная катеrория значений, причем каждое значение относится к одному и только одному типу данных. Наи- более простые типы данных Python приведены в табл. 1.2. О значениях 2 и 30 rоворят. что они ЯR1lЯЮТСЯ цtИО'ЧИCJteu'l/:Ы,м,и. Цело численному (int) типу данных соответствуют значения в виде целых чисел. Числа с десятичной точкой, такие, например, как 3.14, называются 'Числами с плавающей тичКой (тип данных f1 оа t) ИЛИ веществeu'Н:ы,м,и 'Числами. Заметьте, что значение 4 2 целое число, тоrда как значение 42. О вещественное. Та&tица 1.2. Простые типы даниых Python Тип данн"х Целые числа Числа с плаlающей точкоii (Iещест"нные) Строки Пример.. 2, 1, О, 1, 2, 3, 4, 5 1.25, 1.0, 0.5, 0.0, 0.5, 1.0, 1.25 'а', 'аа', 'ааа', 'Hella!', '11 cats' Проrраммируя на языке Python, вы сможете использовать и текстовые значения, так называемые строки (тип данных str). Всеrда заключайте строку в апострофы ('), чтобы Python MOr распознать, rде она начинается и заканчивается (например, 'Hella' или' Goodbye cruel world!'). Строка может вообще не содержать ни одНоrо символа ( , '); такие строки называ ются nустъl.МИ. Более подробно строки рассматриваются в rлаве 4. Если вам коrдалибо приходилось сталкиваться с сообщением об ошибке SyntaxError: EOL while scanning string litеrаl,то,вероятно,этобыло вызвано тем, что вы забыли ввести символ апострофа, завершающий стро-- КУ, как в приведенном ниже примере. »> 'Hello world! SyntaxError: EOL while scanning string literal
46 r пава 1 КонкатенаЦИII и реппикаЦИII строк Смысл оператора может меняться в зависимости от типа соседних с ним данных (контекста). Например, если оператор + при меняется к двум ЗНа- 'Iениям, являющимся числами (целыми или вещественными), то он трак- туется как оператор сложения. Но еСЛИ ero применить к двум строковым значениям, то он объединит их в одну строку, иrрая роль оператора KO'ltKa тенац,ии строк. Введите в интерактивной оболочке следующее выражение. »> 'Alice' + 'ВоЬ' 'АliсеВоЬ' Данное выражение сводится к новому строковому значению, объединя- ющему текст обеих исходных строк. Если же попытаться применить опера- тор + к строке и целому числу, то Python не сможет определить, чеrо имен- но от Hero хотят, и выведет сообщение об ошибке. »> 'Alioe' + 42 Traceback (most recent call 1ast): Fi1e "<pyshell#26>", Нпе 1, in <module> 'АНсе' + 42 TypeError: Can't convert 'int' object to str imp1icit1y Сообщение Сап' t convert 'int' object to str implicitly означает, что Python предположил, будто вы пытаетесь конкатенировать строку' Аliсе ' с целочисленным значением. В IIОДобных ситуациях ваш код должен явно преобра:ювывать целые числа в строки, так как Python не может автомати- чески выполнить такое преобра:ювание. (Преобразования типов данных рассматриваются в разделе "Анализ проrраммы" при обсуждении функций str (), int () и float () .) Если оператор * применяется к двум целочисленным или вещественным значениям, то он трактуется как оператор умножения. Но если одно из значений строка, а второе целое число, то он становится оператором penяuкац,uu стр(Ж,. Введите в интерактивной оболочке строку, умноженную на число, чтобы увидеть, как это работает. »> 'Alioe' * 5 'AliceAliceAliceA1iceAlice' Результатом вычисления этоrо выражения является строковое значе- ние, представляющее собой MHoroKpaTHo повторенную исходную строку, число повторов которой равно данному целочисленному значению. p
Основные понятия языка Python 47 пликация строки очень полезный прием, хотя и ИСПОЛЬ3)'ется реже, чем конкатенация строк. Оператор * может использоваться только в отношении двух числовых значений (умножение) или одной строки и одноrо целочисленноrо значе- ния (репликация строк). В противном случае PythOI1 отобразит сообщение об ошибке. »> 'Alice' * 'ВОЬ' Traceback (most recent call last): File "<pyshell#32>", line 1, in <modиle> 'Alice' * 'ВоЬ' TypeError: can't rnиltiply sequence Ьу nonint of type 'str' »> 'Alice' * 5.0 Traceback (rnost recent call last): File " <pyshell #33>", Нпе 1, in <module> 'Аliсе' * 5.0 TypeError: can't rnultiply sequence Ьу nonint of type 'float' Совершенно очевидно, почему Python не CMor определить смысл ЭТих выражений: невозможно умножить одно слово на друrое, равно как и по- вторить строку дробное количество раз. Сохранение значений в переменных пере.МЕН:Н,ая это область памяти компьютера, в которой может хранить- ся одиночное значение. Если вы предполarаете, что результат вычисления выражения впоследствии будет использоваться в проrpамме, то можете со- хранить ero в переменной. иНару".... "р.,.".."н.. для сохранения значений в переменных используется инструкция nрисва- иванШl. В ней указываются имя переменной, знак равенства (называемый оператором npuсваиваиuя) и сохраняемое значение. Например, инструкция IIРИСВаивания sparn = 42 обеспечивает сохранение значения 42 в перемен- НОйsрam. Образно rоворя, переменную можно уподобить ящику с Мe'J'кой, В КОТо- рый переменная помещается для постоянноrо или BpeMeHHoro хранения (рис. 1.2). Введите в интерактивной оболочке следующие инструкции. . >>> SPaIII = 40 »> SPaIII 40 >>> eqgs = 2
48 r лава 1 . >>> ер_ + eqgs 42 »> Вpalll + eqgs + ераа 82 . »> ераа . араа + 2 >>> SPaDI 42 Pl1c. 1.2. ИНСТРУКЦI1Я spaт 42 сообщает nporpaMMe: "Теперь в переменной spaт хранится цеЛОЧl1сленное значение 42" lсрмин u1tuц,uалuзац,uя (или создание) перемеи'Н-ой относится к ПСрВОllа чальному I1РИСВОСНИЮ переменной какоrолибо значения 8, После ЭТОrо переменную можно использовать в выражениях совместно с дрyrими пере менными и значениями 8. В процессе присваивания переменной HOBoro значения ее прежнее значение теряется, и именно поэтому значением пе ременной spam в конце примера является 42, а не 40. Изменение значения называется nepезапuсью переменной. Попробуйте перезаписать строку, B дя В интерактивной оболочке следующий код. »> SPaDI · 'Не110' »> SPaDI 'Hello' > > > SPaDI = I Goodbye , >>> SPaDI 'Goodbye'
Основные понятия языка Python 49 в этом примере значение' ИеНо' хранится в переменной spam лишь до тех пор, пока вы не замените ero значением 'Goodbye' (рис. 1.3). И_е." "ере_еи"..х в табл. 1.3 приведены примеры допустимых имен переменных. Пер Менным можно присваивать любые имена, при условии, что они удовлстшr ряют следующим трем требованиям. Рис. 1.3. Коrда переменной присваивается новое значение, старое значение теряется 1. Имя перемеНfIОЙ должно представлять собой одно слово. 2. В имени переменной MOryr использоваться только буквы, цифры и символ подчеркивания С). 3. Имя переменной не может начинаться с цифры. Та6ltица 1.3. Допуанмые н недопустимые имена переменных Дрпуnимwеим-иаnеременных balance cиrrentBalance cиrrent balance НeAoIlJC1'llМlll4t имена nepeмeнHЫX currentbalance (недопустимый дефис) current balance (недопустимый пробел) 4account (имя не может начинаться с цифры)
50 r лава 1 account4 ОкО1t'Ча'ltш табл. 1.3 Недопустим..е имена "еременн"х 4 2 (имя не может начинаться с цифры! total $ит (специаЛllные символы, такие как $, I именах недопустимы! I hello' (специальные символы, такие как 1, В именах Н&допустимы! Дрnynим..емменаnеременн"х spam SPAМ Имена переменных чувствительны к реrистру, Это означает, что spam, SPAM, Spam и sPaM четыре разных имени. В соответствии с IlрИНЯТЫМ в Python соrлашением имена переменных должны начинаться с маленькой буквы. В этой книrе вместо стиля именования, преДПОЛaraIOщеro использование символа подчеркивания, используется так называемый "верблюжий стиль", в котором имена наподобие lookinglikethis пре06разуются в имена напо-- добие lookingLikeThis. Некоторые опытные проrpаммисты моrли бы Уl1ре кнуть меня в том, что я Не следую официальному руководству PythOI1 по сти- лям, РЕР8, поощряющему использование символов подчеркивания в именах переменных. Совершенно верно, я самым непочтительным образом Ilред почитаю использовать "верблюжий стиль", а в свое онравдание сошлюсь на раздел "А Foolish Consistency Is the Hobgoblin of Little Minds" I caMoro же руководства РЕР8 2 , в котором есть такие строки: "Соrласованность с этим руководством очень важна. СOI:ласованность внyrри одноrо проекта еще важнее. А соrласованность внyrри модуля или функции самое важное. Но важно помнить, что Иноrда это руко- водство неприменимо, и понимать, коrда можно отойти от рекомен- даций. Коrда вы сомневаетесь, просто посмотрите на друrие примеры и решите, какой выrлядит лучше". Хорошими считаются описательные имена, которые раскрывают смысл данных, содержащихся в переменных. Представьте, что, lIересзжая в HO вый дом, вы разложили в(:е вещи по ящикам и пометили каждый из них одной и той же надписью "Вещи". Леrко ли вам будет после этоrо что либо найти? В данной КНиrе, как, впрочем, и в большей части ДOКYMeHTa ции по Python, в качестве типичных имен в примерах используются такие имена, как spaт, eggs и ли bacon (здесь явно сказалось влияние скетча "Spatn" I "rлупая последовательность пуrало маленьКИХ умов" цитата из эссе "Доверие к себе" известноro американскоro писателя Ральфа Уолдо Эмерсона (см. перевод на русский язык по адресу https://raw.githиbиsercontent.com/boretskiy/selfreliancerи/ master/ selfreliancerи. txt). Прu.'IIеч. ред. 2 https: / /www.python.org/dev/peps/pep0008/ (см. перевод на русский язык по адресу http://pep8 . ru/doc/pep8/). Примеч. ред.
Основные понятия языка Python 51 rРУIIПЫ "Mollty Руtlюп"), но в своих проrраммах: вам лучше использовать описательные имена, что повысит удобочитаемость вашеrо кода. Ваша перва. проrрамма Интерактивная оболочка отлично подходит для выполнения инструк ций PythOH 110 одной за один раз, но при написании полноценных про-- rpaMM на Python вы будете подrотавливать их текст с помощью какоrо--либо файловоrо редактора. Файловые реда",торы аналоrичны текстовым, таким как Notepad (Блокнот) или TextMate, но предлаraют дополнительные воз можно<ти при Вlюде исходноro кода. Чтобы открыть файловый редактор в IDLE, выберите пуикты меню FileNewfile (ФайлСоздать файл). В открывшемся окне вы увидите курсор, ожидающий ввода, но это окно отличается от окна интерактивной оболочки, которое выполняет BвeдeH ную вами инструкцию Pytl10n сразу же после нaж<rrия клавиши <Ellter>. Фай ловый редактор позволяет ввести множестВО инструкций, сохранить файл и выполнить проrрамму. Ниже указано, чем различаются эти два окна: . признаком окна интерактивной оболочки служит приrлашение к вво-- ду вида >>>; . в окне файловоrо редактора приrлашение к вводу вида »> oтcyrcTBYeт. А теперь пришло время создать вашу первую IIporpaMMY! Открыв окно файловоrо редактора, ВВедите в нем следующий Текст. о # Эта проrрамма приветствует пользователя и запрашивает # ВВОД информации. . print ( 'ИеНо world! 1) print('What is your пате?') # запрос имени . myName input ( ) . print('It iз good to meet you, , + myName) . print('The length of your пате i5:') print(len(myName)) Ф print('What iз your age?') # запрос возраста myAge input ( ) print('You will Ье I + str(int(myAge) + 1) + ' in а year. ') Введя и<,ходный код, сохраните ero, чтобы набирать ero заново при сле- дующем запуске IDLE. Выберите в меню, расположенном в верхней части окна файловоrо редактора, пункты FileSave As (ФайлСохранить как), введите hello.py В поле File Name (Имя файла) и щелкните на кнопке Save (Сохранить). В процессе ввода теКста ПрOl'раммы периодически сохраняйте файл. Это позволит избежать потери уже введенноrо кода, если работа компьютера
52 r лава 1 завершится аварийно или вы случайно выйдете из IDLJo:, Вместо сохране- ния файла с помощью меню можно нажать комбинацию клавиш <Ctrl+S> (Windows и Linux) или <X+S> (OSX). После Toro как файл будет сохранен, запустите проrрамму, Выберите пункты меню RunRun Module (ВыполнитьВыполнить модуль) или просто нажмите клавишу <F5>. Ваша проrpамма должна запуститься в окне инте- рактивной оболочки, которое открывалось, коrда вы впервые запускали IDLE. Не забывайте о том, что клавишу <F5> следует нажимать Не в окне интерактивной оболочки, а в окне файловоrо редактора. Введите свое имя в ответ на приrлашение nporpaмMbl. Вывод проrраммы в окне интерактив- ной оболочки должен выrлядеть примерно так, Python 3.3.2 (v3.3.2:d047928ae3f6, Мау 16 2013, 00:06:53) [MSC v.1600 64 bit (AМD64)] оп win32 Туре "copyright", "credits" or "license ()" for more information. »> ============================== RESTART =================== »> НеНе werld! What is your пате? Аl It is good to meet you, Аl The length of your пате is: 2 What is your age? 4 You will Ье 5 in а year. »> Исчерпав все строки КОДа, подлежащие выполнению, прш'рамма завер- шается, Т.е. ее выполнение прекращается. (В этом случае также rоворят о выходе из проrраммы.) Чтобы закрыть окно файловоrо редактора, щелкните на кнопке х в верх- ней части окна. Чтобы перезarрузить ('охраненную проrрамму, выберите в меню пункты FileOpen (ФайлОткрыть). Проделайте это сейчас, выбери- те в открывшемся окне имя файла мио.ру и щелкните на кнопке Ореп ( крыть). В окне файловоrо редактора должна открыться nporpaмMa, кото-- рую вы перед этим сохранили в файле hello.py. АнаllИЗ проrраммы Сейчас мы кратко рассмотрим все инструкции, которые ИСПОЛЬ.1}'ЮТся в новой проrрамме, открытой в окне файловоrо редактора, и проанализиру- ем, что именно делает каждая строка кода.
Основные понятия языка Python 53 lо_е.,.,.. Приведенные ниже строки кода называются комментарием. . # Эта прорамма приветствует пользователя и запрашивает # ввод информации. l>yt.llOn иrнорирует комментарии, и вы сможете использовать их для за писи примечаний или напоминаний самому себе относительно 1'01'0, что делает данный код. Любой текст от символа "решетка" (#) до конца строки считается частью комментария. Иноrда проrраммисты помещают символ # перед строкой кода для Toro, чтобы фактически исключить ее из кода, или, как rоворят, заКOJ,f,.М,eumuро- ватъ, на время тестирования проrраммы. Этот прием оказывается очень полезным при выяснении причин нарушения нормальной работы проrрам- мы. Впоследствии вы сможете восстановить отключенный код, удалив сим- вол #, или, как rоворят, раско.м.чeumuровавстроку, Пустые строки lIосле комментария иrнорируются. Вы можете добавить в свою проrрамму столько пустых строк, сколько пожелаете. Такое форма- тирование кода, напоминающее разбивку КНИЖlюrо текста на абзацы, об леl'чает ero чтение. фуНК.... pr:i.n t ( ) Функция print () отображает указанное в скобках строковое значение на экране. . print ( 'Не110 world!') print('What is your пате?') # запрос имени Строка print ( 'Hella world!') означает "Вывести текст, хранящийся в строке 'Не 110 wor ld! '". При словесном Оllисании этой строки I'ОВОРЯТ, что Python в'Ы3'Ьюает функцию print () , передавая ей строковое значение. Значе ние, передаваемое функции, называется apYMeHтOM. Обратите внимание на то, что кавычки не ВЫВОДятся на экран. Они лишь обозначают начало и конец строки и не являются частью CTpoKoBoro значения. Прu.чечакие Эту же ФУЮ(''I,!,UЮ можно UCnОЛ'Ь30вam'Ь для в'ывода на экран пустой страки; для это- O достаmич1tО в'Ьtnол'Uum'Ь в'Ы30в pr in t () без указан:ия аРiЗументов. Наличие пары скобок после имени указывает на то, что данное имя обозначает функцию. Именно поэтому в книrе везде используется запись print () , а не print. Более подробно функции рассматриваются в rлаве 2.
54 rлава 1 фунК.... input () Функция input () ожидает ввода пользователем текста с последующим на- жатием клавиши <El1ter>. . myName input () В этой строке кода текст, введенный пользователем, преобразуется в строковое значение, которое присваивается переменной myNarne. Вызов функции input () можно рассматривать как выражение, значени- ем KOToporo является строка, введенная пользователем. Если пользователь ввел' Al ' , то предыдущая строка кода эквивалентна строке myNarne = 'Al'. '..8. ..,н. 110..308"".. В следующем вызове функции pr in t () в скобках указано выражение '1 t is good to meet уои, , + myNarne. . print('It is good to meet уои, , + myName) Вспомните о том, что результатом вычисления выражения всеrда явля. ется одиночное значение. Если I Al' это значение, сохраненное в пере- мен ной myName в предыдущей строке, то значением дaHHoro выражения яв- ляется 'It is good to meet уои, Al'. Затем это одиночное строковое значение передается функции pr in t ( ) . которая выводит ero на экран. фунК.... leп ( ) Вы можете передать функции lеп () строковое значение (или перемен ную, содержащую строку). и она возвратит целое число, равное количеству символов в аIlIlОЙ строке. . print ('The length of yoиr пате is: 1) print(len(myName)) Чтобы увидеть, как это работает на практике, введите в интерактивной о6оЛО(lке следующий код. »> 1en( 'Ье110') 5 »> 1en('Мy very energetic Dlonster just scarfed naсЬоа. ') 46 >>> 1еn(") О
Основные понятия языка Python 55 Точно так же, как в этих примерах, вычисление выражения len (myNarne) дает целое число. Далее это число передается функции pr in t ( ) , которая выводит el'o на экран. Заметьте, что функции print () можно передавать любые целочиеленные или строковые значения. Но если вы введете в ин- терактивной оболочке приведенный ниже код, то получите сообщение об ошибке. »> print('I аа ' + 29 + ' years old. ') Traceback (most recent са11 1ast): Fi1e "<pyshell#6>", line 1, in <modи1e> print('1 ат ' + 29 + ' years old.') TypeError: Can't convert 'int' object to str imp1icitly Ошибка связана не с самой функцией print () , а с выражением, которое вы пытаетесь ей передать. То же самое произойдет и в том случае, если вы введете в интерактивной оболочке одно только это выражение. »> '! аа ' + 29 + ' yearB old.' Traceback (most recent са11 1ast): File "<pyshell#7>", Нпе 1, in <modиle> '1 ат ' + 29 + ' years old.' TypeError: Can't convert 'int' object to str imp1icitly PytllOn выводит сообщение об ошибке 110 той причине, что оператор + можно использовать ЛИШь для сложения двух чисел или конкатенации двух строк. Сложить число со строкой невозможно, потому что это запрещено rрамматикой Python. Данную проблему можно устранить, используя вместо целоrо числа ero строковую версию, о чем пойдет речь в следующем раз- деле. Функ.."" str () I int () "float () Если вы хотите конкатенировать целое число, такое как 29, со строкой для передачи функции print () , вы должны получить значение' 29' , явля щееся строковой формой числа 29. Функции str () можно передать целое число, и она преобразует el'o в соответствующую строку. »> etr(29) '29' »> print('I am ' + str(29) + I years old.') 1 ат 29 years old. Поскольку вычисление s tr (29) дает строку' 2 9' , мы получаем в результа- те вычисления выражения' 1 am I + str (29) + ' years old. ' значение' 1 am I +
56 rЛQВО 1 '29' + ' years old. " которое, в свою очередь, возвращает результат в виде строки' 1 ат 29 years old. '. Это значенис и передается функции print (). Функции str (), int () и float () соотвстственно возвращают crроковую, целочисленную и всщественную формы передаваемоrо им значения. По-- пробуйте выполнить в интерактивной оболочке преобразование HeKOТO рых значений с помощью этих функций и посмотрите, что при этом будет происходить. >>> str (О) 101 >>> str(З.14) I З.14 I »> int('42') 42 >>> int('99') 99 >>> int(1.25) 1 »> int (1.99) 1 »> flоаt('З.14') 3.14 >>> float(10) 10.0 В этих примерах функции str (), int () и float () вызываются для получ ния строковой, целочисленной и вещественной форм значений, представ ляющих друrие типы данных. Функцию str () удобно использовать в тех случаях, коrда у вас есть целое или вещественное число, которое вы хотите конкатенировать со строкой, Функция int () будет полезной, если имеется число в строковой форме, ко-- торое вы хотите использовать в математических вычислениях. Например, функция inpиt () всеrда возвращает строку, даже если пользователь вводит число. Введите в интерактивной оболочке инструкцию spaт inpиt ( ) , а за тем в режиме ожидания ввода число 101. »> ерат = input() 101 »> epu '101' Сохраненное в переменной spam значение является не числом 101, а строкой' 101'. Е<:ли вы хотите выполнить вычисления с использованием значения, хранящсrося в spam, воспользуйтесь функцией in t () ДЛЯ получ ния целочисленной формы значения spaт и сохраните новое значение в этой же переменной.
Основные понятия языка Python 57 »> spam = int(spaa) »> spam 101 Теперь вы сможете обрабатывать значение spaт не как строку. а как число. »> spam * 10 I 5 202.0 Имейте в виду. что в случае передачи функции int () значения, которое она Не может привести к цело численному виду. Python отобразит сообще- ние об ошибке. »> 1nt('99.99') Traceback (most recent call last): File "<pyshell#18>", line 1, in <modu1e> int('99.99') ValueError: invalid literal for int() with Ьаве 10: '99.99' »> int (' twelve') Traceback (most recent call last): File "<pyshell#19>", Нпе 1, in <module> int ( 'twel ve ' ) Va1ueError: invalid literal for int() with Ьаве 10: 'twelve' Функцию int () удобно использовать для окрyrления вниз веществеННЫХ чисел. »> int(7.7) 7 >>> int(7.7) + 1 8 в вашей проrpамме функции int () и str () используются в последних трех строках кода для приведения значений к соответствующим типам данных. . print ('What is your age?') # запрос возраста myAge = input ( ) print('You will Ье ' + str(int(myAge) + 1) + ' in а year.') в переменной myAge содержится значение, возвращенное функцией input () . Поскольку функция input () всеrда возвращает строку (даже если пользователь ввел число), для преобразования CTpoKoBoro значения, хранящеrося в переменной myAge, в целочисленное следует использо вать код int (myAge). Затем это значение увеличивается на 1 в выражении int (myAge) + 1.
58 r лава 1 Сравнение строковых и числовых значений В то время как строковое значение числа считается полностью отличающимся от ero целочисленной или вещественной версии, целое зночение может быть равно вещественному значению. »> 42 '42' False >>> 42 42. О True »> 42.0 == 0042.000 True Python учитывоет это различие, поскольку строки это текст, TorAa как и целый, и вещественный типы числа. Результат сложения передается функции str (): str (int (myAge) + 1). Пое- ле ЭТоrО возвращенное СТРОК080е значение конкатенируется со строками 'You will Ье ' и ' in а year. ' для получения единоrо CTpoKoBoro значения. Наконец, это строковое значение передается функции print () для вывода на экран. Предположим, что пользователь вводит в качестве значения перемеи- ной myAge (TPOКY '4 ' . Далее эта строка преобразуется в целое (lИсло, к ко- торому можно прибавить единицу. В результате мы получаем значение 5. Функция str () преобраэует этот результат обратно в строку, которую мож- но конкатенировать <:0 второй строкой, , in а year. ' , для создания окон- ttательноrо сообщения. Последовательность выполнения этих действий I1редставлена на рис. 1.4. cPrint( 'You _111 he ' + еи (1nt (ayAqe) + 1) + ' in а year.') str(1nt( '4' ) + 1) + ' in year. ') cPrint('YOu _i11 he ' + а str( 4 + 1 + ' 1n year. ') cPr1nt( 'You _111 he ' + а str( 5 + ' in year. ') cPr1nt('YOu _111 he ' + а cPr1nt('YOu _111 he ' + '5' + ' 1n а year. ') cPr1nt ( 'You _i11 he 5' + ' 1n а year. ') print ( 'You _111 he 5 in а year. ') Рис. 1.4. Последовательность вычислений для случая, коrда знсчение myAge равно 4
Основные понятия языка Python 59 РеЗlOме Для вычисления выражений можно использовать калькулятор, а для объ- еДинения строк в связный текст текстовый редактор. Вы также можете реплицировать строки путем их копирования и вставки. Однако выраж Ю'lя и их компоненты операторы, переменные и вызовы функций яв ляются теми строительными блоками, на основе которых создаются про rраммы. Научившись обрабатывать эти элементы, вы сможете с помощью pyt ]1011 оперировать большими объемами данных. Вам следуt.'Т запомнить ра.зличные операторы (+, , *, /, / /, % и ** для математических операций и + И * для операций над строками), а также три 'I'ина данных (целые числа, вещественные числа и строки), описанные в этой rлаве. Вы также познакомились с несколькими функциями. Функции print () и inpиt () предназначены ДЛЯ обработки простоrо TeKcToBol'O вывода (на экран) и ввода (с клавиатуры). Функция len() принимает строку и вычис- ляет количество содержащихся в ней символов. Функции st r ( ), in t () и f10at () вычисляют соответственно строковую, целочисленную и веще- ственную формы переданноrо им значения. Из следующей rлавы вы узнаете, как орrанизовать в HporpaMMe на Pyt]lOn принятие решений относительно Toro, какой КОД выполнить, какой пропу- сl'ить, а какой повторить, исходя из проверки выполнения заданных уело- lШЙ. Это обеспечивается управляющими конструкциями языка Python, с помощью которых вы сможете писать проrраммы, способные принимать rибкие решения. Контропьныевопросы 1. Какие из приведснных ниже синтаксических элементов являются операторами, а какие значениями? * 'hello' 88.8 / + 5 2. Что из приведенноrо ниже является переменной, а что строкой? sparn 'sparn'
60 r пава 1 3. Назовите три типа данных. 4. Из чеrо состоит выражение? К чему СВОДИтся любое выражение? 5. В этой rлаве введены выражения присваивания наподобие spaт = 10. В чем cyrb различия между выражением и инструкцией? 6. Каким будет <:одержимое переменной bacon после выполнения сл дующей инструкции? Ьасоп 20 Ьасоп + 1 7. Каким будет результат вычисления следующих двух выражений? 'эрат' + 'spamspam' , эрат' * 3 8. Почему eggs допустимое имя переменной, а 100 таковым не является? 9. Назовите три функции, которые MOryт быть использованы для полу чения цеJIочисленной, вещественной и строковой версий значения?" 10. Что I1РИВОДИТ К возникновению ошибки при попытке вычисления нриведенноrо ниже выражения? Как избавиться от этой ошибки? 'Я съел' + 99 + ' лепешек.' Дополнительное задание. Изучите подробнее функцию len ( ) . проведя поиск в онлайновой документации Python. Нужная вам информация Haxo дится на вебстранице, озаrлавленной "Built-in Fllllct.iOIlS". Ознакомьтесь со СПИСком друrих функций Python, обратив особое внимание на функцию roиnd ( ) , и с"мостоятельно поэкспериментируйте с этой функцией. исполь зуя интерактивную оболочку.
поток VПРАвпЕНИЯ Итак, вам уже известны основы синтаксиса отдельных ин- струкций, и вы знаете, что последовательность таких ин- струкций образует проrрамму. Однако реальная мощь про- rpаммироваllИЯ заlUIючается не впростом llоочередном вы- полнении инструкций проrpаммы, напоминающем заранее спланированное расписание встреч С друзьями в выходные дни. Исходя из результатов вычисления выражений, проrрамма может ca мостоятельно принимать решения относительно Toro, какие инструкции следует пропустить, а какие повторить, а также выбирать одну из несколь- ких инструкций для выполнения. В действительности практически Не суще- ,твует проrрамм, которые выполняли бы CTporo последовательно каждую строку кода, начиная с первой и заканчивая последней. PytllOllllредостав- ляет (:интаксис уnравJ/ЯЮ11&UХ инструк:/&ий, или uнструкций ветвления (друrие названия инстру'Кц,ии передачu управле'ltUFl, и'ltстру'К,/&ии перехода, условн'Ые инструкции), которые предназначены для изменения eCTecTBeHHoro после- довательиоrо порядка выполнения инструкций IlpOrpaMMbI, позволяя коду решать, какая инструкция должна выполняться следующей, исходя из со-- блюдения определенных условий, Последовательность передач управления в процессе выполнения проrpаммы образует ее потак уnравлен,ия (также по- так 8'ЬtnОЛ'ltе'ltия). Управляющие инструкции непосредственно соответ(:твуют определен- ным условным символам, используемым в блок-схемах процессов, поэтому при обсуждении проrрамм в этой rлаве я буду обращаться к их rрафическо-- му представлению в виде блок-схем. На рис. 2.1 показана блок-схема процес- са принятия решений, определяющеrо порядок действий, которые должны предприниматься в зависимости от Toro, идет ли дождь. Обычно на таких блок-схемах отображается несколько возможных марш- pyrOB, ведущих от начальной точки к конечной. То же самое справедливо
62 rлава 2 и для строк кода в компьютерной проrрамме. Блоки условий, в которых происходит ветвление проrраммы, обозначаются на блок-схеме ромбами, блоки действий прямоуrольниками. Конечный и начальный блоки про-- rpaмMbI представляlOТСЯ скрyrленными прямоyrольниками. да Нет HeMHoro выждать Нет да Нет да Рис. 2.1. Блок-схема, определяющая порядок действий на случай дождя Однако, прежде чем приступить к изучению управляющих инструкций, вам предстоит ознакомиться со способами представления отображаемых на блок-схемах вариантов да и нет, а также записи точек ветвления в виде кода на языке Pyt.hon. В качестве основы для ЭТоrо расс.мотрения мы будем использовать булевы значения, операторы сравнения и булевы операторы. 5YlleBbl значеНИII в то время как целый, вещественный и строковый типы данных MOryт иметь неоrраниченное количество возможных значений, лоzu.ческuu, или булев, тип данных может принимать только два значения: True (истина) и False (ложь). (Булев тип данных получил свое название в честь анrЛИЙСКОI'О математика, основателя математической лоrики Джорджа Буля.) При ис- пользовании в коде Python булевы значения True и False не заключаются в кавычки и всеrда начинаются с БОЛЫIIОЙ буквы Т или F, тоrда как остальная
Поток управления 63 часть слова ЗaIlИсывается маленькими буквами. Введите в интерактивной оболочке следующие инструкции (некоторые из них намеренно сделаны некорректными и будут сопровождаться выводом сообщений об ошибке при попытке их выполнения). о »> врав = 'l'rue >>> врав True . >>> true Traceback (mo5t recent call last): File "<pyshell#2>", line 1, 1n <module> true NameError: пате 'true' 15 not def1ned . »> 'l'rue = 2 + 2 SyntaxError: a55ignment to keyword Как и любые друrие значения, булевы значения MOryr входить в выраж иия и сохраняться впеременных О. В случае lIрименения неправилыlOro p rистра букв" или попытки использования идентификатора True или Fa1se в качестве имени переменной .. PytIlOIl вывеДеТ сообщение об ошибке. Операторы сравнения Операторы сравueuия сравнивают два значения между собой и возвраща- ют результат в виде булева значения. Операторы сравнения приведены в табл.2.1. Та6ltица 2.1. Оператор..' сравиеНИII Оператор На.lание РаlНО ! Не равно < Меньше чем > Бопьwе чем <= Меньше или равно >= Бопьwе МIIМ равмо Результатом действия этих операторов, в зависимости от предоставлен ных им значений, является значение True или False. Посмотрим, как они работают, начав с операторов == и ! =. »> 42 == 42 True »> 42 == 99
64 rлаВQ 2 False >>> 2 != 3 Trиe >>> 2 != 2 False как и следовало ожидать, результат применения оператора (равно) равен Trиe в случае равенства значений, записанных слева и справа от Hero. тоrда как результат применения оператора ! (не равно) равен True, еСЛII эти значения различаются. Операторы и ! MOIyr работать, по сyrи, с любыми типами значений. »> 'hello' 'hello' True »> 'hello' == 'Rello' False »> 'doq' != 'cat' True »> True .... True Trиe »> True != False True »> 42 ..... 42. О Trиe О »> 42 .... '42' False Обратите внимание на то, что целые и вещественные значения никоr да не MorYT быть равны строковым. Результат выражения 42 == '42'" равен False, поскольку для Python целое число 42 и строка' 42' разные значения. С дрyrой стороны, операторы <, >, <== и >== работают надлежащим обра зом лишь с целыми и вещественными значениями. >>> 42 < 100 Trиe >>> 42 > 100 False >>> 42 < 42 False »> eggCount = 42 . >>> eggCount <= 42 True >>> Dlуще = 29 . >>> ауще >= 10 True
Поток управления 65 Раз.nичие между операторами == и = Возможно, вы обратили внимание на то, что оператор проверки равенства (==) обозначается двумя знаками равенство, тorдo кок оператор присваивания (=) од- ним. Эти операторы можно леrко перепутать. Чтобы этоrо не произошло, запомните следующее. · Оператор == (равно) проверяет равенство двух значений. · Оператор = (присвоение) помещает значение, указанное справа, в перемен- ную, указанную слева. Вам будет леrче запомнить, что ecn. что, прибеrнув к следующей мнемонике: оба родственных оператора сравнения (== и ! =) остоят из двух символов. Операторы сравнения часто используют для Toro, чтобы сравнить зна ..ение переменной (: некоторым дрyrим значением, как в строках кода eggCount <= 42. иmуАgе >= 10.. (В конце концов, если бы речь шла лишь о сравнении двух значений, то, например, вместо инструкции' dog' ! = 'cat' в своем КОДе вы моrли бы просто использовать значение True.) Со мноrими аналОl'ИЧНЫМИ примерами вы еще встретитесь далее, КОI'да будете изучать управляющие инструкции. Iупевы операторы Для сравнения булевых значений используются три булева оператора: =.nd, or и not. Подобно операторам сравнения, они вычисляют булевы вы- ражения, сводя их к единственному булеву значению. Приступим к более подробному рассмотрению этих операторов, начав с оператора and. '"наРНII' 6,.,... Оll'Р"'ОРII Операторы and (ЛOl'ическое и) и or (лоrическое или) всеrДа работают с двумя булевыми значениями (или выражениями), и поэтому их называют бинарными. Оператор and возвращает значение True только в том случае, если одновременно оба булева значения равны Trиe; В противном случае p зультат равен False. Чтобы увидеть, как это работает, ВЫlIолните в интерак- тивной оболочке следующие примеры. »> True and True :rue »> True and :&'alse :alse
66 r лава 2 Для представления всех возможных результатов применения булевых операторов используют таблицы истин/н ости. Таблица истинности для опе ратора and приведена в табл. 2.2. Та6nица 2.2. Таблица иcrиннасти ДПЯ оператора and В..ражение True and True True and False False and True False and False Реаупllтат True False False False Возможные результаты применения оператора or приведены в ero таб- лице истинности (табл. 2.з). Та6nица 2.3. Табllица истинности дпя оператора or В..ражение True or True True or False False or True False or False Реаупыат True True True False О",ротор not Оператор not, В отличие от операторов and и or, воздействует только на одно булево значение (выражение) и поэтому является yuajпtым.. Этот оп ратор обращает булево значение в ero противоположность. »> not True False . »> not not not not True True В полной аналоrии с использованием двойных отрицаний в устной речи и на письме допускается использование вложенных операторов not 8. хотя в реальных nporpaMMax в этом вряд ли возникнет необходимость. Возмож- ные результаты применения оператора not приведеныl в ero таблице истин- ности (табл. 2.4). Та6nица 2.4. ТабllИца истин насти дпя оператора not В..ражение not True not False Реаупыат True False
Поток управления 67 Сочетание операторов сравнеНИII с 6Уllевыми операторами Поскольку результатом вычисления выражений, ВКЛЮЧаЮЩИХ операто ры сравнения, являются булевы значения, их можно исполь:ювать в Bыpa жениях совместно с булевыми ОlIераторами. Вспомните, что операторы and, or и not на.lываются булевыми, ПОСКОЛk ку всеrда воздействуют на булевы значения Trиe и False. В то время как выражения наподобие 4 < 5 не являются булевыми значениями, будучи вычислеННЫМИ, они дают как раз такие ;шачения. Чтобы увидеть, как это работает, выполните в интерактивной оболочке следующие примеры. »> (4 < 5) and (5 < б) :rue »> (4 < 5) and (9 < б) :alse »> (1 == 2) or (2 == 2) :rue Сначала КОМlIьютер вычисляет левое выражение, а .затем правое, KoJ'- да оба тих результата становятся известными, вычисляется конечный p зультат BcerO выражения в целом, который будет IIре/l,ста8ЛЯТЬ собой един ственное булево значение. И<:пользуемый компьютером процесс вычисле lIия выражения (4 < 5) and (5 < 6) ПРОИЛJlIOCТРИРОван на рис. 2.2. (4 < S) and (S < 6) + True and (S < 6) + True and True + True Рис. 2.2. Процесс вычисления выражения (4 < 5) aпd (5 < б), привадящий к значению True ДОllускается совместное использование в одном выражеlIИИ как булевых I щераторов, так и операторов ("равнения. .» 2 + 2 == 4 and not 2 + 2 == 5 and 2 * 2 2 + 2 _ :....:е Как и для математических операторов, для булевых операторов определен порядок выполнения. После Toro как будут вычислены все
60 rЛQва 2 математические операторы и операторы сравнения, первыми выполняют- ся операТОРbl nat, затем операторы and и только после этоrо операто- ры or. Эllементы потока упраВllеНИII Начальная часть инструкций, управляющих порядком выполнения дру- rих инструкций проrраММbI, часто является условием, за которым следует блок кода. Прежде чем приступить к изучению конкретных управляющих инструкций PytJlOl1, рассмотрим, что собой представляют условие и ассо. циироваННblЙ с ним блок кода. У,.о... Все булеВbI выражения, с которыми вы к этому времени успели позна- комиться, MOryт рассматриваться как условия. Условue это вcero лишь бо. лее специализированное название выражения в контексте управляющих инструкций. Вычисление условия всеrда дает булево значение, True или False. Управляющая инструкция принимает решение относительно даль- нейших действий в зависимости от Toro, какое из этих двух значений при- нимает условие, и условия используются ПОЧти во всех управляющих ин- струкциях. 5по". "011" Строки кода Pyt]lOI1 MOryт rруппироваться в маки. О том, rде находятся начало и конец блока, можно судить по отступам строк кода в тексте про- rpaMMbI. В отношении блоков действуют следующие три правила. Признаком начала блока служит увеличение oTcТYlla. Блоки MOryт содержать друrие блоки. Признаком конца блока служит уменьшение отступа до нулевой величи- ны или до веЛИЧИНbI отступа содержащеrо (внешнеrо) блока. Вам будет леrче понять, что такое блоки, определив, rде они находятся в приведенной ниже части небольшой иrровой IIporpaMMbI. if . . пате == 'Mary I : print ('НеНа Mary') if password == 'swordfish': print ( 'Ассевв granted. I ) else: print{'Wrong password') . else: print{'User nat registered') о
ПОТОК управления 69 Первый блок кода. начинается строкой print ( 'НеНе Mary') И содер- жит все последующие строки, предшествующие второй инструкции else. В этом блоке содержится второй блок ., содержащий только одну строку: ;:rint ( I Access granted. ' ) . Третий блок" также (остоит только из одной строки: print ('Wrong password. 1). Четвертый блок, состоящий из строки ;:rint ('User not registered 1), относит(я ко второй инструкции else, ассо-- циированной с первой инструкцией i f. ВЫПОllнение npOrpaMMbl в проrрамме heUo.py из предыдущей rлавы Pyt]lOn последовательно BЫ 1I0ЛНЯЛ инструкции одну за ДРУI'ОЙ от начала и до конца проrраммы. BЬt r/олиe1-tие пpoepa.M,MЬt это термин, ОТНОСЯЩийся к текущим выполняемым инструкциям. Если вы напечатаете на бумажных листах исходный код про-- rpaMMbI и будете lJеремещать палец по строкам сверху ВНиз, то это и будет схематической иллюстрацией выполнения проrpаммы. Однако не все проrраммы выполняются столь прямолинейно. Если вы приметесь отслеживать пальцем порядок выполнения проrраммы, в ко-- торой есть управляющие инструкции, То вашему пальцу придется переме щаться в пределах Bcero исходноrо кода в соответствии с заданными усло виями И, возможно, пропускать целые блоки. УпраВIIlllOщие инструкции ПРИСТУIlИМ К самым важным элементам потока управления: собствен- но управляющим инструкциям. На блоксхеме, которая была показана на рис. 2.1, эти инструкции представлены ромбами, и именно они реализуют принятие решений вашей проrpаммой. иНару".... iz Наиболее общим типом управляющих инструкций является инструкция :. :. Следующий за инструкцией i f блок кода будет выполняться только в TOI случае, если результат вычисления условия равен True (условие истин- но). Если же результат вычисления условия равен False (условие ложно), то ;).10К не будет выполняться. На обычном языке инструкция if интерпретируется следующим обра- )()e "Е<:ли условие истинно, то выполнить данный блок кода". В Python ин. струкция if включает такие элементы: . ключевое слово if; . условие (т.е. выражение, вычисление KOTOpOl'O дает значение True или False);
70 r лава 2 . двоеточие; . блок кода с- отступом, начинающийся в следующей строке. Предположим. у вас имеетСЯ код, проверяющий совпаденис с именем "Alice". (Здесь предполaraeтся. что переменной пате ранее было присвоено некоторое значение.) if пате == 'Alice': print ( 'Hi, Аliсе.') Все управляющие инструкции заканчиваются двоеточием, за которым следует блок кода (тело инструкции). В данном случае блок состоит из одной инструкции: print «Hi, A1ice.'). Блок-схема рассматриваемом кода I1РИ8Сдена на рис. 2.3. print ('Hi, Alice.') False Рис. 2,3. Блок-схема инструкции if и.пру".... else За блоком кода инструкции if может следовать необязательная инструк- ция else со своим блоком кода, который выполняется лишь в том случае, если условие i f ложно. На обычном языке конструкция i f / е lse может быть прочитана так: "Если условие истинно, выполнить данный блок кода. В про- тивном случае выполнить следующий блок кода". В PytJlOn инструкция else не имеет условия и Всеrда состоит из следующих элементов: . ключевое слово else; . двоеточие; . блок кода с отступом, начинающийся на следующей строке.
Поток управления 71 Возвращаясь к примеру с именем 'Hce", рассмотрим код, содержащий инструкцию else, которая выводит дрyrое приветствие, если имя пользова- теля не "A1ice". if пате == 'Alice': print('Hi, Аliсе.') else: print('Hello, stranger. ') Блок-схема этоrо кода представлена на рис. 2.4. True False print('Hello, stranqer.') Конец Рис. 2.4. Блок-схема инструкции else IIНар,".... el:i.f В то время как из двух блоков кода, ассоциированных с инструкциями if и else, выполняется только один, иноrда желательно выбирать для выпол нения один из uесколькuх возможных блоков кода. Инструкция е 1 i f (COKpa щение от еМе iЛ может помещаться только после инструкции if или дрyrой инструкции elif. Она предоставляет еще ОДНо условие, которое проверя- ется лишь в том случае, если все предыдущие условия оказались ложными. В Python инструкция elif всеrда состоит из следующих элементов: . ключевое слово elif; . условие (т.е. выражение, вычисление KOToporo дает значение True или False); . двоеточие; . блок кода с отступом, начинающийся в следующей строке.
72 rЛQва 2 Добавим инструкцию elif в код, проверяющий имя, чтобы увидеть, как это работает на практике. if пате == 'A1ice': print('Hi, A1ice.') elif age < 12: print('You are not A1ice, kiddo.') На этот раз дополнительно проверяется возраст поль:ювателя, и если он меньше 12, то текст выводимоrо (:ообщения будет дрyrим. Блок-схема этоrо кода представлена на рис. 2.5. True рrint('Иi, Alice.') False True print ('You are not Alice, kiddo.') False Рис. 2.5. Блоксхемо инструкции elif Блок кода elif выполняется, если выражение age < 12 истинно, а Bыpa жение пате == I Аl1се I ложно. Но если оба условия ложны, то пропускаются оба блока кода. Никаких raрантий Toro, что выполнится хотя бы один из этих двух блоков кода, нет. Если имеется цепочка инструкций elif, то будет выполнен либо только один блок кода, либо ни один из них. Как только об- наруживается, что одно из условий истинно, все остальные блоки кода elif автоматически пропускаются. В качестве примера откройте в файловом pe дакторе новое окно, введите в нем приведенный ниже код и сохраните ero в файле vaтpire.py.
Поток управления 73 if пате 'A1ice': print('Hi, A1ice.') elif аче < 12: print('You are not A1ice, kiddo.') elif age > 2000: print('Un1ike you, A1ice is not an undead, immorta1 vampire.') е1Н аче > 100: print('You are not Alice, grannie.') В этом коде добавлены две ДОПOJШительные инструкции elif, обеспечи- вающие вывод разных сообщений в зависимости от возраста (age), указан- Horo пользователем. Блок-схема этоrо кода показана на рис. 12.6. Имейте, однако, в виду, что порядок расположения инструкций e1if имеет значение. Сейчас мы намеренно внесем в код ошибку, переставив ин- струкции местами. как вам уже известно, если одно из условий оказывается истинным, все остальные блоки кода elif автоматически пропускаются, и поэтому перестановка условий может создавать проблемы в КОДе. Измени- те код, как по казан о ниже, и сохраните ero в файле vaтpire2.py. if пате 'A1ice': print (1 Hi, АНсе.') е1Н age < 12: print('You are not Alice, kiddc.') . elif аче > 100: print('You are not A1ice, grannie. ') elif age > 2000: print('Unlike you, Alice is not an undead, immortal vampire.') Предположим, что до выполнения кода в переменной age содержится значение 3000. Вы ожидаете, что код выведет на экран строку 'Unlike уои, Аliсе is not an undead, immortal vampire. '. Однако, поскольку вычисление условия age > 100 дает значение True (ведь 3000 больше 100) 8, на экран будет выведена строка 'Уои are not Аliсе, grannie. " тоrда как остальны<, инструкции elif будyr автоматически пропущены. Вспомните о том, что ВЫIIОЛНЯется не более чем один из блоков инструкций elif, а порядок сл дования этих инструкций имеет значение! Блок-схема предыдущеrо кода представлена на рис. 2.7. Обратите вни мание на то, что ромбы для условий age > 100 и age > 2000 переставлены fестами. При необходимости вслед за последней инструкцией elif можно помес- ТИ1'Ь инструкцию else. В этом случае rарантируется, что будет выполнен хотя бы один (и только один) блок кода. Если условия во всех инструкциях i-f и elif окажутся ложными, то выполнится блок кода инструкции else. Например, переделаем проrрамму для распознавания имени "Alice" таким образом, чтобы в ней использовались инструкции if, elif и else.
74 r лава 2 Fa18e Fa1ge Fa18e False True print (' Hi, Alice.') Рис. 2.6. Блок-схемо кода с несколькими инструкциями elifB nporpaMMe vamp;re.py True print('You are not Alice, kiddo.') True print('Unlike уои, Alice i8 not an undead, immortal vampire,') True print('You are not Alice, grannie.') if name 'Alice': print ( I Hi, Alice.') elif age < 12: print('You are not Alice, kiddo.') else: print('You are neither Alice nor а little kid. ')
Поток управления 75 True print (' Hi, Аliсе.') False Тсие print('You асе not Alice, kiddo,') False Тсие print('You асе not Alice, grannie.') False print('Unlike уои, Alice is not an undead, immortal vampire.') False Конец Рис. 2.7. Блоксхема nporpoMMbl vampire2.py. Выполнение проrpаммы ВДОЛЬ перечеркнутоrо пути лоrически невозможно, поскольку если значение age больше 2000, 'То оно заведомо больше 100 На рис. 2.8 1I0казана блок-схема IIOBOI'O кода, который мы сохраним в файле littleКid. ру. Пользуясь обычным языком, смысл управляющей конструкции ЭТОI'О типа можно lIepeaTb так: "Если первое условие истинно, выполнить этот
76 r лава 2 блок кода. Иначе, если второе условие истинно, выполнить ЭТОТ блок Кода. В противном случае выполнить этот блок кода". в случае cOBMecTHoro ис- пользования всех трех инструкций не забывайте о правилах, устанавли- вающих последовательность их расположения, чтобы избежать ошибок, подобных той, которая ПРОИJuпострирована на рис. 2.7. Во-первых, должна быть только одна инструкция if. Любые инструкции elif, которые вам мо-- ryr понадобиться, должны следовать за инструкцией if. Во-вторых, если вы хотите быть уверены в том, что выполнится хотя бы один блок кода, замкните всю управляющую структуру инструкцией else. True print (' Hi, АНсе.') False True print('You are not Alice, kiddo.') False print('You are neither Alice nor а little kid.') Конец Рис. 2,8. Блоксхема nporpoMMbl Iiff/eКid.py ц.". while Инструкция while 1I0зволяет ОР"aIlИЗ0вать MHoroKpaTHoe повторное BЫ полнение блока кода. Блок кода инструкции while выполняется до тех пор,
Поток управления 77 пока выполняется указанное в ней условие. В PythOIl инструкция while вcer- да состоит из следующих элементов: . ключевое слово while; . условие (т.е. выражение, вычисление KOToporo дает значение True или False); . двоеточие; . блок кода с отступом, начинающийся в следующей строке. Нетрудно заметить, что инструкции while имеет сходную с инструкци- еЙ i f структуру, однако ведет себя иначе. По достижении конца блока кода if управление выполнением передается следующей инструкции. Однако 110 достижении конца блока кода инструкции while управление передает- ся в ее начало, и проrрамма продолжает выполнение Toro же блока кода. Инструкцию while часто называют 14иКJlOM while или просто 14U1СЛОМ. Сравним, как работают инструкция if и цикл while, ИСПОJ1Ь.1ующие одно и то же условие, на основании проверки KOToporo в обоих случаях предпри- нимаются одни и те же действия. Вот так ВЫrЛЯДИТ код с инструкцией i f. эрат = о if spaт < 5: print ('Hello, world.') spaт = эрат + 1 А вот так выrлядит код с инструкцией whi le. spam = о while spaт < 5: print('Hello, world.') эрат = эрат + 1 Эти инструкции весьма похожи: как if, так и while проверяют значение переменной spaт, и если оно меньше пяти, то выводится сообщение. Тем не менее эти фраrменты кода выполняются совершенно по-разному. Если в инструкции if вывод представляет собой одиночное сообщение "Неllо, world. ", то в инструкции while это сообщение выводится целых пять раз! Чтобы разобраться в том, почему так происходит, обратимся к соответству- ющим блок-схемам, представленным на рис. 2.9 и 2.10. Код <: инструкцией if тестирует условие и выводит текст Иеllа, world., если условие истинно. С друrой стороны, код с инструкцией while выво- дит этот текст пять раз. После этоrо он прекращаст свою работу, поскольку целочисленное значение, хранящее<:я впеременной spaт, увеличивается на единицу на каждой итерации ЦИКЛа, а это означает, что цикл выполнится именно пять раз, прежде чем условие spaт < 5 примет значение False.
78 r лава 2 True print ('НеНо, world.') spam spam + 1 False Рис. 2.9. Блок-схема кода с ИНСТРУКЦl1ей if True print('Hello, world.') spam spam + 1 False Рис. 2.10. Блок-схема кода с I1нструкцией while
Поток управления 79 в цикле while условие вссrда проверяется в начале каждой итерации (т.е. каждый раз, Коrда выполняется цикл). Если условие интерпретируется как Trиe, то блок кода выполняется, а затем вновь проверяется условие. Сразу же после Toro как обнаруживается, что условие равно False, блок кода while пропускается . Нозоi"I'''Й ЦIО while Ниже приведен пример простой проrраммы, которая без устали просит вас ввести свое имя, но на самом деле ожидает ввода не вашеrо имени, а бук- валЬНоrо текста I yoиr пате' ('ваше имя'). Выберите пункты меню FileqNew Window (ФайлqНовое окно) для открытия HOBoro окна файловоrо редакто-- ра, введите приведенный ниже код и сохраните eI'o в файле yourNaтe.py. о пате " . while пате != 'yoиr пате': print('Please type yoиr пате. ') . пате = input () . print ( 'Thank уои! 1) Сначала проrрамма присваивает переменной пате О значение в виде пустой строки. Это сделано для Toro, чтобы вычисление у(:ловия пате !". , yoиr пате I дало значение Trиe и проrрамма приступила к выполнению бло- ка кода цикла while .. Затем код в теле цикла запрашивает ввод имени пользователя и присваи вает ero переменной пате .. Поскольку это последняя строка блока, вы- полнение возобновляется с caмoro начала цикла while, rде вновь тестирует-- ся условие. Если значение пате не равно строке' yoиr пате' , то вычисление условия дает значение Trиe, в результате чеrо вновь начинает выполняться тело цикла. Но как только пользователь введет текст yoиr пате, у(:ловие примет сле дующий вид: 'yoиr пате' ! 'yoиr пате', что эквивалентно значению False. Поскольку теперь условие ложно, выполнение цикла прекращается и управ ление передается коду, следующему за циклом О. Блок-схема проrраммы yourNaтe.py приведена на рис. 2.11. Давайте проверим, как работает проrрамма yourNaтe.llJ' Нажмите клави шу <F5> и несколько раз введите текст, отличный от yoиr пате, прежде чем предоставить "porpaMMe то, что она требует. ?lease type yoиr пате. Al ?lease type yoиr пате. Albert ?lease tvpe voиr пате.
80 r лава 2 Please type yoиr пате. yoиr пате Thank уои! пате n print('Please type your пате.') False пате = input () print('Thank you!') Рис. 2. 11. Блок-схема проrраммы yoиrName.py Если вы так и не введете текст yoиr пате, то условие цикла while НИКоrда не сможет принять значение False, и проrрамма будет бесконечно повто- рять Свой запрос. В данном случае вызов функции inpиt () предоставляет пользователю ВОЗМОЖНОСть ввести строку, которая позволит проrрамме 110" кинyrь цикл. В дрyrих проrраммах возможны случаи, коrда значение уело- вия никоrда не сможет измениться, и это станет проблемой. Рассмотрим, как можно ОРl'анизовать принудительный ВЫХОД из цикла while. IfнtтруICЦ". break Существует быстрый способ заставить проrрамму преждевременно выйти из цикла while. Если проrрамма в процесс е выполнения достиrает
Поток управления 81 ИНСТРУКЦИИ break, то выполнение цикла немедленно прерывается (break ключевое слово PytllOП). Довольно просто, не правда ли? Ниже ПРИDCден пример проrраммы, ко- торая делает то же самое, что и предыдущая, но использует инструкцию break для выхода из цикла. Введите следующий код и сохраните ero в файле yourName2.py. . while True: print('Please type your пате.') . пате = inpиt ( ) . if пате == 'your пате': . break . print ( 'Thank уои! ' ) Первая строка. создает бесконеч:н:ый 14U1UL, Т.е. цикл while, условие KO Toporo всеrда истинно (True). Проrрамма всеrда будет входить в цикл и выйдет из Hero только в том случае, если выполнит(я инструкция break. (Бесконечный цикл, выйти И3 KOToporo невозможно, распространенная "роrpаммная ошибка.) Так же, как и ранее, проrрамма запрашивает ввод пользователем текста yoиr пате .. Однако теперь в цикле while I1РИcyrствует инструкция i f ., ко- торая провсряет, совпадает ли значение перемеllНОЙ пате со строкой your пате. В случае истинности этоrо условия выполняется инструкция break О, и управление передается инструкции print ('Thank уои') .. В противном случае предложение Н, содержащее инструкцию break, пропускается, про- rpaмMa переходит в конец цикла while и сразу же возвращается в ero нача- ло для проверки условия. Поскольку условие это просто булево значение True, IIporpaMMa вновь входит в цикл и запрашивает ввод текста your паТЕе. Блок-схема этой nporpaMMbI приведена на рис. 2.12. Запустите nporpaMМY yourNuтber2.py и введите тот же текст, который вы вводили для nporpaMMbI yourNuтber.py. Новая версия проrраммы должна реаrировать на ваш ввод так же, как и исходная. иН"ру".... continue Как и инструкция break, инструкция continue используется в циклах. Коrда проrрамма в процессе выполнения достиrает инструкции conti:1ue, управление немедленно передается в начало цикла, rде условие вычисляет- ся заново. (То же самое происходит и при обычном достижении "porpaM мой конца цикла.)
82 r лава 2 True рrint('Рlеазе type your name.') пате = input () * True break False print ( I Thank you! 1) Рис. 2. 12. Блок-схема nporpaMMbl yoиrName2.py с бесконечным циклом. Обратите внимание на то, что перечеркнутый путь выполнения nporpaMMbl лоrически никоrда не может быть реализован, поскольку условие цикла BcerAa истинно Продолжим написание проrраммы, запрашивающей ввод имени и паро- ля. Введите следующий код в новом окне файловоrо редактора и сохраните ero в файле sword.fish, ру.
Поток управления 83 Попали в повушку бесконечноrо цикла' Если вы обнаружили, что ваша nporpaMMa увязла в бесконечном цикле, на- жмите комбинацию клавиш <Ctrl+C>. В результате будет сrенерирована ошибка KeyboardInterrиpt, что приведет к HeMeдneHHoMY прекращению работы nporpaM- мы. Проверьте, как это работает на практике, создав простой бесконечный цикл в файловом редакторе и сохранив ero в файле ;nf;niteloop.py. while True: print('Hello world!') Коrда вы запустите эту проrpoмму, она начнет безостановочно выводить на экран приветствие Hello wor ld!, поскольку условие инструкции while Bcerдa остается истин ным. В интерактивной оболочке IDLE существуют два способа "ринудительноrо заверше- ния работы проrраммы: нажатие клавиш <Ct+C> и выбор пунктев меню SheIIRestart Shell (Оболачка Перезапустить оболочку). Первый способ удобно использовоть в любой ситуации, коrда требуется HeMeдneHHo прекротить работу проrpаммы, даже если это никак не связано с необходимостью прервоть выполнение бесконечноro цикла. ./ while Trиe: print('Who are уои?') пате = inpиt ( ) . if пате ! = 'Joe': . continue print('Hello, Joe. What is the password? (It is а fish.) ') . password = input ( ) if password == 'swordfish': О break . print ( 'Ассевв granted. I ) F..с.ли пользователь вводит любое друrое имя, кроме Joe"., инструкция continue . заставляет проrрамму перейти в начало цикла. П()('ле повторной IIроверки условия проrpамма всеrда входит D тело цикла, поскольку условие вcela истинно (True). В случае же llрохождения инструкции if запраши- вается ввод пароля .. Если пользователь вводит пароль swordfish, то вы- полняется инструкция break ., nporpaMMa покидает цикл while и выводит на экран текст Access granted .. В противном случае управление выполне- нием проrpаммы передается в конец ЦИКJIа while и сразу же возвращается D ero начало. Блок--схема этой проrрамМЫl1редставлена на рис. 2.13.
84 r лава 2 True pr1nt ('Who are уои?') пате input () cont inue True * False print('Hello, Joe. llhat 19 the pa99word? (It 19 а fi9h.) 1) password input() break Fa1ge Рис. 2.1 З. Блок-схема nporpaMMbl swordfish.py. перечеРI<НУТЫЙ путь выполнения nporpaMMbl лоrически никоrдо не может быть реализован, поскольку условие цикла всеrда истинно
Поток управления 85 .Истинные" и "nожные" значения Существуют значения друrих типов ДаННЫХ, которые при проверке условий счи- таются эквивалентными значениям True и False. При использовании в выражениях условий значени" О, О. О и ' , (пустая строка) считаются ложными (False), тorAo как все друrие значения считаются истинными (True). Взrляните, например, на следую- щую nporpOMMY. name = " while not name:. print('Enter your name:') name = input ( ) print('How many guests will уои have?') numOfGue5ts = int(input()) if numOfGuests:. print('Be sure to have enough room for all your guests.')8 print ( 'Oone') Если пользователь вводит пустую строку в качестве имени, то условие цикла while принимает значение True ., и проrрамма продолжает запрашивать имя. Если значение переменной nurnOfGuests не равно О ., то значение условия счита- ется равным True, и nporpaммa выводит напоминание .. Вместо not пате вы моrли бы ввести not пате ! = ", а вместо numOfGuests numOfGuests ! = О, но использовоние истинных и ложных значений облеrчает чте- ние кода. Запустите nporpaMMY и введите какой-либо текст. Проrрамма не запро-- сит ввод пароля до тех пор. пока вы не подтвердите, что ваше имя "Joe"; после ввода правильноro пароля она должна завершить выполнение. Who are уои? 11т fine, thank5. Whc are уои? Who are уои? Joe Hello, Joe. What i5 the pas5word? (1t i5 а fi5h.) Mary Who are уои? Joe Hello, Joe. What i5 the pa55word? (1t i5 а fish.) swordfish Ассе55 granted.
86 r лава 2 ц.к. zor . фуНК.... raпge ( ) Цикл while продолжает выполняться до тех пор, пока условие остается истинным. Но что если вы хотите ВЫПОЛНить блок кода лишь определен- ное количество раз? Это можно сделать с помощью цикла for или функции range ( ) . Инструкция for выrлядит в коде примерно как for i in range (5) : и всеrДа включает следующие элементы: . ключевое слово for; . имя переменной; . ключевое слово in; . вызов функции range ( ) , которой можно передать до трех целых чисел; . двоеточие; . блок кода с отступом, начинающийся в следующей строке. Чтобы проверить на практике, как работает цикл for, создадим новую проrрамму в файле f i veT imes . ру. print('My пате is') for i in range(5): print('Jimrny Five Times (' + str(i) + ') ') Блок кода цикла for выполняется пять раз. На первой итерации значе- ние переменной i устанавливается равным О. Вызов функции print () в теле цикла выводит текст Jimmy Five Times (О). Коrда Python завершает итера- цию, выполнив весь блок кода, управление передается в начало цикла, rде ин<:трукция for увеличивает значение переменной i на единицу. Именно поэтому вызов функции range (5) обеспечивает пятикратное выполнение блока кода цикла, устанавливая для i последовательные значения О, 1,2,3 и 4. Целое значение, указанное при вызове функции range () , в этот ряд не входит. Блоксхема проrраммы.fivеllтеs.рупоказана на рис. 2.14. Коrда вы запустите эту проrрамму, она должна вывести пять строк текста Jimmy Five Times, каждая из которых заканчивается текущим значением i, и покинуть цикл for. Му пате is Jirnmy Five Tirnes (О) Jirnmy Five Tirnes (1) Jirnmy Five Tirnes (2) Jirnmy Five Tirnes (3) Jirnmy Five Tirnes (4)
Поток управления 87 print ('Ну name is') print('Jimrny Five Times (' + str(i) + ')') Цикл завершен Рис. 2. 14. Блок-схема nporpaMMbl five Times.py Примечание 'в 'ЦиКJI,ax for также доnуск.ается uсnолъзоватъ UнстjJy1c'ЦUU break U continue. Инст/J'Y'К'Ция сопипие 8озвращает уnравленuе в наЧШlО 'ЦиКJI,a для выполнения сле-- дующей иmepа'Ции, как ес.лu бы nроерам.ма достима кон'Ца 'ЦиКJI,a U вериуласъ к еео началу обычны,м, способо,м,. В действuтелъностu инструк'Ции contiпue U break ,м,оеут исnолъзоватъся толъко в 'Циклах while U for. Если вы nоnытаетесъ uсnолъ- зоватъ их едлuбо еще, Python выведет сообщение об ошuбке. в основу еще ОДНоrо примера цикла for положен исторический факт, связанный с известным математиком Карлом Фридрихом rayccoM. Как-то, коrда I'aycc еще учился в школе, учитель дал клаСL'У следующее задание: най- ти сумму всех чисел от О до 100. Используя остроумный прием, юный raycc нашел нужную сумму в течение вcero лишь нескольких секунд, но вы може- те проделать соответствующие вычисления самостоятельно, написав про-- rpaмMY на языке Python, в которой используется цикл for. . tota1 = О . for ПuпI in range (101) : . tota1 = tota1 + ПuпI . print (tota1)
88 r лава 2 Правильный ответ 5050. Сразу после запуска проrраммы значение переменной total устанавливается равным О .. Затем в цикле for . КОД total = total + пит . Вblполняется 100 раз. По завершении 100 итераций цикла в переменной total будет сохранена сумма всех целых чисел от О до 100. После этоrо значение total выводится на экран.. Даже на самых Meд ленных компьютерах выплнениеe этой проrpаммы займет менее секунды. (Юный raycc доrадался, что Bcero имеется 50 пар чисел, сумма которых дает 100: 1 + 99,2 + 98,3 + 97 + ...; 49 + 51. Поскольку 50 х 100" 5000, то после прибавления оставшеrося без нары числа 50 мы получаем окончательный результат 5050. Сообразительный ребенок!) Iв.ваnеll,и..ii ЦIIU while Все то, что делает Цикл for, можно сделать с помощью цикла while; просто циклы for более компактны в записи. Перепишем код проrраммы fiveТiтes,py, использовав в новой версии цикл while, квивалентный цик- лу for. print('My пате is') i = О while i < 5: print('Jimmy Five Times (' + str(i) + ') ') i = i + 1 Если вы занустите эту проrрамму, то увидите, что она позволяет полу- чить те же результаты, что и ее версия, в которой используется цикл for. Арryмеи,.. .a..ana, IОllца 11 wara Аllаnазоиа "".1&1111 range () Некоторые функции MOryr вызываться с передачей им нескольких apry- ментов, разделенных запятыми, и функция range () одна из них. Это по- зволяет изменять диапазон целых чисел, задаваемый функцией range ( ) , включая ero начальный и конечный элементы. for i in range(12, 16): print(i) Первый apryMeHT определяет, с KaKoro значения начинает изменяться переменная цикла for, а второй верхнюю rраницу диапазона изменения этой переменной, но сам в ЭТОт диапазон не включается. 12 13 14 15
Поток управления 89 Также допускается вызов функции range () с тремя арryментами. Первые два apryмeHTa определяют начало и конец диапазона изменения переменной цикла for в соответствии с приведенным выше описанием, а тpt..'Тий задает шar. Шае это приращение переменной в конце каждой итерации цикла. for i in range(O, 10, 2): print(i) Следовательно, вызов range (О, 1 О, 2) обеспечивает изменение перемен ной цикла от нуля ДО восьми С шаrом два. О 2 4 6 8 Функция range () очень rибкая в отношении формирования последова- тельностей целых чисел для циклов for. Например, задание отрицатель Horo числа в качестве шаrа обеспечивает изменение переменной цикла от больших значений к меньшим. for i in range(5, 1, l): print(i) Выполнение цикла for для вывода значений переменной i с использова нием вызова range (5, 1, 1) даст следующий результат. 5 4 3 2 1 О Импортирование МОДУllей Любой проrрамме на языке Python доступен базовый набор функций, которые называются встроен:н:ыми, в число которых вхоДЯТ такие функции, как print () , input () и lеп (), с которыми вы уже успели познакомиться. Кроме Toro, в поставку Python входит набор модулей, который называется сrпшндартuой библиотекой. Каждый модуль это "porpaMMa на Python, содер- жащая rруппу родствеНIIЫХ функций, которые вы можете внедрять в свои
90 r лава 2 проrраммы. Например, модуль math включает математические ФУНКЦИИ, модуль random функции для работы со случайными числами, и Т.д. Прежде чем вы сможете использовать функции, входящие в модуль, ero необходимо импортировать с помощью инструкции import. Инструкция import в коде состоит из следующих элементов: . ключевое слово import; . имя модуля; . необязательные дополнитеJII>ные имена модулей, разделенные запя. тыми. как только модуль импортирован, вы можете использовать любую из входящих в Hero функций. Давайте проверим, как работает модуль random, предоставляющий доступ к функции random. ranint () . Введите в файловом редакторе приведенный ниже код и сохраните ero в файле printRandom. ру. import random for i in range(5): print(random.randint(l, 10)) Выполнив проrрамму, вы должны получить следующий вывод. 4 1 8 4 1 Функция random. randint () возвращает случайное число, лежащее в диа- пазоне межу двумя целочисленными значениями, которые передаются функции в качестве apryмeHToB. Поскольку функция randint () находится в модуле random, ero имя должно указываться в виде префикса (через точку) перед именем функции, чтобы Pyt]lOn Mor определить, что данную функ- цию следует искать в модуле random. Вот пример инструкции import, которая импортирует четыре различ- ных модуля: import random, зуз, оз, math Теперь мы можем использовать любую из функций, находящихся в этих четырех модулях.
Поток управления 91 Ifн"рукц.. zrom import Альтернативная форма инструкции import состоит из ключевоrо сло ва from, за которым следуют имя модуля, ключевое слово import и символ "звездочка", например from random import *. При использовании этой формы импорта добавлять префикс random. к имени функции, вызываемой из модуля random, не требуется. Однако ис пользование полноrо имени функции повышает удобочитаемость кода, поэтому лучше использовать обычную форму инструкции import. Преждевременное прекращение ВЫПОllнеНИII nporpaMMbl с помощыо вызова ауа . exi t () и в завершение хочу познакомить вас с тем, как прекратить выполн ние проrраммы. Это всеrда происходит автоматически после выполнения последней инструкции проrраммы. Однако существует возможность при нудительно прекра1'ИТЬ работу проrраммы, или, как rоворят, выйти из нее, с помощью вызова функции sys. exi t () . Поскольку эта функция находится в модуле sys, вы должны импортировать eI'o до Toro, как он будет исполь зоваться. Откройте новое окно файловоrо редактора, введите в Hero приведен- ный ниже код и сохраните ero в файле exitExaтple.frJ. irnport sys while True: print('Type exit to exit.') response = inpиt() if response == 'exit': sys.exit() print('Yoи typed ' + response + '. ') Запустите эту проrрамму в IDLE. В проrрамме имеется бесконечный цикл, в котором отсутствует инструкция break. Единственная возможность завершить работу этой проrраммы это ввести exi t в ответ на запрос, что приведет к вызову функции s ys. exi t ( ) . в случае совпадения значения пе ременной response со строкой' exi t I выполнение проrраммы прекраща ется. Поскольку значение переменной response устанавливается функцией inpиt (), для завершения проrраммы пользователь должен ввести слово ехН.
92 r лава 2 РеЗlOме Используя выражения, результатом вычисления которых является зна- чение True ИЛИ False (такие выражения называют условиями), можно пи- сать проrраммы, способные принимать решения относительно Toro, какие участки кода должны выполняться, а какие проnyскаться. Кроме Toro, можно орrанизовать MHoroKpaTHoe выполнение кода в цикле до тех пор, пока вычисление условия дает значение True. В тех случаях, Коrда требу- ется осуществить выход из цикла или возврат к ero началу, используются инструкции break и continue. Управляющие инструкции позволяют писать HaMHorO более интеллекту. альные ПрОI'раммы. Существуют и друrие возможности по управлению вы- полнением проrрамм, обеспечиваемые написанием собственных функций, о чем пойдет речь в следующей rлаве. KOHTpOlIbHble вопросы 1. Каковы два возможных значения данных булева типа? Как они запи- сываются? 2. Назовите три булевыx оператора. 3. Напишите таблицы истинности (т.е. запишите результаты для всех возможных комбинаций оператора и BYX булевых значений) для каж- доrо из булевых операторов. 4. Каковы результаты вычисления приведенных ниже выражений? (5 > 4) and (3 == 5) not (5 > 4) (5 > 4) or (3 == 5) not ((5 > 4) or (3 == 5)) (True and True) and (True (not False) or (not True) Fa1se) 5. Назовите шесть операторов сравнения. 6. В чем Cyrb различия между оператором равенства и оператором при- сваивания? 7. Объясните, что такое условие и rде используются условия. 8. Идентифицируйте три блока в приведенном ниже Коде. эрат О if эрат == 10: print ( 'eggs' ) if эрат > 5: print ( 'Ьасоп I ) AlctA-
Поток управления 93 print ('ham') print ( 'spam' ) print ('spam') 9. Напишите КОД, который выводит разные сообщения в зависимости от значения, хранящеrося в переменной spaт: Hello при значении 1, Howdy при значении 2 и Greetings! при любом Друl'ОМ значении. 10. Какую комбинацию клавиш следует нажать, чтобы вывести проrрам- му из бесконечноrо цикла? 11. Чем различаются инструкции break и continue? 12. Чем различаются вызовы функций range (10), range (О, 10) и range (О, 10,1) в цикле for? 13. Напишите короткую проrрамму, выводящую числа от 1 до 10 с помо- щью цикла for. Затем напишите аналоrичную проrрамму, в которой используется цикл whi1e. 14. Как бы вы вызвали функцию bacon ( ) , хранящуюся в модуле эрат, 110- еле TOI'O, как ИМIIОРТИРОВали этот модуль? Дополнительное задание. Поищите в Интернете информацию о ФУНК- циях round () и abs () и Выясните, что они делают. Самостоятельно поэксп риментируйте с ними в интерактивной оболочке.
фуНКции в предыдущих rлавах вы уже познакомились с функциями print () , inpиt () и len () . Python предоставляет встроенные функции наподобие этих, но вы можете писать и собствен- ные функции. ФуНК1&ия это нечто вроде мини-проrраммы в рамках большой проrраммы. Чтобы лучше понять, как работают функции, обратимся к конкретному примеру. Ввеlщте в файловом редакторе исходный код при- веденной ниже проrpаммы и сохраните ero в файле helloPunc.py. о def hello () : . print ( 1 Howdy! ' ) print ('Howdy!! ! 1) print('Hello there.') . hello() hello ( ) hello () Первая строка это инструкция de f ., которая определяет функцию с именем hello ( ) . Блок кода, следующий за инструкцией def ., образует тело функции. Этот код выполняется при вызове функции, а не при ее первона чальном определении. Последующие три строки hello () . ЭТО вызовы функции. В коде вызов функции обозначается указанием ее имени с последующей парой круrлых скобок, которые MOryr содержать некоторое количество apl)'MeHToB. Коrда проrpамма в IIроцессе выполнения достиraет вызова функции, управление передается первой строке кода этой функции, п<кле чеrо выполняется весь ее код. По достижении конца функции управление возвращается строке, которая ее вызвала, и продолжается выполнение дальнейшеrо кода.
96 r лава 3 Поскольку в данной проrрамме ФУНКЦИЯ hel10 () вызывается три раза, столько же выполняется и код этой функции. Выполнив эту проrрамму, вы должны получить следующий вывод. Howdy! Howdy!! ! Hel10 there. Howdy! Howdy!! ! Hello there. Howdy! Howdy ! ! ! Не1l0 there. Основное назначение функции rруппирование MHoroKpaTHo выпл-- няемоrо кода. Если бы мы не определили функцию, то пришлось бы копи- ровать этот код в буфер обмена и вставлять ero в nporpaMMY везде, rде он используется, в результате чсrо проrрамма выrлядела бы примерно так. print ( I Howdy! ' ) print ( 'Howdy! ! ! ' ) print('Hello there.') print ( 'Howdy! ' ) print (' Howdy! ! ! ') print('Hello there.') print ('Howdy!') print ('Howdy!!!') print('Hello there.') В целом старайтесь избеrать дублирования кода, поскольку, если понадо- бится обновить проrpамму (например, для испрамения ошибок), вам при дется вносить изменения в код везде, куда вы ero копировали. По мере накопления проrраммистскоro опыта вы заметите, что все чаще занимаетесь тем, что избамяетесь от дублирования кода, сводя к минимуму использование метода копирования и вставки. Такие меры позволяют со- кратить размер проrрамм, а также упростить их чтение и обновление. ИНСТРУКЦИИ def С параМ8трами Вызывая функции наподобие print () или len (), вы передаете им знач ния, которые называются ареумен.тOJИU в данном контексте, указывая их в скобках. Вы также можете определять собственные функции, принимаю- щие арryмеиты. Введите в файловом редакторе приведенный ниже пример и сохраните ero в файле heUoFunc2.frJ.
Функции 97 о ctef hell0 (пате) : . print (' Неl10 ' + пате) . hell0 ( , Аliсе ' ) hel10 (' ВоЬ') Выполнив эту проrpамму, вы должны нолучить следующий вывод. Hello Аliсе Неll0 ВоЬ в данной проrрамме определение функции hel10 () содержит параметр :1ате .. Парa.мemр это псрсмеllНая, в которой сохраняется aplyмeHT функ ции при ее вы:ю"е. Коrда функция вызываетея в первый pa:J, ей передается арryмеит 'Alice' .. Коrда ноток управлспия псреходит " функцию, Hepe менной пате автоматически присваивается значение' Alice', которое и BЫ водится на экран инструкцией print () .. Следует особо отметить то обстоятельство, что ПОСJlС выполнения воз врата из функции сохраненное в параметре значение теряется. Например, если вы добавитс вызов print (пате) ПOCJlе вызова hello ('ВоЬ') в предыду- щей проrрамме, то будет выведено сообщение об ошибке NameError, по скольку вне функции переменной с именем пате не существует. При возвра те из функции после вызова hel10 ('ВоЬ') ЭТа IIсременная уничтожается, по-- этому вызов print (name) будет ссылаться на несуществующую перемснную. Это аналоrично тому, как при завсршении ПрOl'раммы информация о ее переменных теряется. Более подробно о том, почему так происходит, речь пойдет далее, Коrда мы будем обсуждать локальную область видимости переменных внyrри функции. ИНСТРУКЦИII return и возвращаемые значеНИII Коrда вы вызываете функцию len () и передаете ей арryмеит, например I Hel10 ' , функция ВЫЧИСJlЯСТ целое :ша"сние 5, нреДставляющсе длину CTpO ки, которая была передана функции. В общем СЛУЧаС значение, вычисляе юе в результате вызова функции, называется во.16ращае.м:ь/'м' зuа'Ц,еuuр.м, ДаН- ной функции. Создавая функцию с использованием инструкции de f, ВЫ можетс ука- зать, какое значение должно возвращаться, с IIOМОЩI)Ю инструкции retиrn. Инструкция retиrn состоит из следующих частей: . ключевое слово retиrn; . значение или выражение, которое должна вернуть функция.
98 rпaoa 3 Если в инструкции return используется выражение, то возвращается ре- зультат вычисления данноrо выражения. Например, в следующей проrрам ме определяется функция, которая каждый раз возвращает дрУI]'Ю строку, в зависимости от Toro, какое число передано в качестве apryMeHTa. Введите в файловом редакторе следующий код и сохраните ero в файле тagic8BaU.py. о import random . def . getAn5wer(an5werNumber) : if an5werNumber == 1: retиrn 'It i5 certain' elif answerNumber == 2: return 'It i5 decidedly 50' e1if answerNumber з: retиrn 'Уе5' elif an5werNumber 4: retиrn 'Reply hazy try again' e1if an5werNumber == 5: return 'A5k again later' e1if an5werNumber == б: retиrn 'Concentrate and a5k again' elif an5werNиmber == 7: return 'Му reply i5 по' e1if an5werNumber == 8: return 'Outlook not 50 good' elif answerNumber == 9: retиrn 'Very doubtfu1' е r = random.randint(1, 9) . fortиne = getAn5wer (r) Ф print(fortune) Коrда запускается эта проrрамма, Python в первую очередь импортирует модуль random .. Затем определяется функция getAn5wer () .. Поскольку функция ЛИlUь определяется (а не вызывается), ее код не выполняется. Дa лее вызывается функция random. randint () с двумя арryментами 1 и 9 .. Эта функция возвращает случайное целое число, при надлежащее диапазо-- ну чисел от 1 до 9 (включая сами эти значения), и это число сохраняется в переменной r. Функция getAnswer () вызывается с передачей ей переменной r в качес тве арryмеита .. Выполнение проrраммы переходит в наtlaЛО функции getAn5wer () ., и значение r сохраняется в параметре answerNurnber. Затем функция возвращает одно из множества в03можныx строковых значений, зависящих от значения an5werNumber. Выполнение возвращается строке в нижней части проrраммы, которая первоначально вызвала функцию getAn5wer () .. Возвращенная строка присваивается переменной fortune, которая затем передает(я функции print () ф и выводится на экран.
ФУНКЦИИ 99 Поскольку возвращасмые значения МOIУ!' lIередаваться в качестве apry ментов друrим вызываемым функциям, вместо строк r = random.randint(l, 9) fortune = getAnswer(r) print(fortune) можно использовать слсдующую эквивалентную им строку: print (getAnswer(random.randint (1, 9))) Вспомните, что выражения состоят из значений и Оllераторов. Вызов функции можно использовать в выражениях, поскольку это эквивалентно использованию возвращаеМОI"О значеllИЯ функции. Значение None В языке Python Оllределено значение None, IIрсдставляющее отсутствие значения. None единственный прсдставитсль ТИllа даНIIЫХ NoneType. (Ана- JIоrичные значения в друrих языках IIpOI-раммирования фиrypируют IIОД именами пиН, nil и undefined.) Точно так жс, как имсна булсвых значений True и False, имя значения None всеrда должно вводиться с ИСIIОЛЬЗ0ванием IIрОПИСIIОИ буквы N. ЭТО "Зllаченис, lIе имеющее значения" может быть очень IIОЛСЗНЫМ, если вам нужно сохранить нечто такое, что невозможно перенутать с на- стоящим значением неременной. В частности, оно исполЬ.зуется в качсстве возвращаемою значения функции print (). Последняя отображает текст lIа экране, и ей необязательно возвращать значение так, как это дслают функ- ции lеп () и input () . Однако, поскольку вычисление любой функции долж- но давать возвращаемое значение, функция print () возвращает значение one. Чтобы увидетl>, как это работает, введите в интерактивной оболочке следующий код. »> spam = print('Hello! ') ::е 110 ! > > > None == spam rue Незаметно для нользователя, "за кулисами", Pytholl добавляет инструк- цию return None в конец Оllределения любой функции, в которой инструк- ция return отсутствует. Это аllалоrично тому, как цикл while или for закан- чивается неявной инструкцией continue. Кроме Toro, если вы используете инструкцию, не указывая значения (т.е. занисываете только само КJlЮчевое слово return), то функция возвращает значение None.
100 r лава 3 Именованные aprYMeHTbl и ФУНКЦИII print () Большинство арryмеитов идентифицируется по их позиции в вызовс функ- ции. Например, вызов random.randint (1, 10) отличается от вызова random. randint (10, 1). При вызове функции random.randint (1, 10) будет возвра- щено случайное целое число из диапазона от 1 до 10, поскольку первый ар- ryмеит имеет смысл нижней rраницы диапазона, а второй верхней (в то время как вызов r andom. randin t ( 1 О, 1) приводит К возникновению ошибки). В то же время и.м.еиова'Н:н:ые ареу.м.еит'Ы идентифицируются по имени, указываемому перед ними при вызове функции. Именованныс apryMeHTbI часто используются в качестве необязательных параметров. Например, функция print () имеет необязательные параметры end и sep, с помощью которых можно задать соответственно текст, который должен выводиться в конце apryMeHToB, и текст, который должен выводиться между apryмeHTa- ми (разделитель). Если вы запустите проrpамму print ( 'ИеНо' ) print ('World') ТО вывод будет таким: ИеНо World Две строки появятся в виде отдельных строк вывода, поскольку функция print () автоматически добавляет символы новой строки в конец каждой строки, которая ей передается. В случае необходимости вместо <:имвола новой строки можно использовать друryю строку, задав ее с помощью име- HOBaHHoro apryмeHTa end. Например, для проrpаммы print('Hello', end=") print ('World') вывод будет таким: HelloWorld Здесь текст выводится в одной строке ввиду отсyrствия символа новой строки после текста' ИеНо' . Вместо Hero выводится пустая строка. Этот прием приrодится вам в тех случаях, коrда потребуется отмена использова- ния символов новой строки, автоматически добавляемыIx в конце каждоrо вывода, осуществляемоrо с помощью функции print () .
ФУНКЦИИ 1.1 Точно так же, если ВЫllередаете функции print () несколько строковых значений, то по умолчанию в качестве разделителя используется пробел. Введите в интерактивной оболочке следующий код. »> print (1 cats', I dogs', 'mice') :a1:S dogs mice Однако вместо заданной по умолчанию пустой строки можно использо-- вать дрyryю, передав функции именованный apryмeнт sep. Введите в инте- рактивной оболочке следующий текст. »> print('cats', 'dogs', 'mice', sep=', ') :ats,dogs,mice Именованные aplyмeHTbI также можно добавлять в функции, написанные вами самостоятельно, однако сначала необходимо изучить такие типы дан- ных, как списки и словари, о которых пойдет речь в двух следующих rлавах. .- пока что достаточно знать лишь то, что у некоторых функций имеются именованные apryMeHTbI, которые можно задавать при вызове функции. Локапьнаll и rllo6allbHall обllасти ВИДИМОСТИ о параметрах и переменных, получающих значения в теле вызванной функции, rоворят, ЧТО они существуют в лсжалЪ1tОЙ области видимости этой функции. О перемеllНЫХ, значения которым присваиваются вне функций, rоворят, что они существуют в 2Лобал'Ь1tОЙ области видимости. Переменные, существующие в локальной области видимости, называются ЛlЖал'Ь1t'Ьt.Ми перемен'Ными, Тоrда как переменные, существующие в rлобальной области ВИДИМОСТи, называются ZJlобал'Ь1tыми nеремe'lt1tым.. Переменная может от. носиться только к одному ИЗ этих типов; она не может быть локальной и rлобальной одновременно. Вы можете представлять себе область видимости как контейнер для пер менных. При уничтожении области видимости все значения, которые хра- нятся в относящихся К ней переменных, теряются. Существует только одна rлобальная область видимости, и она создается в момент начала выполн ния проrраммы. Коrда проrрамма завершает работу, rлобальная область ви. J.имости уничтожается, и все ее переменные теряются. В противном случае при запуске проrраммы в очередной раз переменные содержали бы те же значения, что и во время пос.леднеrо сеанса выполнения проrpaммы. Локальная область видимости создается всякий раз, коrда вызывается функция. Любая lIеременная, которой присваивается значение в этой функ- ции, существует в данной локальной области видимости. При возврате из функции локальная область видимости уничтожается, и эти переменные
102 rлава 3 теряются. Коrда вы в следующий раз вызовете эту же функцию, локальные переменные не будут помнить те значения, которые хранились в них при последнем вызове функции. Области видимости переменных иrрают важную роль по следующим причинам: . локальные переменные не MOryт использоваться в коде, относящемся к rлобальной области видимости; . в то же время локальная область видимости имеет доступ к rлобаль ным переменным; . код, находящийся в локальной области видимости функции, не может использовать переменные из любой дрyrой локальной области види- мо<:ти; . разные переменные MOryт иметь одно и то же имя, если они относят- ся к разным областям видимости. Это означает, что одновременно MOryт существовать как локальная переменная с именем spam, так и rлобальная переменная с таким же иМенем. Использование в Python различных областей видимости и отказ ОТ 1'01'0, чтобы считать все переменные rлобальными, обеспечивает то преимуще- ство, что функции, изменяющие переменныс, MOryт взаимодействовать с остальным кодом проrраммы только через свои параметры и ВО:Jвращаемос значение. При таком подходе контролировать поведение проrраммы на- MHOro леrче и безопаснее. Если бы все переменные в вашей проrpаммс были только rлобальными, то ошибочное значение какой-либо переменной MOr- ло бы передаваться в ДРУl'ие части проrраммы, тем самым затрудняя поиск причины ошибки. Значение rлобальной переменной может устанавливать- ся в любом месте проrpаммы, а вся проrрамма может нас::читывать тысячи строк! Но если ошибка связана с неверным значением локальной перемен- ной, 1'0 для нахождения "ричины ошибки вам достаточно исследовать лишь код функции, в которой устанавливается значение данной переменной. В использовании rлобальных переменных внебольших проrраммах нет ничеro предосудителыlOro, но придерживаться TaKoro же подхода в случае крупных проrрамм плохая привычка. JlOICO..HII. "'ре_...II. Н. _о", .'110.'30'''''''. . ,.060..0. OUOtrll В.Д._О". Рассмотрим следующую проrрамму, попытка выполнения которой при водит к ошибке. def spam(): eggs = 31337
Функции 103 эрат() print (eggs) Выполнив эту проrрамму, вы получите следующий вывод. Traceback (most recent cal1 1ast): File "С:/tеstЗ784.ру", line 4, in <modи1e> print (eggs) NameError: пате 'eggs' is not defined Ошибка возникла потому, что переменная eggs существует только в ло- кальной области видимости, созданной при вызове функции spam ( ) . Как только осуществляется возврат из функции spam ( ) , эта локальная область видимости уничтожается, и переменная eggs прекращает существование. Поэтому, коrда ваша I1pOl'paMMa пытается выполнить вызов print (eggs), Python ВbIВОДИТ сообщение об ошибке, информируя вас о том, что пере- менная eggs не определена. Если вдуматься, то в этом есть смысл: коrда про- rpaMMa выполняется в rлобальной области видимости, локальные области видимости не существуют, а это означает, что нет никаких локальных пере- eHHЫX. Вот почему в rлобальной области видимости MOryr использоваться только rлобальные переменные. в .0"".'НIfХ 06."".х ..А..О"" н, .0,.,., .',,011.30.,,"'. "'Р'.'ННIf' .3 IIРУ'.Х .0"".'.IfХ 06."",. ..А..О"" Новая локальная область видимости создается всякий раз, Коrда вызыва- ется функция, включая случаи, коrда функция вызывается из дрyrой функ- ЦИИ. Рассмотрим следующую проrрамму, def spam () : . eggs 99 . bacon() . print(eggs) def Ьасоп(): ham = 101 О eggs '"' О . spam() Коrда она запускается, вызывается функция spam () .. и создается ло- кальная область видимости. Локальной переменной eggs .. присваивается значение 99. Затем вызывается функция Ьасоп () .. и создается вторая ло- кальная область видимости. В одно и то же время MOryr существовать н<..... сколько локальных областей видимости. В этой новой локальной области
104 r лова 3 видимости значение локальной переменной ham устанавливается равным 101, а кроме Toro, создается и устанавливается равной О локальная перемен ная eggs е, которая отличается от одноименной переменной. созданной в локальной области видимости функции spam ( ) . После возврата из функции Ьасоп () ее локальная область видимости уни чтожается. Выполнение проrраммы продолжается в функции spam ( ) , кото- рая выводит значение переменной eggs О, а поскольку локальная область видимости для вызова функции spam () В это время все еще существует, то значение переменной eggs устанавливается равным 99. Именно это значе- ние и выводит проrрамма. Таким образом, локальные переменные одной функции полностью OT делены от локальных переменных друroй функции. rл060Л6н..е пере.енн..е .о", ".'016t. .3 лоltОЛ6ноj 06лоt,. '.II..ot,. Рассмотрим следующую проrрамму. def spam(): print(eggs) eggs = 42 spam() print (eggs) Поскольку имя eggs oTcyrcTBYeт в списке параметров функции spam () и в коде этой функции OTcyrcTBYeт присваивание значения переменной с Ta ким именем, то Python, встретив эту персменную в теле функции spam () , по- лаrает, что в данном случае имеется в виду ссылка на rлобальную пере мен- ную eggs. Именно поэтому при выполнении данной проrраммы на экран будет выведено значение 42. Ло1tОЛ6н..е . rл060Л6н..е пере.енн..е t O/I.ноltо..... ..ено.. Чтобы не усложнять себе жизнь, избеrайте использования локальных переменных, имена которых совпадают с именами l'Jюбальных или дpy rих локальных переменных. Но с технической точки зрения это вполне допустимо в Pythоп. Чтобы посмотреть, что при этом происходит, введи- те в файловом редакторе приведенный ниже код и сохраните ero в файле saтeNaтe.py. def spam () : О eggs = r spam local' print(eggs) # выводится строка 'spam local'
Функции 105 def Ьасоп() : . eggs '" 'Ьасоп local' print(eggs) # выводится строка 'bacon local' spam() print(eggs) # выводится строка 'Ьасоп local' . eggs '" 'global' Ьасоп ( ) print(eggs) # выводится строка 'global' Выполнив эту nporpaMМY, вы должны получить следующий вывод. Ьасоп local spam local Ьасоп local global В этой nporpaMMe существуют фактически три разные переменные с одним и тем же именем eggs, что может сбивать с толку. Перечислим эти неременные. . Переменная eggs, которая существует D локальной области видим сти, коrда вызывается функция spam () . . Переменная eggs, которая существует в локальной области видим сти, коща вызывается функция bacon ( ) . . Переменная eggs, которая существует в rлобальной области видимости. Тот факт, что все три независимые переменные имеют одно и то же имя, южет сбивать с толку, если вы хотите отслеживать, какая из них исполь- зуется в любой заданный момент времени. Именно поэтому старайтесь из. беrать использования одних и тех же имен переменных D разных областях видимости. ИНСТРУЦИЯ global Если возникает потребность изменить в коде функции rлобальную пере- Iенную, используйте инструкцию global. Например, наличие инструкции ;lobal eggs в начале функции сообщает PytllOIl следующее: "В этой функ- ции имя eggs ссылается на rлобальную переменную, поэтому создавать ло- кальную переменную с таким же именем не следует". Введите в файловом редакторе следующий код и сохраните ею в файле samPNaтe2.py. def spam() : О global eggs . eggs '" 'spam I c:.nnc:: = 1,,1 ("n.::.1 I
116 r лава 3 spam () print(eggs) При выполнении этой проrpаммы завершающий вызов функции print () должен вывести следующий текст: spaт Переменная eggs объявлена как rлобальная в начале функции зрат () О, и поэтому. коrда eggs присваивается зпачение I spaт I О, эта операция вы- полняется по отношению к rлобальной переменной eggs. Никакая локаль- ная переменная eggs не создается. Суще(твуют четыре правила, позволяющие (удить о том, В какой облас- ти видимости находится переменная локальной или rлобальной. 1. Если переменная используется в I:лобальной области видимости (т.е. вне какой-либо функции), то она вcerдa яВJIЯется rлобальной пере- менной. 2. Если переменная была объявлена в функции с использованием ин- струкции global. то она является rлобальной. 3. В противном случае, если переменная используется в операции при- сваивания в функции, то она является локальной. 4. Но если переменной ниrде в функции не присваивается значение, то она является rлобальной. Рассмотрим пример проrраммы, который поможет вам усвоить эти пра- ВИла. Введите в файловом редакторе приведенный ниже код и сохраните ею в файле sатеNатеЗ.ру. def spam(): . global eggs eggs = 'зрат' # это rлобальная переменная def Ьасоп(): . eggs = 'Ьасоп' # это локальная переменная def ham(): . print(eggs) # это rлобальная переменная eggs = 42 # это rлобальная переменная зрат( ) print(eggs) В функции spam () переменная eggs l'Лобальная, поскольку в начале функции для eggs используется инструкция global О. В функции Ьасоп ()
Функции 107 переменная eggs локальная, поскольку в этой функции она вводится с по-- мощью операции присваивания .. В функции ham () . переменная eggs rлобальная, поскольку в этой функции для нее отсутствует инструкция global и она не вводится с помощью операции присваивания. Выполнив проrрамму saтeNaтe3,py, вы должны получить следующий вывод: spam в функции любая переменная будет либо всеrда rлобальной, либо всеrда локальной. Не может быть такоro, чтобы в одной и той же функции перемен ная использовалась сначала как локальная, а впоследствии как rлобальная. пpu.м,ечаuие Если вы хотите u.мemъ возможuост:ь 'IL1.Мe'НЯmъ с no.м.О?«ъю кода фуUК1,!,ии 31tа'Ч,euue, храuящееся в елобалъuой nсремеииой, mo npuмeuитe к этой nepемеииой ииструк- 1,!,ию global. Если вы попытаетесь использовать в функции локальную переменную до Toro, как присвоите ей какое-либо значение, то Python выведет сообщение об ошибке. Чтобы в этом убедиться, введите в файловом редакторе следукr щий код и сохраните ею в файле saтeNaтe4.frY. def арат(): print(egga) # ERROR! О eggs = 'spam local' . eggs = 'global' spam() Выполнив эту проrрамму, вы получите следующее сообщение об ошибке. Traceback (most recent call last): File "C:/test3784.py", Нпе 6, iп <module> spam() File "С:/tеstЗ784.ру", line 2, in spam print(eggs) # ERROR! UnboundLocalError: local variable 'eggs' referenced before аssigпmепt Эта ошибка обусловлена тем, что PytllOn, обнаружив присваивание пере- менной eggs значения в функции spam () О, предполаrает, что это локальная переменная. Однако, поскольку функция print (eggs) выполняется до Toro, как переменной eggs присваивается какое-либо значение, в момент се вы- зова такой l1среМСIIНОЙ не существует. В этой ситуации PytllOn не будет пы- таты:я использовать одноименную rлоБмыlюю переменную eggs ..
108 r лава 3 ФУНКЦИИ как Нчерные ящики Н Зачастую все, что вам нужно знать о функции, TO какие входные данные 'па- раметры) ей следует предоставить и каково ее выходное значение. Вам не BcerAa нужна обременять себя знанием Toro, как в действительности работает ее код. Если вы применяете к функциям такой высокоуровневый подход, то можно сказать; что вы рассматриваете любую функцию как "черный ящик". Эта идея имеет фундаментальное значение для cOBpeMeHHoro проrраммиро вания. В последующих rлавах вы познакомитесь с некоторыми модулями, которые содержат функции, написанные друrими людьми. Если вы любознательны, то може те заrлянуть в их исходный код, однако для Toro, чтобы использовать ти функции, вам вовсе не обязательно знать их внутреннюю структуру. А поскольку написание функций, в которых rлобальные переменные не используются, только приветствует- ся, вам, как правило, не приходится беспокоиться относительно тoro, что код тих функций будет нежелательным образом взаимодействовать с остальным кодом ва- шей nporpaMMbl. Обработка искпючений На данном этапе возникновение ошибки, или ис"лючения, в вашей про-- rрамме на Pyt}lOn означает крах проrpаммы, Т.е. ее аварийное завершение. Однако для реальных проrрамм такое поведение недопустимо. Поэтому в них используются средства, позволяющие обнаруживать ошибки, обраба тывать их и после этою продолжать выполнение nporpaMMbI. В качестве npимера рассмотрим nporpaMМY, в которой возникает ошибка "деление на О". Введите в файловом редакторе следующий код и сохраните ею в файле zeroDivide.frY. def spam(divideBy) : return 42 / divideBy print (spam(2)) print (spam(12)) print (эраm(О)) print (spam(l)) Мы определили функцию spam ( ) , предоставили ей параметр, а затем попытались вывести на экран возвращаемое этой функцией значение при различных параметрах, чтобы I10наблюдать, что при этом происходит, За пустив на выполнение этот код, вы получите следующий вывод. 21.0 3.5
Функции 109 Traceback (most recent call last): Fi1e "C:/zeroDivide.py", line 6, in <module> print (sраm (О) ) File "C:/zeroDivide.py", line 2, in spam return 42 / divideBy ZeroDivisionError: division Ьу zero Сообщение об ошибке ZeroDi visionError выводится всякий раз, коrда предпринимается попытка разделить число на О. По указанному в сообщ нии об ошибке номеру строки можно леrко определить, что виновницей является инструкция return функции spam (). Ошибки можно обрабатывать с помощью инструкций try И except. Код, относительно KOToporo у вас есть подозрения, что он может привести к ошибке, помещается в блок try. В случае возникновения ошибки выполне- ние проrраммы передается в начало блока except. Вы можете поместить предыдущий код в блок инструкции try и орraни ;ювать обработку соответствующей ошибки в блоке инструкции except. def spam(divideBy): try: return 42 / divideBy except ZeroDivisionError: print('Error: Invalid argument.') print(spam(2) ) print (sраm(12)) print(spam(O)) print (spam(1)) Коrда в коде, номещенном в блок try, возникает ошибка, выполнение проrраммы немедленно нереходит к коду в блоке except. После ВЫПОЛllе ния это ['о кода дальнейшее выполнение проrраммы продолжается, как обычно. Вывод предыдущей проrpаммы должен быть таким. 21.0 3.5 Error: Invalid argument. None 42.0 Обратите внимание на то, что ошибки, которые MoryT возникать при вызовах функций в блоке try, также будут перехватываТl)СЯ. Рассмотрим следующую проrpамму, в которой вызовы функции spam () помещены в блок try.
11. r лава 3 def spam(divideBy): retиrn 42 / divideBy try: print(spam(2)) print (spam(12)) print(spam(O)) print(spam(l)) except ZeroDivisionError: print('Error: Invalid argument.') Вывод этой проrраммы должен быть таким. 21.0 3.5 Error: Invalid argument. Инструкция print (spam(l)) не была выполнена IIO той причине, что после выполнения кода в блоке except воаврата в блок try не происходит. Вместо этоrо далее, как обычно, выполняются инструкции, следующие за блоком except. Короткаll nporpaMMa: уrадай чиспо Те небольшие примеры, которые приводились до сих пор, были вполне приrодны для знакомства с базовыми понятиями, но теперь настал подхо- дящий момент показать вам, как на основе Toro, что вы уже успели изучить, можно составить более интересную проrpамму. В этом разделе я продемон- стрирую вам простую проrрамму, реализующую иrру в yrадывание чисел. Запустив ее, вы получите примерно следующий вывод. Мною задумано число в интервале от 1 до 20. Попробуйте ero уrадать. Ваш вариант: 10 Предложенное число меньше задуманноrо. Ваш вариант: 15 Предложенное число меньше задуманноrо. Ваш вариант: 17 Предложенное число больше задуманноrо. Ваш вариант: 16 Верно! Количество попыток: 4
Функции 111 Введите в файловом редакторе следующий код и сохраните ею в файле gиessTheNuтber. ру. # Ира в уадывание чисел. import rапdоm secretNumber = random.randint(l, 20) рriпt('Мною задумано число в интервале от 1 до 20. Попробуйте eo yaдaTЬ.') # Предоставить ироку 6 попыток для уадывания числа. for gиessesTaken in range(l, 7): print ( 'Ваш вариант:') gиess = int(inpиt()) if gиess < secretNumber: рriпt('Предложенное число меньше задуманноо.') elif gиess > secretNumber: рriпt('Предложенное число больше задуманноо.') else: break # Соответствует правильному ответу! if gиess == secretNиmber: print('BepHo! Количество попыток: ' + str(gиesseSTaken)) else: print('HeT. Было задумано число' + str(secretNиmber)) Проанализируем этот код с caMol'o начала, строка за строкой. # Ира в уадывание чисел. import random secretNиmber = random.randint(l, 20) Первая строка это строка комментария, содержащая описание Ha значения проrраммы. Далее проrрамме необходимо импортировать MO дуль random, чтобы иметь возможность использовать функцию random. randint () для rенерирования случайноrо числа, которое поль:юватель ДОk жен yraдaTb. Возвращаемое функцией значение, которое будет представ лять собой целое число в диапазоне от 1 до 20, сохраняется в переменной secretNumber. рriпt('Мною задумано число в интервале от 1 до 20. Попробуйте eo уNдать.') # Предоставить ироку 6 попыток для уадывания числа. for gиessesTaken in range(l, 7): print ('Ваш вариант:') gиess = int(inpиt()) Проrрамма сообщает ю'року, что она задумала секретное число, и IIpe доставляет возможность yraдaTb ero не более чем за шесть попыток. Код,
112 r лава 3 который предлаrает ввести число и осуществляет проверку этоrо числа, помещен в цикл for, выполняющий не более шести итераций. Первое, что происходит в цикле, это ввод иrроком пробноrо числа. Поскольку Функ ция inpu t () возвращает строку, ее возвращаемое значение передается неlt средственно функции in t ( ) , которая преобразует строку в целочисленное значение. Это значение сохраняется впеременной guess. if guess < secretNumber: рriпt('Предложенное число меньше задуыанноо. ') elif guess > secretNumber: рriпt('Предложенное число больше задуманноо.') в этих нескольких строках кода пробное число сравнивается с ceKp ным и проверяется, больше ли первое из них, чем второе, или меньше. В обоих случаях на экран выводится соответствующая подсказка. else: break # Соответствует правильному ответу! Если оказывается, что пробное число одновременно не больше и не меньше ceKpeтHoro числа, то это означает, что оно и есть секретное число, и в таком случае инструкция break осуществляет выход из цикла for. if guess == secretNиmber: print('BepHo! Количество попыток: I + str(guessesTaken)) else: print('HeT. Было задумано число' + str(secretNиmber)) Располаrающаяся вслед за циклом for инструкция if/else проверяет, ЯВJIяется ли введенное пользователем число правильным, и выводит на экран соответствующее сообщение. В обоих случаях проrрамма отобра жает переменную, содержащую целочисленное значение (guessesTaken и secretNumber). Поскольку эти значения необходимо конкатенировать со строками, они передаются функции str ( ) , которая возвращает полученные числа в виде строк. Теперь эти строки можно конкатенировать с помощью операторов + и передать результирующую (."Троку функции print (). Реэюме Функции это основной способ разбиения кода на лоrические rpуппы. Поскольку переменные в функциях существуют в собственных локальных областях видимости, код одной функции не может непосредственно воз- действовать на значения переменных друroй функции. Эти оrраничения, налаrаемые на возможность изменения значений переменных, MOryr быть полезными при отладке кода.
ФУНКЦИИ 113 Функции ЯВJIяются великолепным средством орrанизации кода. Можете предстаВJIЯТЬ себе любую функцию как "черный ящик". Для вас имеет зна- чение лишь то, какие входные данные необходимо предоставить функции в качестве apryмcHToB и какое значение она возвращает, а также то, что код функции не может воздействовать на переменные в коде ДРУI'ИХ функций. В примерах, приведенных в предыдущих l'Лавах, единственная ОUlибка моrла приводить к краху проrраммы. В этой rлаве вы изучили инструкции try и except, обеспечивающие дальнейшее выполнение проrраммы, даже если в ней возникла ошибка. Это позволяет вам обеспечить устойчивую работу (:во- их проrрамм при возникновении в них распространенных ошибок. Контропьные вопросы 1. Что дает использование функций в проrраммах? 2. Коrда именно выполняется код функции: коrда она определяется или коrда вызывается? 3. С помощью какой инструкции создаются функции? 4. Чем отличается определение функции от ее вызова? 5. Сколько rлобальных областей видимости может иметь проrрамма на языке Pythоп? Сколько локальных? б. Что происходит с переменными, находящимися в локальной области видимости, при возврате из функции? 7. Что такое возвращаемое значение? Может ли возвращаемое значение быть частью выражения? 8. Каково возвращаемое значение функции, если в ней отсутствует ин струкция return? 9. Как заставить переменную в функции с(ыатьсяя на rлобалl)НУЮ псре-- менную? 10. Что такое тип данных None? 11. Что делает инструкция import аrеаllуоurреtsпamеdеriс? 12. Если бы у вас была функция Ьасоп (), содержащаяся в модуле spam, то как бы вы ее вызвали после импортирования этоrо модуля? 13. Как можно предотвратить аварийное завершение проrраммы при возникновении в ней ошибки? 14. Какой код помещается в блок try? Какой код помещается в блок except? Учебные проекты Чтобы закрепиТl. получеппые знания на нрактике, паllишите проrрам- мы для нредложеНIIЫХ lIиже задач.
114 r лава 3 nооеIlО'''Feл..оm КОЛЛImI" Напишите функцию collatz (), принимающую один параметр: number. Если number четное число, функция collatz () должна вывести на экран и возвратить значение number / / 2. Если же пшnbеr нечетное число, то функ- ция должна вывести на экран и возвратить значение 3 * number + 1. После этоrо напишите проrpaмму, которая предлarает поль:юnaтелю B сти целое число, а затем последовательно вызывает функцию collatz () для этоrо числа и значений, возвращаемых очередным вызовом этой функции, пока на каком-то fl'апе не будет 80звращено значение 1. (Любопытно отме- тить, что, независимо от выбора наЧaJlьноrо числа, вы все равно рано или поздно получите 1! Даже математики не MOryr объяснить, почему так пp<r исходит. Числовая последовательность, которую вы исследуете с помощью этой проrраммы, называется nоследовате.л:ь1tост'Ью Ко.л.лаm'Ца 1 и иноrда харак- теризуется как "простейшая из неразрешенных пр06лем математики".) Не забывайте о том, что возвращаемое функцией inpиt () значение нуж- дается в преобразовании в целое число с помощью функции in t ( ) , иначе это будет строковое значение. Подсказка. Условие четности значения number % 2 == О, условие Не- четности number % 2 == 1. Примерный вывод этой проrpаммы показан ниже. Enter nurnber: 3 10 5 16 8 4 2 1 nро.ерк" KOppeIC'lllO". "011" Добавьте в предыдущий проект инструкции try и except с целью обнару- жения ввода поль:ювателем нецелочисленных значений. Обычно при пере- даче функции int () строки, представляющей нецелочисленное значение, как, например, при вызове int ('рирру'), reнерируется ошибка ValиeError. Поместите в блок except код, который выводит для пользователя сообще- ние о том, что требуется ввод целоrо числа. 1 См. https: / /ru.wikiреdiа.оrg/wiki/rипотеэаКоллатца. Пршwеч. ред.
списки Еще одна тема, с которой вам обязательно следует познако- миться, прежде чем приступать к написанию собственных серьезных проrрамм, это списки и родственный им тип данных: кортежи. Списки и кортежи MOryr содержать более чем одно значение, что упрощает написание проrpамм, об.. раба:rывающих большие объемы данных. А поскольку спи- ски сами MOryr содержать друrие списки, вы сможете использовать их для <:оздания иерархических структур данных. В этой l'JIaBe приведены основные сведения о списках. Вы также узнае- те о методах, которые представляют собой функции, СВЯ:Jaнные с данными апределенноrо типа. Затем мы вкратце рассмотрим такие типы данных, как кортежи и строки, и проанализируем, как они связаны ('О ('писками, аналоraми которых они являются. В следующей rлаве речь пойдет о ДРУl'ИХ типах данных словарях. Что такое список Список это значение, которое представляет собой коллекцию значе- ний, образующих упорядоченную последовательность. Термин r.nис'/{;овое значeuueотносится к списку как единому целому (значение KOToporo может сохраняться в переменной или передаваться функции нодобно значению любоrо дрyrorо типа), а не к отдельным значениям, которые в нем coдep жатся. Например, список может иметь следующее значение: ['cat', 'bat', 'rat', 'elephant') Подобно тому как строковы е значения заключаются в кавычки, пока- зывающие, rде начинается и заканчивается строка, список заключается в
116 rЛQВQ 4 квадратные скобки, []. Значения, образующие список, называются элемен- тами списка. Элементы списка разделяются запятыми. Введите в интерактивной оболочке следующие команды. »> [1, 2, 3] [1, 2, 3] »> ['cat', 'bat', 'rat', 'elephant'] ['cat', 'bat', 'rat', 'elephant'] »> ['hello', 3.1415, True, Нonе, 42] ['hello', 3.1415, True, None, 42] 8»> зраа = ['cat', 'bat', 'rat', 'elephant'] >>> 8palll. ['cat', 'bat', 'rat', 'elephant'] Здесь lIеременной эрат . присвоено лишь одно значение (писковое. Но это значение само содержит дрyrие значения. Значению [] соответству- ет пустой список, в котором отcyrствуют элементы, аНaJIоrично тому, как значению ' , соответствует пустая строка. IIotryп IC оrllеЛ6Н... ,ле.енr". tп.tlC" t пО.ОЩ61О .Hlle"to. Предположим, у вас есТь список [' cat', 'bat', 'rat', 'elephant'], (:0- храненный в переменной spam. pyt 11011 интерпретирует выражение эрат [О] как 'са t ' , выражение spam [ 1] как 'Ьа t' И т.д. Целое число, которое ука- зывается в квадратных скобках после имени списка, называется индексом. Первому из значений, входящих в список, соответствует индекс О, второ- му индекс 1, третьему индекс 2 и т.д. На рис. 4.1 представлен список, значение Koтoporo присвоено переменной spaт, И показано, какие iлемен- ты соответствуют различным индексным выражениям. spam ["cat", "bat", / I spam[O] spam[l] "rat", "elephant"] "- spam[2) sраm[З] Рис. 4. J. Список, хранящиi1ся в переменнон зрат, и индексные выражения, соответствующие ero отдельным элементам Введите в интерактивной оболочке следующие выражения. Начните с присваивания списка переменной spaт. »> SpaIII. = ['cat', 'bat', 'rat', 'elephant'] > » Spalll. [ О ] 'cat' > » epalll. [ 1 ] 'bat' > » Spalll. [ 2 ]
Списки 117 'rat' »> spu[З] 'elephant' »> ['cat', 'bat', 'rat' , 'elephant'] [З] 'elephant' . >>> 'Hello ' + spam[O] . 'Неllо cat' >>> 'ТЬе ' + spaш[1] + ' ate the ' + spam[O] + '.' 'The bat ate the cat.' Обратите внимание на то, что выражение 'НеНо ' + spam [О] . вычис ляется как 'Не Но , + 'cat', поскольку значением spam [О] является строка 'cat '. Таким образом, конечным результатом вычисления данноrо выраже- ния ЯWlяется строка' НеНо cat' .. Если вы попытаетесь исполь:ювать индекс, значение KOToporo превыша- ет количество элементов в списке, то PythOH выведет сообщение об ошибке IndexError. »> Зpalll. - ['cat', 'bat', 'rat', 'elephant'] >>> ЗpaИI[10000] Traceback (most recent call last): File "<pyshell#9>", Нпе 1, in <module> spam[lOOOO] IndexError: list index out of range Индексы MOryr принимать только целочи<:ленные значения (не вещес твенные). В следующем примере возникает ошибка ':'ypeError. »> ЗpaиI - ['cat', 'bat', 'rat', 'elephant'] » > 8раш. [ 1 ] 'bat' »> 8palll. [1. О] Traceback (most recent call last): File "<pyshell#13>", liпе 1, in <module> spam [ 1 . О] TypeError: list indices must Ье integers, not float »> spalll.[int(1.0)] 'bat' Элементы списков сами MOryr быть списками. Доступ к значениям в Ta ких списках списков осуществляется с помощью нескольких индексов. >>> spam = [[ 'cat', 'bat'], [10, 20, ЗО, 40, 50]] > » sраш. [ О ] [ 'cat " 'bat'] »> spaш[О] [1] 'bat'
118 r лова 4 >>> ераш[1] [4] 50 Первый индекс указывает, какой элемент-список следует использовать, а второй к значению Kaкoro элемента в этом списке осуществляется доступ. Например, для выражения spam [О] [1 J будет выведено значение 'Ьа t ' , Т.е. второе значение в первом списке. Если вы используете только один индекс, то проrрамма выведет в качестве значения полный спи<:ок, cooTBeтcTBYкr щий данному индексу. ОrР8ЦtlrеЛ6н.,е 8HllelCt., Несмотря на то что отсчет индексов начинается с нуля, в качестве индек- сов разрешается использовать отрицательные значения. Отрицательному значению 1 соответствует последний элемент списка, значению 2 пред- IIоследний и т.д. Введите в интерактивной оболочке следующие команды. »> ерам - ['cat', 'bat', 'rat', 'elephant'] >>> epaм[1] 'еlерhапt' »> sрam[З] 'bat' »> 'The ' + зраш[1] + 1 is afraid of the 1 + spaш[З] + 'Te elephant is afraid of the bat.' , , пол,.,еН8е 9t1tr11 tп8tICtI t пOMOIЦ61O tpeJtI В то время как с помощью индексов можно извлекать из списка одиноч- ные элементы, срезы позволяют получать сразу несколько значений в ВИДе HOBoro списка. Срез списка обозначается, как и при индексном доступе, квадратными скобками, однако в скобках указываются два индекса, разде- ленные двоеточием. Обратите внимание на различие между индек(:ами и <:резами: . spam [2] список с индексом (одно целое число); . spam [ 1 : 4] список со срезом (два целых числа). Первое целое число в срезе это индекс, с Koтoporo начинается cpe:J. Второе целое число это индекс, который обозначает конец среза, но сам в <:рез не включается. Значением среза является новый список. Введите в интерактивной оболочке следующие команды. »> spam = ['cat', 'bat', 'rat', 'elephant'] »> ерам[О:4] ['cat', 'bat', 'rat', 'elephant']
Списки 119 >>> sраш.[l: З] [ I Ьа': 1, I rat 1] >>> ераш[О:11 [ I cat 1, I bat 1, 'rat'] Допускается сокращенная запись среза с пропуском одноrо или двух ин- дексов по обе стороны двоеточия. Отсyrствующий первый индекс равноси- лен использованию значения О, Т.е. соответствует началу списка. Отсутству- ющий второй индекс означает расширение среза до конца списка. Введите в интерактивной оболочке следующие команды. »> spam = ['cat', 'bat', 'rat', 'elephant'J >>> spam[:2] [ 'cat " I bat' ] >>> spaJD[l:] ['bat', 'rat', 'elephant'] »> SPaJD[:] ['cat', 'bat', 'rat', 'elephant'] получен.е /lЛ.Н., t".tKII t "0.ОЩ61О ФУ.К".. leп ( ) Функция lеп () возвращает количество значений, содержащихся в пере- данном ей списке, а в случае передачи ей CTpoKoBoro значения количес- тво символов в строке. Введите в интерактивной оболочке следующие ко- манды. »> sраш. - ['cat', 'dog', 'шооее'] >>> len (sраш.) 3 ".енен.е JНllчен.. . t".tKIIX t "0.ОЩ61О .H/leKtO' Обычно слева от оператора присваивания располarается имя перемен- ной, например spam = 42. Однако для изменения значения в списке, харак- теризующеrося определенным индексом, можно использовать индексацию. Например, инструкция spam[l] = 'aardvark' означает следующее: "Назна- чить элементу с индексом 1 в списке spam строковое значение' aardvark'". Введите в интерактивной оболочке следующие команды. »> spam - ['cat', 'bat', 'rat', 'elephant'] »> spaJD[l] = 'aardvark' >>> spam [' cat', 'aardvark', 'rat', 'elephant'] »> spaJD[2] = ераш[1] >>> spam
120 rЛQВQ 4 l' cat " I aardvark', 'aardvark', 'elephant'] »> ераш[1] - 12345 > » еpalll. ['cat', 'aardvark', 'aardvark', 12345] Конкатена..,.. . ".л.ка..,.. tn.tKO. с помощью оператора + можно объединить два списка в новый список аналоrИЧ1l0 тому, как с помощью этоrо же оператора можно объединить два строковых значения в одну строку. Кроме Toro, умножая список с помощью оператора * на целое число, можно повторить список заданное количество раз. Введите в интерактивной оболочке следующие команды. »> [1, 2, 3] + ['А', 'В', 'С'] [1, 2, 3, 'А', 'В', 'С'] »> ['Х', 'У', 'Z'] * Э ['Х', 'У', 'Z', 'Х', 'У', 'Z', 'Х', 'У', 'Z'] »> ераш - [1, 2, Э] »> еpalll. - ераш + ['А', 'В', 'С'] > > > Sp8111. [1, 2, 3, 'А', 'В', 'С'] Удален.е зна"ен.. .3 tn.tKa t пО.ОЩ61О ..прУК",.. del Инструкция del удаляет из списка значение с заданным индексом. Все значения, нахОДЯЩиеся после удаленноrо, сдвиraются к началу списка на одну позицию. Например, введите в интерактивной оболочке следующие команды. »> еpalll. - ['cat', 'bat', 'rat', 'e1ephant'] »> d_1 еpalll. [2] »> еpalll. ['cat', 'bat', 'elephant'l >>> de1 ераш[2] »> ераш [ I cat 1, 'bat'] Инструкция del также может удалять простые переменные. Если вы по- пытаетесь использовать удаленную переменную, то получите сообщение об ошибке NameError, поскольку такой переменной больше не существует. На практике вам почти никоrда не придется удалять простые переменные. Основное назначение инструкции del удаление значений И3 списков.
Списки 121 Работа со списками у тех, кто впервые приступает к написанию проrрамм, возникает со-- блазн создавать множество отдельных переменных для rруппы родствен- ных значений. Например, если бы я захотел сохранить имена своих котов и кошек, то Mor бы это сделать с помощью примерно Taкoro кода. catName1 catName2 catName3 catName4 catName5 catName6 'Zophie' 'pooka' 'Simon' 'Lady Macbeth' 'Fattail' 'Miss Cleo' (Клянусь, на самом деле у меня не так уж и Mlloro кошек в доме.) Однако этот способ далеко не самый удачный. К примеру, если количество кошек изменится, проrрамма не сможет сохранить имен больше, чем имеется переменных. К тому же в ПРOl'раммах этоrо тина часто наблюдается повто- рение одних и тех же или почти одинаковых фраrментов кода. Посмотри- те, как часто дублирует(:я код в следующей I1pOrpaMMe, которую вы должны ввести в файловом редакторе и сохранить в файле allMyCatsl.py. print('Enter the пате of cat 1: 1) catName1 = input() print('Enter the пате of cat 2:' ) catName2 = input() print('Enter the пате of cat 3:' ) саtNаmеЗ = input() рriпt('Епtеr the пате of cat 4: ') catName4 = input() рriпt('Епtеr the пате of cat 5:' ) catName5 = input() print('Enter the пате of cat 6: ') catName6 = iпрut() print('The cat names are: 1) print(catName1 + , , + catName2 + , , + саtNаmеЗ + , , + catName4 + , , + catName5 + , , + catName6 ) Вместо множества однотипных переменных лучше использовать одну переменную, содержащую список. Ниже приведен пример улучшенной версии проrраммы allМyCatsl.py. В этой новой версии используется всею один список, в котором может храниться любое количество имен, введен- ных пользователем. Откройте в файловом редакторе новое окно, наберите в нем приведенный ниже текст и сохраните ею в файле allМyCats2.py.
122 r лава 4 catNames = [] while True: print('Enter the пате of cat ' + str(len(catNames) + 1) + , (Or enter nothing to stop.): ') пате = input ( ) i f паmе == ": break catNames = catNames + [пате] # list concatenation рriпt('Тhе cat патез are:') for пате in catNames: print(' , + пате) Запу(тив эту проrрамму, вы lIолучите следующий вывод. Enter the пате of cat 1 (Or enter nothing to stop.) : Zophie Enter the пате of cat 2 (Or епtеr поthiпg to stop.) : Pooka Enter the пате of cat 3 (Or епtеr поthiпg to stop.) : Simon Enter the пате of cat 4 (Or enter nothing to stop.) : Lady Macbeth Enter the пате of cat 5 (Or enter nothing to stop.) : Fattail Enter the пате of cat 6 (Or enter nothing to stop.) : Miss Сlео Enter the пате of cat 7 (Or enter nothing to stop.) : Вот все кошачьи имена. Zophie Pooka Simon Lady Macbeth Fattail Miss Сlео Список дает то преимущество, rro теперь ваши данные сосредоточены в одной структуре. а самапроrpамма стала намноro более rибкой по cpaBH нию с тем ее вариантом, в котором ИСПОЛЬЗОВaJIось множество однотипных Ilеременных. Iftп0ll6J081188e Ц8К.08 ror (О tп8tKII"8 Из ['лавы 2 вы узнали о том, как использовать циклы for для выполнения одноro и TOI'O же блока кода определенное количество раз. С технической точки зрения цикл for выполняет блок кода по одному разу ДЛЯ каждоro значения из списка или подобной ему структуры данных. Например, если выполнить код
Слиски 123 for i in range(4): print(i) то еro вывод будет выrлядеть так: О 1 2 3 Это происходит потому, что возвращаемое вызовом функции range ( 4 ) зна чение Python трактует как список [О, 1, 2, 3]. Поэтому предыдущий вы- вод можно воспроизвести с помощью следующей nporpaмMbI. for i in [О, 1, 2, З]: print(i) Данный цикл MHoroKpaTHo выполняет блок кода, используя на каждой итерации переменную i, которая принимает последовательный ряд значе- ний из списка [О, 1, 2, 3]. Примечаuие ИспОЛМУЯ в этой к'Н,ии выражение "тип да'Н,"ых "аподoбue спИС1Са", я подразумf'? ваю даЮl'Ш!, дм которЫХ существует терМИ'Н, "последово:те.л:ь'Н,оcm'Ь". Од'Н,ако з'Н,a:m'Ь техиИ'ЧеС1Сое onредeлeuие эmozо терми'Н,а вам вовсе необя.зо:те.л'Ь'Н,О. Один из часто применяемых в PytllOIlприемов использование выра- жения range (len (someList)) С циклом for для итерирования по индексам в списке. Например, введите в интерактивной оболочке следующие ко- манды. »> supplies - ['репе', 'staplers', 'fl...thrower8', 'binders') »> for i in range(len(supplies»: print('Index ' + str(i) + ' in supplie8 is: I + supplies[i) Index О in supp1ies is: pens Index 1 in supplies is: staplers Index 2 in supp1ies is: flamethrowers Index 3 in supp1ies is: binders Использовать выражение range (len (supplies)) в представленном выше цикле for очень удобно, поскольку код в цикле может иметь доступ к индексу (в виде переменной i) и к значению по этому индексу (предо- ставляемому выражением supplies [i] ). Что самое rлавное, выражение
124 r лава 4 range (len (supplies) ) обеспечивает итерирование по всем индексам списка supplies, независимо от количества содержащихся в нем значений. Оператор.. in 8 not in Определить, находится ли каколибо значение в списке, можно с по-- мощью операторов in и not in. Как и дрyrие операторы, in и not in исполь- эуются в выражениях, соединяя два значения: То, поиск KOToporo выполня ется в списке, и список, в котором это значение может находиться. Резуль- татом вычисления этих выражений ЯВJIяется булево значение. Введите в интерактивной оболочке следующие команды. »> 'howdy' in ['hello', 'hi', 'howdy', 'heyas'] True »> еpalll. = ['hello', 'hi', 'howdy', 'heyas'] »> 'cat' in ерам False »> 'howdy' not in Spalll. False »> 'cat' not in еpalll. True В качестве при мера рассмотрим проrpамму, которая предлаrает пользо-- вателю ввести имя CBoero домашнеro питомца и проверяет, содержится ли оно в списке pets. Откройте в файловом редакторе новое окно, введите в нею следующий код и сохраните еro в файле туРш.ру. mypets = [' Zophie 1, 'Pooka 1, I Fattail '] print('Enter а pet пате: ') пате = iпрut () if пате not in mypets: print('I do not have а pet named I + пате) else: рrint(пате + I is ту pet.') Вывод этой проrраммы может выrлядеть примерно так. Enter а pet пате: Footfoot 1 do not have а pet named Footfoot 'РlOк t (руппо.... пp8tJa8.aH8e. Используя трюк с еруnповы.м присваивание.м, можно быстро присвоить значения ряду переменных в одной строке кода. Итак, вместо выполнения последовательности ИНСТРУКЦИЙ
Списки 125 »> cat - ['fat', 'black', 'loud'] »> .ize = cat[O] »> color = cat[l] »> disposition = cat[2] можно оrраничиться следующим КОДОМ: »> cat = ['fat', 'black', 'loud'] »> size, color, disposition = cat Число переменных должно совпадать с длиной списка, иначе Python вы- ведет сообщение об ОJIlибке. »>cat= ['fat', 'black', 'loud'] »> size, color, disposition, паше = cat Traceback (most recent ca11 1ast): File "<pyshell#84>", line 1, in <modu1e> size, co1or, disроsitiоп, пате = cat ValueError: need more thап 3 va1ues to unpack Комбинированные операторы присваивания в ходе присваивания значения переменной справа от оператора присва- иван ия часто используется эта же переменная. Например, если перемсн- ной spam необходимо присвоить значение 42, а затем увеличить cro на 1, то ЭТО можно сделать с помощью следующеrо кода. »> Spalll. - 42 »> spaш - spam + 1 »>8palll. 43 Однако, используя комбинированный оператор присваивания +=, мож но HeMHoro сократить этот код: >>> sраш. - 42 > > > 8palll. += 1 >>> 8palll. 43 Комбинированные операторы присваивания существуют для онерато- ров +,, *, / и % (табл. 4.1).
126 r лава 4 Тамица 4.1. Комбинированные опsрации присваиваНИI "РИС80ивание spam = spam + 1 spam = spam 1 spam= spam * 1 spam = spam / 1 spam= spam % 1 КомбинированноеnрисваИ80ние spam+= 1 spam = 1 spam *= 1 spam /= 1 spam %= 1 Кроме Toro, оператор += может при меняться для конкатенации, а опе- ратор *= для репликации строк и списков. Введите в интерактивной обо- лочке следующие команды. »> зраа - 'Hello' »> зраа += ' world!' >>> зраа 'НеНо world!' >>> bac::on = [' Zophie' ] »> bac::on *= 3 >>> bac::on ['Zophie', 'Zophie', 'Zophie'] Метод... Meтoд это то же самое, что и функция, но он вызывается для значения. Например, если список хранится впеременной spam, то вы можете вызвать для нее метод index () списка (о чем далее будет рассказано более подроб- но): spam. index (' hello'). Метод указывается после значения и отделяется от Hero точкой. Каждый тип данных имеет собственный набор методов. Так, ДЛЯ списков предусмотрен ряд полезных методов, позволяющих выполнять поиск, до- бавление, удаление элементов и дрyrие манипуляции со значениями, обра- зующими список. по.,1t ,НII"ен.. . 'п.'ltе , п080Щ61О 8е'01l1l iпdex ( ) Списки имеют метод index () , который принимает значение и возвра- щает ero индекс, если оно содержится в списке. Если указанное значение OTcyrcTByeT в списке, то PythOI1 rенерирует ошибку ValueError. Введите в интерактивной оболочке следующие команды. »> зрам = ['hello', 'hi', 'howdy', 'heyas'] »> sраш.indех('hеllо') О
Списки 127 >>> spaJD. index ( , Ьеуае ' ) 3 > > > spaID. index ( 'howdy howdy howdy') Traceback (most recent call last): Fi1e "<pyshe11#31>", liпе 1, in <module> spam.index('howdy howdy howdy') ValueError: 'howdy howdy howdy' is not in 1ist В случае наличия в списке дубликатов данноrо значения возвращается индекс первоrо из элементов, в котором встречается это значение. Введите в интерактивной оболочке следующие команды (обратите внимание на то, что метод index () возвращает 1, а не 3). »> ераш = ['Zophie', 'Pooka', 'Fattail', 'Pooka'] »> spaID.index('Pooka') 1 1I06"аеН8е ,на"еН8. . tп8tOlt t 11080"610 8еТОIIО' append () . iпsert () для добаВJIения новых значений в список используются методы append ( ) и insert (). Введите в интерактивной оболочке следующий код, чтобы вы- звать метод append () для списка, хранящеrося вперсменной spam. »> ерам = ['cat', 'dog', 'bat'} »> spaм.append('moose') »> ераш [ , cat " , dog " 'bat', 'moose'] Здесь вызов метода append () добавляет apryMeHT в КОНец списка. Метод insert () позволяет добавить в список элемент с определенным индексом. Первый apryMeHT метода insert () это индекс для HOBoro значения, авто- рой вставляемое значение. Введите в интерактивной оболочке следую- щие команды. »> ерам - ['cat', 'dog', 'bat'] »> spaм.insert(l, 'chicken') »> SPaID [' cat', , chicken', 'dog'., 'bat 1] Обратите внимание на то, в каком виде записан код: spam. append ( 'moose ' ) и spam. insert (1, 'chicken'), а не spam = spam. append ( 'moose') и spam = spam. insert (1, 'chicken'). Ни метод append () , ни метод inse rt () не пре- доставляют новое значение зрат в качестве возвращаемоrо :Jначения.
120 r лава 4 (В действительности оба метода возвращают значение None, однако вряд ли вы захотите присваивать el'o в качестве новоro значения переменной.) Вместо этоro список изменяется, как rоворят, иа.меcme. Более подробно из менение списка на месте обсуждается в разделе "Изменяемые и неизменя мые типы данных". Методы принадлежат к одному определенному типу данных. Методы append () и insert () являются методами списков и MOryr вызываться только для списков. Вызывать их для дрyrих типов значений, таких, например, как строки или целые числа, нельзя. Введите в интерактивной оболочке сл дующие команды и обратите внимание на появление сообщения об ошибке AttributeError. »> eggs - 'hello' »> eggs.append('world') Traceback (most recent call last): File "<pyshel1#19>", line 1, in <module> eggs.append( 'world') AttributeError: 'str' object has по attribute 'append' >>> bacon - 42 »> bacon.insert(l, 'world') Traceback (most rесепt call last): File "<pyshell#22>", 1ine 1, in <module> bacon.insert(1, 'wor1d') AttributeError: 'int' object has по attribute 'insert' У/lllлен.е ЗНII"ен.й .3 '..'''11 , ПОМОЩ61О ме'О/l1l reшоvе () Методу rernove () передается значение, подлежащее удалению из (:писка, для KOToporo он вызывается. Введите в интерактивной оболочке следую щие команды и обратите внимание на появление сообщения об ошибке. »> ерам - ['cat', 'bat', 'rat', 'elephant'] >>> sрам.reш.оve ('bat') >>> ераш. ['cat', 'rat', 'еlерhапt'] При попытке удалить значение, отсутствующее в списке, будет сrенери- рована ошибка ValиeError. Чтобы в этом убедиться, введите в интерактив- ной оболочке следующие команды. »> ераа - ['cat', 'bat', 'rat', 'elephant'] > > > еpalll. . reш.0V8 ( , ahicken ' ) Traceback (most recent call last): File "<pyshell#ll>", liпе 1, in <module> sраm.rеmоvе('сhiсkеп') ValueError: list.remove(x): х not iп list
Списки 129 Если в списке имеется несколько одинаковых значений, будет удалено лишь то из них, которое встретится первым. Введите в интерактивной uбо- лочке следующие команды. »> spaш - ['cat', 'bat', 'rat', 'cat', 'hat', 'cat'] »> SPalll..ruove( 'cat') » > Spalll. ['bat', 'rat', 'cat', 'hat', 'cat'] Инструкция de 1 используется в тех случаях, коrда вам известен индекс значения, которое вы хотите удалить, а метод remove () коrда известно значение, подлежащее удалению из списка. (орт.ро.ltll ,НII,ен.. . '".'Ile , IIОМОЩ61О меТОДII sort ( ) Для сортировки списков, содержащих числа или строки, используется метод sort () . Введите в интерактивной оболочке следующий код. »> ерам - [2, 5, 3.14, 1, 7] »> Spalll.. sort () »> spaш [7, 1, 2, 3.14, 5] »> 8palll. = ['ants', 'cats', 'dogs', 'badgers', 'elephants'] » sраш..sоrt() >>> sраш. ['ants', 'badgers', 'cats', 'dogs', 'e1ephants'J Чтобы задать сортировку в обратном порядке, следует указать именован ный apryмeHT reverse со значением True при вызове метода sort () . Введите в интерактивной оболочке следующий код. »> spaш.sоrt(reversе-Тrue) >>> SPaIII ['elephants', 'dogs', 'cats', 'badgers', 'ants'J Относительно метода sort () слсдует сделать три замечания. Во-первых, метод sort () выполняет сортировку списка на месте; не нытайтесь ИСПОЛlr зовать ero возвращаемое значение с номощью кода наподобие spaт = spaт. sort (). Во-вторых, невозможно отсортировать список, который содержит oд новременно и числа, и строки, поскольку PytllOIl не знает, как сравнивать разные типы значений между собой. Введите в интерактивной оболочке следующий код и обратите внимание на появление сообщения об ошибке TypeError.
130 r лава 4 »> арам = [1, З, 2, 4, 'Alice', 'ВоЬ'] >>> ерам. sort О Traceback (most recent са11 1ast): Fi1e n<pY5he11#70>n, 1ine 1, in <modu1e> spam.sort() TypeError: unorderable types: str() < int() В-третьих, метод sort () сортирует строки не в алфавитном порядке, а в соответствии с таблицей А.<;СII. Это означает, что буквы в верхнем реrи стре предшествуют буквам в нижнем реrистре. Поэтому, например, буква а будет располаrаться в процессе сортировки после буквы Z. Введите в инт рактивной оболочке следующий код. »> spam = ['Alice', 'ants', 'ВоЬ', 'badgers', 'Carol', 'cats'] >>> арам. sort О »> .рам [1 A1ice', 'ВоЬ', 'Caro1', 'ants', 'badgers', 'cats' J Если вам необходимо отсортировать строку в обычном алфавитном по рядке, то передайте методу sort () именованный apryMeHT key со значением str.lower. »>ерам= ['а', 'z', 'А', 'Z'] »> spam.sort(k_y=str.low_r) »> ераш ['а', 'А', 'z', 'Z'] Это приведет к тому, что метод 50rt () будет обрабатывать все элементы списка так, как если бы они были записаны только с использованием ниж неro реrистра, не изменяя при этом самих элементов. Пример nporpOMMbl: Mogic 8 8011 со списком Используя списки, можно написать rораздо более элеl'антную версию проrраммы Magic 8 ВаН. Вместо 1'01'0 чтобы использовать нссколько строк, содержащих почти идентичные инструкции е 1 i f, можно создать един ственный список, с которым будет работать код. Откройте в файловом ре-- дакторе новое окно, введите в нем следующий код и сохраните ero в файле тagic8BaU2.py. import random messages = ['It is сеrtаiп', 'It is decided1y 50', 'Уез definite1y',
Списки 131 'Reply hazy try again', 'Ask again later', 'Concentrate and ask again', 'Му reply i5 по', 'Outlook not 50 good', 'Very doubtful'] print (messages [rапdоm.rапdiпt (О, lеп(mеssаgеs) 1)]) Запустив этот КОД, вы увидите, что он работает точно так Же, как и пред ыдущая версия nporpaMMbI тagic8Ball.py. Обратите внимание на выражение, которое используется в качестве ин декса: random. randint (О, len (messages) 1) . Оно позволяет получать слу чайные числа в нужном диапазоне, независимо от длины списка messages. Исключения из правип использования отступов в коде Python в большинстве случаев величина отступа строки кода сообщает интерпретатору Python, к какому блоку оне относится. Однако из этоrо правила есть исключения. Например, слисок может располаrаться в нескольких строках в файле исходноrа кода. В подобных случаях величина отступа не имеет значения; Python знает, что до тех пор, пока не встретится закрыlOЮщая квадратная скобка, список еще не закон ЧИЛСR. Например, у всс может быть код следующеrо вида. зрат = ['аррlез', 'oranges' , 'bananas', 'cats'] print(spam) Разумеется, большинство людей используют эту особенность поведения Python для Toro, чтобы придать аккуратный вид спискам, подобным списку сообщений в проrрамме Magic 8 Ball, и облеrчить чтение кода. Также допускается разбиение инструкции на несколько строк с ломощью сим-- вопа , который ставится в конце строки и указывает на то, что допее спедует ее продолжение. Считайте, что СИМ80Л эквивалентен такому утверждению: "Эта ин струкция продолжается 8 следующей строке". Величина отступа строки, следующей за СИМ80ЛОМ , не иrрает никакой роли. Вот пример KoppeICfHoro кода Python. print('Foиr score and seven ' + 'years ago...') Эти приемы приrодятся 10М дЛЯ реорrанизации длинных строк с целью повыше- ния удобочитаемости кода.
132 r лава 4 В данном случае имеется в виду, что вы получаете случайное число в диа пазоне от О до значения len (messages) 1. Преимуществом TaKOl'O подхода ямяется то, что вы можете леrко добамять и удалять строки из списка, не изменяя друrис с.троки кода. Если впослсдствии вы захотите обновить код, то вам придется изменить меныIl{ количе(:тво (TpoK кода, а это означает, что уменьшится и вероятность внесения ошибок при вводе. ТИП". данных, подобные спискам: строки и кортежи Списки не единственный тип данных, который предстамяет упорядо-- ченные последовательности значений. Например, строки фактически aHa лоrичны спискам, сели рассматривать строку как "список" одиночных TeK стовых символов. MHoroe из 1'01"0, что можно делать со списками, можно делать и со строками: индексирование, получение срезов, а также ИСlIОЛЬ зование в циклах for с функцией len () и с операторами in и not in. Чтобы убедиться в этом, введите в интерактивной оболочке следующие команды. »> паше = 'Zophie' >>> name [О) 'z' »> паше [2) 'i' »> nаше[0:4) 'Zoph' »> 'Zo' in паше True »> 'z' in паше False »> 'р' not in паше False »> for i in паше: print('* * * I + i + ' * * *') * * * Z * * * * * * о * * * * * * р * * * * * * h * * * * * * i * * * * * * е * * * ".ен.е...е . .евме..е...е "'".. II".Н"Х Вместе с тем между списками и строками существует одно важное разли- чие. Список это uз.меияем:ый тип данных: значения, хранящиеся в списках, можно д06амять, удалять и изменять. Строки же относятся к нeuз.меияе.м,о- МУ типу данных: строку нельзя изменить. Попытка заменить одиночный
Списки 133 символ в строке приведет к возникновению ошибки TypeError, в чем вы сможете убедиться, введя в интерактивной оболочке следующий код. »> паше - 'Zophie а cat' »> name[7] - 'the' Traceback (most rесепt call last): File "<pyshell#50>", liпе 1, in <module> паmе[7] = 'the' TypeError: 'str' object does not support item аssigпmепt Правильный способ "изменения" строки заключается в использовании Оllераций среза и конкатенации для создания Н0вои С1'рОКИ путем копирова- ния частей исходной строки. Введите в интерактивной оболочке следую- щие команды. »> паше - 'Zophie а cat' »> nе.Маше - nаше[О:7] + 'the' + name[8:12] »> паше 'Zophie а cat' >>> newName 'Zophie the cat' Эдесь для 1'01"0, чтобы сослаться На символы, которые мы не намерены заменять, испольэованы срезы [о: 7] и [8: 12]. Заметьте, что исходная стро- ка 'Zophie а cat' не I10дверrла<ъ изменению ввиду неизменяемости строко- Boro типа данных. Несмотря на 1'0 что списки изменяемый тип данных, в IIриведснном ниже коде вторая строка не изменяет список eggs. »> eggs = [1, 2, З] »> egче = [4, 5, б] »> egче [4, 5, 6] Списковое значение, которое хранилось в eggs, здесь не изменилоCl.. В действительности было просто создано совершенно новое значение ( [ 4, 5, 6]), которое заменило прежнее ( [ 1, 2, 3]). Эту ситуацию поясняет рис. 4.2. Если бы мы хотели действительно изменить первоначальный список, хранящийся впеременной eggs, чтобы он содержал значения [4,5,6],1'0 ДОЛЖНЫ были бы сделать примерно следующее. »> eggs - [1, 2, З] »> ciel eggs [2] >>> ciel eggs [1]
134 r лава 4 »> del egg. [О] »> 8ggs.append(4) »> 8ggs.append(5) »> eqgs.арреnd(б) »> eqgs [4, 5, б] -+ -+ Рис. 4.2. При выполнении инструкции eggs [4, 5, 6] содержимое eggs ЗОменяется новым списковым значением в первом примере списковое значение, которое в конечном счете co держится в переменной eggs, является тем же значением, которое эта псре-- менная имела ИЗllачально. Cyrb в том, что данный список лишь изменился, а не был перезаписан. На рис. 4.3 в иллюстративной форме представлены семь изменений, которые выполняются первыми семью строками кода в последнем примере. Рис. 4.3. Инструкцня del и метод appeпd () изменяют элементы списка на месте
Списки 135 В случае изменяемых типов данных изменение значений осуществляет ся на месте (как это делают инструкция del и метод append () в предыдущем примере), поскольку значение переменной не заменяется новым списко- вым значением. Разrраничение изменяемых и неизменяемых типов данных может пока- заться не имеющим особоrо смысла, однако, как будет ноказано в разделе "Передача ссылок", различия между вызовами функций с изменяемыми и неизменяемыми арryментами весьма существенны. А сейчас нам предстоит рассмотреть кортежи тип данных, который является неизменяемой фор-- мой списков. Корте.. Кортежи почти идентичны спискам и отличаются от них лишь в двух от- ношениях. Во-первых, кuртежи заключаются в круrлые скобки, ( и ), а не в квадратные, [ и ] . Введите в интерактивной оболочке следующие команды. »> 89g8 - ('hello', 42, 0.5) >>> egq8 [О) 'hello' >>> 8gg8[1:3] (42, 0.5) >>> len (8gg8) 3 Однако rлавным отличием кортежей от списков является то, что корте- жи, подобно строкам, относятся к неизменяемому типу данных. Их значе- ния не MOryт изменяться, добавляться и удаляться. Введите в интерактив- ной оболочке следующие команды. »> 8gg8 = ('hello', 42, 0.5) »> egg8[1] = 99 Traceback (most recent са11 1ast): Fi1e "<pyshe11#5>", 1ine 1, in <module> eggs[l] = 99 TypeError: 'tuple' object does not support item assignment Если кортеж (остоит Bcero лишь из одноrо значения, после Hero надо ставить запятую внутри скобок. В противном случае PytllOn будет полaraть, что вы ввели обычное значение и просто заключили ero в скобки. Запятая укажет PythoI1, что данное значение является кортежем. (В отличие от не- которых друrих языков проrраммирования, в Рythоп использование завер- шающей запятой в кортежах и списках вполне допустимо.) Введите в инте- рактивной оболочке следующие команды, чтобы увидеть, к чему приводит отсутствие такой запятой.
136 r лава 4 >>> type( ('hello' ,» <с1азз 'tup1e'> »> type«'hello'» <c1ass 'str'> Используя кортежи, вы тем самым сообщаете тому, кто читает ко/ вашей проrраммы, что не намереваетесь изменять значения, входящие в данную последовательность. Если вам требуется упорядоченная последователь ность значений, которые не будут изменяться, то используйте кортеж. Еще одним преимуществом кортежей по сравнению со списками является то, что, блаrодаря неизменяемости их содержимоro, Python может реализо- вать некоторые схемы оптимизации, ускоряющие работу кода. пpeo6ptlJo.tlH.e ,.по. t пОМОЩ61О функ..,.. list () . tuple ( ) Подобно тому как вызов функции s t r ( 42) возвращает значение '42', являющееся строковым представлением целоrо числа 42, функции 1 i s t ( ) И tup1e () возвращают версии переданных им значений соответственно й виде списка и кортежа. Введите в интерактивной оболочке следующие ко- маlЩЫ и обратите внимание на различие типов передаваемых и возвращае- мых значений функций. >>> tuple(['cat', 'dog', 5]) ( 'cat " 'dog', 5) »> list«'cat', 'dog', 5» [ , са t " , dog', 5 ] »> list('hello') ['h', 'е', '1', '1', 'о'] Преобра:ювание кортежа в список удобно использовать в тех случаях, коrда необходимо получить изменяемую версию значений кортежа. Ссыпки Как вы уже знаете, переменные хранят строковые и целочисленные зна- чения. Введите в интерактивной оболочке следующие команды. > » Spalll. = 42 > > > сЬееее = Spalll. >>> Spalll. = 100 » > .palll. 100 > > > сЬееее 42
Списки 137 Здесь lIеременной spaт сначала присваивается значение 42, а затем это :шачение копируется и нрисваивается переменной cheese. Коrда впослед (:твии переменной spaт присваивается значение 100, это никак не отража- ется на значении, хранящемся в переменной cheese. Такое поведение объ- ясняется тем, что spam и cheese это различные переменные, хранящие различные значения. Однако списки работают не так. Присваивая переменной список, на са- мом деле вы присваиваете ей ссыл"'у на этот список. Ссылка это значение, которое указывает на некоторую порцию данных, а ссылка на список это значение, которое указывает на список. Приведенный ниже код позволит вам лучше понять суть этоro различия. Введите в интерактивной оболочке следующие команды. о »> ера - [О, 1, 2, З, 4, 5] . »> сЬе&е& - ера е »> che_s_[l] - '8&1101' »> ера [О, 'Не11о!', 2, 3, 4, 5] »> сЬееее [О, 'Не11о!', 2, 3, 4, 5] Возможно, полученные результаты вас несколько озадачивают. Несмо- тря на то что изменялся лишь список cheese, изменение затронуло также список spaт. Создавая СIIИСОК О, вы присваиваете ссылку на Hero l1еременной spam. В следующей строке О в переменную cheese копируется не сам список, а только ссылка на Hero, храllЯЩаяся в переменной spaт. Это означает, что теперь оба значения, хранящиеся в переменных spaт И cheese, ссылаются на один и тот же список. Собственно список сущtXаВУет лишь в единствен- ном экземпляре, поскольку он никуда не копировался. Поэтому, изменяя первый элемент списка с помощью переменной cheese О, вы изменяете тот же список, на который ссылается переменная spam. Вспомните о том, что переменные можно уподобить ящикам, в KO'ropbIX хранятся значения. Предстааленная на предыдущих рисунках в этой rлаве аналоrия с ящиками для списков не совсем точна, поскольку на самом деле в переменных хранятся не сами списки, а лишь ссылки на них. (Эти ссылки снабжаются числовыми идентификаторами (11), которые используются вну- тренними механизмами PytllOn, но для вас это несущеетвенно.) На рис. 4.4, также с использованием ящика в качестве метафоры для переменной, нока- зано, что происходит, Korдa переменной зрат присваивается список. Далее на рис. 4.5 прсдставлен процесс копирования ссылки из перемен- ной spaт в переменную cheese. При этом создается и сохраняется в пере-
138 r лова 4 менной cheese новая ссылка, а не новый список. Заметьте, что обе ссылки ссылаются на один и тот же список. Если вы измените список, на который ссылается переменная cheese, то список, на который ссылается переменная spam, также изменится, посколь- ку обе эти переменные ссылаются на один и тот же список. Эта ситуация проиллюстрирована на рис. 4.6. r------. I I Refereпce ID: 57207444 ID: 57207444 [0,1,2, 3, t, 5] Рис. 4.4. Инструкция зрат = {О, 1, 2, 3, 4, 5 J означает сохранение ссылки но список, о не caMoro списка r.............................. I Refereпce ID: 57207444 r.............. I Refereпce Ш: 57207444 ID: 57207444 [0,),2, 3. lt, 5] Рис. 4.5. Инструкция зрат = сЬеезе означает копирование ссылки но список, а не самоro списка
Списки 139 ,........----.....-........., I Refereпce ID: 57207444 r-.........." I ID: 57207444 [О, 'НеНо', 2, З, 4-, 5] Рис. 4.6. Инструкция cheese (1) "",' Hello! ' изменяет список, но который ссылаются обе переменные Переменные содержат ссылки на списки, а не сами списки. Однако в случае строк и целых чисел l1еременные содержат lIеносредственно сами строковые и целочисленные значения. Python всеrда использует ссылки в тех случаях, Korдa в переменнblX должны храниться изменяемые тины дaH ных, такие как списки или словари. В случае неизменяемых типов данных, таkИX как строки, целые числа или кортежи, 8 переменных Pytl10n хранятся сами значения. Несмотря на то что с технической точки зрения в переменных Рytlюп хранятся лишь ссылки на списки и словари, часто просто rоворят, что п ременная содержит список или словарь. переllО'О ttWЛОIl Ссылки особенно важны для понимания механизма передачи apryMeH- тов функциям. Korдa вызывается функция, значения apryMeHToB копируют- ся в переменные параметров. Для списков (и словарей. о которых lIойдет речь в следующей rлаве) это означает. ЧТО параметры используют ссылки. Чтобы увидеть, к каким следствиям это приводит. введите следующий код и сохраните ею в файле passiпg&ference.py. def eggs(someParameter): someParameter.append('He11o') spam = [1, 2, 3] eggs(spam) print(spam)
148 r лава 4 Обратите внимание на то, что присваивание Hoвoro значения перемен ной spam осуществляется не за счет использования значения, возвращаемо-- rо вызовом eggs () . Вместо этоrо список изменяется непосредственно на месте. Запустив эту проrpамму, вы получите следующий вывод: [1, 2, 3, 'He110'] Несмотря на то что переменные spam и someParameter содержат отдель- ные ССЫJIКИ, они обе ссылаются на один и тот же список. Вот почему вызов метода append ( 'Hello') в функции оказывает воздействие на список даже после Toro, как был выполнен возврат из функции. Имейте в виду, что забывчивость относительно Toro, как Python обраба- Тывает переменные, в которых хранятся списки и словари, чревата возник- новением самых неожиданных ошибок. .унк",.. сору () . cleepcopy () МОДУЛ. сору Несмотря на то что передача ссылок нередко оказывается самым удоб- ным способом работы со списками и словарями, в тех случаях, коrда их изменяет функция, возможны ситуации, в которых изменение исходноrо списка или словаря является нежелательным. По этой причине Python пре доставляет модуль сору, в котором имеются функции сору () и deepcopy () . Первая из них, сору. сору ( ) , может использоваться для создания дубликата изменясмоrо значения, такоro как список или словарь, а не просто копии ссылки. Введите в интерактивной оболочке следующие команды. >>> import сору »> ераш = ['А' I 'В' I 'С' I 'D'] »> сЬееее · сору.сору(ераш) »> cheese[l] - 42 »> араш ['А' I 'В' I 'С' I 'О'] »> cheese ['А', 42, 'С', 'О'] ТепеРI> переменные spam и cheese ссылаются на разные списки, чем и объясняется тот факт, что в результате присваивания значения 42 элементу с индексом 7 в списке cheese изменяется только этот список. как показано на рис. 4.7, числовые идентификаторы ID CCbVIOK, предстаWlяемых обеими переменными, больше не совпадают, поскольку теперь переменные cCbVIa- ются на два Jlезависимых списка.
Списки 141 ID: 57205555 ID: 57208868 r.... I I I Refereпce ID: 57205555 ['А', '8', 'С', 'D'] ['А', 't 2, 'С', 'D'] ';..' . Рис. 4.7. Инструкция cheese сору. сору (spam) создает второй список, который можно изменять неэависимо от nepBora Если список, копию KOToporo нужно создать, сам содержит списки, то в этом случае вместо функции сору. сору () следует использовать функцию сору. deepcopy ( ) . Эта функция выполняет копирование OCIIOBHoro списка вместе со всеми внyrренними. Р831ОМ8 Списки весьма полезный тип данных, поскольку позволяют писать код, способный работать с варьируемым количеством значений в одной переменной. Далее вы познакомитесь с lIримерами проrрамм, позволяю щих сделать то, что без использования списков было бы трудно или вообще невозможно выполнить. Списки изменяемый тип данных в том смысле, что их содержимое ма- жет изменяться. Кортежи и строки, несмотря на их сходство со списками, являются неизменяемыми типами данных и не MOryr изменяться. Значение переменной, содержащей кортеж или строку, может быть переопределено пyrем присвоения ей HOBOl'O значения в виде кортежа или строки, НО это не то же самое, что изменение существующеrо значения на месте, как это, например, делают методы append () и remove ( ) в отношении списков. В переменных хранятся не непосредственно сами СllИСКИ, а ссылки на них. Это чрезвычайно важное обстоятельство следует учитывать при KO пировании переменных или передаче apryMeHToB функциям при их вызо- ВС. Поскольку копируемое значение представляет собой ссылку на список, остереrайтесь Toro, чтобы внести непреднамеренные изменения в дру- rие переменные, имеющиеся в вашей проrрамме. Если вы хотите иметь
142 r пава 4 возможность изменять копию списка таким образом, чтобы это не влияло на исходный список, то используйте функцию сору () или deepcopy ( ) . KOHTpoпbHble вопросы 1. Что означают эти скобки: [J? 2. Как бы вы присвоили значение' hello' в качестве TpeTьero элемента списка, хранящеrося впеременной spam? (Предполаraется, что в пере- менной spam содержится список [2, 4, 6, 8, 1 О J .) в следующих трех вопросах предполаrается, что переменная spam содержит список [' а', 'Ь', 'с', 'd' J . 3. Каково значение выражения spam[int (' 3' * 2) / 11] ? 4. Каково значение выражения spam [1 J ? 5. Каково значение выражения spam [ : 2 J? В следующих трех вопросах предполаrается, что переменная Ьасоп содержит список [3.14, 'cat', 11, 'cat', TrueJ. 6. Каково значение выражения Ьасоп. index ( 'cat I ) ? 7. Как будет выrлядеть список, хранящийся в переменной Ьасоп, после следующеrо вызова: Ьасоп. append (99) ? 8. Как будет выrлядеть спи{ок, хранящийся в переменной Ьасоп, после следующеrо вызова: Ьасоп. remove ( 'cat ' ) ? 9. Какие операторы используются для конкатенации и реrurnкации списков? 10. В чем состоит различие между предусмотренными для списков MeTO дами append () и insert () ? 11. Назовите два способа удаления значений из списков. 12. Назовите несколько общих признаков списков и строк. 13. Чем кортежи отличаются от списков? 14. Как бы вы записали кортеж, содержащий единственное значение в виде целоrо числа 42? 15. как преобразовать список в кортеж? как преобразовать кортеж в спи- сок? 16. Переменные, которые "содержат" список, на самом деле не содержат непосредственно сам список. Что же тоща они содержат? 17. В чем состоит различие между функциями сору. сору () и сору. deepcopy ( ) ? Учебные проекты Чтобы закрепить полученные знания на практике, напишите проrрам мы для предложенных ниже задач.
Списки 143 30В.ТО. . Itо"еа.е РОJllел.rеll. Предположим, у вас имеется список наподобие следующеI"О: spam = [' арр1еs' I I bananas', 'tofu', 'cats'] Напишите функцию, которая принимает список в качестве apryMeHTa и возвращает строку, в которой все элементы списка разделены запятой и пробелом, а перед последним элементом вставлено слово апd. Например. передав функции предыдущий список spam, вы должны получить строку 'apples, Ьапапаз, tofu, and cats'. Но ваша функция должна работать с лю- быми списками. '.,о,он.е '...0110.. Предположим, у вас есть список списков, D котором каждое значение внутренних снисков представляет собой строку, состоящую из ОДНOl"о сим вола, как в приведенном ниже примере. grid = [['.' , , , , , I , , , , .'] , , , , . , [1 , 'О' , 'О' , , I , , , . ' ] , . , , . , ['О' , 'О' , 'О' , 'О' , , , , . ' ] , , ['О' , 'О' , 'О' , 'О' , 'О' , '.' ], [' , 'О' , 'О' , 'О', 'О' , 'О'] , . , ['О' , 'О' , 'О' , 'О' , 'О' , '.'] , ['О' , 'О' , 'О' , 'О' , , , , .'] , , [' , 'О' , 'О' , , , , , , . ' ] , . , , . , [' . I , , , , , , , I , . ' ] ] , , , , . , Элемент grid[x] [у] можно интерпретировать как символ с КООРДИllата ми х и у в составе "рисунка", нарисованноI'О текстовыми символами. Нача JIO координат (О, О) находится в верхнем левом уrлу, координата х увсличи вается слева направо, а координата у сверху вниз. Скопируйте предыдущее значение gr id и напишите код, который ис- пользует ero для вывода следующеl"О изображения. . .00.00.. .0000000. .0000000. . .00000. . .. .000... ....о.... Под сказка. Используйте цикл в цикле для вывода элементов grid [О] [О], grid [1] [О], grid[2] [О] и так далее вплоть до элемента grid [8] [О]. Этим вы
144 r лава 4 заполните первую строку, после чеrо вам следует вывести символ новой строки. Затем nporpaмMa должна вывести элементы grid [О] [1] , grid [1] [1], grid[2] [1] и т.д. Последний элемент, который должна вывести IIpO rрамма grid [8] [5]. Кроме Toro, не забудьте передать функции именованный apryMeHT end, если хотите отменить автоматический вывод символа новой строки при каждом вызове функции print ( ) .
спОВАРИ И СТРУКТУРИРОВАНИЕ ДАННЫХ В этой rлаве речь пойдет о словарях типе данных, кото- рый обеспечивает rибкие возможности до<:тупа к данным и их эффективную орrанизацию. Затем, объединив словари со списками из предыдущей rлавы, вы создадите структуру даннbIXДJIЯ иrры "крестики-нолики". Что такое сповар.. Как и список, словаръ это коллекция мноrих значений. Однако в слова рях, в отличие от списков, индексами MOryт служить не только целые числа, но и ряд друrих типов данных. Индексы в словарях называются КJ1,юча.мu, а ключ вместе с ассоциированным с ним значением парий "КJ1,ЮЧ-З1tа'Чe1tuе ". В коде словари обозначаются фиrypными скобками { } . Введите в инте- рактивной оболочке следующий код. »> myCat = I'size': 'fat', 'color': 'gray', disроsitiоп': 'loud'} Здесь переменной туСа t присваивается словарь. Ключами в нем служат строки' size', 'color' и 'disposition 1. Значениями ключей являются сооТ"- ветственно строки' fat', 'gray' И 'loud 1. Доступ К значениям осуществля- ется посредством их ключей. »> myCat['size'] 'fat' »> 'Му cat has ' + myCat['color'] +, fur.' 'Му cat has gray fur.'
146 r пава 5 Индексами в словарях MOryr служить также целые числа, как и в списках, однако их отсчет не обязательно должен вестись от О, и они MOryr быть любыми числами. >>> SPaJD = {12345: 'Luggage COJDbination', 42: 'Тhe Answer'} 'Рll..е..е ио'''ре. . tп.tKO. В отличие от списков, в словарях элементы не упорядочены. Первым элементом в списке spaт был бы spaт [О] . Однако к словарям понятие "пер- вый элемент" неприменимо. В то время как порядок элементов ш'рает су- ще(:твенную роль при проверке совпадения списков, для словарей не имеет ни малейшеro значения, в каком порядке в них были включены пары "имя значение". Введите в интерактивной оболочке следующие команды. »> spam '"' ['cats', 'dogs', 'аооее'] »> Ьасоn '"' ('dogs', 'moоее', 'cats'] »> sраш .. Ьасоn False »> 8g9s '"' {'паше': 'Zophie', 'SpeOi8S': 'cat', 'age': '8'} »> hаш '"' {'speCies': 'cat', 'ag8': '8', 'паше': 'Zophie'} »> eggs -= ham True Поскольку словари не упорядочены, они не допускают создание срезов, как это возможно в случае списков. Попытки получения доступа к ключу, отсутствующему в словаре, при водят к возникновению ошибки KeyError, что аналоrично возникновению ошибки IndexError error при попытке выхода за пределы допустимоrо диа пазона индексов в случае списков. Введите в интерактивной оболочке сле дующий код и обратите внимание на сообщение об ошибке, которое выво- дится, поскольку В словаре отсуктвует ключ' color' . »> ераш '"' {'паше': 'Zophie', 'age': 7} »> spam['oolor'] Traceback (most recent call last): File "<pyshell#1>", line 1, in <module> spam [' color' ] KeyError: 'color' Несмотря на то что словари не упорядочены, ВОЗМОЖНОСiЬ ИЗWlекать про-- иавольное значение по ero ключу позволяет использовать rибкие способы ор- rанизации данных. Предположим, вы хотите, чтобы ваша nporpaмMa coxpa няла данные о днях рождения ваших друзей. Для этой цели вполне подойдет словарь, в котором ключами являются имена друзей, а значениями даты их
Словари и структурирование данных 147 дней рождения. Откройте в файловом редакторе новое окно, введите в нем следующий код и сохраните ero в файле birthdays.frY. . birthdays = {'АНсе': 'Apr 1', 'ВоЬ': 'Оес 12', 'Carol': 'Mar 4' I while True: print('Enter а пате: (blank to quit) ') пате = input ( ) i f пате == ". break . if пате in birthdays: . print(birthdays[naтe] + ' is the birthday of ' + пате) else: print('I do not have birthday information for ' + пате) print('What is their birthday?') bday = input ( ) . birthdays[name] = bday print('Birthday database updated.') Здесь создается исходный словарь, который сохраняет(:я в переменной birthdays О. Проверить, содержится ли введенное имя в качестве ключа в словаре, можно с помощью ключевоrо (лова in О, точно так же, как и в случае списков. Если имя содержится в словаре, то доступ к ассоциирован- lЮму с ним значению осуществляется посредством квадратных скобок о: если же данное имя в словаре OTcyrcTByeT, вы сможете добавить el'O, ис- пользуя тот же синтаксис квадратных скобок в сочетании с оператором присваивания .. Выполнив эту проrрамму, вы получите примерно следующий вывод. Enter а пате: (blапk to quit) A1ice Apr 1 is the birthday of Alice Enter а пате: (blank to quit) Eve I do not have birthday information for Eve What is their birthday? Dec 5 Birthday database updated. Enter а пате: (blank to quit) Eve Dec 5 is the birthday of Eve Enter а пате: (blank to quit) Разумеется, по завершении работы проrраммы все ранее введенные вами данные теряются. О том, как сохранить данныс в файле на жеСТIЮМ диске, вы узнаете в rлаве 8.
148 r лава 5 МетO/l" keys ( ), val иев () . i tems ( ) Существуют три метода для работы со словарями keys (), values () и i tems ( ) , возвращающие соответственно ключи, значения и пары "ключ значение" в виде коллекций, подобных спискам. Возвращаемые этими Me тодами значения не являются ИСТИННЫми списками: их нельзя изменить, и они не имеют метода append (), однако эти типы данных (dictkeys, dict values и dictitems соответственно) можно использовать в циклах for. Что- бы увидеть, как работают эти Методы, введите в интерактивной оболочке следующие команды. »> ерам - {'oolor': 'red', 'age': 42} »> for v in spaм.values(): print (v) red 42 Здесь цикл for используется ДJIя итерирования по всем значениям, co держащимся в словаре spam. Однако циклы for можно также использовать ДJIя итерирования по ключам или одновременно 110 ключам и значениям. »> for k in spam.keys(): print (k) color age »> for i in spaa.items(): print(i) ('color', 'red') ('age', 42) Используя методы keys () , val ues () и i tems ( ) , можно орrанизовать с по- мощью цикла for итерации по ключам, значениям и парам "ключзначение" соответственно. Обратите внимание на то, что элементами значения dict i tems, возвращаемоrо методом i tems ( ) , являются кортежи, образуемые ключами и ассоциированными с ними значениями. Если вы хотите получить реЗУЛlirат в виде истинноrо списка, то пере- дайте возвращаемое любым из этих трех методов значение функции list () . Введите в интерактивной оболочке следующие команды. »> ераа = {'color': 'red', 'ag8': 42} >>> ераа. keys () dictkeys(['color', 'age'])
Споварн н структурирование данных 149 »> list(spaм.keys(» ['color', 'age'] В строке list (зрат. keys () ) значение dict keys, возвращенное функцией keys ( ) , передается функции list () , которая возвращает список [' color' , 'age' ]. Кроме тоro, можно воспользоваться rpупповым присваиванием в цикле for ДIIя присваивания ключей и ассоциированных с ними значений отдель- ным переменным. Введите в интерактивной оболочке следующие команды. »> зрам - {'color': 'red', 'age': 42} »> for k, v in spaи.items(): print('R8y: ' + k + ' Value: ' + str(v» Кеу: аче Value: 42 Кеу: color Value: red пр08ерlt" t1ще"808"... кл.,,,,, .л. ,н""ен.. 8 tло'''ре Как вам уже известно из предыдущей rлавы, операторы in и not in по- зволяют проверить, существует ли в списке данное значение. Эти же опе- раторы можно использовать и ДIIя Toro, чтобы проверить, содержится ли в CJIOBape определснный ключ или значение. Введите в интерактивной обо- лочке следующис команды. »> зрам - {'nаше': 'Zophi.', 'age': 7} »> 'naше' in spam.keys() True »> 'Zophie' in spam.values() True »> 'color' in spam.keys() False »> 'oolor' not in sраш.kеуs() True »> 'oolor' in spaи False Обратите внимание на то, что использованная в этом примере инструк- ция 'color' in spam по сути ЯWlяется сокращенной записью инструкции 'color' in spam. ke ys () . Это общее правило: если вам нужно проверить, ЯWlяется ли (или IIС ЯWlяется) данное значение ключом в словаре, то ДЛЯ этоrо достаточно указать после ключевоrо слова in (или not in) одно толь- КО имя словаря.
15. r лава 5 МеТОII get ( ) Проверять каждый раз при доступе к ключу, есть ли 011 в словаре, ДО-- вольно yrомительно. К счастью, для словарей предусмотрен метод get ( ) , принимающий два apryмeHTa: ключ извлекаемоrо значения и :шачение по умолчанию, возвращаемое в случае отсутствия данноrо ключа в словаре. Введите в интерактивной оболочке следующие команды. »> picnicItems = {'apples': 5, 'сор.': 2} »> '! am bringinq , + str(picnicItems.get('cups', О» + ' cups.' '1 ат bringing 2 сирв.' >>> '! am bringing , + str (picnicItems . get ( 'eggs', О» + ' eggs.' '1 ат bringing О eggs.' Поскольку ключ' eggs' отсрствует в словаре picnicItems, метод get () возвращает заданное по умолчанию значение о. Если не использовать M тод get ( ) , то в коде возникнет ошибка. »> picnicItems - {'apples': S, 'cups': 2} »> '! am bringing , + str(picnicItems['eggs') + ' eggs.' Traceback (most rесепt call last): E'ile "<руshеll#З4>", Нпе 1, in <module> '1 ат Ьriпgiпg , + str(picnic1tems['eggs']) + ' eggs.' KeyError: 'eggs' МеТОII setdefaul t ( ) Часто приходится устанавливать значение для определенноrо ключа лишь в том случае, если значение ему еще не присваивалось. В коде это BЫ I'ЛЯДИТ примерно так. spam = {'пате': 'Pooka', 'аче': 5} if 'color' not in spam: spam['color'] = 'black' с помощью метода setdefaиl t () это можно сделать в одной строке кода. Данный метод принимает два apryмeHTa. Первый из них это проверяе мый ключ, а второй значение, устанавливаемое для этоrо ключа в случае ero отсутствия. Если же ключ существует, то метод setdefaиl t () возвращает ero значение. Введите в интерактивной оболочке следующий код. »> ераа = {'пате': 'Pooka', 'age': 5} »> spam.setdefault('color', 'blaCk') 'black' >>> араш
Словари и структурирование данных 151 {'color': 'black', 'age': 5, 'пате': 'Pooka'} »> spaш.sеtdefаult('соlоr', 'white') 'black' >>> SPaID {'co1or': 'black', 'age': 5, 'пате': 'Pooka'} Первый вызов метода setdefault (} изменяет словарь, который те- перь выrлядит так: { 'co1or': 'black', 'age': 5, 'пате': 'Pooka'}. Метод setdefau1 t (} возвращает значение' black', которое бьvlO устаномено даll ным вызовом для ключа' co1or' . Значение этоrо ключа не изменяется при последующем вызове spam.setdefau1t ('co1or', 'white'}, поскольку в слова ре уже имеется ключ 'co1or'. Метод setdefau1 t () очень удобно использовать в ситуациях, коrда Tpe буется rарантированное наличие ключа. Ниже приведена короткая про- rрамма, которая подсчитывает, сколько раз встречается в строке каждая из входящих в нее букв. Откройте новое окно в файловом редакторе, введите в Hero следующий код и сохраните ero в файле characterCount.py. message = 'It was а bright cold day in Apri1, and the clocks were striking thirteen.' count = {} for character in message: count.setdefault(character, О) соuпt [ct1aracter] = count [character] + 1 print(count) Проrрамма циклически просматривает B(e символы строки в перемен- ной message и IlOдсчитывает, как часто встречается каждый из них. Вызов метода setdefau1 t () rарантирует существование ключа в словаре (значение KOToporo по умолчанию равно О), и поэтому при выполнении инструкции count [character] = count [character] + 1 ошибка KeyError возникать не бу- дет. Выполнив эту проrрамму, вы получите примерно следующий вывод. {' ': 13, ',': 1, '.': 1, 'А': 1, 'I': 1, 'а': 4, 'с': 3, 'Ь': 1, 'е': 5, 'd': З, 'g': 2, 'i':б, 'h': 3, 'k': 2, '1': 3, 'о': 2, 'n': 4, 'р': 1, '5': 3, 'r': 5, 't': 6, 'w': 2, 'у': 1} Отсюда, например, видно, что, как и следовало ожидать, буква с в ниж- нем реrистре встречается 3 pa:Ja, пробел 13 раз, а буква А в верхнем реrи- стре 1 раз. Эта проrрамма будет работать со (:трокой любой длины, даже если в переменной message хранится строка, содержащая миллионы симво-- лов!
152 r лава 5 /(p"t..". пе,,,т. Импортировав в свою проrрамму модуль pprint, вы получите доступ к функциям pprint () и pformat (), осуществляющим "красивую печать" зна- чений словаря. Такая возможность может быть полезной, если требуется более аккуратный вывод элементов словаря, чем это обеспечивает функция print (). Измените предыдущую проrрамму characterCount.py, как показано ниже, и сохраните ее в файле prettyCharacterCount.py. illlpOrt pprint message = 'It was а bright cold day in April, and the clocks were striking thirteen.' count = (} for character in message: count.setdefault(character, О) count[character] = count[character] + 1 ррrint.ррrint(cюunt) На этот раз ВЫВОД выrлядит HaMHoro аккуратнее, и к тому же он pacC0}F тирован по ключам. {' ': 13, 1, 1: 1, 1 1. 1, 'А': 1, 'I': 1, 'а': 4, 'Ь': 1, 'с': З, 'd': З, 'е': 5, 'g': 2, 'h': 3, 'i': 6, 'k': 2, '1': 3, 'n': 4, 'о': 2, 'р': 1, 'r': 5, 'а': 3, 't': 6, 'w': 2, 'у': 1} Функция ppr in t . ppr in t () особенно полезна в тех случаях, Korдa сам ело- варь содержит вложенные списки или словари.
Словари и структурирование данных 153 Если вы хотите получить аккураТIIО оформленный текст в виде строко. Boro значения, а не выводить ero на экран, то воспользуйтесь функцией pprint .pformat (). Следующие две строки эквивалентны. ррriпt.ррriпt(sоmеDiсtiопаrуVаluе) print(pprint.pformat(someDictionaryValue) ) ИспопЬЗ0вание структур данных дпll модепироваНИII реапьных объектов Возможность иrрать в шахматы с партнером, находящимся на друrой стороне земноrо шара, существовала еще до Toro, как появился Интернет. Каждый из иrроков, сидя у себя дома за шахматной доской, сообщал пар- тнеру о ходах, которые он делал своими фиrypами, по почте. Эrо требовало применения способа записи шахматных партий, позволяющеrо однознач- но описывать положения фиryp на доске и их перемещения. В алrебраической шахматной нотации клетки шахматной доски обозна чаются с использованием буквенно-цифровой записи (рис. 5.1). .. . . t1t . ' ' . ""'"'.'" ... ;{181 8ft ',. ..... "л...,!,,4Р :::.",мi:'t'i i$:;"''fl> ! ' ! '!il! , eJ а 9 h Рис. 5.1. Система координат но шахматнон доске, в /(оторон используется буквенно-цифровоя нотация Шахматные ФИl'УРЫ обозначаются буквами: К (king) король, Q (queel1) ферзь (королева), R (rook) ладья, В (bishop) слон и N (knigllt) конь. Описание хода включает букву, соответствующую фиrypе, которая делает ход, и координаты поля, в которое перемещается данная фи- rypa в резулЮ'ате выполнения хода. Запись пары таких ходов информирует
154 rЛQВQ 5 о том, что происходит при выполнении иrроками хода и oTBeтHoro хода (первый ход за белыми). Например, запись 2. NfЗ Nc6 означает, что на вто- ром ходу белые переместили коня на поле f3, а черные cBoero коня на поле с6. Это далеко не полное описание системы записи шахматных партий, од. нако для нас важнее Bcero тот факт, что существует возможность однознач. но описывать шахматную партию, даже не находясь за шахматной доской. Вы можете просто читать ходы противника, которые он пересьтает вам по почте, и мысленно обновлять положения фиryp на шахматной доске. Компьютеры обладают хорошей памятью. Современные проrpаммы по- зволяют хранить в компьютере миллиарды строк наподобие' 2. Nf3 Nc6' . Это 1I0зволяет компьютеру иrрать в шахматы без использования шахмат. ной доски. Он моделирует данные для представления шахматной ДОСКИ, а вы можете писать код, который работает с этой моделью. Вот тут-то и приходят на помощь списки и словари. Их можно исполь- зовать для моделирования объектов реальноrо мира, таких как шахматы. В качестве первоrо при мера мы используем более простую, чем шахматы, иrру "крестики-нолики". поле /PI. 8'Р" . Ultреn81t8.НОЛ81t8U Поле для иrры в "крестики-нолики" похоже на увеличенный символ "ре- шетки" (#) с девятью клетками, каждая из которых может быть пустой или содержать "крестик" (х) или 'нолик" (о). Чтобы представлять клетки иrpо- Boro поля с помощью словаря, можно назначить каждой И3 них ключ В виде строки (рис. 5.2). 'top-L' 'top-M' 'topR' 'mid-L' 'midM' 'mid-R' 'Iow-L' 'lowM' 'Iow-R' Рис. 5.2. Клеткн иrры в Uкрестики-нолики" С указанием соответствующих ключей
Словари и структурирование данных 155 Для представления содержимоrо клеток можно использовать строки: 'Х', 'О' и ' , (символ пробела). Таким образом, Bcel'O нам понадобится девять таких строковых значений. Чтобы связать эти значения с клетка- ми иrровоrо поля, используем словарь. Состояние BepxHero правоrо уrла можно представить с помощью строковоro значения, ассоциированноrо с ключом 'topR', состояние нижнеrо леВОl'О Уl'Ла с помощью CTpoKoBoro значения, ассоциироваННОl'О с ключом 'lowL', состояние центральной клетки с помощью CTpoKoBoro значения, ассоциированноrо с ключом 'midM' , и Т.д. Этот словарь и есть структура данных, которая представляет состояние поля для иrры в "крестики-нолики". Сохраните ero в переменной theBoard. Откройте новое окно файловоrо редактора, введите в Hero следующий ис- ходный код и сохраните ero в файле ticTacToe.py. theBoard {'topL': I I 'topM':" ItopR': 'midL': " 'midM': l' 'midR': 'lowL': I " 'lowM': I " 'lowR': , , I I , '} Структуре данных, сохраненной в пере мен ной theBoard, соответствует состояние поля, представленное на рис. 5.3. Рис. 5.3. Пустое поле ДЛЯ иrры в "крестикн-нолнки" Поскольку в словаре theBoard каждому ключу соответствует строка в виде одиночноrо символа пробела, данное состояние соответствует пусто- му полю. Если иrрок Х своим первым ходом выберет центральную клетку, то :TO состояние поля будет преДСТawIено следующим словарем.
156 r лава 5 theBoard {'topL': ' , 'midL': ' , 'lowL': ' , 'topM' : 'midM' : 'lowM' : , 'Х' , , , , I , topR ': ' , 'midR': ' , , lowR': ' '1 Теперь структуре данных, сохраненной в переменной theBoard, соответ-- ствует вид иrровоrо поля, представленный на рис. 5.4. х Рис. 5.4. Вид иrpО80ro поля после nep8oro хода Ниже показана структура данных, соответствующая выиrрышу иrрока О, разместившеrо три символа Ов верхних клетках. theBoard = ('topL': 'midL' : '.lowL' : 'О' , 'х' , , I I topM' : 'midM' : 'lowM' : 'О' , 'Х', , , 'topR': 'О', 'midR': ' " , 1 ow R 1: 'Х' I Этой структуре данных соответствует вид поля, представленный на рис. 5.5. Разумеется, иrроки MOryт видеть только то, что выведено на экран, а не непосредственное содержимое переменных. Создадим функцию, отобра- жающую содержимое словаря на экране. Внесите в файл ticTacToe. ру сле- дующие изменения (новый код выделен полужирным шрифтом). theBoard = ('topL': I " 'midL': ' I , low L ': I , def printвoard(board) : 'topM': ' " 'topR': ' " 'midM': " 'midR':' " , lowM': ' " 'lowR': ' '1
Словари и структурирование данных 157 print(board[ I topL 1] + 11' + board[ 'topM'] + '1' + board[' topR']) print ( , ++ , ) print(board['midL'] +'1' +board['midM'] + '1' + board[ 'midR']) print(' ++') print(board[ 'lowL'] + 11' + board[ 'lowM'] + '1' + board[ 'lowR']) printВoard(theВoard) о о о х х х Рис. 5.5. 8blHrpon HrpoK О Коrда вы запустите эту lIporpaммy, функция printBoard () выведет на экран пустое иrровое поле. I 1 ++ I 1 ++ 1 I Функция printBoard () может обрабатывать любую структуру "крестиков-ноликов", которую вы ей передадите. Внесите в код следую щие изменения. theBoard = {'topL': 'О', 'midL': 'Х', '101fL': ' , 'topM': 'О', 'lIIidM': 'Х', 'lowM': ' , 'topR': 'О', 'midR': I 1, , lO1fR': I Х' }
158 rЛQВQ 5 def printBoard(board) : print(board[ltopL'] + III + board[ltopM'J + 11' + board [ I topR 1] ) print ( I ++ I ) print(board[lmidL'] + III + board['midM'] + '1 I + board [ 'midR 1] ) print ( ,++ I ) print(board['1owL'] + '1 I + board['lowM'] + III + board [ I lowR I ] ) printBoard(theBoard) В результате выполнения этой nporpaMMbI на экран будет выведено сле дующее состояние иrровоrо поля. 01010 ++ XIXI ++ 'Х Поскольку вы создали структуру данных для предстаВJIения иrр080rо поля и написали код (функцию printBoard(), способный интеРI1Ретиро-- вать эту структуру, тем самым вы создали nporpaMМY, которая "моделирует" иrру в "крестики-нолики". Вы моrли бы орraнизовать свою структуру дaH ных иначе (например, использовать ключи наподобие 'TOPLEFT' вместо 'topL') , но коль скоро ваш код правильно обрабатывает выбранную cтpyк туру данных, IIporpaмMa будет работать корректно. Например, функция printBoard () ожидает, что ей будет передаваться структура данных в виде словаря с ключами для всех девяти клеток. Если, скажем, в передаваемом функции словаре отсутствует ключ 'midL', то ваша nporpaMMa работать не будет. 01010 ++ Traceback (most recent са11 1ast): Fi1e "ticTacToe.py", line 10, in <modu1e> printBoard(theBoard) Fi1e "ticTacToe.py", 1ine 6, in printBoard print(board['midL'] + '1' + board['midM'] + '1 I + board [ I midR' ] ) KeyError: 'midL' А сейчас мы добавим код, который позволяет иrрокам вводить свои ходы. Внесите изменения в nporpaMMY tic7ac1Oe.py, чтобы она приняла с.ле дующий вид.
Словари и структурирование данных 159 theBoard {'topL': I , 'midL': I I '101fL': I I 'topM' : 'midM' : 'lowM' : . , 'topR': ' · 'midR': · , 'lo_R': · '} , . , , def printBoard(board): print(board['topL'] + '1 I + board['topM'] + '1' + board [ , topR ' ] ) print ( I ++, ) print(board['midL'] + '1' + board['midM'] + '1' + board [ I midR ' ] ) print ( ,++ I ) print(board['lowL'] + '1' + board['lowM'] + '1' + board [ 'lowR 1] ) turn = 'Х' for i in range(9) : О printВoard (theBoard) print ( 'Тurn for ' + turn +, мove оп _hich .расе?') . move = input () . theВoard [move] - turn . if turn ... 'Х': turn - 'О' else: turn - 'Х' рriпtВоаrd(thеВоаrd) НОВЫЙ код выводит состояние иrровоrо поля В начале каждоrо очеред HQrO хода О, получает ход активноrо иrрока О, соответствующим образом обноWlЯСТ иrровое поле О. а затем передает право хода дрyrому иrроку 8, прежде чем перейти к следующему ходу. Коrда ВЫ запустите проrрамму. ВЫ- вод в процессе иrpы будет выrлядеть примерно так. I I ++ 1 I ++ I I Turn for Х. Move оп which space? midM 1 I ++ IXI ++ I I Turn for о. Move оп which space? lo_L I I ++ IXI ++ 01 I
160 r лава 5 опущено OIOIX ++ XIXIO ++ 01 IX Turn for Х. Move оп which space? 101fM OIOIX ++ XIXIO ++ OIXIX Это еще далеко не полный вариант проrраммы, поскольку в нем, Ha npимер, вообще не определяется, выиrpал ли иrpок. Однако и Taкoro кода вполне достаточно для Toro, чтобы понять, как структуры данных MOryr ш:- пользоваться в проrраммах. Прu.мечаuие Любозна:тел/ь'Н/ые читатели .моеут 0знако.митъся с nOJт'ЬLМ иcxoдu'ыJlt кодо.м про- ера.м..мъ' для иаръ, в "крестики-нолики", посетив сайт http://пostarch.com/ automatestuff/. Вложенн.,е tловар. . tп.tlt. Моделировать иrру в "крестики-нолики" было сравнительно просто: для описания состояния иrровоrо поля потребовался Bcero лишь один (ловарь с девятью парами "ключзначение". Может оказаться так, что по мере воз- растания сложности ваших моделей вам понадобятся словари и списки, со. держащие друrие списки и словари. Списки удобны для хранения упорядо" ченных последовательностей значений, а словари для ассоциирования значений с 1UIючами. Ниже приведена проrрамма, в которой для описания Toro, что приносит с собой каждый из roстей, приrлашенных на пикник, ис.. пользуется словарь, содержащий друrой словарь. Функция totalBrought ( ) способна читать эту структуру данных и рассчитывать общее количество принесенноro rостями. allGuests = {'АНсе': ('apples': 5, 'pretzels': 12}, 'ВоЬ': {'ham sandwiches': 3, 'apples': 2}, 'Carol': ('cups': 3, 'apple pies': 1)}
Словари и структурирование данных 161 def totalBroиght(gиests, item): nurnВroиght = О . for k, v in gиests.items(): . numBroиght = nиmBrought + v.get(item, О) retиrn nиmBroиght print('Number of things print (' Apples being broиght:') , + str(totalBroиght(allGиests, 'apples'))) , + str(totalBroиght(allGиests, 'сирэ'))) , + str(totalBroиght(allGuests, , cakes ' ) ) ) , + str(totalBroиght(allGиests, 'ham sandwiches'))) str(totalBroиght(al1Gиests, 'applepies'))) рriпt (' Cups print (' Cakes print(' Нат Sandwiches print(' Apple Pies ' + В функции totalBroиght () цикл for выполняет итерации по парам "ключзнаqение", хранящимся в переменной gиests О. В цикле перемен ной k присваивается строка с именем юстя, а переменной v словарь с информацией опринесенном rостями. Если в словаре имеется ключ, со- ответствующий тому, что принес ['ость, то еro значение (количество при несенных единиц) прибавляется к переменной numВroиght о. В случае от. сyrствия TaKoro ключа метод get () возвращает значение О, которое, будучи прибавленным к значению numВroиght, не влияет на результат сложения. Вывод этой проrpаммы выrлядит так. Number of things being broиght: Apples 7 Сирэ 3 Cakes О Нат Sandwiches 3 Apple Pies 1 Может показаться. что эта модель слишком простая, чтобы обременять себя написанием специальной проrpаммы для нее. Однако задумайтесь над тем, что эта же функция totalBroиght () позволит леrко обрабатывать слова ри, содержащие тысячи rостей, которые MOryr приносить тысячи ра3JIИЧ ных блюд. В подобных случаях сохранение такой информации в структуре данных и ее обработка с помощью функции totalBroиght () сохранит вам MaCt"}' времени. Вы можете моделировать различные задачи с помощью структур данных по своему усмотрению при условии, что остальная часть кода корректно обрабатывает выбранную модель. Коrда вы только начинаете проrрам мировать, не задумывайтесь особенно о выборе "наилучшей модели" для
162 r лава 5 описания своих данных. Возможно, по мере обретения опыта вам удастся найти более подходящие модели, но самое rлавное заключается в том, что- бы эта модель удовлетворяла задачам вашей проrраммы. Резюме Из этой rлавы вы узнали вес о словарях. Спш:ки и словари MOryт содер- жать множество различных значений, включая друrие списки и словари. Словари удобны тем, что позволяют отображать одни элементы (ключи) в друrие (значения), в отличие от списков, которые просто содержат упоря' доченные последовательности значений. Доступ к значениям словаря осу- ществляется посредством использования квадратных скобок, точно так же, как и в списках. Однако вместо целочисленных индексов в словарях допу- скается использование ключей в виде самых разных типов данных: целых и вещественных чисел, строк, кортежей. Орrанизуя данные проrраммы в структуры, можно создавать представления реальных объектов. Примером этоrо может служить иrра в "крестики-нолики". Рассмотренный материал охватывает почти все основные концепции проrраммирования на Python. В оставшейся части книrи вам предстоит узнать еще очень MHoro HOBoro, но вам уже известно достаточно для Toro, чтобы писать полезные проrраммы, автоматизирующие выполнение ряда важных задач. Возможно, вы даже не осознаете, что ваших знаний вполне достаточно для Toro, чтобы заrружать веб-страницы, обновлять электрон- ные таблицы или ОТПРawIять текстовые сообщения, и неоценимую помощь в этом вам окажут модули РytJlOП! Эти модули, написанные друrими про- rраммистами, предоставляют функции, которые упрощают для вас выпл-- пение задач TaKoro рода. Поэтому беритесь за написание реальных про- rрамм для выполнения полезных автоматизированных задач. Контропьныевопросы 1. Как ВЫf'Лядит код для пустоro словаря? 2. Как выrлядит элемент словаря с ключом' foo' и значением 42? 3. Опишите основные различия между словарем и списком. 4. Что произойдет при попытке получения доступа к элементу spam[' foo'], если spam это ('bar': 100 '? 5. Если в переменной spam хранится словарь, то в чем состоит разница между выражениями' cat I in spam и 'cat' in spam. keys ()? 6. Если в переменной spam хранится словарь, то в чем состоит разница между выражениями I cat I in spam и 'cat' in spam. va1иes (I? 7. В какой сокращенной форме можно записать приведенный ниже код?
Словари н структурирование данных 163 if 'co1or' not iп зрат: spam['co1or'] = 'black' 8. Какие модуль и функция MOryr быть использованы для "кра<:ивой пе чати" значений словаря? Учебные проекты Чтобы закрепить полученные знания на практике, напишите проrрам мы для предложенных ниже задач. Инаентар. "Р.КЛIO"ен,еtltоi .rp'" Вы создаете приключенческую видеоиrру. Структурой данных для ин- вентаря иrрока должен быть словарь, в котором ключи это строковые значения, опйсывающие инвентарь, а значения количество имеющихся у и['рока единиЦ данноrо инвентаря. Например, в случае словаря {'rope': 1, 'torch': 6, 'gold coin': 42, 'dagger': 1, 'arrow': 12} это означает, что у и['рока есть 1 веревка, 6 фонарей, 42 золотые монеты, 1 кинжал и 12 стрел. Нишите функцию displayInventory (}. которая принимает в качестве apryмeHTa описание любоro "инвентаря" и отображает еro примерно в сле- дующем виде. Inventory: 12 arrow 42 gold coin 1 rope 6 torch 1 dagger Tota1 number of items: 62 Под сказка. Для просмотра всех ключей словаря можно использовать цикл for. # iпvепtоrу.ру stuff = {'rope': 1, 'torch': 6, 'gold coin': 42, 'dagger': 1, 'arrow': 12} def disр1ауlпvепtоrу(iпvепtоrу): рriпt("Iпvепtоrу;") item tota1 = О for k, v in inventory.items(): print (str (v) + ' I + k)
164 r лава 5 item total += v print ("Total number of items: " + str(itemtotal)) displayInventory(stuff) .УНItЦ.. пpe06ptlJO'tlH.. t..tKtI , tло,tlр' АЛ. пР.ItЛIO"енчеtкоi .rp'" Представьте, что добыча побежденноrо дракона представлена в виде списка строк наподобие следующеrо: drаgопLооt = ['gold coin', 'dagger', 'gold coin', 'gold coin', I ruby I ] Напишите функцию addToInventory (inventory, addedItems), в которой параметр inventory это словарь, предстаВJIЯЩИЙ инвентарь иrрока (как в предыдущем проекте), а параметр addedItems это список наподобие dragonLoot. Функция addTOInventory () должна возвращать словарь, пред- ставляющий обновленный инвентарь. Обратите внимание на то, что в списке addedItems один и тот же элемент может встречаться несколько раз. Ваш код может выrлядеть примерно так. def addToInventory(inventory, addedItems): # сюда должен быть вставлен ваш код inv = {'gold coin': 42, 'rope': 1} dragonLoot = ['gold coin', 'dagger', 'gold coin', 'gold coin', I ruby' ] inv = addToInventory(inv, dragonLoot) displayInventory(inv) Предыдущая проrpамма (с вашей функцией displayInventory () из преды дущеrо проекта) должна вывести следующее. Inventory: 45 gold coin 1 rope 1 ruby 1 dagger Total number of items: 48
МАнипvпИРОВАНИЕСТРОКАМИ ТСКСТ одна из наиболее распространенных форм дан- HbIX, которые будут обрабатываться вашими ПрOl'раммами. Вы уже знаете, как конкатенировать два <:троковых значе- ния с помощью оператора +, но ваши возможноcrи rораздо шире. Python позволяет извлекать части crpoK из строковых значений, добawmть и удалять пробелы, преобразовывать буквы из нижнеro рсrистра в верхний и обратно, а также форматировать строки по своему желанию. Вы даже можете написать код на PytJ1Ol1 для до-- ступа к буферу обмена, используя ero для копирования и вставки текста. В этой rлаве мы поработаем над двумя проrраммными проектами: про-- стым менеджером паролей и проrраммой для автоматизации рyrинной ра- боты по форматированию текста. Работа со строками Приступим К рассмотрению различных способов записи строк, а также их вывода на экран и получения доступа к ним с помощью кода на языке PythOl1. С'роко,ые литераЛ61 Ввод строк в кодс Pythоп не составляет труда: они начинаются и заканчи. ваются символами апострофа (одинарными кавычками). Но как быть, если кавычки требуются в самой строке? Ввод строки в виде 'Tha t is АНсе ' s са t. ' не сработает, поскольку Ру. 110n интерпретирует вторую кавычку по. (ле слова Alice как конец строки и сочтет остальную часть текста (5 са t. ,) нсдопустимым кодом. К счастью, существует несколько способов ввода строк.
166 rЛQВQ 6 10."'10 Начало и конец строки можно обозначать не только парой апострофов, но и парой кавычек. Одно из преимуществ использования кавычек в том, что они позволяют включать символ апострофа в состав строки. Введите в интерактивной оболочке следующий код: »> SPaJD '"' "Тhat is Alioe's cat." Поскольку строка начинается с кавычки, Pytlюп в состоянии идентифи цировать апостроф как часть строки. Если же в составе строки требуются как апострофы, так и кавычки, то необходимо прибеrать к экранированию символов. Jкраиироваии..ес..IO." Техника экранирования позволяет использовать символы, вставка кото- рых в строку иными способами не возможна. Эк.ра'ltuрова'lt'Н:ый C1J.Мвол вклю- чает символ обратной косой черты (), за которым следует сам символ, ДО-- бавляемый в строку. (Несмотря на то что экранированный символ состоит из двух символов, ero рассматривают как одиночный.) Вот так, например, выrлядит экранированный апостроф: '. Ero можно использовать даже в строке, которая начинается и заканчивается апострофом. Чтобы увидеть, как работают экранированные символы, введите в интерактивной оболоч ке следующий код: >>> ераш. = 'Вау hi to ВоЬ'е mother.' Поскольку в слове ВоЬ 's апострофу предшествует символ обратной KO сой черты, Python знает, что в данном случае апостроф не служит MapKe ром конца строки. Экранированные символы ' и " позволяют включать в строку соответственно апострофы и кавычки. Экранированные символы, которые можно использовать, приведены в табл.6.1. Таблица 6.1. Экранированные СИМВОЛ"I t п \ Отображаемый СИМIlOll Апостроф Кавычка Символ табуляции Символ новой строки (разрыва строки) Символ обратной косой черты ЭкранмраванныйсимlIOII ' "
Манипупнрование строками 167 Введите в интерактивной оболочке следующую команду. >>> print("Hello there!nHow are you?nI'm doing fin..") Hello there! How are уои? 1'm doing fine. UС..рме" строки Поместив символ r перед начальной кавычкой, вы обозначаете строку как "сырую". Сирая (т.е. необработанная) строка полностью иrнорирует механизм экранирования и выводит все символы обратной косой черты, которые встречаются в строке, как обычные символы. Введите, например, в интерактивной оболочке следующую команду. »> print(r'Тhat is Carol's cat.') That is Carol's cat. Поскольку это "сырая" строка, Python считает ВСС символы обратной косой черты ее частью, а не началом экранированноrо символа. "Сырые" строки полезны в тех случаях, кш'да вы вводите строковые значения с MH rочисленными Символами обратной косой черты, как это бывает при ис пользовании реryлярных выражений, описанных в следующей rлаве. МиоrОСТРОllиwе 610lИ Несмотря на то что вы Bcerдa можете включить в строку символ НОВОЙ строки с помощью экранированноrо символа п, во мноrих случаях rораздо проще использовать мноrоcrрочные блоки. В PytllOП мноrострочпые блоки представляют собой rруппу строк, заключенных в тройные апострофы или кавычки, Любые апострофы, кавычки или символы новой строки в блоке, оrраниченном такими "тройными кавычками", считаются частью строки. Правила PytllOn, реrламентирующие форматирование блоков кода с помо- ЩЬЮ отступов, в отношении мноrострочных блоков не действуют. Откройте файловый редактор и введите следующий код. print(' I 'Dear Alice, Eve's cat has Ьееп arrested for саtпаррiпg, cat burglary, and extortion. Sincerely, ВоЬ' I 1)
168 rЛQва 6 Сохраните эту проrрамму в файле catпapping.py и выполните ее. Вы долж- ны получить следующий вывод. Dear АНсе, Eve's cat has been arrested for саtпаррiпg, cat burglary, and extortion. Sincerely, ВОЬ Обратите внимание на то, что для символа апострофа 1 слове Е ve ' s экранирование не понадобилось. В "сырых" строках экранирование апо- строфов и кавычек необязательно. Для вывода текста в таком же виде без использования мноrострочноrо блока потребуется следующий вызов функ- ции print (). print('Dear Alice,nnEve's cat has been arrested for catnapping, cat burglary, and extortion.nnSincerely,nBob') МиоrОnРОllиwе коммеитарии В то время как символом "решетка" (#) обо:шачается однострочный ком- ментарий, длина KO'ropOl'o оrраничивается концом строки, мноrОСТРОЧlfые блоки часто используют в качестве МНOI'острочных комментариев, охваты- вающих произвольное количество строк. Приведенный ниже текст абсо- лютно корректен 8 PytllOn. '""'This is а test Python program. Written Ьу Al Sweigart al@inventwithpython.com This program was designed for Python 3, not Руthоп 2. """ def spam(): """This is а mиltiline comrnent to help explain what the spam() function does.""" print ( 'Иеllо! ' ) IfHlleKt_po.tlH_e nрок _ _J.лечен_е tpeJo. в случае строк операции индексирования и извлечения срезов выпол- няются точно так же, как и в случае спи(ков. Можете рассматривать строку 'ВеНо world! I как список, в котором каждый символ строки является эле- ментом списка и име<.--т соотвt.'Тствующий индекс.
Манипулирование строками 169 Н О е 1 1 2 1 3 о 4 5 w 6 о 7 r 8 1 9 d 10 11 Пробелы и восклицательный знак входят в число символов, поэтому фраза 'Не 110 wor 1d! ' содержит 12 символов, от символа Н с индексом О до символа! с индексом 11. Введите в интерактивной оболочке следующие команды. »> ерам = 'Hello world!' ерам[О] 'н' » > Вpalll. [ 4 ] 'о' »> ер&lll[1] '! I >>> ераш[О:5] '!1ello' » > Вpalll. [ : 5 ] 'Hello' >>> врam[б:] 'world! ' Указав индекс, вы получаете символ, находящийся в соответствующей позиции в строке. В случае указания диапазона индексов, Т.е. ИЗWlечения среза, элемент с начальным индексом включается в срез, тоrда как эле мент с конечным индексом не включается. Поэтому, если spam это строка 'Hello wor1d! ',то срез spam[O: 5] содержит строку 'Hello'. Подстрока, по-- лучаемая с помощью среза spam [о: 5] , будет включать в себя все символы от spam [О] до spam [ 4 ] , тоrда как символ пробела, имеющий индекс 5, в нее не войдt.'Т. Имейте в виду, что извлечение части строки посредством среза не со-- провождается изменением исходной строки. Вы можете сохранить срез, извлеченный из одной переменной, в дрyrой переменной. Введите в интеJr активной оболочке следующие команды. »> SPaID = 'Hello world!' »> fizz = spaш[О:5] >>> fizz I Иеllо I Извлекая срез и сохраняя результирующую подстроку в друrой перемен ной, вы получаете быстрый и простой доступ как ко всей строке, так и к ее подстроке.
170 r лава 6 И,пОЛ6J08"н.е опеptl,ОР08 in . not: in ,О "Pol(".. Операторы in и not in применяются к строкам точно так же, как и к сни- скам. Результатом вычисления выражения в виде двух строк, соединенных любым из этих операторов, является булево значение Trиe или t'alse. Вве- дите в интерактивной оболочке следующие команды. »> 'Hello' in 'Hello World' True »> 'Hello' in 'Hello' True »> 'НELLO' in 'Hello World' False »> l' in '8раш.' True »> 'cats' not in 'cats and dogs' False Эти выражения проверяют, содержится ли первая строка (о CTporOM <:0-- ответствии с рсrистром набора символов) во второй <:троке. Попезные методы дпя работы со строками Некоторые методы анализируют строки или создают нреобразованные строковые значения. В этом разделе описаны те из них, которые вы будете использовать наиболее часто. "е'ОIl'" upper ( ) , lower ( ) , isupper () . islower ( ) Методы upper () и lower () возвращают новую строку, в которой все буквы исходной строки преобразованы соответствеlllЮ в верхний или нижний рсrистр. Небуквенные символы не иснытывают никаких изменений. Вве- дите в интерактивной оболочке следующий код. »> ераш = 'Hello worldl' >>> ераш = ераш. upper () »> ераш I HELLO WORLD! I »> ераш = spam.lower() »> ераш 'hello world!' Имейте в виду, что эти методы возвращают новые строковые значения, не изменяя саму исходную строку. Чтобы изменить исходную строку, не- обходимо вызвать для нее метод upper () или lower () и присвоить реЗУJII). тирующее строковое значение той же переменной, в которой хранилась
Манипулирование строками 171 исходная строка. Именно поэтому для тою, чтобы изменить строку, хра- нящуюся в Ilеременной spaт, следует выполнить операцию приснаивания spaт = spaт. upper () , а не просто вызвать функцию spaт. upper () . (В каче(тне аналоrии можно привести перемеНIlУЮ eggs, содержащую значение 10. Вы- ражение eggs + 3 не изменит значения eggs, но это можно сделать с помо- ЩЬю инструкции eggs eggs + 3.) Методы upper () и lower () удобно Ilрименять в тех случаях, коrда при сравнении строк не должен учитываться реrистр букв. Строки 'great' и 'GREat' это разные строки. Однако в приведенной ниже небольшой про- rpaмMe не имеет значения, в какой форме пользователь введет слово, Great, GREAT или grEAT, поскольку строка предварительно преобразуется в верх- ний реrистр, print('How are уои?') feeling = input() if feeling.lower() == 'great': print('I feel great too.') else: print('I hope the rest of your day is good.') Korдa вы запустите эту проrpамму, на экране отобразится вопрос, и даже если вы ответите на Hero, введя слово GREat, все равно будет выведена стро- I,а I feel great too. Добавляя в свою проrрамму код, нивелирующий раз- личия в написании слов и иrнорирующий такие ошибки, как неправильное использование строчных и прописных букв, вы упростите работу с ней и уменьшите вероятность ее аварийноrо завершения из-за ошибок, допущен- ных пользователем при вводе текста. How are уои? GREat 1 feel great too. Методы isupper () и islower () возвращают булево значение True, если в строке имеется хотя бы одна буква и все буквы относятся соответственно к верхнему или нижнему реrистру, В противном случае метод возвращает значение False. Введите в интерактивной оболочке следующие команды и обратите внимание на возвращаемые методами значения. »> ераш 'Hello world!' »> spam.islower() False »> sраш.isuрреr() False >>> 'НELLO'. i supper ()
172 r лава 6 True »> 'аЬс12З45' .islower() True »> '12345'.islower() False »> '12345'.isupper() False Поскольку методы upper () и lower () сами возвращают (:троки, для этих строк, в свою очередь, также можно вызывать строковые методы. Выра- жения, с помощью которых это делается, выrлядят как цепочки вызовов методов. Введите в интерактивной оболочке следующие команды. »> 'Hello'.upper() 'HELLO' »> 'Hello'.upper().lower() 'hel!o' »> 'Hello'.upper().lower().upper() 'HELLO' >>> I НELLO' .lower () 'hello' »> 'НELLO'.lower().islower() True 'ТрОК0861е .еТОIl" isX() Наряду с функциями islower () и isupper () существует ряд мет<?дов для работы со строками, имена которых начинаются со слова is. Методы этой катеrории возвращают булево значение, описывающее природу строки, ДЛЯ которой они вызываются. Ниже приведен перечень часто используе- мых строковых методов типа isX() . . isalpha () возвращает значение Trиe, если строка состоит только из букв и не является пустой. . isalnum () возвращает значение True, если строка состоит только из буквеннцифровых символов и не является пустой. . isctecimal () возвращает значение True, если строка состоит только из цифровых символов и не ЯWlяется пустой. . isspace () возвращает значение True, если строка состоит только из символов пробела, табуляции, новой строки и не является пустой. . istitle () возвращает значение True, если строка состоит только из слов, которые начинаются с буквы в верхнем реrистре, за которой следуют только буквы в нижнем реrистре. Введите в интерактивной оболочке следующие команды.
Маннпупирование строками 173 »> 'hello'.i8alpha() True »> 'hel10123'.isalpha() False »> 'hеllо12Э' . i8alnum() True »> 'hello' .i8alnum() True »> '123'.i8decimal() True »> ' '.i8spaceO True »> 'Тhi8 I8 Title Ca8e'.i8title() True »> 'Thi8 I8 Title Са8е 123' .i8title() True »> 'Thi8 I8 not Title Са8е' .i8title() False »> 'Thi8 I8 NOT Title Са8е Either'.i8title() False Методы isX() удобно применять для проверки допустимости введенных пользователем данных. Например, приведенная ниже проrрамма повторя ет запрос ввода пользователем cBoero возраста и пароля до тех пор, пока :ти данные не будут предоставлены в корректном формате. Откройте но-- аое окно в файловом редакторе, введите в нем следующий код nporpaмMbI и сохраните ero в файле validatelnput.py. while True: print('Enter your age:') age = input ( ) if age.isdecimal(): break print('Please enter а numЬer for your age.') while True: print('Select а new password (letters and numbers опlу): ') password = input() if password.isalnum(): break print('Passwords сап only have letters and numЬers.') в первом цикле while nporpaмMa запрашивает ввод возраста пользовате- ля и сохраняет ввеДенное значение. Если пользователь ввел возраст в виде допустимor'о значения (десятичноro числа), первый цикл прерывается и Уl1рawIение передается второму циклу while, в котором запрашивается na роль. В противном случае nporpaмMa информирует пользователя о том, что должно быть введено число, и предлаrает повторно указать возраст.
174 r лова 6 Во втором цикле while запрашивается и сохраняется пароль. Если поль зователь ввел буквенно-цифровое значение, выполняется выход из цикла. В противном случае nporpaмMa информирует пользователя о том, что дo пускаются только пароли, состоящие из букв и цифр, и предлаrает ему по- вторить ВБОд. Запустив nporpaммy, вы должны получить примерно такой вывод. E:nter yoиr age: forty t_o Please enter а nиmber for yoиr age. Епtеr yoиr age: 42 Select а new password (letters and nиmbers only): sесrЗt! Passwords сап only have letters and nиmbers. Select а new password (letters and nurnbers only) : sесrЗt Вызывая методы isdecirnal () и isalnиm() для переменных, мы можем BЫ яснить, являются ли введенные пользователем значения цифровыми или буквенно-цифровыми. В данном случае эти про верки позволили OТBeprнyrb ввод пользователем строки forty two при указании возраста, но принять ввод числа 42, а также oTвeprнyrb ввод значения sесrЗt! в качестве пароля, но принять ввод значения secr3t. Мето"" starts"i th () . ends"i th () Методы startswith () и endswi th () возвращают значение True, если стро- ка, для которой они вызываются, соответственно начинается или заканчи- вается строкой, переданной методу; в противном случае они возвращают значение False. Введите в интерактивной оболочке следующие команды. »> 'Hello _orldl '.start8_ith('Hello') True »> 'Hello _orldl' .enda_ith('_orldl') True »> 'аЬо12Э'.stаrts_ith('abcdef') False »> 'аЬс12З'.еnds_ith('12') False »> 'Нello _orldl '.start8with('Hello worldl') True »> 'Hello _orldl'.ends_ith('Hello worldl') True
Манипулирование строками 175 Эти методы полезная альтернатива оператору сравнения , если cpaB нение с друrой строкой требуется выполнить не для всей исходной строки, а только для первой или последней ее части. "роко..., ."011" joiп () . spli t () Метод j oin () удобно использовать в тех случаях, коrда ряд строк, npeд ставленных в виде списка, необходимо объединить в одно строковое зна чеllие. Метод join () вызывается для некоторой строки, принимает спи- сок строк в качестве apryмeHTa и возвращает строку. Возвращаемая строка представляет собой результат конкатенации всех строк, переданных в виде списка. Введите в интерактивной оболочке следующие команды. »> " '.join(['cats', 'rats', 'bats') 'cats, rats, bats' »>' '.join(['Мy', 'паше', 'is', 'Simon') 'Му пате is Simon' »> 'AВC'.join(['Мy', 'паше', 'is', 'Simon') 'MyABCnameABCisAВCSimon' Обратите внимание на то, что строка, ДIIЯ которой вызывается метод join (), вставляется между переданными посредством apryMeHTa строками. Например, если выполнить вызов j oin ( [ 'cats', 'rats', 'bats']) ДIIЯ стро- ки " ',ТО будет возвращена строка' cats, rats, bats'. Не забывайте о том, что метод j oin () вызывается для <:TpOKoBoro значе- ния и в качестве apryMeHTa принимает список. (Вы можете вызвать метод как-то иначе, сами Toro не осознавая.) Метод spli t () делает совершенно противоположное: он вызывается для CTpoKoBoro значения и возвращает список строк. Введите в интерактивной оболочке следующую команду. »> 'НУ паше is SiJUon'.split() [ , Му 1, ' пате', 'is " 'Simon'] По умолчанию строка 'Му пате is Simon' разбивается на отдельные стро-- ки в тех местах, ['Де встречаются пробельные символы: пробел, символ та- буляции, символ новой строки. Эти пробеШ)IIЫС символы не включаются в строки, возвращаемые в виде списка. Вы можете указать дрyryю CTPOКY разделитель, передав ее методу spH t ( ) . Например, введите в интеракти& ной оболочке следующие команды. »> 'НУАВСnашеАВСisAВСSiаоn'.sрlit('АВС') ['Му', 'пате', 'is', 'Simon'] »> 'НУ namе is Simon' .split('m') [ 'Му па', 'е is Si', 'оп']
176 r лава 6 Обычный способ применения метода spli t () разбиение мноrостроч Horo блока по символам новой строки. Введите в интерактивной оболочке следующие команды. »> 8pam - "'Dear Alice, How have you been? 1 u fine. There is а oontainer in th. fridqe that is labeled "Мilk Experiment". Please do not drink i t. Sincerely, ВоЬ" , »> spu.split('n') ['Dear Alice, " 'Иоw have уои Ьееп? I am fine.', 'There is а container in the fridge', 'that is labeled nMilk Experiment".', ", 'Please do not driпk it.', 'Sincerely,', 'ВоЬ'] Передача методу spli t () строки I n' в качестве apryмeHTa позволяет вы. полнить разбивку МНОl'острочноrо блока, coxpaHeHHoro впеременной эрат в позициях символов новой строки, и возвратить список, каждый элемент KOToporo соответствует одной строке текста. Вырtl.н..tlн.е "каtl t п0801Ц61O 8еТО1I0' rjust (), ljust () . ceпter () Строковые методы rjиst () и 1just () возвращают версию строки, для которой они вызываются, выровненную за счет вставки пробелов. В обоих методах первый apryмeHT целое число, определяющее длину выровнен- ной строки. Введите в интерактивной оболочке следующие команды. »> 'Hello' .rjust(10) , Ие1l0' »> 'Нello'.rjust(20) , Неllо I »> 'Hello World'.rjust(20) , Иеllо World' »> 'Hello' .ljust(10) 'Иеllо I Выражение 'Не 110' . rj иэ t ( 10) rоворит о том, что мы хотим выровнять строку' Не110' вправо так, чтобы полная длина строки составляла 10. В сло- ве 'Ве110' насчитывается пять символов, поэтому слева от Hel'o будут до- баВJIены пять пробелов, в результате чеrо полная длина строки составит 10 символов, причем слово' Не 110' будет выровнено вправо.
Монипулирование строками 177 Необязательный второй apryмeHT в обоих методах указывает на символ- заполнитель, отличный от пробела. Введите в интерактивной оболочке следующие команды. »> 'Hello'.rjust(20, '*') '***************Hello' »> 'Hello'.ljust(20, '') 'Hello1 Метод center () работает аналоrично методам ljust () и rjust (), но цeH трирует ero, а не выравнивает по правому или левому краю. Введите в инт рактивной оболочке следующие команды. »> 'Hello'.center(20) , Hello I »> 'Hello'.center(20, '=') '=======Hello========' Эти методы особенно полезны в ситуациях, коrда необходимо вывести табулированные значения с подходящей разрядкой. Откройте новое окно в файловом редакторе, введите в HeI'O следующий код и сохраните ero в файле picnicTahle.frY. def printPicnic(itemsDict, leftWidth, rightWidth): print('PICNIC ITEMS' .center(leftWidth + rightWidth, '')) for k, v in itemsDict.items(): print(k.ljust(leftWidth, '.') + str(v) .rjust(rightWidth)) picnicltems = {'sandwiches': 4, 'apples': 12, 'cups': 4, 'cookies': 8000) printPicnic(picnicltems, 12, 5) printPicnic(picnicItems, 20, 6) В этой проrрамме мы определяем метод printPicnic () , который получа ет данные в виде словаря и использует методы center (), ljust () и rjust () для отображения этих данных в аккуратном формате в виде выровненной страницы. Функции printPicnic () передается словарьрiспiсltеms. В нем содержат- ся переменные, Имеющие следующие значения: sandwiches 4, apples 12, cups 4 и cookies 8000. Мы хотим представить информацию в двух столб.. цах, причем слева должно отображаться название блюда, а справа коли- чество. Для этоrо нужно решить, какой ширины должны быть левый и правый столбцы. Эти значения будут передаваться методу printPicnic () вместе со словарем.
178 r лова 6 Метод printPicnic () принимает словарь, а также значения ширины для левою (leftWidth) и правоrо (rightWidth) столбцов. Он выводит заrоловок PICNIC ITEMS над таблицей по ее центру. Затем он обрабатывает в цикле эл " » Й менты словаря, выводя каждую пару имязначение в отдельно строке таблицы, причем ключ выравнивается влево, значение вправо, а остав- шиеся в каждом столбце пустые позиции заполняются пробелами. Определив метод printPicnic () , мы определяем словарь picnicItems и дважды вызываем метод printPicnic () , передавая ему различные значения ширины левоrо и правою столбцов. В процессе выполнения проrрамма дважды выводит данные. В пер вый раз ширина левой колонки составляет 12 символов, а правой 5. Во второй раз эти значения СОСТawIяют соответственно 20 и 6 символов. PICNIC ITEMS sапdwiсhеs.. 4 app1es. . . . .. 12 сирs. . . . . . .. 4 ceekies..... 8000 PICNIC ITEMS sandwiches.......... 4 app1es. . . . . . . . . . . . .. 12 cups. . . . . . . . . . . . . . .. 4 ceekies............. 8000 Используя методы rj ust () , lj ust () и center () , вы можете быть уверены в том, что данные в строках таблицы аккуратно выровнены, даже если точ ное количество символов, содержащихся в каждой строке, неизвестно. 'lIt1лен.е пробело. t пО.ОЩ61О .e1Ollo. strip () , rstrip () . lstrip () Иноrда возникает необходимость в удалении из строки ее ведущих, за- мыкающих или и тех, и друrих пробельных символов (пробелы, симв<r лы табуляции и символы новой строки). Метод strip () возвращает новую строку без начальных и конечных пробельных символов. Методы lstrip () и rstrip () удаляют пробельные символы соответственно в начале и конце строки. Введите в интерактивной оболочке следующие команды. »> .p8111. - , Hello World ' »> sp8111.. strip () 'НеНе Werld' »> 8p8111..1strip() 'НеНе Wer1d ' »> 8p8111..rstrip() , НеНе Wer1d'
Манипупирование строками 179 с помощью необязательноrо CTpoKoBoro apryмeHTa можно указать, какие символы должны удаляться с обоих концов строки. Введите в интерактив- ной оболочке следующие команды. »> зрам = 'sрамSpaшВааоnSрamEggsSpaшSраш' »> spaм.strip('aмpS') 'ВасопSраmЕggs' Передавая методу strip () apryMeHT I ampS' , мы сообщаем ему, что в на- чале и конце строки, сохраненной в переменной spam, должны быть удале- ны все вхождения символов а, т, р и S. Порядок указания символов в стро-- ке, передаваемой методу strip (), несущеетвен: вызовы strip ('mapS') или strip (1 Spam') дaдyr тот же результат, что и вызов strip (' ampS'). Коп8РО.IIН8е 8 '''"'К" "рок , пОМОЩ61О модул. pyperc:lip В модуле pyperclip имеются функции сору () и paste () , которые MoryT выполнять операции копирования и вставки текста через буфер обмена ва- шеrо компьютера. Пересылка вывода nporpaMMbI в буфер обмена упрощает вставку текста в сообщения электронной почты или документы TeKCToBoro процессора, а также еro передачу друroму nporpaмMHOМY 06е<:печению. Модуль pyperclip не постаWlЯется вместе с Python. Чтобы cro устано- вить, следуйте указаниям по установке модулей сторонних разработчиков, приведенным в приложении А. Установив модуль, введите в интерактивноЙ оболочке следующие команды. »> import pyperclip >>> pyperclip. сору (' Hello 1forld! ') »> pyperclip.paste() 'НеНо world!' Разумеется, если содержимое буфера будет изменено внешней nporpaм- мой, то именно оно будет возвращено методом paste (). Например, если я С1Соnирую дан/ное nред.п.ожен:ие в буфер об,М,еиа, а затем 8'Ы3oвy.мemoд pas te (), 7tЮ получу следуЮ11Jий lJ'Ы8oд: »> pyperclip.paste() 'Например, если я скопирую это предложение в буфер обмена, а затем вызову метод paste(), то получу сле вывод:'
180 r лава 6 Выпопнение сценариев Python вне IDLE До сих пор вы выполняли свои сценарии на Python с помащью интерактивной оболочки или фаЙl10воrо редактара в IDLE. Однако вряд ли вам понравится откры- вать IDLE всякий раз, Korдa потребуется выполнить сценарий. К счастью, существуют более удобные способы, упрощающие запуск сценариев Python. Соответствующие процедуры запуска сценариев дпя Windaws, OS Х и Linux HeMHoro различаются, но все они описаны по отдельности в приложении Б. 3аrляните в Hero, если хотите узнать подробнее об зтих способах, а также о том, как передавать сценариям apry- менты командной строки. (В IDLE такая ВОЗМОЖНОСТь отсутствует.) Проект: паропьная защита Возможно, у вас есть учетные записи на МНОI'ИХ веб-сайтах. Использо- вать для каждой из них один и тот же пароль плохая привычка, поскольку при наличии брешей в системе безопасности любоrо из этих сайтов хаке- рам открывается доступ ко всем вашим учетным записям. Наилучшее реше- ние использовать менеджер паролей на своем компьютере <: одним rлав. ным паролем для доступа к нему. СЛкрыв менеджер паролей, вы сможете скопировать нужный пароль в буфер обмена и вставить ero в поле Введите пароль, предоставляемое при попытке доступа к ве&сайту. Предлаrаемая в этом примере проrрамма менеджера паролей не обеспt..... чивает полной безопасности, но демонстрирует базовые принципы работы проrрамм подобною рода. Проекты в rnaBax Это первый из "npoeUOB в rлаве", которые встречаются в книrе. Начиная с этой rnaBW и во всех последующих вам будут пpeдnаrатъся "роекты, демонстрирующие применение на практике понятий, рассмотрению которых была посвящена rлава. Особенностью написания всех праектов ямяется то, что каждый из них начинается с "чистоrо листа" (nycToro окна файловоrо редактора) и заканчивается полностью функциональным вариантом nporpaММbl. Как и при работе с примерами в интерак- тивной оболочке, было бы желательно, если бы вы не просто читали зти разделы, а прорабатывали их у себя на компьютере. Шоr ,. проеКТ8рО'ОН8е пporpa..... 8 иpyкryp ДОНН"Х МЫ хотим, чтобы проrрамма принимала apryмeHT командной строки, ко-- торым будет служить имя учетной записи, например учетной записи элек- тронной почты или блоra. Пароль для доступа к этой учетной записи будет
Манипулирование строками 181 копироваться в буфер обмена, откуда пользователь сможет вставить ero в ноле Password (Пароль). Блaroдаря этому пользователь не должен держать в памяти длинные пароли, которые надежны, но трудно запоминаются. Откройте новое окно файловоrо редактора и сохраните ero пустое со-- держимое в файле frшру. Проrpамма должна начинаться строкой директи вы #! (см. приложение Б), за которой следует комментарий, содержащий краткое описание назначения проrpаммы. Поскольку вы хотите связывать с именем каждой учетной записи пароль для доступа к ней, целесообразно хранить эти данные в виде строк словаря. Этот словарь и будет той CTPYКТY рой данных, которая обеспечивает орrаниэованное хранение имени учет ной заПИСИ и соответствующеrо пароля. Введите следующий код. #! python3 # pw.py Прорамма для незащищенноо хранения паролей. PASSWORDS = ('email': 'F7minlBDDuvMJuxESSKHFhTxFtjVB6', 'blog': 'VmALvQyКAxiVH5G8v01iflMLZF3sdt', , luggage': '12345') Шаr 2. Обработка apry8eHTo' К08а81180. (ТРОК8 ApryMeHTbI команДНОЙ строки будут храниться впеременной зуз. argv. (Более подробная информация о том, как использовать apryMeHTbI KOMaнд НОЙ строки В проrраммах, приведена в приложении Б.) Первой в списке sys. argv всеrда должна быть строка, содержащая имя файла проrраммы ( 'pw. ру' ), а вторым первый из apryMcHToB командной строки. Таким apry ментом для нашей проrраммы ЯWlяется имя учетной записи, с которой вы хотите ассоциировать lIароль. П(){кольку этот apryMeHT обязательный, вы отображаете сообщение, напоминающее пользователю о необходимости ввода имени учетной записи, если он забыл ero ввести (это будет в том слу чае, если в списке sys. argv (:одержится менее двух apryMeHToB). Дополните ранее введенный код (:лсдующим. #! руthопЗ # pw.py Прорамма для незащищенноrо хранения паролей. PASSWORDS {'email': 'F7minlBDDuvMJuxESSKHFhTxFtjVB6', 'blog': 'VmALvQyКAxiVH5G8v01if1MLZF3sdt', 'luggage': '12345') import .У. if len(sys.argv) < 2: рrint('Ис::попЪ80Вание: python p1f.py [ИIPI )'Четной записи] копирование napoпR )'Четной записи') ауа .exit() aoc::oun t - ауа. argv [ 1 ] I nераЪ1Й ap%'YМ8Н'l' командной C'l'p01СИ это I ИIPI )'Четной записи
182 r лава 6 Шоr 3. Коп.ро,он.е порол. Теперь, коrда имя учетной записи сохранено в виде строки в перемен ной account, вы должны проверить, существует ли оно в словаре PASSWORDS в виде ключа. Если существует, вы копируете значение ключа в буфер об- мена, используя вызов pyperclip. сору (). (Поскольку используется модуль pyperclip, ero необходимо импортировать.) Заметьте, что на самом деле без переменной ассоип t можно было бы обойтись, поскольку везде в про- rpaMMe вместо нее можно было бы использовать выражение sys . argv [1] . Однако лучше использовать переменную account, так как читать проrрамму в этом случае roраздо леrче. Дополните раиее введенный код, как ноказано ниже. #! руthопЗ # pw.py Проrрамма для незащищенноrо хранения паролей. PASSWORDS ('email': 'F7miпlВDDuvМJuхЕSSКНFhТхFtjVВ6', 'blog': 'VmALvQуКАхiVН5G8vО1iflМLZFЗsdt', 'luggage': '12345'} import зуз, pyperclip if len(sys.argv) < 2: рriпt('Использование: python pw.py [имя учетной записи] копирование пароля учетной записи') зуз. exit () account = sys.argv[l] # первый apryмeHT командной строки это # имя учетной записи if account in PASSNORDS: руреrсliр.сору(РASSИORDS[ассоunt) рrint('паропь ДnR ' + account + ' Qхоnиpoван в буфер. ') else: print ( 'Учетна. запись ' + account + 'отсутствует в cnисхе.') Добавленный код выполняет поиск имени учетной записи в словаре PASSWORDS. Если это имя ЯWlяется ключом в словаре, то мы получаем значе иие, соответствующее этому ключу, копируем ero в буфер обмена и выводим сообщение, которое подтверждает выполнение копирования. В противном случае выводится сообщение о том, что учетная запись с таким именем 0'1'- cyrCTBYeт в списке. Этот сценарий предстаWlяет собой полностью завершенную проrрамму, Вопюль;ювавшись приведенными в приложении Б инструкциями по запу- (KY проrрамм из командной строки, вы можете теперь быстро копировать l1арОJlИ своих учетных записей в буфер обмена. Если вам понадобится об- новить проrрамму для изменения или д06awJения паролей, то достаточно будет внести необходимые изменения в код словаря PASSWORDS.
Манипулирование строками 183 Разумеется, вряд ли вы захотите хранить все свои паро.ли в одном Mecre, от- куда любой человек может леrко их скопировать. Но можно модифицировать эту проrрамму и исполыовать ее для быстроrо копирования обычноro текста в буфер обмена. Предположим, вы отправляете несколько писем электронной почты, в которых содержится MHoro общих абзацев. Вы можете поместить каждый абзац в качестве значения в словарь PASSWORDS (в этом случае вы, веро- ятно, измените имя словаря), а затем будете иметь возможноcrь быcrро выби рать и копировать множество стандартных фрarментов текста в буфер обмена. Пользователи Windows MOryт создать пакетный файл (файл с расшире.- нием . Ьае) для запуска этой проrраммы из окна Выполнить, которое можно леrко открыть, нажав комбинацию клавиш <Windows+R>. (Более подробно о пакетных файлах читайте в приложении В.) Введите в окне фаЙЛОВОI'О редактора и сохраните в файле рш.Ьаtв папке С:Windoмследующий код. @ру.ехе C:Python34pw.py %* @pause с использованием только что созданноrо пакетноrо файла выполнение проrраммы для хранения паролей сводится к нажатию комбинации клавиш <Windows+R> и вводу команды pw <имяучетной записи>. Проект: добавпение маркеров в разметку Wiki-Аокументов Редактируя статьи в Википедии, можно создавать маркированные спи- ски, вводя каждый элемент списка в отдельной строке, которая предваря- ется маркером в виде звездочки. Предположим, что у вас имеется очень большой список, в который нужно добавить маркеры. Вы моrли бы сделать это вручную, вводя символы звездочки в начале каждой строки, и так стро- ка за строкой. Но эту задачу можно лсrко автоматизировать с помощью ко- pOTKoro сценария Pytlюп. Сценарий bulletPointAdder.py будет получать текст из буфера обмена, до- бавлять звездочку и пробел в начале каждой строки, а затем возвращать этот новый текст в буфер обмена. Например, если бы я скопировал в буфер обмена текст (предназначенный ДIIЯ публикации в Википедии статьи "Спи- сок списков списков") Lists of animals Lists of aquarium life Lists of biologists Ьу author abbreviation Lists of cultivars а затем ВЫIIOJlНИЛ ПрOl'рамму bulletPointAdder.py, то в буфере обмена содер- жался бы следующий текст:
184 r лава 6 * Lists * Lists * Lists * Lists of animals of aquarium life of biologists Ьу author abbreviation of cultivars Этот предваряемый звездочками текст полностью подroтовлен к вставке в статью Википедии в качестве маркированноrо списка. ш", ,. (ОnllРО'''IIII' 11 ."".К" nocpella.o. 'уф.", 0'..11" Вы хотите, чтобы проrpамма bиlletPointAdder.py делала следующее: 1) вставляла текст из буфера обмена; 2) выполняла над ним некоторые действия; 3) копировала новый текст в буфер обмена. С пунктом 2 придется немноro повозиться, а вот пункты 1 И 3 достаточно просты: они требуют использования Bcero лишь двух вызовов pyperclip. сору () и pyperclip. paste (). Поэтому на данном этапе мы напишем только ту часть проrраммы, которая реализует шаrи 1 и 3. Введите в файловом peдaк торе приведенный ниже код и сохраните ею в файле bиlletPointAdder.py. #! руthопЗ # bulletPointAdder.py Добавляет маркеры Википедии в начало # каждой строки текста, cOXpaHeHHoro в буфере обмена. import pyperclip text = pyperclip.paste() # TODO: разделить строки и добавить звездочки. pyperclip.copy(text) Комментарий TODO (ЧТО СДЕЛА ТЬ) напоминание о том, что эту часть проrраммы еще предстоит написать. Следующий шаr фактическая реали- зация данной части проrраммы. Ш",2. '''J'"'K'' reKt", н" арОКII 11 1I0'"и'Нllе J"J/I0"'K Вызов pyperclip.paste () возвращает весь текст из буфера в виде одной большой строки. Если бы мы использовали пример со статьей "Список списков списков", то строка, сохраненная в виде текста, выrлядела бы так: 'Lists of animalsnLists of aquarium lifenLists of biologists Ьу author abbreviationnLists of cultivars'
Манипулирование строками 185 Наличие в этой строке символов новой строки п приведет к тому, что она будет отображаться в виде мноrострочноrо текста при выводе на экран или вставке из буфера обмена. В этом одном строковом значении содер- жится MHoro "строк". Вам нужно добавить звездочку в начале каждой из этих строк. Можно было бы написать код, который выполняет поиск всех символов n и вставляет после каждоrо из них звездочку. Однако rораздо проще ис пользовать метод spli t () для возврата списка строк, по одной для каждой строки ориrинальноrо текста, а затем добавить звездочку в начале каждой строки, входящей в список. Придайте проrрамме следующий вид. #! руthопЗ # bulletPointAdder.py Добавляет маркеры Википедии в начало # каждой строки текста, coxpaHeHHoro в буфере обмена. import pyperclip text = pyperclip.paste() t Pa8AeпReT строки и добаапяет lines = text.split('n') for i in range(len(lines»): lines[i] - '* , + lines[i] звездочки. t ЦИJCJJ по cnиску "lines" I доб8.8lUlет ЗВ&8доЧJCУ в KaJКДytO I строку в cnиске "lines" pyperclip.copy(text) Выполняя разбиение текста по символам новой строки, мы получаем СIIИ. сок, в котором каждый элемент списка располаrается в отдельной строке текста. Мы сохраняем этот список в строках, а затем проходим в цикле по всем элементам в этих строках. В начале каждой строки мы добавляем зве дочку и пробел. Теперь каждая текстовая строка начинается со звездочки. Шаr 3. 06.еll.нен.е .,.ененн.,х (трок Теперь строки списка содержат измененные строки, начинающиеся со звездочек. Но метод pyperclip. сору () ожидает значение в виде одиночной строки, а не списка строковых значений. Чтобы создать это одиночное строковое значение, передайте строки методу j oin () для их объединения в одну строку. Дополните проrраммный код, как показано ниже. #! руthопЗ # bulletPointAdder.py Добавляет маркеры Википедии в начало # каждой строки текста, coxpaHeHHoro в буфере обмена. import pyperclip text = pyperclip.paste()
186 r лова 6 # Разделяет строки и добавляет lines = text. split (1 n') for i in rапgе(lеп(liпеs)): lines[i] = '* , + lines[i] звездочки. # цикл по списку "lines" # добавляет звездочку в каждую # строку в списке "lines" text - 'n'.join(lines) pyperclip.copy(text) В процессе выполнения проrрамма заменяет текст из буфера обмена тек- стом, в начале каждой строки KOToporo располаrаются звездочки. Этот код пред(тавляет завершенный вариант проrраммы, и вы можете попытаться выполнить ее в отношении текста, скопированноro в буфер обмена. Даже если вы не испытываете потребности в автоматизации :той дo вольно специфической задачи, вам MOryт понадобиться друrие виды мани пуляций с тек(:том, такие как удаление замыкающих пробелов в конце строк или преобразование текста в верхний или нижний реrистр. Какой бы ни была специфика ваших запросов, вы можете использовать буфер обмена для выполнения операций ввода и вывода. Резюме 1скст распространенная форма представления данных, и PyLllOll по-- ставляется с множеством полезных методов, предназначенных для обра- ботки текста, сохраненнш'о в строковых значениях. Вы можете использо-- вать индексирование, ИЗWlечение срезов и строковые методы практически в любой проrpамме на Python, которую будете пи(ать. Проrраммы, которые вы сейчас пишете, кaжyrся не слишком сложными: они не имеют rрафическоrо интерфейса пользователя с красочными изо-- бражениями и цветным текстом. Пока что вы отображаете текст с помо щью метода print () и предостаWlяете пользователю возможность вводить текст, используя метод input () . Однако пользователь может ускорить ввод больших объемов текста посредством буфера обмена. Эта возможность открывает широкий простор для написания nporpaмM, манипулирующих большими объемами текстовых данных. Пусть даже эти проrраммы не со- держат окон или rрафики, но они Moryr быстро выполнять массу полезной работы. Дрyroй способ манипулирования большими объемами текста это чт ние и запись текста путем непосредствеШlOrо обмена данными с жестким диском. О том, как это делается с помощью РytlЮI1, вы узнаете из следую. щей rлавы.
Манипулирование строками 187 Контропьные вопросы 1. Что такое экранированные символы? 2. Что представляют собой экранированные символы п и t? 3. Как добавить символ обратной косой черты () в строку? 4. Строковое значение "Howl' s Moving Castle" это допустимая строка. Почему она не вызовет ошибку, несмотря на наличие неэкранирован Horo символа апострофа в слове Howl' s? 5. Если вы не хотите вставлять символ п в свою строку, то как вы напи шете строку, содержащую символы новой строки? 6. Каковы будут результаты вычисления приведенных ниже выражений? . 'Hello world!' [1] . 'Helloworld!' [0:5] . 'Helloworld!' [:5] . 'Helloworld!'[3:] 7. Каковы будут результаты вычисления npиведенных ниже выражений? . 'Hello' .upper() . 'Hello' .upper() .isupper() . 'Не Но'. upper () .lower () 8. Каковы будут результаты вычисления приведеllНblX ниже выражений? . 'RememЬer, rememЬer, the fifth of NovernЬer. '.split() . ''. join ('There сап Ье only опе. ' . split () ) 9. Какие методы используются для выравнивания строки 110 левому краю, правому краю и центру? 10. как удалить пробельные символы в начале и конце строки? Учебный проект Чтобы закрепить полученные знания на практике, напишите проrрамму для предложенной ниже задачи. т",..,,.... ....OIII1".HIIX Напишите функцию printTable (), которая принимает список списков строк и отображает ero в виде аккуратной таблицы с выравниванием тек(:та по правому краю в каждом столбце. Исходите из предположения, что вну- тренние списки будyr содержать одно и то же количество строк. Например, список Mor бы выrлядеть так.
188 rЛQВQ 6 tableData [['app1es', 'oranges', 'cherries', 'Ьапапа'], ['A1ice', 'ВоЬ', 'Caro1', 'David'], ['dogs', 'cats', 'moose', 'goose']] Manipu1ating Strings 143 ВЫВОД функции printTable () будет примерно таким. app1es oranges cherries Ьапапа Alice dogs ВоЬ cats Carol moose David goose Подсказка. Ваш код должен в первую очередь найти самую длинную строку в каждом из внyrренних списков; ширина столбца должна быть дo стаТОЧIIО большой для 1'01"0, чтобы В нем уместилась любая строка. Значения максимальной ШИРИНЫ столбцов MOryr храниться в виде списка целых чи. сел. Код функции printTable () может начинаться с инструкции co1Widths = [О) * 1еп (tableDa ta) , которая создает список, содержащий значения О в ко-- личестве, равном количеству внyrренних списков в списке tab1eData. Таким образом, в элементе co1Widths [О] может храниться ширина самой длинной строки, содержащейся в tableData[O], в элементе colWidths [1] ширина самой длинной строки, содержащейся в tableData [1] и т.Д. Затем вЫ може- те найти самое большое значение в списке colWidths, чтобы определить, какое целочисленное значение ширины следует передать строковому ме- тоду rjust ().
ЧАСТЬ 11 АВТОМАТИЗАЦИЯ ЗАДАЧ
ПОИСК по ША&пону с ПОМОЩЬЮ РЕrупярных ВЫРАЖЕНИЙ Вероятно, вам не раз приходилось выполнять текстовый поиск, вводя слова, которые нужно найти, и нажимая клави II1И <Ctrl+F>. Рееулярuш выражения представляют собой сле.- дующий шar в этом нанраWlении: они позволяют задавать uщбло1t искомоro текста. Вы можете не знать телефонный номер компании, но если вы живете в США или Канаде, то вам известно, что он будет включать три цифры, за которыми следуют де- фис и еще четыре цифры (в самом начале номера может указываться необя зательный территориальный код). Зная этот шаблон, можно сразу же IIpeд ПОЛОЖИТЬ, что цифры 4155551234 означают телефонный номер, а цифры 4,155,551,234 таковым не ЯWlяются. Как ни полезны реryлярные выражения, лишь немноrие из тех, кто не является ПРOl'раммистом, знают, что это такое, хотя большинство COBpe меНIlЫХ текстовых процессоров, таких как Microsoft Word или ОрспОШсс, прсдлarают средства IIОИСка и замсны, основанные на использовании peI)' лярных выражсний. Реryлярные выражения сэкономят массу времени не только пользователям, но и проrраммистам. Более Toro, по мнению KaHaд cKoro писателя Кори Доктороу, изучение реryлярных выражений ДОЛЖНО предшествовать изучению caмoro проrpаммирования: "Знание [реryлярных выражений] может означать разницу между ре- шением задачи за 3 действия или за 3000 действий. Korдa ты дока в проrраммировании, то леrко забываешь о том, что на задачу, решение которой стоит тебе нажатия какой-то пары клавиш, дрyrие MOryт по- тратить несколько дней напряженной работы, чреватой внесением дополнительных ошибок"l. ] Cory Doctorow, "Here's what ICT shou}(l really (еаС)l kids: how to do regular expressions," Guardian, December 4, 2012, http://www.theguardian.com/ technology/2012/dec/04/ictteachkids regularexpressions/.I1t..
192 r лава 7 в этой rлаве вы сначала напишете проrрамму для нахождения образцов текста без использования реryлярных выражений, а затем увидите, как реryлярные выражения позволяют сделать то же самое с использованием rораздо более ЯСlюrо кода меньшеrо объема. Я продемонстрирую вам, I<ак работают простые шаблоны на основе реryлярных выражений, после чеro мы займемся более сложными операциями, такими как замена строк и соз дание собственных символьных классов. Наконец, в завершение rлавы вы напишете проrpамму для автоматическоrо извлечения телефонных HOMe ров и адресов электронной почты из блока текста. Поиск образцов текста без испопьзования реrупярных выражений Предположим, вы хотите найти телефонный номер, содержащийся в строке. Исходный текстовый шаблон вам известен: три цифры, дефис, три цифры, дефис, четыре цифры, например 4155554242. Для проверки соответствия строки данному образцу мы будем исполь- зовать функцию isРhопеNшnbеr (). возвращающую одно из двух возможных значений: Trиe или False. Откройте в файловом редакторе новое окно, B дите в нем приведенный ниже код и сохраните ero в файле isphoпeNиmЬer.py. def isPhoneNиmber(text): . if len (text) ! = 12: retиrn False for i in range(O, З): . if not text[i].iSdecimal(): retиrn Fa1se . if tехt[З] != '': retиrn False for i in range(4, 7): . if not text[i].isdecimal(): retиrn False . if text[7] != '': retиrn False for i in range(8, 12): Ф if not text[i].isdecimal(): retиrn False . retиrn Trиe print('4155554242 это телефонный номер:') print(isPhoneNиmber('4155554242'») print('Moshi moshi это телефонный номер: ') print(isPhoneNиmber('Moshi moshi')) Выполнив эту проrрамму, вы должны получить следующий вывод.
ПОИСК ПО шаблону с ПОМОЩЬЮ реryлярных выражений 193 4155554242 это телефоннь номер: Trиe Moshi moshi это телефонный номер: False Код функции isPhoneNumber () выполняет несколько видов тестов, цель которых проверить, представляет ли содержащаяся в переменной text строка телефонный номер в корректном формате. При отрицательном ис ходе любой из проверок функция возвращает значение False. Сначала про-- веряетси, содержит ли строка в точности 12 символов О. Затем выполняет-- си проверка Toro, что территориальный телефонный код (т.е. первые три символа текста) состоит только из цифр О. Остальная часть кода функции проверяет соответствие строки формату шаблона телефонноrо номера: за территориальным кодом следуют первый дефис О, еще три цифры О. еще один дефис О и, наконец, еще четыре цифры ф. Если ПрOl'рамме удается выполнить все виды проверки, то возвращается значение True О. При вызове функции isPhoneNиmber () с apryмellToM '4155554242' воз- вращается значение True. Вызов функции isPhoneNumber () с apryмeHToM 'Moshi moshi' возвратит значение False; при этом не будет пройден уже первый тест, поскольку количество символов в строке 'Moshi moshi' отли- чается от 12. Для нахождения приведенноrо выше TeKcToBoro образца в строке боль- шеr() размера вам потребуется написать еще больше кода. Замените послед- ние четыре вызова функции print () в файле isPhoпeNuтber.frY следующим кодом. message = 'Позвони мне завтра по номеру 4155551011. 4155559999 это телефонный номер Moero офиса.' for i in range(len(message)): . chunk = message[i:i+12] . if isPhoneNumber (chunk) : рriпt('Найденный телефонный номер: I + chunk) print ( 'rOTOBO' ) Выполнив эту nporpaMмy, вы должны получить следующий вывод. Найденный телефонный номер: 4155551011 Найденный телефонный номер: 4155559999 rOTOBO На каждой иrерации цикла for переменной chunk присваиваются очеред' ные 12 последовательных символов строки message О. Например, на первой итерации i равно О, и переменной chunk присваиваеl'СЯ срез message [о: 12 ]
1М rnOBO 7 (т.е. строка' Позвони мне '). На следующей итерации i равно 1, и lIеремен- ной chunk присваивается срезmеssаgе [1: 13] (т.е. строка 'озвони мне з'). Вы передаете очередную порцию строки функции isPhoneNumber ( ) , что- бы проверить, соответствует ли она образцу телефонноrо номера О, и если это действительно так, то выводите эту часть строки на экран. Циклический просмотр строки message продолжается, и в конечном счете будут обнаружены 12 символов, представляющих телефонный но. мер. В ходе выполнения Bcero цикла просматривается вся строка, причем каждый раз тестируются части строки длиной 12 символов и выводят ся те из них, которые удовлетворяют условиям, проверяемым в функции i sPhoneNumber () . По окончании анализа всей строки message будет выведено слово rOTOBO. В то время как содержащаяся в данном примере впеременной message строка очень короткая, в друrих случаях она может содержать миллионы символов, и тем не менее проrрамма выполнится менее чем за секунду. Аналоrичная проrрамма, выполняющая поиск телефонных номеров с П0 мощью реryлярных выражений, также будет выполняться менее ceКYHДьr, однако реryлярные выражения позволяют тратить на написание подобных проrрамм rораздо меньше времени. Поиск образцов текста с помощью реryпярных выражений Предыдущая проrрамма для нахождения телефонных номеров вполне функциональна, однако, учитывая ее относительно большой размер, пред- лаrает лишь оrраниченные возможности: функция isphoneNumber () вклю- чает 17 строк кода, но способна нахОДить телефонные номера, соответ, ствующие только одному виду шаблона. Что если телефонный номер будет записан в строке в формате 415.555.4242 или (415) 5554242? Что если теле- фонный номер включает дополнительные цифры и выrлядит, например, так: 4155554242 х99? Функция isPhoneNumber () не в состоянии идентифи- цировать подобные номера. Чтобы учесть дрyrие допустимые шаблоны, вы моrли бы написать дополнительный код, однако существует rораздо более простой способ. Компактное описание текстовых шаблонов возможно с помощью pery- лярных выражений. Например, реryлярному выражению d соответствует любой цифровой символ, Т.е. любая одиночная цифра от О до 9. Реryлярное выражение dddddddddd используется в Python для нахождения текстовых строк 1'01"0 же формата, что и в случае функции isPhoneNumber ( ) : строка из трех цифр, дефис, еще три цифры, дрyrой дефис и еще четыре цифры. Никакая дрyrая строка не будет соответствовать реryлярному BЫ ражению dddddddd dd.
Поиск ПО шаблону с помощью реrулярных выражений 195 Вместе с тем реryлярные выражения MOryт быть l'ораздо более СЛОЖНbl ми. Например, указав 3 в ФИl'УРНblХ скобках ( (3) ) после шаблона, мы co общаем следующее: "Искать троекратное соответствие этому шаблону". Поэтому корректному телефонному номеру будет соответствовать также следующий шаблон: d{ 3} d{3}d{4}. '0111"..' 06.,.108 'евех В PytllOn все функции, предназначенные для работы с реryлярными BЫ ражсниями, содержатся в модуле re. Введите в интерактивной оболочке ОIедующую команду для Toro, чтобы импортировать этот модуль: >>> ilDport re Примечание Для вЫnОЛНe1tuя бол'Ьшuнства nос.ледующuх npшtеров z.ла8'bl вам потребуется мо- дул'Ь re, поэтам у не заб'bl8аuте uмnopтиpoвaт'Ь еео в но:чале люfЮeо Сl/.епаpuя, кото- рый напишете, шu при каждом nepl!3anycкe IDLE. В npomивШJМ случае вы noлучu- те сообще1tuе об ошибке NaтeError, z.ласящее о 'Т1UJМ., чmo имя ' re' не оnредeлeuо. Вызов метода re. compile () с передачей ему CTpoKoBoro значения, пред- ствляющеrо реryлярное выражение, возвращает объект соответствующеrо реryлярноrо выражения (или, как их принято называть, объект Regex). Чтобы создать объект Regex, совпадающий с шаблоном телефонноro но- мера, введите в интерактивной оболочке следующую команду (вспомните, /" Передача 11 сырых" строк методу re. COIIIpile () Вспомните, что в Python ДЛЯ экранирования символов используется символ аб- ротной косой черты (). Строковае значение' n' представляет одиночный символ новой строки, а не символ обратной косай черты, за котарым следует строчная бук- ва n. Чтобы выеести символ обратной косой черты, вам патребуется ero экрани ровать ( ). Таким образом, СТрОКа ' n' представляет символ обратной косой черты, за которым следует строчная буква n. Однако, поместив символ r перед пер- вым апострофом cтpoKoBoro значения, вы можете указать, что это и сырая" строка, в которой символы не экранированы. Поскольку символы обратной косой черты часта используются в реryлярных вы- ражениях, удобно передавать методу re. compile () "сырые" строки, а не вводить дополнительные символы обратной косой черты. Ввести r' dddddddd dd' HOMHoro проще, чем' d d d d d d d d d d', ./
196 rЛова 7 что выражение d означает "цифровой символ", а выражение ddddd ddddd реryлярное выражение, соответствующее шаблону корректно- ['о телефонноrо номера): »> phoneNumRegex = re.compile(rfdddddddddd') Теперь в переменной phoneNиrnRegex содержится объект Regex. пO.tK too,.e,t,.., 06.ек,аll 'е,ех Метод search () объекта Regex ищет в переданной ему строке любые сов. падения с реryлярным выражением. В случае отсyrствия в строке совпадс. ний с реryлярным выражением он возвращает значение None. Е(:ли совпа- дения обнаружены, то метод search () возвращает объект Match. Объекты Match имеют метод group (), который возвращает найденные соответствия шаблону, (О том, что такое rруппы, будет сказано ниже.) В качестве нриме- ра введите в интерактивной оболочке следующие команды. »> phoneNumRegex = rе.сошрilе(r'd'ddd'dd'dd'dd') »> шо = рhоneNumRegех.sеаrch('Мой номер: 41S5554242. ') »> рrint('НайденНblЙ тепефонНblЙ номер: ' + шо.grоuр(» Найденный телефонньм номер: 4155554242 Здесь то это не более чем произвольное имя, выбранное в качестве ти повоrо имени переменной для работы с объектами Ма tch. Возможно, этот пример поначалу покажется вам слишком сложным, но он значительно ко- роче представлснной ранее проrpаммы isPhoneNumЬer.py, хотя делает то же самое. В этом примере мы передаем желаемый шаблон методу re. compi le ( ) и сохраняем результирующий объект Regex в переменной phoneNumRegex. Затем мы вызываем метод search () для переменной phoneNurnRegex и пер даем ему строку, в которой хотим найти соответствия шаблону. Результат поиска сохраняется в переменной то. В данном при мере нам известно, что искомый образец будет найден в строке, и поэтому мы знаем, что метод search () вернет объект Match. Зная, что переменная то содержит объект Match, а не нулевое значение None, мы можем вызвать для нее метод groиp () , возвращающий найденное соответствие. Передача функнии print () выра- жения то . group () обеспечивает вывод получеШlOrо соответствия шаблону, Т.е. отображение телефонною номера 4155554242.
Поиск ПО шаблону с ПОМОЩЬЮ реrулярных выаженнйй 197 nOllltlro.". про"му", ПО.,",, ,oorвeTa... реryл.рно.у .",,,,.енн. Процедура использования реryлярных выражений включает несколько шаrов, каждый из которых довольно прост. 1. Импортируйте модуль regex С помощью инструкции import re. 2. Создайте объект Regex с помощью функции re. compi le ( ) . (1 le забы вайте о том, что необходимо использовать "сырую" строку.) 3. Передайте строку, в которой хотите ВЫIЮЛНИТЬ поиск, методу search () объекта Regex. Этот метод возвращает объект Match. 4. Вызовите метод group () объекта Мatch, который вернет строку, содер- жащую фактический найденный текст, соответствующий данному ре... ryлярному выражению. Прuмечаuuе Вся'ЧеС1СU поощряя са.м'оcmОЯтeJlЪUое въtnолuеиие вами 'кода nPUltte/Joo в иитфа",тuв- нoU оБОJlО'Ч",е, я ре1Сом,e1tдую uаРяду с этим uспол'ЬЗоватъ пРедЛа2аемые в Инmepжте тестер'Ы реzyлярu'Ых въфажеиuй, "'omojJые дают тО'ЧuуюкаРтUlIУ соответствия реZYЛЯрUЪtХ въtpаЖe1tuй фРаzмеuта.м ooeaeuuozo вами текста. Лu'Чuо я пojJeкo-м,eи- дова.л бы вам воспол'ЬЗоватъся mecтepoM. ",оторый предлаzaemся 'На caurne h t tp: 11 regexpal. сот/. Друrие возможные wабпоны реryпярных выражений Теперь, Korдa основные Шal'И по созданию и нахождению объектов pcry лярных выражений с помощью Python вам уже известны, вы вполнс roтопы испытать более мощные возможности поиска 110 ша6лонам. 'oJДtlH.e (рупп , по.о",,,. "рyrлыж ,,,o6o,, Предположим, вы хотите отделить территориальный код от осталыюй части телефонноrо номера. 1(о6авление круrлых скобок ПрИ80ДИТ к созда нию rрупп В реryлярных выражениях: (ddd) (ddddddd). Послс этоrо вы можете использовать метод group () объекта совпадения для захва та совпавшею текста, соответствующеrо только одной rруппе. Псрвый набор КРУl'Лых скобок в строке реryлярноrо ВЫР<lЖения будет rpYIl ной 1. Второй набор будет IРУППОЙ 2. Передавая целые числа 1 или 2 методу group () объекта совпадения, вы мож<-"Те избирательно захватывать различ lIые части совпаВШеl'О текста, Если методу group () передается О или вообще lIичеrо не передается, то он возвращает весь найденный текст, COOTBeтCTBY ющий шаблону, Введите в интерактивной оболочке следующие команды.
198 rЛQВQ 7 >>> phoneNwuRegex = re.OOIIIpile(r' (ddd)(ddddddd) ') »> ао = рhоneNUВRegex.sеаrch('НЬй номер: 4155554242.') »> mo.group(l) '415 ' »> mo.group(2) '5554242' »> mo.group(O) '4155554242' »> mo.groupO '4155554242' Если вам нужно ИЗWlечь сразу все rРУl1ПЫ, используйте метод groиps () (обратите внимание на показатель множественноrо числа в имени мето- да букву s). >>> ао. groups О ( , 415', '5554242 I ) »> areaCode, mainNU8ber = mo.groups() »> print(areaCode) 415 »> print(mainNuaber) 5554242 Поскольку метод то . groups () возвращает кортеж, состоящий из несколь- ких значений, вы можете использовать трюк с rрynповым присваиванием, коrда каждое значение присваивается отдельной переменной, как в строке areaCode, mainNиmbe r = то. g roиps () приведенноrо выше кода. Крyrлые скобки имеют в реryлярнbIX выражениях особый смысл, но что если вам нужно находить в тексте сами скобки? Ведь, например, в телефон- ных номерах круrлые скобки часто используются для выделения террито-- риальноrо кода. В подобных случаях символу ( или ) должен предшество- вать символ обратной косой черты. Введите в интерактивной оболочке следующие команды. >>> phoneNwllRegex = re.OOIIIpile(r' ((ddd» (ddddddd) ') > > > ао = phоneNшlRegex. еепсЬ ('НЬй номер: (415) 5554242.') »> mo.group(l) , (415) , >>> mo.group(2) '5554242' Экранированные символы ( и ) в "сырой" строке, передаваемой ме- тоду re. compile ( ) , означают соответствие фактическим символам крyrлых скобок.
Поиск по шаблону с помоЩЬЮ реryлярных выражений 199 'ы60р аЛ6rер.аr...6IХ (рупп t пО.ОЩ61О канала Символ I называется кана.ло.м. Вы можете использовать ero всеrда, Korдa хотите находить соответствие одному из нескольких аJII.тернативных BЫ ражений. Например, реryЛЯРIIОМУ выражению r' Batman I Tina Fey' будут со-- ответствовать как строка 'Babnan', так и строка 'Tina Fey'. Если в исследуемой строке содержатся обеэти строки, то в объекте Мatch будет возвращена та из них, которая встретится первой. Введите в интерак тивной оболочке следующие команды. »> heroRegex = re.сощрilе (r'ВatmanITina Fey') >>> шоl = heroRegex. search ( 'Ваtшan and Tina Fey.') >>> шоl. group () 'Batman' »> ш02 = heroRegex.search('Tina Ьу and Ваtman. ') >>> шо2. group () 'Tina Fey' ПjJuечаuие Для поиска всех совпадений с шаблоно.м .мОЖ1l0 UСnОЛ'Ь.10ваrпъ .метод Eiпdal1 (), котор'ый обсуждается в разделе "Метод Eiпda 11 () ". Для выбора альтернативных вариантов при поиске совпадений можно использовать канал. Предположим, например, что вы заинтересованы в поиске соответствий любой из строк 'Batman', 'Batmobile', 'Batcopter' и 'Batbat'. Поскольку в начале каждой из них содержатся символы Bat, было бы неплохо иметь возможность задать этот префикс только один раз. Это можно сделать с помощью круrлых скобок. Введите в интерактивной оболочке следующие команды. »> batRegex - rе.сошрilе(r'Ваt(шanlшОЬilеlаорtеrIЬаt) ') >>> 1110 = batR8gex.search('BatmObile потеряп капесо') >>> шо. group () I Batmobile' >>> шо.grоuр(l) 'mobile' Вызов то. group () возвращает весь совпавший с шаблоном текст, Т.е. строку' Batmobile' , тоrда как вызов то. group (1) возвращает лишь часть совпавшеrо текста, соответствующую первой rруппе в круrлых скобках, Т.е. 'mobile'. Используя символ канала и rруппирование с помощью Kpyr лых скобок, вы можете указать несколько альтернативных шаблонов для поиска соответствий с помощью реryлярноrо выражения.
200 rЛQВQ 7 Если требуется поиск соответствий самому символу канала, то в pery лярном выражении ему должен предшествовать символ обратной косой черты: 1. YIlOJtlH.e .е06.JtneЛ6.0i ""n61 ,...оло. , nOllО"61О '0Ilp.."1I6.0'0 ,нtI"" Иноrда возникает необходимость в шаблонах, содержащих необязателъ- Ilые символы или rpynпы символов. Это означает, что реryлярное выраж ние должно найти соответствие, Независимо от TOro, содержит ли оно H который фраrмент текста. Символ? указывает на то, что предшествующая ему rруппа является необязательной частью поисковоro шаблона. Введите в интерактивной оболочке следующие команды. »> batRegex = re.СОЩРil&(r'Ваt(wо)?aan') »> ао1 = batRegex.search('Тb& Adventures of Вatllan') >>> шо1. group () 'Batman' >>> ао2 = batRegex.search('Тbe Adventures of Вatwoman') >>> ао2. group () 'Batwoman' Часть (wo)? реryлярноrо выражения означает, что шаблон wo является необязательной rруппой. Реryлярному выражению будет соответствовать текст, в котором 110ДСТрОка wo либо вообще не встречается, либо встреча- ется только один раз. Именно поэтому данному реryлярному выражению соответствует как слово 'Ба twoman ' , так и слово 'Ва tman ' . Используя ранее приведенный пример телефонноrо номера, вы можете составить реryлярное выражение, находящее номера, которые MOryт содер- жать, а MOryт и не содержать территориальный код. Введите в интерактив- ной оболочке следующие команды. »> phoneRegex = re.coapile(r'(ddd)?ddddddd') »> шо1 - рhоneRegex.sеаrch('Мой номер: 41SSSS4242') >>> JlЮ1. group () '4155554242' »> ао2 = рhоneRegex.sеarch('Мой номер: S554242') »> 802. group () '5554242' Считайте, что символ? имеет следующий смысл: "Искать соответствие тексту, в котором rРУПllа. предшествующая вопросительному знаку, BCTpe чается нуль или один раз".
Поиск по шаблону с помощью реryлярных выражений 201 Если вам необходимо находить соответ(твие самому вопросительному знаку, то в реryлярном выражении ему должен предшествовать символ об.- ратной косой черты: ? У"tlзt1н.е (oorвe,т.. (руппе t...оло., по""р..ще." .УЛ6 .л. .еt"ОЛ6"0 РIlЗ, t пО.ОЩ61О з.е3ll0.". Символ * ("звездочка") означает "совпадение с нулевым или большим количеством экземWlЯрОВ", Т.е. rрynпа, предшествующая звездочке, может встречаться в тексте любое количество раз подряд. Она может либо вообще отсуктвовать, либо повторяться снова и снова. Вновь вернемся к примеру с Batm.an. »> batR8gex - re.соmрilе(r'Ваt(wo)*шan') >>> аоl - batReq&x. search ( 'Тh& Adventures of Ваtшan') »> mol.groupO 'Batman' »> ао2 - batReqex.search('The Adventures of Batwoun') >>> mo2.group() 'Batwoman' »> mоЗ = Ьа tRegex. search ( 'Тh& Adventures of BatW01f01fowoman') »> mоЗ.grоuр() 'Batwowowowoman' в случае слова 'Batm.an' часть (wo) * реryлярноrо выражения соответству- ет нулевому количеству (т.е. отсyrствию) экземпляров rруппы wo в строке; в случае слова' Batworoan' часть (wo) * совпадает с одним экземпляром wo; а в случае слова 'Ва twowowoworoan' часть (wo) * совпадает с четырьмя экземпля- рами rрynпы wo. Если вам необходимо находить соответствие самому символу звездочки, то в реryлярном выражении ему должен предшествовать символ обратной косой черты: * . УКtlЗt1н.е tOO".,t".. ОДНО.У .л. неt"ОЛ6".. по.,орен... (руппы t пО.ОЩ61О п".ttl Если символ * в реryлярном выражении означает "совпадение с нулевым или большим количеством экземпляров", то символ + ("плюс") означает "совпадение с одиночным или большим количеством экземпляров". В отли- чие от символа звездочки, который не требует появления предшествующей rруппы в исследуемой строке, rруппа, предшествующая плюсу, должна поя- виться в строке хотя бы один раз. Такая rруппа не является необязательной.
202 r лова 7 Введите в интерактивной оболочке следующие команды и сравните полу ченные результаты с результатами из предыдущеrо раздела, относящимися к реryлярным выражениям со звездочкой. »> batR8g_x - re.аошрilе(r'Ваt(wо)+шan') »> шо1 = batRegex.search('Тhe Adventures of Batwoman') >>> шо1. group () 'i3atwomaп, »> 802 - batReq_x. search ( 'The Adventures of Ваtwоwоwо_ошan') >>> ш02. group () 'Batwowowowoman' >>> шоЗ = batRegex.search('Тhe Adventures of Ваtman') »> шоЗ == None True Реryлярное выражение Bat (wo) +man не найдет соответствия в строке 'The Adventures of Batman', поскольку плюс требует наличия по крайней мере ОДНОI'О экземпляра подстроки wo. Если вам необходимо находить соответствие самому символу плюса, то в реryлярпом выражении ему должен предше(:твовать символ обратной ко-- сой черты: +. 'КII3I1н.е (001leT"... опреllе.е..о.у"о...е".у по.торен.' (ру..", ( .0.ОЩ61О Ф.rур.",х (ко60к Если у вас имеется rрynпа, которую вы хотите повторить определенное количество раз, укажите за ней число необходимых повторений в фиrypных Сlюбках в своем реryлярном выражении. Например, реryлярному выраже- нию (На) (3} будет соответствовать строка 'НаНаНа', но не строка 'НаНа I , по-- скольку В последнем случае I'рynпа (На) повторяется BcerO лишь два раза. Вместо одноrо числа вы можете указать диапазон, записав в фиrypных скобках значения минимальноrо И максимальноrо числа допустимых по- вторений, разделенные запятой. Например, реryлярному выражению (На) {3, 5} будут соответствовать строки 'НаНаНа', 'НаНаНаНа ' и 'НаНаНаНаНа 1. Как первое, так и второе из этих чисел в фиryрных скобках MOryт опу- скаться, оставляя неоrpаниченными минимальное и максимальное количес- тва повторений. Например, реryлярному выражению (На) {З, } будут соот- ветствовать три и более экземпляров rруппы (На), тоrда как реryлярному выражению (На) ( , 5} будут соответствовать от нуля ДО пяти экземпляров. Фиryрные скобки позволяют запи(:ывать реryлярные выражения в более компактном виде. Следующим двум реryлярным выражениям соответству- ют идентичные шаблоны.
Поиск по шаблону с помощью реryлярных выражений 203 (На) {3) (На) (На) (На) Этим двум реryлярным выражениям также соответствуют идентичные шаблоны. (На) {3,5) ( (На) (На) (На)) I ( (На) (На) (На) (На) ) I ((На) (На) (На) (На) (На)) Введите в интерактивной оболочке следующие команды. »> haRegex = re.сошрilе(r' (На)(3}') »> 801 = haRegex. search ( 'НаНаНа 1) >>> шо1. group () 'НаНаНа' »> ш02 = haRegex.search('Ha') »> ш02 == None True Эдесь реryлярное ВЫРC:lЖение (На) {3 I совпадает со строкой 'НаНаНа', 110 не со строкой 'На I . Поскольку оно не соответствует строке 'На', метод search () возвращает значение None. Жадный и неж:адный виды поиска Поскольку реryлярному выражению (На) {3, 51 MOryr соответствовать три, четыре или пять вхождений На В строке 'НаНаНаНаНа ' , вас может удив лять, почему вызов объектом Match метода groиp () В предыдущем приме ре с фиryрными скобками возвращает строку 'НаНаНаНаНа ' , а не ее более короткие 110ДСТрОКИ. В конце концов, строки 'НаНаНа' и 'НаНаНаНа ' также представляют (обой действительные соответствия реryлярному выраже нию (На) {З, 51. Реryлярные выражения Python по умолчанию являются жад'НьtМu в том смысле, что в lIеоднозначных ситуациях они бyдyr пытаться COOTBeтCTBO вать как можно более длинной строке. Нежадиая версия фиrypных скобок, которая пытается соответствовать самой короткой из возможных строк, характеризуется наличием вопросительноrо знака сразу за закрывающей фиrypной скобкой. Введите в интерактивной оболочке следующие команды и обратите вни мание на ра:JЛИЧИЯ между жадной и нежадной формами фиryрных скобок при выполнении поиска в одной и той же тестируемой строке.
2м r лава 7 »> qreedyHaRegex = А. compi1e (r' (На) {3, S} , ) > > > 1101 .. qreedyНaReqex. _earch ( 'НаНаНаНаНа' ) »> 1I01.groupO 'НаНаНаНаНа' »> nonqreedyHaReqex = rе.сошрilе(r' (Ha){3,S}?') »> 11102 .. nonqreedyHaReqex. _earch ( 'НаНаНаНаНа I ) >>> 11102. qroup () 'НаНаНа' Обратите внимание на то, что в реryлярных выражениях ВОIIроситель ный знак может использоваться в двояком смысле: указывать на нежадный поиск и обозначать необязательную rруппу. Эти две el'o функции никак между собой не связаны. Метод findall () В дополнение к методу search () объекты Regex имеют метод findall ()" Если метод search () возвращает объект Match первоrо совпадения, найден- Horo в тестируемой строке, то метод findall () возвращает строки каждом из найденных совпадений. Чтобы увидеть, как метод search () возвращает лишь объект Match для первом найденноrо вхождения совпадающеrо тек- ста, введите в интерактивной оболочке следующие команды. >>> phon8NuDlRsgex .. re.compile(r'dddddddddd') »> 110 = рhоneNumRegex._еarch('СО8ЫЙ: 41SSSS9999 Рабочий: 212SSSOOOO') >>> 110. qroup () '4155559999' с дрyrой <:тороны, метод findall () возвратит не объект Match, а список строк, 'Колъ cк()jJo 8 рееу.ляjmо.м 8ъtpажеиuu отсутствуют еруnn'Ь'. Введите в инте- рактивной оболочке следующие команды. >>> phoneNUIIIR8gex · re. cOlllpile (r ' ddd ddd dddd') * не ИИ88'l' :rpvnn »> рhоneNumRegех.findall('СО'l'оаый: 41SSSS9999 Рабочий: 212SSSOOOO') [' 4155559999', '212555OOOO'] В случае же nалu'Чuя rрупп в реryлярном выражении метод findall () воз- вратит список кортежей. Каждый кортеж представляет найденное cooт ствие, а ero элементы совпавшие строки для каждой rруппы в реryлярном выражении. Чтобы увидеть, как работает метод findall () , введите в инте- рактивной оболочке следующие команды (обратите внимание на то, что
Поиск по шаблону с помощью реryлярных выражений 205 теперь в компилируемом реryлярном выражении имеются rРУПIIЫ, заклкr ченные в круrлые скобки). »> phon.NumReq.x · х-е .cODIpil. (r' (ddd) (ddd) (dddd) ') #имн'1' 1"РУIUUol »> рhоneNuшRвqех.findall('СО'1'СВЫЙ: 41SSSS9999 Рабочий: 212SS50000') [('415', '555', '1122'), ('212', '555', '0000')] Подведем краткое резюме работы метода findall ( ) . 1. Будучи вызванным для реryлярноrо выражения, не содержащеrо rpynII, например dddddddddd. метод findall () возвращает список совпавших строк, такой как ['415555 9999', '2125550000']. 2. Будучи вызванным для реryлярноro выражения, содержащеl'О rрynпы, например (ddd) (ddd) (d ddd), метод findall () возвращает список кортежей строк (по одной строке для каждой rpYl1l1bI), такой как [('415', '555', '1122'), ('212', '555', '0000')]. Симвопьные кпассы Из предыдущих примеров вам уже известно, что сокращение d может представлять любую цифру. Таким образом, d это сокращенное обозна чение реryлярноrо выражения (О 1112 131 4 1 51 617 18 1 9) . Аналоrичные сокра- щения существуют для мноrих друrих символьных классов (табл. 7.1). Та6JIица 7.1. Сокращенн..е обоэначеНИII распроараненных символьных классов Сокращение d w w s 8 Предста......... CllМIIOJI" Любаll цифра в диапазоне от О до 9 ЛlOбой СИМ80n, не IIВnIlIOЩИЙС. цифрой в диапазоне от О АО 9 Люба. буква, цифра ИJlИ CНМ80Jl подчеркиваНИII (так называемые сло.арные CНМ8OJlы) Любой симвм, не .8JlllIOЩИЙСII буквой, цифрой ИIIИ СИМВОJlОМ подчеркивани. Симвм пробела, табуJlllЦИИ ИJlИ новой строки (ток иазываемые пробепьные СИМВОJlЫ) Любой симвм, не 1I1J1111ОЩИЙСII СИМВОJlОМ пробела, таБУJlllЦИИ иnи новой СТРОКИ Символьные классы (клаССbl символов) удобно использовать дЛЯ KOM пактной записи реryлярных выражений. Классу символов [05] будет со- ответствовать любая одиночная цифра в диапа:юне от О до 5; такая запись rораздо короче, чем запись (О 111 2 1 з 1 4 15) . Введите в интерактивной оболочке следующие команды. »> XDa.Regex - x-e.compil.(r'd+.w+') »> XDa.Reqex.findall('12 drumшer., 11 piper., 10 lord., 9 ladie.,
206 r лава 7 8 Dlaid8, 7 swans, 6 чвезе, 5 ring8, 4 Ьirdз, 3 hens, 2 doW8, 1 partridqe') ['12 drиmrners', '11 pipers', '10 10rds', '9 ladies', '8 maids', '7 swans', '6 geese', '5 rings', 14 birds 1, '3 hens', '2 doves', '1 partridge'] Реryлярному выражению d+sw+ будет соответствовать текст, содер- жащий одну или несколько цифр (d+), за которыми следует пробельный символ (з), за которым следует один или нес.колько словарных символов буква, цифра или символ подчеркивания (w+). Метод findall () возвращает все совпавшие строки шаблона реryлярноrо выражения в виде списка. Соэдание собственных симвопьных кпассов ИНOI'да возникает необходимость в с..опоставлении реryлярноI'О Bыpa жения с символами из определенноrо набора, для KOToporo сокращенные символьные классы (d, w, s и т.д.) оказываются слишком широкими. Вы можете создать собственный символьный клас.с, используя квадратные скобки. Например, символьному классу [aeiouAEIOU] будет соответствовать любая rласная, будь то в нижнем или верхнем реrистре. Введите в интерак тивной оболочке следующие команды. »> vowelRegex - rе.сошрilе(r'[аеiоuAEIОU)') >>> vowelReqex.findall('RoboCop eat8 ЬаЬУ food. ВАВУ FOOD.') ['о', 'о', 'о', 'е', 'а', 'а', 'о', 'о', 'Д', 'О', 'О'] в классы также можно включать диапазоны букв и цифр, используя де- фис. Например, классу [azAZO9] будуr соответствовать все буквы в ниж- нем и верхнем реrистрах, а также цифры. Обратите внимание на то, что в пределах квадратных скобок обычные символы реryлярных выражений не интерпретируются как таковые. Это означает, что перед символами ., *, ?, ( и ) не следует ставить символ обрат ной косой черты. Например, классу :O5.] будут соответствовать цифры от О до 5 и точка. Вы не должны записывать этот класс как [o 5 . ] . Поместив сразу за открывающей квадратной скобкой класса символ "крышка", или циркумфлекс (Л) ,можно создать u.uвертUр08ан'Н:ый CШtво.л:ьн'Ый 'КJЩСС. Инвертированному символьному клас(,,'У будет соответствовать любой символ, не принадлежащий исходному классу. Например, введите в инте- рактивной оболочке следующие команды. »> oOn8onantRegex = re.сошрilе(r' [AaeiouAEIOU) ') »> cOn8onantRegex.findall('RQboCop eat8 ЬаЬу food. ВАВУ FOOD.') ['R', 'Ь', 'с', 'р', I " 't', 's', I " 'Ь', 'Ь', 'у', , " If', 'd', '.', , " '8', '8', 'У', , " 'F', 'О', '.']
Поиск по шаблону с помоЩЬЮ реrУЛЯРНblХ Вblражений 207 Теперь вместо rласных реryлярному выражению будут соответствовать все символы, не являющиеся rласными. Симвоп крыwки И знак доппара Вы также можете использовать символ" ("крышка") в начале реryляр-- HOI'O выражения для указания 1'01"0, что соответствие рсryлярному Bыpa жепию должно находиты:я в nачале тестируемоro текста. Аналоrичным 0& разом можно поместить знак доллара ($) в конце реryлярноrо выражения для указания Toro, что строка должна заканчиваться данным шаблоном pe ryлярноrо выражения. Еще можно использовать символы" и $ совместно, для тою чтобы показать: данному реryлярному выражению должна соответ- ствовать вся строка, Т.е. совпадения с ним лишь какой-то подстроки будет недостаточно. Например, реryлярному выражению r' "Hello' будут соотвеТ<:твовать строки, начинающиеся СО слова' Hello' . Введите в интерактивной оболоч- ке следующие команды. »> beqinsWithHello = re.соmpilе(r'ЛНеllо') »> beqinsWithHello.search('Hello world!') <sre.SREMatch object; span(O, 5), matchIHello'> > > > beqin_Wi thHello. _earch ( 'Не _aid he1l0. ') == Ном True Реryлярному выражению r' d$' будут соответствовать строки, ;)аканчи. вающиеся любой цифрой в диапазоне от О до 9. Введите в интерактивной оболочке следующие команды. »> endsWithNumber = rе.сощрilе(r'd$') »> endsWithNumber._earch( 'Your number i_ 42') <sre.SREMatch object; span(16, 17), match12'> >>> endsWithNumber._earch( 'Your number i_ forty two.') == None True Реryлярному выражению r'" d+$' будут соответствовать строки, KOT<r рые начинаются и заканчиваются одной или несколькими цифрами. Вве- ДИте 8 интерактивной оболочке следующие команды. »> wholeStrinqIsHum = rе.сошрilе(r'Лd+$') »> wholestrinqI_Num._earch('1234567890') <sre.SREMatch object; span(O, 10), match'1234567890'> »> wholeStringIsNum. search( '12345xyz67890 1) == Ноne True »> wholeStrinqI_Num._earch('12 34567890') == None True
208 r лава 7 Последние два вызова метода search () в предыдущем примере дeMOH стрируют необходимость совпадения с реryлярным выражением всей cтp<r ки, если в нем используются одновременно символы л и $. Вам нужно лишь твердо запомнить, что символ л относится к началу строки, а символ $ к концу. rрупповой симвоп Символ. ("точка") в реryлярных выражениях называется epynn08ЪLМ cuм- волом и представляет собой сокращенную форму записи символьноrо клас. са, совпадающеro с любым одиночным символом, за исключением символа новой строки. Например, введите в интерактивной оболочке следующие команды. »> atRegвx · r..сощрil.(r'.аt') >>> atReqex.findall('Тhe cat in the hat _at оп the flat IIUlt. ') [ 'cat '1 'hat 1, 1 sat', 'lat', 'mat'] Вспомните о том, что символу точки соответствует только одиночный произвольный символ, что И объясняет, почему в качестве совпадающеrо текста в слове flat была выбрана только подстрока lat. Если вы хотите искать соответствие самой точке, то в реryлярном выражении ей должен предшествовать символ обратной косой черты: .. Ук",,,,,.е (00f8em... .",60_, ,екау ( 110110""'" ко_6.""".. иro.к",.е'1I0.к,,и Иноrда желательно, чтобы реryлярному выражению соответствовало все и вся. Предположим, например, что вы хотите найти совпадение со строкой I First Name:', за которой следует любой текст, за которым следует строка' Last Name:', а за ней вновь любой текст. для представления этоro "любоrо текста" можно использовать комбинацию "точказвездочка" (. *). Вспомните о том, что символ. означает "любой одиночный символ, за ис ключением символа новой строки", а символ * означает "нуль или более вхождений предыдущеrо символа". Введите в интерактивной оболочке следующие команды. »> nameReqвx = rе.сощрil.(r'Fir_t Наше: (.*) La_t Наше: (.*) ') >>> 110 = l1aIIeReqex. _earch ( I Fir_t Hue: Al La_t Н_.: Sw.iqart') >>> lIo.qroup (1) 'Al' >>> lIo.qroup(2) 'Sweigart'
Поиск по шаблону с помощью реryлярных выражений 209 Комбинация . * работает в режиме жадnоzо поиска: она вcelДa будет CTpt.>" миться захватить как можно больше текста. Чтобы заставить ее работать в нежадной манере, дополните ее вопросительным знаком: . *? Как и в слу- чае фиrypных скобок, вопросительный ЗНак указывает Python на необходи мость использования lIежадиоео поиска. Чтобы увидеть, чем различаются жадная и нежадная версии реryлярноro выражения, введите в интерактивной оболочке следующие команды. »> nonqreedyRegex · re.сошрilе(r'<.*?>') »> 1110 .. nOng'reedyRegex. вearcb ( ,<То serve un> for dinn.r. >' ) >>> 110. qroup () ,<То serve таn> , »> g'r.edyR8gex = re.compile(r'<.*>') >>> IDO · gre.dyRegex._earch('<To _erve un> for dinn.r.>') >>> 1110. group () '<То serve man> for dinner.>' в общих чертах смысл обоих реryлярных выражений можно выразить следующими словами: "Найти открывающую уrловую скобку, за которой следует произвольный текст, завершающий(:я закрывающая уrловая скоб- Ка". Однако в строке' <То serve тап> for dinner. >' имеюп'я два возможных варианта совпадения для закрывающей УI:ЛОВОЙ скобки. В нежадной версии реryлярноrо выражения Руthоп оrраничивается самой короткой из воз- можных строк: '<То serve тап> ' . в жадной версии PythOI1 стремится к тому, чтобы найти соответствие в виде как можно более длинной строки из всех возможных: ,<То serve тап> for dinner . >' . Уко,"..е 'OOf8ern... ,...аll. .о.о. ирок. , 110.0"..10 ro.к. Комбинации "точказвездочка" будет соответствовать все, за исключе нием символа новой строки. Передав методу re. compi le () при ero вызове константу re. DOTALL в качестве BToporo apryмeHTa, можно установить ре- жим, при котором точке соответствует также символ новой строки. Введите в интерактивной оболочке следующие команды. »> noN.wlineReqex = re.сошрilе('.*') »> noN.wlineReqex._.arch('Serve the public tru_t.nProt8ct the innocent. nUphold th. law.'). g'roup () 'Serve the pиblic trust.' >>> n.wlineReqex = re.compile('.*', юе.DOТALL) »> n.wlineReqex._earch('Serve the public tru_t.nprotect the innocent. nUphold th. law. I ) . qroup () 'Serve the pиblic trust.nProtect the innocent.nUphold the law.'
218 r лава 7 Реryлярному выражению noNewlineRegex, при создании KOToporo методу re . cornpile () не передавался apryмeнт re. DOTALL, будет соответствовать весь текст, но только до первоrо символа новой строки, тоrДа как реryлярному выражению newlineRegex, при создании котороro методу re. соrnpНе () была передана константа re. DOTALL, будет соответствовать весь текст, включая символ новой строки. Именно поэтому вызов newlineRegex. search () В03Bpa щает полную строку вместе с ее символами новой строки. СвоДкасимвояовреryя.рныхвыра_ений в этой l'Лаве вы I10знакомились со мнш'ими элементами нотации pel'Y лярных выражений, 110:')ТОМУ имеет смысл привести их полный перечень. . ? соответствует отсутствию или одиночному вхождению IlреДlUе ствующей rРУIlПЫ. . * соответствует отсутствию или произвольному количеству вхожде- ний предшествующей rpynпы. . + соответствует одиночному или большему количеству вхождений предшествующей rpynпы. . {n} соответствует в точности n вхождениям предшествующей rpynпы. . {n,} соответствует n или более вхождениям предшествующей rpуппы. . { , т} соответствует отсyrствию или вплоть до т вхождений предше- ствующей rруппы. . { п, т} соответствует не менее чем n и не более чем т вхождениям предшествующей rруппы. . {n, т} ? , или *?, или +? выполняет нежа,дный поиск вхождений ПреД шествующей rруппы. . Л spam означает, что строка должна начинаться символами spam. . зрат$ означает, что строка должна заканчиваться символами spam. . . соответствует любому символу, за исключением символа новой строки. . d, w и 5 совпадают с одиночным цифровым, словарным и I1p<r бельным символами соответственно. . , W и S совпадают с произвольным одиночным символом, не яв ляющимся цифровым, словарным и пробельным соответственно. . [аЬс] совпадает с любым одиночным символом из числа тех, KOT<r рые указаны в квадратных скобках (например, а, Ь или с). . [ЛаЬс] совпадает с любым одиночным символом, не относящим(я к числу тех, которые указаны в квадратных скобках.
Поиск по шаблону с помощью реryлярных выражений 211 Иrнорирование реrистра при поиске соответствий Обычно при установлении соответствия между реryлярным выражением и текстом учитывается, в каком реrистре записаны буквы. Например. сле- дующим реryлярным выражениям будут соответствовать разные строки. »> reqexl = rе.сощрilе('RoЬоСор') >>> reqex2 = re. COJIIрilе ( 'ROвосор' ) »> rеqвхЗ = re.compile('robOcop') »> reqвx4 = re.COJIIpile('RobooOp') Однако иноrда вас интере(:ует лишь сам факт совпадения букв, незави- симо от их реrистра. Можно установить режим, в котором реrистр букв иrнорируется, передав методу re. compile () при ero вызове константу re. IGNORECASE или re. 1 в качестве BTOporo apryмeHTa. Введите в интерак тивной оболочке сле/ующие команды. »> rObooop. re.compile(r'robocop', re.I) »> rObocop. .earch ( 'RoboCop i& part 1II8J1, part machine, а11 сор. ') .qroup() 'RoboCop' »> rObocop.&earch('ROВOCOP protect& tbe innocent.') .group() 'ROBOCOP' »> rObocop. &earch ( I М, why doe& your proqr8Jlllinq book ta1k about robocop &0 DlUch?') . qroup () 'robocop' Замена строк с помощыо метода sub () Реl'Улярные выражения MOryт не только находить заданные образцы тек- ста, но и заменять их новым текстом. Объекты Regex имеют метод sub ( ) , который принимает два арl'}'Мента. Первый apryMeHT это строка, которая должна 1I0ДСТаоляться вместо найденных СОВПадений. Второй apryMeHT это строка реryлярноrо выражения. Метод sub () возвращает строку. к Koт<r рой применены замены. Введите в интерактивной оболочке следующие команды. »> namesReqex = re.COJIIpile(r'Agent w+') >>> nam.esReqex.&ub('CENSORED', 'Aqent Alice qave the secret docwaent& to Aqent ВоЬ. ') 'CENSORED gave the secret documents to CENSORED.'
212 rлава 7 Иноrда возникают ситуаЦИИ, Korдa совпавший с реryлярным выражени ем текст сам используется в качестве части подстановочноrо текста. Для этою можно использовать в первом apryмeHTe при вызове метода sиb () об- ратные ссылки 1, 2, 3 и Т.д., которые имеют с.ледующий СМI>lС.л: "Вставить в под(тановочный текст rpyппы ], 2, 3 и так далее". Предположим, например, что вы хотите скрыть имена секретных areH тов, отображая в них лишь первые буквы. Для этоrо можно ВОСfюльзоваТI.- ся реryлярным выражением Agent ( w) w* и передать методу sub () в каче- стве первоrо apryмeHTa строку r' 1 * * * * , . Ссылка 1 в этой строке будет за меняться тем текстом, который будет захвачен I'РУIШОЙ 1, T.{. rРУllllОЙ (w) реryлярноrо выражения. »> agentNam.8Regex - r..сошрil.(r'Agent (w)w*') »> agantNam.8Regex.8ub(r' 1**** ' , 'Agent Alioe told Agent Carol that Agent Ev. kn." Agent ВОЬ n8 а doubl. agent.') А**** told С**** that Е**** knew В**** was а doub1e agent.' Работа СО СЯО_НЫМИ реryпярными выра_ениями с реryлярными выражениями леrко работать в случае простых TeKcT<r вых шаблонов. Однако сопоставление с более с.ложными текстовыми ша& лонами может требовать построения длинных и запутанных реryлярных выражений. Один из способов внесения ясности в подобных (итуациях за- ключается в использовании режима работы метода re . compile ( ) , при Кото- ром пробелы и комментарии в строке реryлярноrо выражения иrнорируют- ся. Чтобы включить этот "мноrословный режим", следует передать методу re . compile () константу re . VERBOSE в качестве второю apryмeHTa. 1Ьrда вместо сложною для восприятия реryлярною выражения наподобие phoneRegex = re.compile (r' ((d{3) 1 (d{3} })? (5 I I .) ?d{3} (5 I I . ) d {4 } (5* (ext I х I ext. ) s* d{ 2,5} ) ?} '} можно использовать развернутое в нескольких строках и снабженное комментариями реryлярное выражение следующеrо вида, понять смысл которою значительно леrче: phoneRegex = re.compile(r'" ( (d{3}\(d{3}})? (5 1. ) ? d{3} (5 I I .) d{4} (5*(extlxiext.)5*d{2,5})? ) l' " re.VERBOSE) i территориальный код # раздепитель # первые 3 цифры # разделитель # последние 4 цифры # доОавочный номер
Поиск по шаблону с ПОМОЩЬЮ реryлярных выражений 213 Обратите внимание на использование в предыдущем примере синтак сиса с тройными апострофами (. · · ) для создания мноrострочноrо текста, что позволило растянyrь определение реryлярноro выражения на нссколь-- ко строк, блаroдаря чему читать ero стало HaмHoro леrче. Для комментариев в пределах строки реryлярноro выражения действyюr те же правила, что и в случае обычноrо кода Python: символ # и все содер-- жимое до конца строки иrнорируются. Кроме TOro, дополнительные пр<r белы в мноrострочном реryлярном выражении не считаются частью TeK cтoBoro шаблона, соответствие которому ищется. Это по:шоляет записать реryлярное выражение таким обра:JOМ, чтобы оно леrче читалось. Комбинация констант re. IGNORв:CASE, re. DOТALL и re. VERВOSE Что если вы захотите использовать режим re. VERBOSE для включения комментариев в свое реryлярное выражение, но одновременно иметь воз- можность иrнорировать реrистр с помощью константы re. IGNORECASE? К сожалению, метод re. compile () принимает лишь одиночное значение в качестве BToporo apryMcHTa. Это оrраничение можно обойти, объединяя константы re. IGNORECASE, re. ООТА11 и re. VERBOSE с помощью символа KaHa ла ( I ), который в данном контексте имеет смысл nобumoвоzо оператора ИЛИ. Следовательно, если вы хотите, чтобы реryлярное выражение иrнориp<r вало реrистр и символам точки в нем соответствовали бы также символы но- вой строки, вызывайте метод re. compile () примерно следующим образом: »> someRegexValU8. re.compile('foo', re.IGNORECASE I re.DOТALL) А вот пример объединения всех трех опций во втором apryMeHTe: »> sошeRegeХVаluе = re.compile('foo', re.IGNORECASE I re.DOTALL I re. VERВOSE) Этот синтаксис HeMHoro старомоден и происходит от ранних версий Python. Детальное рассмотрение работы побитовых операторов выходит за рамки книrи, но вы сможете получить более подробную информацию по этому вопросу, посетивсайт http://nostarch.com/aиtomatestиff/. Во втором apryмeHTe можно передава1'Ь и друrие опции; они не используются широко, и при желании вы сможете найти более подробную информацию о них са- мостоятельно.
214 r лава 7 Проект: И3ВJ1ечение теяефонных номеров и адресов эяектронной почты Предположим, вам предстоит заняться рутинпой работой пайти все телефонные номера и адреса электронной почты, которые содержатся на ДЛИННОЙ веб--странице или в документе 6ольшоrо размера. l<::СЛи прокручи вать страницу вручную, то на такой поиск у вас может уйти MHOI'O времсни. 110 если бы у вас была проrрамма, способная выполнять поиск тслефон пых номеров и электронных адресов в буфере обмена, то можно было бы просто нажать комбинацию клавиш <Ctrl+A> для выделения Bcel'o текста и комбинацию клавиши <сн'I+С> для копирования выделеННОfО текста в буфер, а затем выполнить проrpамму. Такая I1pOrpaMMa моrла бы заменить НilXодящийся в буфере текст найденпыми телефонными номерами и aдpe сами электронной почты. ВСЯКИЙ раз, коrда вы приступаете к новому проекту, возникает соблазн сразу же браться за написание кода. Однако в подавляющем большинс.тве случаев лучше не спеШИТI. и оценить общую картину. Я рекомендую всеrда начинать с составления BblCOKoypoBHelOrO плана '1'01'0, что должна делать проrрамма. На данном этапе не <:тоит задумываться о фактическом коде об этом можно будст побеспокоиться позже. Приступая к ра(юте, оrраllИЧИ вайтесь "широкими мазками". I lаl1ример, вот что должна делать ваша проrрамма, предназначснная для извлсчения телефонных номеров и адресов электронной почты: . получать текст из буфера обмена; . находить в тексте все телефонные номера и адреса электронной по'пы; . IIсреносить найденный текст в буфер обмепа. Вот тепсрь уже можно подумать над тем, как реализовать все это в виде кода. Код должен выполнять следующие операции: . использовать модуль pyperclip для копирования и вставки строк; . создавать два рСI)'ЛЯрНЫХ выражения, первое из которых COOTBeтCTBY ст телефонным номерам, а второе адресам электронной почты; . паходить все совпадения, а не только первое, для обоих реryлярных выражений; . аккуратно форматировать найденпые строки, преобразуя их в одну строку для вставки о буфер; . отображать соответствующее сообщение, если искомые соответствия в тексте не бblJlИ обнаружсны. Этот СIlИ<:ОК служит CBoero рода дорожной картой проекта. В процессе написания кода вы сможете сконцеlприровать внимание на каждом этапе по отдельности. Любой из этих этапов вполне выполним и выражается в терминах TOro, 'по вы уже умеете делать с помощью Python.
Поиск по шаблону с помощью реryлярных выражений 215 ш", ,. '0111"".' per,..p"o,O ."р".'''.. /111. II0"'К" re.,фо""..х "о"'ро. Прежде BCeI'O, необходимо создать реryлярное выражение для поиска телефонных номеров. Создайте новый файл, введите в Hero следующий код и сохраните ero в файле phoneAndEтail.py. #! python3 # phopeAndEmail.py Находит телефонные номера и # адреса электронной почты в буфере обмена. import pyperclip, re phoneRegex = re.compile(r'" ( (d{311 (d{31))? (511.)? (d{31) (5 I I .) (d{ 41) ( s * (ext I х I ext . ) s * ( d { 2 , 5} ) ) ? ) , I " re . VERBOSE} # территориальный код # разделитель # первые 3 цифры # разделитель # последние 4 цифры # добавочный номер # тооо: Создать реrулярное выражение для адресов электронной # почты. # тооо: Найти соответствия в тексте, содержащемся в # оуфере обмена. # тооо: Скопировать результаты в оуфер обмена. Комментарии ТООО (ЧТОСДЕЛАТЬ) всеro лишь обозначают "скелет" npOI'paMMbI. Впоследствии на их месте будет находиться фактический код. 1елефонный номер начинается с территориальноro Кода, который не является обязательным, в СIЯ3И с чем за соответствующей ему rpynпой сле- дует вопросительный знак. Поскольку территориальный код может иметь ровно три цифры (т.е. d{ 3}) или ровно три цифры в круrлых скобках (т.е. (d{ 3} ) ), ати две альтернативы следует соединить символом канала. Вы также можете добавить в реryлярное выражение комментарий # терри ториальный код, напоминающий о том, с чем должно совпадать реl'УЛЯРlюе выражение (d{3} 1 (d{3} }}? В качестве разделителя rрупп цифр в телефонном номере MOI'yr ИСПОЛIr зоваться пробел ( s), дефис () или точка ( . ), поэтому данные компонеll ты реryлярноrо выражения также должны быть соединены символами канала. В следующих трех компонентах нет ничеro сложноrо: три цифры, за которыми следует друroй разделитель, а затем еще четыре цифры. П<r следняя часть это необязательный добавочный номер, состоящий из
216 r лава 7 произвольноrо количества пробелов, за которыми следует буквенное об<r значение ext, х или ext., а затем и сам добавочный номер, содержащий от двух ДО пяти цифр. Ш", 2. '0311"".е pery..p"o,o ."р".е".. /111. "О.'К" "дреео. J.епро""оj no.r" Вам также необходимо иметь реl'Улярное выражение для поиска адресов электронной IIОЧТЫ. Добавьте в ПрOl'рамму новый код, выделенный ниже полужирным шрифтом. #! python3 # phoneAndEmail.py Находит телефонные номера и # адреса электронной почты в буфере обмена. import pyperclip, re phoneRegex = re.compile(r'" ( пропущенный KOД # Создание peI'YnярНОl"о lIblpuения дn_ адресо& М8JC'l'pОННОЙ поч'1'Ы. emailRegex = rе.сощрilе(r' , , ( о [azAZO9. ,+]+ # ИМR пользователя . @ #CИN8cm@ . [azAZO9.]+ # ИМR домена (.[azAZ]{2,4}) # ост&nьная чаC'l'Ь адреса ) I I " re.VERВOSE) # ТООО: Найти соответствия в тексте, содерхащемся в # буфере обмена. # тооо: Скопировать результаты в буфер обмена. Часть адреса, содержащая имя пользователя О, включает один или б<r лее символов, которыми MOryr быть любые из следующих символов: буквы в верхнем или нижнем реrистре, цифры, точка, символ подчеркивания, знак процента, знак "плюс" и дефис. Все эти символы можно указать в виде символьноrо класса: [azAZO9. %+). Имя домена отделяется от имени пользователя символом @ .. Домен- ное имя. представляется с помощью более узкоrо класса, включающеrо только буквы, цифры, точку и дефис: [azAZO9. ]. А последняя, так назы- ваемая часть "dot-cotn" (с технической точки зрения представляющая до.мен верхnеео уровnя) , фактически может содержать только точку и любой текст. Эта часть может включать от двух ДО четырех символов. Формат адресов электронной почты определяется мноrими, подча(: причудливыми, правилами. Данному реryлярному выражению будут соот- ветствовать не все корректные адреса электронной почты, но ero будет
Поиск по шаблону с помощью реryлярных выражений 217 Достаточно почти для всех видов электронных адресов. с которыми вы M<r жете столкнyrься. Ша, 3. nО"'К .teЖ ,о."аllе"". . "кт, ,ко".ро.а".о. . 6уфер ..е"а 'Iеперь, Korдa у вас уже есть реryлярные выражения для поиска телефон ных номеров и адресов электронной почты, можно поручить модулю re выполнить утомительную работу по поиску в буфере обмена всех строк, c<r ответствующих составленным реryлярным выражениям. Методруреrсliр. paste () получит строковое значение текста. хранящеrося в буфере обмена, а ме-rод findall () вернет список кортежей. Добавьте в проrрамму новый код. выделенный ниже полужирным шрифтом. #! рythопЗ # phoneAndEmail.py Находит телефонные номера и # адреса электронной почты в буфере обмена. import pyperclip, re phoneRegex = re.compile(r" I ( лропущенный KOД * DоиоlC соотв.тствий В 'l'8JCC'1'B, coдepIC8IЦ8МСЯ в * бvфере обмена. text - .tr(pyperclip.pa.t8(» О Utch8. .. [] Ofor qroup& in phoneRegex. findall (text) : phoneNum - ''.join([group.[l], grouр.[З], qroup.[S]]) if group.[8] != ": phoneNum +- · х' + qroup. [8] шаtc:he.. арреnd (phoneNwa) Ofor qroup. in emailRegex.findall(text): u tc:he. . append (qroup. [ О] ) i ТООО: Скопировать результаты в буфер обмена. Для каждоrо совпадения создается один кортеж. и каждый кортеж со- держит строки для каждой rруппы в реryлярном вьrражении. Не забывайте о том. что rрynпе О соответствует все реryлярное выражение. поэтому то, что вам нужно, это rруппа с индексом О в кортеже. Найденные совпадения с реryлярным выражениям сохраняются в списке тatches. Поначалу этот список пуст ..' Далее следуют два цикла for. В слу чае адресов электронной почты достаточно IIрисоединять к списку та tches rРУПI1У О каждоrо найденноrо совпадения .. В случае же телефонных HOM ров мы не можем оrраничиться только этим. Поскольку проrpамма ищет
218 rлава 7 телефонные номера, формат которых может быть различным, то, прежде чем присоединять их к списку, их нужно привести к единому стандартному формату. В переменной phoneNum содержится строка, скомпонованная из rрупп 1, З, 5 и 8 совпавшеrо текста 8. (Этими rруппами являются террито- риальный код, первые три цифры, последние четыре цифры и добавочный номер.) Ша, 4. 06"11."'''.' ,oll,all'''.' . ОIl"У"РОКУ RJI. ко""ро.а".. . 6уф.р 06.."а Теперь, коrда адреса электронной почты и телефонные номера coxpa нены в переменной matches, их необходимо скопировать в буфер обмена. Функция pyperclip. сору () принимает одиночное строковое значение, а не список строк, поэтому для перемеllНОЙ matches вызывает(я метод join (). Чтобы упро(тить проверку работы проrраммы, выведем все найденные совпадения на экран. А если ни телефонных номеров, ни мресов электрон ной почты в тексте не найдено, будет выведено соответствующее сообщение. Внесите в проrрамму следующие изменения. #! руthоnЗ # phoneAndEmail.py Находит телефонные номера и # адреса электронной почты в буфере обмена. пропущенный KOД for groups in emailRegex.findall(text): matches.append(groups[O]) * Копиро.ание pe8YnЬ'l'a'l'o. 8 буфер обмена. if 18n(aatch8.) > о: pyperclip.oopy('n'.join(aatch88» рrint('СJCОПИРОваио 8 буфер обмена: ') рrint('n'.jоin(шаtchе.» 81.8: print ( 'ТenефоННЪ18 номера и адреса ЭЛ8хтроНИОЙ t!> ПОЧ'1'bl Н8 обнар}'1l8НЫ.') ..."0.,,.... .ро'рам.. в качестве примера откройте свой браузер на странице контактов сайта No StaI'Ch Press по адресу http://www.nostarch.com/contactus.htm. нажмите комбинацию клавиш <Ctl'l+A> для выделения вcero текста на странице, а затем комбинацию клавиш <Ctl'l+C> для копирования этоrо текста в буфер. Выполнив проrрамму, вы должны получить примерно с.ледующий вывод. Скопировано в буфер обмена: 8004207240
Поиск по шаблону с помощью реryлярных выражений 219 4158БЗ9900 4158БЗ9950 info@nostarch.com media@nostarch.com academic@nostarch.com help@nostarch.com Иllе. OrHOC.ren6HO е031111Н.. IIНllnО,..,Н",Х про,р".. Распознавание образцов текста (и их замена с помощью метода sub ()) имее'f' множество возможных областей применения, например: . нахождение URL-адресов веб-сайтов, начинающихся с префикса http:// или https: //; . унификация дат, приведенных в различных форма'f'ах (например, 1/21/2015, 01212015 и 2015/1/21), пyrем их замены датами, представ ленными <: помощью выбранноrо С'f'андарТllоrо формата; . удаление конфиденциальной информации, такой как номера социаль-- ных страховок или кредитных карт; . исправление типичных опечаток, таких как наличие нескольких про- белов между словами, случайное повторение слов или наличие не- скольких восклицательных знаков в конце предложения. Это так pa:r дражает!! ! РеЗlOме Ilесмотря на то что компьютер способен очень быстро выполнять тек- стовый поиск, он нуждается в конкретных указаниях относительно Toro, что именно необходимо найти. Реryлярные выражения позволяют точно определить символьные шаблоны для поиска. В действительности некото- рые текстовые процессоры и электронные таблицы предоставляют сред- ства поиска и замены, использующие механизм реryлярных выражений. Поставляемый вместе с Python модуль re обеспечивает компиляцию объектов реryлярных выражений, так называемых объектов Regex. В част- ности, эти объекты имеют следующие методы: search () поиск одиночных совпадений с реryлярным выражением, findall р поиск всех :К:Jемпля- ров совпадений и sub () поиск с заменой тек<:та. В этой 1'JlaBe синтаксис реryлярных выражений был рассмотрен далеко не 110ЛНО<:ТЬЮ. Более подробную информацию по этой теме вы сможете получить, обратившись к официальной документации Python по адресу http://docs .python. оrg/З! library/re .html. Вам также будет полезно про- читать практическое руководство по работе с реryлярными выражениями, доступное по адресу http://www.regularexpressions.info/.
220 r лава 7 Теперь, КOI'да вы уже приобрел и определенньrй опыт манипулирования текстовыми строками и научились работать с текстовыми шаблонами, мы можем перейти к уrлубленному рассмотрению методов чтения и записи данных в файлы, сохраняемые на жестком диске вашеrо компьютера. Контропьные вопросы 1. С помощью какой функции создаются объекты реryлярных выраже- ний (объекты Regex)? 2. Почему при создании объектов Regex часто используются "сырые" строки? 3. Что возвращает метод search ()? 4. Как получить из объекта Match фактические строки, соответствующие шаблону реryлярноrо выражения? 5. Что именно представляет в объекте реryлярноrо выражения, создан- ном на основе строки r' (ddd) (ddddddd) ',rpуппа о? Iруппа 1? Iруппа 2? 6. В синтаксисе реryлярных выражений круrлые скобки и точки имеют особый смысл. как бы вы указали в реryлярном выражении, что сим- волы круrлых скобок и точки сами являются объектом поиска? 7. Метод findall () возвращает список строк ИЛИ список кортежей строк. В каких именно случаях возвращается тот или иной тин значений? 8. Что означает символ I в реryлярных выражениях? 9. Какие две функции выполняет символ 1 в реryлярных выражениях? 10. В чем разница между символами + и * в реryлярных выражениях? 11. В чем разница между записями {3} и {З, 5} в реryлярных выражениях? 12. Что означают сокращенные символьные классы d, w и s в реryляр- ных выражениях? 13. Что означают сокращенные символьные классы О, W и 8 в реryляр- ных выражениях? 14. Как сделать реryлярные выражения нечувствительными к реrистру? 15. Чему обычно соответствует символ. ? Чему он соответствует, если ме- тоду re. сотрНе () в качестве BToporo apryмeHTa передана константа re.DOTALL? 16. В чем разница между сочетаниями символов. * и . *11 17. Как записать символьный класс, которому соответствуют все цифры и буквы в нижнем реrистре? 18. Если nиmRegex re. compile (r' d+' ) , то что вернет вызов numRegex. sиb('X', '12drшmnеrs, l1pipers, fiverings, 3hens'}?
Поиск ПО шаблону с ПОМОЩЬЮ реryлярных выражений 221 19. Что стаНО8ИТСЯ возможным при передаче константы re. VERВOSE в ка- честве BTOporo apryмeHTa при вызове метода re . СОПlpНе ( ) ? 20. Как бы вы записали реryлярное выражение, которому СООТ8етствуют числа с запятой в качестве разделителя между каждыми тремя цифра- ми? Этому 8ыражению должны соответствовать следующие числа: . '42' . '1,234' . '6,368,745' и не должны соответствовать следующие: . '12,34,567' (только две цифры между занятыми); . '1234' (отсутствуют запятые). 21. как бы вы записали реryлярное выражение, которому соответствуют всс полные имена, включающие фамилию Nakamoto? Можно предполо- жить, что имя, предшествующее фамилии, всеrда состоит из одноro слова, начинающеrося с большой буквы. Этому реryлярному выраже- нию должны соответствовать следующие имена: . 'Satoshi Nakamoto' . 'A1ice Nakamoto' . 'RoboCop Nakamoto' и не должны соответствовать следующие: . I satoshi Nakamoto' (имя не начинается с большой буквы); . 'Mr. Na kamoto' (в предшествующем слове имеется небуквенный символ); . 'Nakamoto' (имя отсутствует); . 'Satoshi nakamoto' (фамилия Nakamoto не начинается с большой буквы). 22. Как бы вы записали реryлярное выражение, совпадающее с преДJlO- жениями, которые начинаются с одноrо из слов Alice, ВоЬ и Carol; вторым словом является одно из слов eats, pets и throws; третьим словом является одно из слов apples, cats и baseballs; и которые за- канчиваются точкой? Это реryлярное выражение должно быть не- чувствительным к реrистру. Ему должны соответствовать следующие предложения: . 'Alice eats apples.' . 'ВоЬ pets cats.' . 'Carol throws baseballs.' . 'Alice throws Apples.' . 'ВОВ EATS CATS.'
222 r лава 7 и не ДОЛЖНЫ соответствовать следующие: . 'RoboCop eats apples.' . 'ALICE THROWS FOOTВA11S.' . 'Carol eats 7 cats.' Учебные проекты Чтобы закрепить полученные знания на практике, напишите ПрОI'рам- мы для предложенных ниже задач. 06"ару..".е ,.л.".,,, пароле;; Напишите функцию, которая использует реryлярные выражения для проверки TOI'O, что переданная ей строка представляет собой сильный пароль. Сильными считаются пароли, которые состоят по крайней мере из восьми символов, содержат символы в верхнем и нижнем реrистрах и включают 110 крайней мере одну цифру. Вер'''. фУ"КЦ.. strip () I ",,,О..,,lOlIIа. ре"..р..,е ."ра.е".. Напишите функцию, которая принимает строку и делает то же, что и строковый метод str ip () . [ели ей не переданы никакие друrие apryMeHTbI, кроме строки, то данная функция должна удалить из строки начальные и K<r нечные пробельные символы. В противном случае из строки должны быть удалены символы, переданные функции в качестве BToporo apryмeнтa.
ЧТЕНИЕ И ЗАПИСЬ ФАЙЛОВ Переменные отличное средство хранения данных во время выполнения проrраммы, но если вы хотите, что бы данные существовали и после ее выполнения, то co храните их в файле. Можно рассматривать содержимое файла как одну строку, размер которой способен исчис- ляться rиrабайтами. Из этой rлавы вы узнаете о том, как использовать Pytl10n для создания, чтения и сохранения файлов на жест ком диске. Файпы и пути доступа к НИМ Файл имеет два ключевых свойства: имя файла (обычно записываемое в ви- де одноrо слова) и путъ доступа к файлу. Пyrь определяет, rде именно в ком- пьютере располarается файл. Например, в моем ноутбуке, работающем под управлением Windows 7, есть файл с именем projects.docx и пyrем доступа с: Userslp,sweigartlpocumeпts. Часть имени файла, которая следует за последней точ- кой, называется расш,uреиие.м имени файла (для краткости часто roворят рас- ш.иpeuue фаила или просто расширение) и указывает на тип файла. Файл project. docx это документ W01ll, а U5ers, asweigart и Docuтeпts это названия папок (также называемых ката.лоеами). Папки MOIyr содержать файлы и дрyrие пап- ки. Например, файл projесt.dосхсодержится в папке Docuтeпts, которая содер- жится в папке asweigart, которая, в свою очередь, содержится в папке Users. Этот способ орraнизации папок проиллюстрирован на рис. 8.1. Часть с: пyrи к файлу это корневая папка, которая содержит все осталь- ные папки. В Windows корневой является папка c: которая также на..1ы- вается диском с:. В 05 Х и Linиx корневой является папка /. В этой книrе для корневой папки используется обозначение с: в стиле Windows. Если вы выполняете при меры в интерактивной оболочке 05 Х или Linиx, то ис пользуйте вместо этоrо обозначение /.
224 r лава 8 с: L L L Users asweigart Docитeпts L pro j ect . docx Рис. 8. J. Расположение файла 8 иерархии папок Дополнительные тома, такие как диски DVD или U5Вфлешки, будут отображаться по-разному в разных операционных системах. В Windows они отображаются в виде корневых дисков с дрyrими буквенными обозна- чениями: D: Е: и т.Д. В 05 Х они отображаются в виде новых папок в папке jVolитes, в Liпuх в виде новых папок в папке jтnt (от aнrл. тoиnt). Кроме Toro, имсйте в виду, что, в то время как в Windows и 05 Х имена файлов,и папок нечувствительны к реrистру, в Linux они зависят от реrистра. И,пОЛ.,08аНllе обратной 1(0'0. .,ер'" 8 Wiatlows 111(<<0. .ерт.. 8 05 Х 11 Liпиx В Windows пути к файлам записывают, разделяя имена папок обратной косой чертой (О. В то же время в 05 Х и Linux разделителем служит косая черта (/). Если вы хотите, чтобы ваши nporpaмMbI работали во всех опсра- ционных системах, пишите свои сценарии на PytllOn так, чтобы обрабаты- вались оба случая. К счастью, это очень просто делается с помощью функции 05. ра th. join (). Если вы передадите ей строковые значения имен файлов и папок в своем пyrи доступа, то вызов 05 .path. join () возвратит строку, в которой пyrь доступа к файлу указан с использованием корректной версии раздели- теля. Введите в интерактивной оболочке следующие КОМаНДЫ. >>> iш.роrt о. »> 0..path.join('U8r', 'bin', 'враа') 'usr\bin\spam' я выполняю все примеры в интерактивной оболочке Windows, поэтому BbI30B05.path.join('usr', 'bin', 'зрат') вернул мне строку 'usr\bin\ 5рат' . (Обратите внимание на то, что символы обратной косой черты np<r дублИрО8аны, поскольку каждый из них нуждается в экранировании друrим символом обратной косой черты.) Е(:ли бы я вызвал эту функцию в 05 Х или [jnux, то была бы возвращена строка' U5r /bin/ зрат' .
Чтение и запись файлов 225 Функция 05 .path. join () оказывается полезной при создании строк для имен файлов. Эти строки будут передаваться ряду функций, используемых при работе с файлами, с которыми вы познакомитесь в данной I'лаве. На- пример, в следующем примере имена файлов из заданноrо списка прис диняются К имени папки. »> myFi18. - [' accounts . txt', 'cletail. . сВУ', ' invi t8. docx' ] »> for filenaшe in шуFilеs: print(05.path.join('C:\Users\asweigart', filename)) C:Usersasweigartaccounts.txt C:Usersasweigartdetails.csv C:Usersasweigartinvite.docx Те.,..... ра60... к"",.о, Каждая проrpaмма, которая выполняется на компьютере, имеет meкуц4UЙ раБCfЧUЙ каmaлоz (current wOl'kil1g direcLory r.w<l). Предполarается, что лкr бые имена файлов или пути, которые не начинаются с указания корневой папки, заданы относительно текущеrо рабочеrо каталоrа. Для получения значения текущеro рабочеrо каталоrа в виде строки используется функция 05. getcwd ( ) , а для ero изменения функция 05. chdir ( ) . Введите в интерак- тивной оболочке следующие команды. »> iJllport о. >>> o..qetcwdO 'C:\Python34' »> 0..chdir('C:\Window.\Sy.tem32') >>> o..qetcwd() 'С: \Windows\System32 , дecь в качестве текущеrо рабочсrо каталOl'а устанавливается папка C:Python34, поэтому имя файла projecl.docx ссылается на файл C:Python34 project.docx. Если мы заменим текущий рабочий каталоr каталоюм с:ит,ndоw, то имя файла project.docx будет интерпретироваться как полное имя с: Windows'фrojесt. docx. При попытке перейти внесуществующий каталоr PytllOn выведет соо& щение об ошибке. »> о.. chdir ( 'С: Тhi.FolderDoesNotExi..t' ) Traceback (most recent call last): File "<pyshell#18>", line 1, in <module> os.chdir('C:\ThisFolderDoesNotExist') FileNotFoundError: [WinError 2] The system cannot find the file specified: 'С: \ThisFolderDoesNotExist ,
226 r лава 8 При.мечаnue Несмоmря на mo чmо более совремеин'ы.м эививaлe1f,mо.м тер.мина каmа.лое явл.я.еmся тер,м,ин "папка", в качестве стандартноео исполъзуеmся тер,м,ин "текущий рабо-- v " ( Р "-1 ,. " ) " Р " чии каталое или п осто раиО'Ч.иu кamалое ,а не meкущая аиО'Ч.ая папка . А6,ОlllOrиwе . оrио'.rе.....е пyr.1l0ayпa Существуют два способа определения пyrи доступа к файлу: . абсолюmныЙ nутъ, который Всеrда начинается с имени корневой папки; . оmносumeJtънъtu. путъ, который задается относительно текущеl'О рабо- чеrо каталоrа проrраммы. Существуют также папки, обозначаемые одним (.) ИЛИ двумя (..) симво- лами точки. Это не реальные папки, а специальные имена, которые MOryr использоваться при задаНИИ пyrей. Одиночная точка является сокращен ным обозначением, имеющим смысл "данная папка". Двойная точка имеет смысл "родительская папка". На рис. 8.2 IIоказан пример расположения папок и файлов. Если в каче-- crBe текущеrо установлен рабочий каталоI' C:'{Jacoп, то относительные пyrи к друrим папкам и файлам задаются так, как показано на рисунке. Относительные пути Текущий рабочий ............... каталоr Ьасоп .. . Абсолютные пути С: С: С: Ьасоп t L fiZ: paт . txt spaт. txt . fizz С: bacoпfizz . fizzspaт. txt C:bacoпfizzspaт.txt . spaт. txt C:bacoпspam.txt eggs ..eggs С: eggs L spaт. txt .. eggsspaт.txt С: eggsspam.txt spam. txt . . spaт. txt С: spam. txt Рис. 8.2. Относительные пути доступа к попкам и файлом 8 рабочем коталоrе С:Ьасо" Имя. в начале относительноrо пyrи является необязательным. Напри мер, пyrи . pam.lxl и spam.txl ведут к одному и тому же файлу.
Чтение и заПИСь файлов 227 'Ollfa".e "О.... "аnок , "ОМОЩ61О фу "к".. 08 . zaaJcedirs ( ) Ваши проrраммы MOryr создавать новые папки (каталоrи) с помощью функции 05 .makedirs () . Введите в интерактивной оболочке следующие ко- манды. >>> illport 08 »> о..шakеdir8('С:\deliСiоus\wаlnut\wаfflе.') В результате будет создана не только папка C:delicious, но и расположен- ная в ней папка walnиt, а также расположенная в папке C:'rJelicioиswalnut пап- ка waffles. Таким образом, функция 08 .makedirs () будет создавать ВСС необ- ходимые промежуточные папки, l'арантируя существование полноI'О пути. Соответствующая иерархия папок показана на рис. 8.3. с: L L L delicious walпut waff1es Рис. 8.3. Результат роботы фуикции os. makedirs ('C:\delicious\walпut\waffles') Модуяь 08 . path Модуль os.path содержит множество полезных функций для работы с именами файлов и путями до<:тупа. Например, вы уже и(:пользовали функ- цию 05. path. j oin ( ) , создающую корректные пути для любой операционной системы. Поскольку 05. path ЭТО модуль, содержащийся в модуле 05, для er() импортирования достаточно выполнить ин(:трукцию import 05. Всякий раз, Korдa вашей проrрамме необходимо работать с файлами, папками или путя- ми доступа, вы можете обращаться для справки к коротким примерам, при- веденным в этом разделе. Полная документация модуля 05.path приведена на сайте Python по адресу http://docs.python .org/3/1ibrarY/05 .path. html. Ilpu.мечанuе Для ООлЪШU1t(:тва nor.tteдy'lO'lJ&Ux nfJ1.utePoB, nриведеи'Н:ых в Э1nOМ разделе, вам nотре- буется .lltодулъ ОБ, nD.'mюму ue забывайте u.м,nортироваmъ еео в uа'Чале ",аждоео с-це- пария 1.1. при ",аждом перезапуске IDLE. В nроти8ио.llt случае будет в'Ыведеuо сообще- ние об ошибке NameError: пате 'os' is пot defiпed.
228 r лава 8 06ра60nrа a6'OlllOr.6IX " orнo,,,reIl6"6I. пут'. Модуль 05. ра th предоставляет ФУНКЦИИ для возврата абсолютноl"O пути по заданному относительному пyrи и проверки '1'01"0, является ли путь абсо- лютным. . выовB os . ра th. ab5path (ра th) возвращает строку абсолютноro пути ар- ryMeHTa. Это простой способ преобразования относительноrо пути в абсолютный. . Вызов os.path.isab5 (path) возвращает значение True, если apryMeHT абсолютный путь, и Fa15e, если apryMeHT относительный путь. . Вызов os.path.relpath (path, start) возвращает строку относитель- Horo пyrи от start к path. Если путь start не предоставлен, то в качестве пеrо используется текущий рабочий каталоr. Испытайте работу этих функций в интерактивной оболочке. »> 08.path.abspath(' ,') 'C:\Python34' »> 08.path.abspath(' .\Script.') 'C:\Python34\Scripts' »> 0..path.i8ab.('. ,) False »> о. .path. i.8b. (08 .path. 8bspath (' . ' ) ) True Поскольку в момент вызова функции 05. path. аЬ5ра th () текущим раб<r чим каталоrом был с: Python34, папка. представляет аБсолютный пyrь 'с: Python34 ' . Прu.мечанue тк как файл:ы и папки в вашей системе, вероятUl!е всеео, будут отли'Чатъс.я от моих. в'Ы 'не сможете воспроизвести все npuмefrы в этой елаве в moм виде, в каком они npuводятс.я. Тем nе мeuee nоn'Ытайтесъ их в'Ыnолнитъ, иСnОJtъзуя папки па свор"", комnъюmepe. Выполните в интерактивной оболочке следующие команды. »> o..path.relpath('C:\Window.', 'С:\') 'Windows' >>> o..path.relpath('C:\Window.', 'C:\spa\egq.') , . . , . Windows I »> о.. qetcwd () 'C:\Python34'
Чтение и запись файлов 229 Вызов 05.path.dirname (path) возвращает строку, содержащую всю часть пути, которая предшествует последней косой черте в apryмeHTe раш. Вызов 05. path. ba5ename (ра th) возвращает строку, содержащую всю ту часть пути, которая следует за lю(:ледней косой чертой в apryMeHTe path. Имя папки и базовое имя пути I10казаны на рис. 8.4. C:WiпdowsSysteт32calc.exe 1 11 I Имя папки Базовое имя Рис. 8.4. Базовое имя указывается за последнеЙ КОСОЙ чертой в обозиачении пути и совпадает с именем файла. Имя папки это вся часть пути, которая предшествует последней косоЙ черте Например, введите в интерактивной оболочке следующие команды. »> path = 'с: \Window.\Sу.t8aЭ2\саlс.8хе , >>> 08.path.ba88n8JII8(path) 'calc.exe' >>> 08. path. dirn8J118 (path) 'С:\Windоws\SуstеmЗ2' Если вам нужны как имя папки, так и базовое имя, достаточно вызвать функцию 05.path. split (), которая возвращает кортеж, включающий обе эти строки. »> calcFi18Path = 'C:\Window8\SY8t8m32\calo.exe' »> 0..path.8plit(calcFilePath) ('С:\Wiпdоws\SуstеmЗ2' I 'calc.exe') Заметьте, что тот же кортеж можно получить, вызвав функции 05. path. dirname () и 05. path. basename () и поместив возвращаемые ими значения в кортеж. >>> (08.path.dirn8Jlle(calcFi1ePath), 08.path.ba88name(calcFilePath» ('С:\Windоws\SуstеmЗ2', 'calc.exe') Однако если вам нужны оба значения, то вызов o5.path.5plit () является более коротким. Обратите внимание на то, что возвращаемое значение функции 05. path. 5рН t () н,е представляет собой список строк, соответствующих каждой из папок пути по отдельности. Для получения Taкoro списка следует исполЬ3<r вать строковый метод spl i t () совместно с разделителем 05. 5ер. Вспомни те, что персменная 05. 5ер содержит ту версию косой черты (прямой или
230 r лава 8 обратной), используемой в качестве разделителя имен папок, которая соот- ветствует установленной на компьюrере операционной системе. Например, введите в интерактивной оболочке следующие команды. »> calcFilePath.split(o_.path._&p) ['С:', 'Windows', 'SуstеmЗ2', 'calc.exe'] На компьютерах, работающих под управлением 05 Х и Linux, в начале возвращенноro списка будет указана пустая строка. »> '/usr/bin'.split(08.path._&p) [' " 'usr', 'bin'] Строковый метод 5pli t () обеспечивает возврат списка всех частей пyrи. Если вы передадите ему разделитель 05. path. 5ер, то он сработает в любой операционной системе. ОпР'I'Л'.'" ра_'Р08 ф"'n08 " "'1.,.".0,0 папок Научившись работать с путями доступа к файлам, можете при ступить к сбору информации о конкретных файлах и папках. Модуль 05.path предо ставляет функции, позволяющие находить размеры файлов, выраженные в байтах, и определять, какие файлы и папки содержатся в заданной папке. . Вызов функции os. path. get5ize (path) возвращает выраженный в бай тах размер файла, указанноro в apryмeHTe path. · Вызов 05.1istdir(path) возвращает список строк с именами всех файлов с пyrем доступа, указанным в арryмеите path. (Обратите вни мание на то, что эта функция содержится в модуле 05, а не в модуле os.path.) Вот что я получаю, КOI'Да выполняю эти функции в интерактивной об<r лочке. »> 08.раth.gеtsizе('С:\Windоw_\Sу_t8aЗ2\саlс.ехе') 776192 »> 08.1i_tdir('С:\Window_\Sу_t8aЗ2') ['0409', '12520437.срх', '12520850.срх', '5U877.ax', I aac1ient. dll' , пропущеннь KOД 'xwtpctui.dll', 'хwtрwЗ2.dll', 'zhCN', 'zhHK', 'zhTW', 'zipfldr .dll' ] Как видите, на моем компьютере ПРОI'рамма calc. ехе имеет размер 776192 байта, и в папке С:WiпdоwssуstеmЗ2 содержится множество
Чтение и запись ф айлов 231 файлов. Если бы потребовалось найти суммарный размер всех файлов, Ha ходящихся в этой папке, то для этоI'О Я Mor бы воспользоваться функциями 05.path.get5ize() иоs.li5tdir(). »> totalSize · О »> for filename in o..li.tdir('C:\Window.\Sy.tem32'): totalSize · totalSize + о..раth.gеt.izе(о..раth.jоin('С:\Window.\Sу.temЗ2' , filename) ) »> print(totalSize) 1117846456 По мере TOI'O как в цикле перебираются имена всех файлов, содержа- щихся в папке с: WiпdОWSSУ5tеmЗ2, значение переменной totalSize каж дый раз увеличивается на размер очереДНОI'О файла. Заметьте, как я исполь- зую функцию 05. path. j oin () для присоединения имени папки к текущему имени файла при Вblзове функции оз .path.get5ize (). Целочисленное зна- чение, возвращаемое функцией оз .path .getsize (), добавляется к текущему значению переменной totalSize. По завершении цикла выводится значе- ние totalSize, отображающее суммарный размер содержимоrо папки с: WiпdОW5SУ5tеmЗ2. nро..рк" QlllfI080."".. "". Мноrие функции Python завершаются аварийно с выдачей сообщения об ошибке в случае, если предоставленный им путь не существует. Модуль 05. ра th представляет функции, позволяющие проверить, существует ли за данный путь и соответствует ли он файлу или папке. . Вызов 05 .path.exi5t5 (path) возвращает значение True, если файл (или lIапка), на который ссылается apryMeHT, существует, и значение Fa15e, с(:ли он не существует. . Вызов 05 .path. i5file (path) возвращает значение True, е<:ли заданный apl')'MeHToM путь существует и является файлом, и значение Fa15e в противном случае. . Вызов 05. path. i5dir (path) возвращает значение True, если заданный apryмellToM путь существует и является папкой; иначе Fa15e. Вот что я получаю, KOl'дa выполняю эти функции в интерактивной обо- лочке. >>> o..path.exi.t.('C:\Window.') True >>> o..path.exi.t. ('С: .OJIIemadeupfolder')
232 rлава 8 False >>> о. .path. i.dir (' с: Window. Зу.t8I132') True »> 0..раth.i.filе('С:\Window.\Sy.teшЗ2') False »> 0..раth.isdir('С:\Window.\Sу.t8Ш32\calс.ехе') False »> 0..раth.i.filе('С:\Window.\Sу.t8Ш32\calс.ехе') True Чтобы определить, подключен ли в данный момент к компьютеру DVD или флешдиск, можно воспользоваться функцией 05. path. exist s () . На- пример, если бы я хотел проверить, подключен ли флешдиск D: на моем Wiпdоwsкомпьютере, то я Mor бы это сделать с помощью следующей ко- манды. »> os.path.exi_t.('D:\') False Ой! Похоже, я забыл подключить свою флешку! Чтение и запись файпов Научившись уверенно работать с папками и относительными путями, вы сможете задавать расположение файлов для операций чтения и записи. Функции, рассматриваемые в нескольких последующих ра.зделах, будут при- меняться к простым текстовым файлам. Простые meкcmoвш фашыl содержат лишь базовые текстовые символы, не сопровождающиеся информацией о шрифте, размере и цвете текста. В качестве примера про<:тых текстовых файлов можно привести файлы с расширением .txt или файлы сценариев Python с расширением .ру. Эти файлы MOryr быть открыты (: помощью при- ложения Notepa<l в Windows или приложения TextEdit в OSX. Ваши np<r l'paмMbl Moryr леrко читать содержимое простых текстовых файлов и об- рабатывать их как одиночные строковые значения. Друrим типом файлов являются двоичuш (бинарные) файлы, такие как файлы документов, создаваемых текстовыми процессорами, РDF-файлы, а также файлы изображений, электронных таблиц и выполняемых np<r rpaмM. Открыв двоичный файл в приложении Блокнот или TextEdit, вы увидите бессмысленный набор странных символов (рис. 8.5).
Чтение и запись файлов 233 ",2'':;; .... .' . ,. о..,''' О" :. ;';..:< :". . j:=f lа" Ф.йл Пр.... Формат ВII,II (пр..... MZ1) L" j' .iIii'е.... ._@....... .. ртоgraш cannot Ье М1 in DOS mode. $ aKtJ..-с:«..С«..:с:«...YI,I:«,,.YhJЬ«,,С«"ОЕ:"-У%о!l.. .«..- уты!Ji«..-У1)у«..Jl.У1'1Jlr<....у<r«..RichC«.. РЕ dt ФЙ[J Р " с!', fJ т. 0.2 + + , 0.11 .то, @t а + + + Tc Т p.' n. fJ IL 1-- 8 11, d @ 1Db-- @ .text Й + fJ . .rdata ДЛ + <t-- @ @.data ЪN о. N $. @ A.pdata cd n. f rt @ @.rзтс . Р. ( ш. @ @.reloc I L JJ J .11 @ BTa[ Ya[Jr tIO[JA kЮ[JМ +а[JЩ a[Jr ;'a[Jp "а[Jь @ю[J--- +аfJЩ ЯЯ[J!I +а[JЩ €a[! +а[JЩ Sa[J+ Оа[Jб ,a[J@ Я [n.. sЯ[JV +а[JЩ SНЕLL32.dП SНL\I.dП gdiр1us.dП ADVАРI32.dП пtdП.DLL ОLЕАUТ32.dП UхТhemе.dП о1e32.dП СОМCТL32.dП КERNEL32.dU USER32.dU RPCRТ4.dП \'INММ.dП o-'ERSION.dU GDI32.dП ms'crt.dU , H<lItlЦ JI.Lk А;ЕО"йr, Н..!'''. JI.Lu r.К". LK 11)7. нК.LLб L нК) ИХ' Не ЪD9-I)7. O...ir, 3ЙJJ.LЕt H<IlJtE YМ нКl.!' Ai'P р Р(Л r H!i! LН!Т1us Рис. 8.5. Windоws-проrpамма са/с.ехе, открытая 8 приложеНlfИ Блокнот Поскольку различные типы двоичных файлов должны обрабатываться по-разному, мы не будем пытаться в этой книrе непосредственно читать и записывать "сырые" двоичные файлы. К счастью, МНОl'ие модули упрощают работу с двоичными файлами. с одним из них, модулем shel ve, вы познако- митесь чyrь позже. В Python операции чтения/записи файлов выполняются в три этапа: 1) вызовите функцию open ( ) , которая возвратит объект File; 2) вызовите метод read () или wr i te () для объекта File; 3) закройте файл, вызвав метод clase () для объекта File. OrKp6l",e фlll.II , "0_0,,,'10 фу"к".. ореп ( ) Чтобы открыть файл с помощью функции apen ( ) , вы передаете ей стро- ку, содержащую пyrь к файлу, который хотите открыть, причем это может быть абсолютный или относительный путь. Функция open () возвращает объект File. Создайте текстовый файл heUo.txt с помощью приложения Notepad или TextEdit. Введите Hella, warld! и сохраните файл в своей пользователь- ской папке. Затем, если вы используете Windows, введите в интерактивной оболочке следующую команду: »> helloFil. ('с:\User8\ватапапкапользавателя\ hello. txt ' )
234 r лава 8 Если вы используете 05 Х, введите в интерактивной оболочке следую- щую команду: »> helloFile = open(I/User8/вашапапкапользователЯ/hеllо.txt') Подставьте вместо заменителя вашаnаn1(аn()л'ЬЗоватl'.л.я реальное имя своей папки. Например, на своем компьютере с Windows я зареrистриро- ван как пользователь asweigart и поэтому использую пyrь 'C:\Users\asweigart\ hello.txt'. Обе приведенные выше команды открывают файл в режиме "чтения простоro текста" или, для краткости, "в режиме чтения". Если файл откры- вается в режиме чтения, то Python позволяет лишь читать данные из файла; вы не сможете записать данные в файл или каким-либо образом изменить ero содержимое. Режим чтения это режим по умолчанию для файлов, открываемых в Python. Но если вы не хотите 1I0лаrаться на установки по умолчанию, то можете явно указать этот режим, передав функции open () (TpOKOBoe значение 'r' В качестве BToporo apryMeHTa. Поэтому вызовы open('/Users/asweigart/hello.txt', 'r') иореп('/Usеrs/аswеigаrt/hеllо. txt I } делают одно и то же. Вызов open () возвращает объект File. Этот объект представляет файл на вашем компьютере и является просто еще одним типом значений в Рytlюп, во MHoroM таким же, как списки или словари, с которыми вы уже знакомы. В предыдущем примере Bbl сохранили объект File в переменной helloFile. Теперь всякий раз, коrда вы захотите прочитать или записать данный файл, вам достаточно будет вызвать соответствующий метод для объекта File, со- xpaHeHHoro в переменной hel1oFile. Чrе""е ,одер."мою ф".." Имея объект File, можно при ступить к чтению данных из Hero. Если требуется прочитать все содержимое файла в виде одноrо строковоro зна- чения, используйте метод read () объекта File. Продолжим работу с объ- ектом File файла hello.txt, сохраненным в переменной helloFile. Введите в интерактивной оболочке следующие команды. »> helloContent = helloFile.read() »> helloContent 'Hello, world!' Если вы хотите рассматривать содержимое файла как одну большую строку, то метод read () возвращает (:троку, хранящуюся в этом файле.
Чтение и запись файлов 235 Возможен и друrой подход, Korдa вы используете метод readlines () для получения из файла списка СТРОК08ЫХ значений, по одной (TpOKe для каж- дой строки текста. Например. создайте файл sonnet29. txt в той же папке, в которой находится файл hello.txt. и введите в нею следующий текст. When, in disgrace with fortune and men's eyes, 1 all alone beweep ту outcast state, And trouble deaf heaven with ту bootless cries, And look ироп myself and curse ту fate, В процессе ввода текста не забывайте разрывать строки, нажимая клави- шу <Enter>. Затем введите в интерактивной оболочке следующие команды. »> _onnetFile .. ореn ( , _onnet29. txt' ) »> _onnetFile.readline_() [When, in disgrace with fortune and men's eyes,n', , 1 all alone beweep ту outcast state,n', And trouble deaf heaven with ту bootless cries,n', And look upon myself and curse ту fate, ') Обратите внимание на то, что каждое из строковых значений, за исклю- чением последней строки файла. заканчивается СИМВОJlОМ новой с.троки, n. Во мноrих случаях работать со списком строк проще, чем с одним длин- ным строковым значением. 3""." . ф,,;;л Python позволяет записывать содержимое файла аналоrично тому. как это делается при "записи" строк на экран с помощью функции print (). Од- нако запись в файл, открытый в режиме чтения, невозможна. Вместо этоrо файл должен быть открыт в режиме записи простоrо текста или добавле- ния простоrо текста (далее для краткости "в режиме записи" и "в режиме добавления" соответственно). В режиме записи содержимое существующеrо файла удаляется, и новые данные записываются "с чистоrо листа" аllалоrично тому, как в процессе операции присваивания старое значение переменной заменяется новым. Чтобы открыть файл в режиме записи, следует передать методу apen () стро- ку 'w' в качестве BToporo apryMeHTa. С друrой стороны. в режиме добавле- ния новый текст добавляется в конец существующеrо файла. Эту операцию можно рассматривать как нрисоединение HOBoro значения к хранящемуся в переменной списку, а не полную перезапись содержимоrо переменной. Чтобы открыть файл в режиме добаВJIения. следует передать методу open ( ) строку' а' В качестве BToporo apryмeнтa.
236 r лава 8 Если файла с именем, переданным методу apen ( ) , не существует, то как в режиме записи, так и в режиме добавления будет создан новый, пустой файл. Прежде чем вновь открывать файл после выполнения операций чтения или записи, ero предварительно нужно закрыrь с помощью метода close ( ) . Сведем все это воедино. Введите в интерактивной оболочке следующие команды. »> baconFile = open('bacon.txt', 'w') »> baconFile.write('Hello, world!n') 13 »> baconFile.clo.8() »> baconFile = open('bacon.txt', 'а') »> baconFile. wri t8 ( 'Васоп i. not а vegetab18. ' ) 25 »> baconFile. alO.8 О »> baconFile = open('bacon.txt') >>> content = baconFile.readO »> baconFile.clo.8() »> print(cont8nt) Hello, world! Васоп is nat а vegetable. Прежде Bcero Mbl открываем файл bacon.txtB режиме записи. Поскольку файла bacoп.txt пока что не существует, PythOll создает ero. В результате вы- зова метода wri te () для OTKpbIToro файла и передачи ему CTpoKoBoro apry' мента' НеНа, warld! /n' осуществляется запись строки в файл и возвра- щается количество записанных символов, включая символы новОй строки. Затем мы закрываем файл. Чтобы ДОIJОЛНИТЬ новым текстом содержимое существующеrо файла, а не заменить только что записанную строку, мы oTKpblВaeM файл в ре. жиме добавления текста. Мы записываем в файл строку' Bacan is not а vegetable. ' и закрываем ero. Наконец, чтобы вывести содержимое фай' ла на экран, мы открываем файл в установленном по умолчании режиме чтения, вызываем метод read () , сохраняем результирующий объект File в переменной content, закрываем файл и выводим на экран ero содержимое. Обратите внимание на то, что метод wr i te () не записывает автоматиче- ски символ новой строки в конце строки, как это делает функция print (). Этот символ вы должны добавлять самостоятельно. Сохранение переменных с помощыо модупя shel уе Используя модуль shelve, можно сохранять переменные в двоичных файлах-хранилищах. БJlаrодаря этому впоследствии проrрамма может восстановить значения переменных, читая данные с жесткOI'О диска.
Чтение и запись файлов 237 С помощью модуля shel ve можно добавить в проrрамму возможности Save (Сохранить) и Ореп (Открыть). Например, выполнив проrрамму и введя ряд конфиrypационных параметров, вы сможете сохранить их в файле xpa нилища и зarpузить при последующем запуске проrраммы. Введите в интерактивной оболочке следующие команды. »> iaport _helve »> _helfFile = _hеlve.open('шydata') »> cat_. ['Zophie', 'Pooka', 'Simon'] »> _helfFile['cat_'] = cats »> _helfFile.clo_e() Чтобы иметь возможность читать и записывать данные с помощью мо- дуля shel Уе, прежде вcero еro необходимо импортировать. Вызовите метод she 1 ve. open () и передайте ему имя файла, а затем сохраните возвращен- ное значение в переменной. Доступ к хранилищу осуществляется по клю- чу, как при работе со словарями. Закончив работу, вызовите метод close ( ) . В данном случае значение сохраняется в переменной shelfFile. Мы соз- даем список cats и записываем ero в хранилище с помощью инструкции shel fFile [ 'cats'] = cats, которая сохраняет впеременной shelfFile спи- сок в виде значения, ассоциированноrо с lUlючом 'cats'. Затем мы вызыва- ем метод close () для переменной shelfFile. Выполнив предыдущий код на Wiпdоwsкомпьютере, вы увидите в Teкy щем рабочем каталоrе три новых файла: тydata.bak, тydata,dat и тydata.dir. На компьютере, работающем под управлением 05 Х, будет (о:щан только один файл тydata. db. Описанные двоичные файлы содержат данные, которые вы сохранили в хранилище. Точный формат хранения данных в этих двоичных файлах для вас не имеет значения; вам достаточно знать лишь то, что именно де- лает модуль shel ve, а не как он это делает. Данный модуль освобождает вас от всех забот, связанных с орrанизацией хранения данных проrраммы в файлах. Проrрамма может использовать модуль shel ve для последующеro откры- тия файлов хранилища и извлечения из них данных. Хранилища не нужда- ются в открытии в режиме чтения или записи как только хранилище ()'f-- крыто, вы можете выполнять оба типа операций. Введите в интерактивной оболочке следующие команды. »> _helfFile = _hеlve.ореn('шydata') »> typ8(_helfFile) <class 'shelve.DbfilenameShelf'> »> _helfFile['cat_'] [ 'zophie 1, 'Pooka 1, I Simon 1] »> _helfFile.clo_e()
238 r лава 8 Здесь мы открываем файлы хранилища для проверки TOI'O, что данные были корректно сохранены. Команда shelfFile [ 'cats'] возвращает тот же список, который был сохранен ранее, что подтверждает корректность c<r хранения данных, а метод close () закрывает хранилище. Как и словари, хранилища имеют методы keys () и values () , извлекаю- щие из хранилища коллекции ключей и значений, подобные спискам. П<r скольку возвращаемые этими методами коллекции лишь подобны спискам, а не являются истинными списками, для 1'01"0 чтобы получать их в виде списков, их следует передавать функции list () . Введите в интерактивной оболочке следующие команды. >>> _helfFile = _helve. open ('шydata') »> li_t(_helfFile.key_()) ['cats' ) »> li_t(_helfFile.value_()) [['Zophie', 'Pooka', 'Simon')) »> _helfFile.clo_e() Формат простоrо текста удобно использовать для создания файлов, которые вы будете читать в текстовом редакторе наподобие Norepad или TextEdir.. Если же вы хотите сохранять данные из своих проrрамм на языке Python, то используйте модуль shelve. Сохранениепеременныхспомощью функции pprint .pfonnat () Вспомните, как в разделе "Красивая печать" в rлаве 51'0ВОРИЛОСЬ о том, что функция pprint. pprint () обеспечивает "красивый" вывод содержимоrо списка или словаря на экран, тоrда как функция pprint .pformat () просто возвращает тот же текст в виде строки. Эта строка не только отформати- рована так, что ее удобно читать, но и представляет собой синтаксически правильный код Python. Предположим, у вас есть словарь, сохраненный в переменной, и вы хотите сохранить эту переменную и ее содержимое для будущеrо использования. Применив функцию pprint. pformat () , вы получи- те строку, которую можно записать в .ру-файл. Этот файл будет вашим соб- ственным модулем, который вы сможете импортировать всякий раз, коrда захотите ИСIlользовать хранящуюся в нем переменную. Например, введите в интерактивной оболочке следующие команды. »> iшport pprint »> cat_ = [{'namе': 'Zophie', 'де_с': 'chubby'}, ('паше': 'Pooka', 'де_с': 'fluffy')] »> pprint.pformat(cat8)
Чтение и запись файлов 239 "[{'desc': 'chubby', 'пате': 'Zophie'}, {'desc': 'fluffy', 'пате': , Pooka ' } ] " »> fileObj .. open('DlyCat8.py', '1") »> fileObj.writ8('cats = ' + ррrint.рfоrшаt(саt8) + 'n') 83 »> fileObj.clo.8() Здесь мы импортируем модуль pprint, чтобы иметь возможность исполь- зовать функцию pprint.pformat (). у нас есть список словарей, сохранен- ный в переменной cats. Чтобы сохранить возможность обращения к спи- ску, хранящемуся в переменной cats, даже после Toro как будет закрыта обо- лочка, мы используем функцию pprint. pformat () , возвращающую список в виде строки. Получив данные, хранящиеся переменной cats, в виде строки, мы сможем леrко записать их в файл, который мы назовем туСам.ру. Модули, импортируемые с помощью инструкции import, сами являются не более чем обычными сценариями Python. После Toro как строка, воз- вращаемая pprint. pformat () , будет сохранена в ,ру-файле, этот файл станет модулем, который может быть импортирован подобно любому друrому мо- дулю. А поскольку сценарии Python сами по себе являются пр<><:тыми тексто- выми файлами с расширением .ру, ваши ПрОl'раммы на Python MOryт даже I'енерировать друrие Руthоп-проrраммы. Впоследствии эти файлы MOryт импортироваться в сценарии. »> iDlport DlyCats >>> DlyCat8.cats [{'пате': 'Zophie', 'desc': 'chubby'}, {'name': 'Pooka', 'desc': 'ПиНу' }] »> myCats.cat.[O] {'пате': 'Zophie', 'desc': 'chubby'} »> DlyCat..cat.[O] ['nаше'] 'zophie' Преимуществом создания .ру-файлов (в отличие от сохранения пере мен- ных с помощью модуля shel ve) является то, что, поскольку они представля- ют собой обычный текстовый файл, ero содержимое можно читать и изме- нять с помощью обычноrо TeKcToвoro редактора. Однако ДЛЯ большинства приложений сохранение данных с использованием модуля shelve является более предпочтительным способом сохранения переменных в файле. Запи- сывать в файл в виде простоrо текста можно только данные элементарных типов, такие как целые числа и числа с плавающей точкой, строки, списки и словари. Объекты же File, например, не MOryт быть закодированы в виде текста.
240 r лава 8 Проект: rенерация файпов Сllучайных зкэаменационных 6Иllетов Предположим, вы читаете reоrpафию rруппе из 35 студентов и хотите провести контрольную работу на знание столиц штатов 8 США. Увы, 0& становка в вашем классе такая, что вы не можете быть уверены в том, что студенты не будут списывать друr у дрyra. Вы хотели бы составить экзамена ционные билеты таким образом, чтобы вопросы в них располаraлись в ('.лу чайном порядке, блarодаря чему каждый билет будет отличаты-я от дрyrоro, и это затруднит списывание ответов. Разумеется, составлять такие билеты вручную задача утомительная и к тому же отнимающая MHoro времени. К счастью, вы уже освоили некоторые возможнО{ти Python. Вот примерный план действий, которые должна выполнять проrрамма: . создать 35 различных билетов; . создать по 50 вопросов с множественным выбором для каждоro биле- та, расположив их в случайном порядке; . предоставить правильный ответ и три случайно выбранных непра вильных ответа на каждый вопрос, располаrая их в случайном порядке; . записать билеты в 35 текс.товых файлов; . записать ключи ответов в 35 текстовых файлов. Это означает, что код должен будет выполнять следующие операции: . сохранять названия штатов и их столиц в словаре; . вызывать методы open () , write () и close () для текстовых файлов, в которых хранятся билеты и ключи ответов; . использовать функцию random. shuffle () для рандомизации (располо- жения в случайном порядке следования) вопросов и вариантов MHO жественноro выбора. Ша, '. Сохране".е lIан".,х 6..ero8 8 '.О8аре Первый шar состоит в том, чтобы создать "скелет" сценария и напол нить ero данными билета. Создайте файл raпdoтQyizCnтerator.py и введите в Hero следующий текст. #! руthопЗ i randomQuizGenerator.py Создает экзаменационные билеты с t вопросами и ответами, расположенными в случайном порядке, # вместе с ключами ответов. о import random # Данные билета. Ключи названия штатов, а значения столицы. О capitals = {'Alabama'; 'Montgomery', 'Alaska': 'Juneaи" 'Arizona': 'Phoenix', 'Arkansas'; 'Little Rock', 'California';
Чтение и запись файлов 241 'Sacramento', 'Colorado': 'oenver', 'Соппесt icut ': 'Hartford', 'Delaware': 'Dover', 'Florida': 'Tallahassee', 'Georgia': 'Atlanta', 'Hawaii': 'Honolulи" 'Idaho': '80i5e', 'Illinois': 'Springfield', 'Indiana': 'Indianapolis', 'Iowa': 'Оев Moines' , 'Kansas': 'Topeka', 'Kentucky': 'Frankfort', 'Louisiana': 'Baton Rouge', 'Maine': 'Augusta', 'Maryland': 'Annapolis' , 'Massachusetts': 'Boston', 'Michigan': 'Lansing', 'Minnesota': 'Saint Paul', 'MiS5issippi': 'Jackson', 'Missouri': 'Jefferson City', 'Montana': 'Helena', 'Nebraska': 'Lincoln', 'Nevada': 'Carson City', 'New Hampshire': 'Concord', 'New Jersey': 'Trenton', 'New Mexico': 'Santa Fe', 'New York': 'Albany', 'North Carolina': 'Raleigh', 'North oakota': 'Bismarck', 'Ohio': 'Columbus', 'Oklahoma': 'Oklahoma City', 'Oregon': 'Sa1em', 'pennsylvania': 'Harrisburg', 'Rhode Island': 'Providence', 'South Carolina': 'Columbia', 'South Dakota': 'Pierre', 'Tennessee': 'Nashville', 'Texas': 'Austin', 'Otah': 'Salt Lake City', 'Vermont': 'Montpelier' , 'Virginia': 'Richmond', 'Washington': 'Olympia', 'West Virginia': 'Charleston', 'Wisconsin': 'Madison', 'Wyoming': 'Cheyenne'} # rенерация 35 файлов билетов. . for quizNum in range(35): # тооо: Создать файлы билетов и ключей ответов. # TODO: Записать заrоловок билета. # TODO: Перемешать порядок следования штатов. # тооо: Орrанизовать цикл по всем 50 штатам, # создавая вопрос для каждоrо из них. Поскольку эта nporpaMMa должна располаrать вопросы и ответы в слу- чайном порядке, вам понадобится импортировать модуль random ., чтобы использовать el'o функции. Переменная capitals . содержит словарь, в K<r тором штаты CIIIA иrрают роль ключей, а значениями являются названия столиц штатов. А поскольку вы хотите создать 35 билетов, код, который бу- дет фактически reнерировать файлы билетов и ключей ответов (на данном этапе отмечен комментариями ТООО), должен бьrrь помещен в цикл for, выполняющий 35 итераций .. (Это число можно изменить для rенерации любоro заданноrо количества билетов.) Ш",2. COIlf"""e ."..08 6"..108 " "ере.'."8""'" 80"1'0'08 А теперь настал черед приступить к замене комментариев TODO реаль- ным кодом. Код в цикле будет повторен 35 раз 110 одному разу на каждый билет, в связи с чем вам достаточно сосредоточивать внимание в цикле каждый раз
242 rлава 8 только на одном билете. Прежде Bcero, необходимо создаТI. фактический файл билета. Он должен иметь уникальное имя, а также содержать некий стандартный зarоловок с пустыми полями для имени, даты и класса, кото- рые будут заполняться студентами. Далее вам нужно будет IIОЛУЧИТЬ список штатов, расположенных в случайном порядке, который впоследствии мож- но будет использовать для создания вопросов и ответов 1< каждому билету. Добавьте в файл rаndoтQиiz.Generаtor.рууказанные ниже строки кода. #! python3 # randomQиizGenerator.py Создает экзаменационные билеты с # вопросами и ответами, расположенными в случайном порядке, # вместе с ключами ответов. пропущенный KOД # rенерация 35 файлов билетов. for quizNum in range(35): # Создание фай,п08 БИnИ08 И 1UJJOЧ8Й 0'1'88'1'08. О quizFile = open('capital.quiz%..txt' % (quizNwa + 1), '1") О 8n81'erKeyFile - оpen (1 capital.quiz answer.%.. txt' % (quizNwa + 1), '1") * Зanиоь зarroп08ка билиа. . quizFile. 1'ri t8 ( 'Имя: n nДa'1'a: n nКypc : n n I ) quizFile.1'rite«' I * 15) + 'Пр08ерка зlUUlИJl CI'1'OJJИЦ lI'1'а'1'08 (Випи %.)' % (quizNuDI + 1» quizFile.1'rit8('nn') * П8р8М811И88НИе ПopядlCа спед08ания CI'1'OJ1ИЦ 1I'1'a'1'08. 8tate. = li.t(capital..key.(» о randoJD. shuffle (.аteз) # тооо: Орrанизовать цикл по всем 50 штатам, # создавая вопрос для каждоrо из них. Файлы будут иметь имена capitalsquiz<N>.txt, I'де <N> это уникальный номер билета, который берется из переменной цикла qui zNuт. Ключи от- ветоВ для файлов capitalsquiz<N>. txt будут храниться в тек<.:товых файлах capitalsquizanswers<N>.txt. При каждом прохождении цикла вместо замести. теля %s в строках' capitalsquiz%s. txt' и 'capitalsquizanswers%s. txt' бу- дет подставляться значение (quizNum + 1), поэтому файлами l1epBoro из соз- даваемых билетов и ключа ответа будут capitalsquiz.1. txl и capitalsquiz.aпswers 1. txt. Эта файлы будут создаваты-я вызовами функции open () в инструкциях О и . с передачей им строки 'w' в качестве ВТОрОI'O apryмeHTa для открытия файлов в режиме записи. Инструкции wri te () . создают заl'OЛОВОК билета с полями, которые будут заполняться студентами. Наконец, с помощью функции random. shuffle () ., которая случайным образом переynорядочивает список любых переданных ей значений, создается рандомизированный список всех штатов США.
Чтение и запись файлов 243 Ша, 3. COJllaH"e .ap"aHrOB OrBero. Теперь необходимо crенерировать варианты ответов для каждоrо вопро- са, предоставляя возможность выбора одноrо из ответов, обозначенных буквами от А до О. Вам понадобится создать еще один цикл for он будет rенерировать содержимое для каждоrо из 50 вопросов билета. Далее будет третий, вложенный цикл for, предназначенный для rенерации вариантов множественноrо выбора для каждоrо вопроса. Дополните имеющийся код, как показано ниже. #! руthопЗ # randomQuizGenerator.py Создает экзаменационные билеты с # вопросами и ответами, расположенными в случайном порядке, # вместе с ключами ответов. пропущенный KOД * Ор:rаНИ8ация цима по 8оем 50 штатам * с созданием 80npоса для К8ЖДо:rо из них. for que.tionNum in range(50) : * Попучение npавипьНblX и нenpaвипьных отве'1'О8. О correctAn."er - capital. [.tate8 [questionNwa)) . wrongAn.wer. = li.t (capi tal. . val ue. () ) о del "rongAn.wer.[wrongAn.wer..index(correctAnswer)) О wrongAn.wer. = randoВl..ample(wrongAn.wer., 3) О an."erOption. = wrongAn.wer. + [correctAn.wer) О randoJa. .huffle (answerOption.) * 'l'ODO: Записа 8apиaR'1'W ВОпроСО8 И 0'1'88'1'08 * 8 фвйn esипе'1'а. * 'l'ODO: Зanиса JCJDIN 0'1'88'1'а 8 ф8Йn. Корректный ответ можно леrко получить он хранится в виде значе- ния в CJюваре capitals е. Данный цикл итерирует по штатам, содержа- щимся в перемешанном списке штатов, от states [О] До states [49], находит каждый штат в capitals и сохраняет название ero столицы в переменной correctAnswer. Со списком возможных неправильных ответов дело обстоит несколько сложнее. Вы сможете ПOJIYЧить еro, продублировав все значения из словаря capitals е, удалив правильный ответ е и выбрав три случайных значения из этоrо списка е. Функция random. sample () упрощает этот выбор. Ее пер- вый apryмeнт это список, из Koтoporo вы хотите выбирать значения; вто- рой apryмeнт это количество значений, которые вы хотите выбрать. Пол- ный список вариантов ответа представляет собой сочетание трех непра-
244 r лааа 8 вильных ответов справильным .. Наконец, ответы следует перемешать ., чтобы правильный ответ не Bcerдa соответствовал варианту Н. Ш", 4. 3"""" 'ОllеР."IIО'О . ф".л., 6"nе,о. " КЛIO,е. о,.е,о. Теперь все, что осталось сделать, это записать вопрос в файл билета, а ответ в файл ключей ответов. Дополните код, как 110казано ниже. #! python3 # randomQuizGenerator.py Создает экэаменационные билеты с # вопросами и ответами, расположенными в случайном порядке, # вместе с ключами ответов. пропущенный KOД # Орrанизация цикла по всем 50 щтатам # с созданием вопроса для каждоrо иэ них. for questionNum in range(50): пропущенный KOД # Запись варианто. .ОПРОСОВ и ответов . фaйn 6иnета. quizFi18.writ8('%8. выберете monицу lI'1'aTa %8.n' % (qu88tionNwD + 1, 8tat8. [qu88tionNum]) ) о for i in range (4) : О quizFi18.writ8(' %8. %.n' % ('AВCD' [i], an8w8rOption8 [i] ) ) quizFi18.writ8('n') # Зanис. a ОТ.8та . ф8Йn. О an8werКeyFi18.writ8('%8. %8n' % (qu88tiOnNum + 1, 'AВCD'[an8w8rOption8.index(00rreotAn8w8r)]» quizFi1e. 01088 () an8werhyFil8. 01088 О Цикл for, перебирающий целые числа от О До 3, записывает варианты ответов в список answerOptions.. В выражении 'АВСО' [i] . строка 'АВСО' трактуется как массив с элементами 'А', 'В', 'С' и 'О' , выбираемыми на со- ответствующей итерации ЦИкла. В последней строке. выражение answerOptions. index (correctAnswer) находит целочисленный индекс правильноro ответа среди случайно рас- положенных вариантов, а вычисление выражения 'АВСО' [answerOpt ions. index (correctAnswer) ] дает буквенное обозначение правильноrо варианта ответа, подлежащеrо записи в файл ключа ответа. Ниже показан примерный вид содержимоrо файла capitalsquizl.txt, хотя, разумеется, вопросы и варианты ответов в вашем файле будут выrлядеть иначе, в зависимости от результатов вызова функции random. shufПе ().
Чтение и запись файлов 245 Имя: Дата: Курс: Проверка знания столиц штатов (Билет 1) 1.Выберите столицу штата West Virginia. А. Hartford В. Santa Fe С. Harrisburg О. Charleston 2. Выберите столицу штата Colorado. А. Raleigh В. Harrisburg С. Denver О. Lincoln опущено Соответствующий текстовый файл capitalsquiz.answersl.txt будет выrля- деть примерно так. 1. D 2. С З. А 4. С опущено Проект: буфер обмена дnя работы с нескояькими значениями Предположим, вам пред<:тоит yrомительная работа по заполнению ряда форм на веб-странице или мноrочисленных текстовых полей в проrpaмме. Буфср обмена обеспечивает экономию времени, избавляя вас от необходи- мости 1I0BTOpHOI"O ввода одноro и тoro же текста. Вместе с тем в Hel'O можно копировать только один фраrмент TeKLTa за один раз. Если же у вас имеется несколько рааличных тек(:товых ФраrмеllТОВ, ПОДJIсжащих копированию и вставке, то вам приходит(я каждый раз заново выделять и копировать одни и те же фраrменты. Однако можно написать проrрамму на языке Python, которая будет отслеживать различные фраrмеllТЫ текста. Мы назовем этот "мноrоза- рядный" буфер ("multiclipboar"d") тсЬ.руш (поскольку "mсЬ" короче, чем "шullkliрЬоar"d"). Расширение .руш означает, что Python не будет отобра- жать окно терминала в процессе выполнения проrpаммы. (Более подробно об этом читайте в ПРИЛОЖСIIИИ Б.)
246 r лава 8 Проrрамма будет сохранять каждый фрarмент копируемоrо в буфер тек- ста с использованием сиоеrо ключевою слова. Например, е<:ли вы ВЫIЮЛ- ните команду ру тсЬ. pyw save spam, то текущее содерЖИМое буфера обмена будет сохранено с ключевым словом spam. Впоследствии этот тек<:т можно будет вновь зarрузить в буфер обмена с помощью команды ру тсЬ. pyw spam. А если пользователь забудет, какие ключевые слова соответствуют тем или иным текстовым фрarмептам, то он сможет выполнить команду ру тсЬ. pyw 1 ist для копирования списка всех ключевых слов в буфер обмена. Вот что делает данная проrpамма: . проверяет apryMeHT командной строки для ключевOI'О слова; . е<:ли этот apryмeHT save, то содержимое буфера обмtна сохраняется с данным ключевым словом; . если этот арryмент list, то все ключевые слова копируются в буфер обмена; . в противном случае текст, соответствующий ключевому слову, копиру- ется в буфер обмена. Это означает, что код должен выполнять следующие действия: . читать apryмeHTЫ командной строки из переменной sys. argv; . выполнять операции чтения и записи в буфер обмена; . сохранять и заrружать текст в файл хранилища. Если вы работаете в Windows, то вам будет леrко выполнить этот сцена- рий из окна Run (Выполнить), создав пакетный файл тrh.batco следующим содержимым: @pyw.exe C:Python34mcb.pyw %* Ша, '. Ko_eнrap.. " "aapoiKa .жра".....а Начнем с создания каркаса сценария, содержащеr() некоторые коммен- тарии и базовые настройки. Создайте следующий код. #! python3 # mcb.pyw Сохраняет и заrружает фраrменты текста # в буфер обмена. О # Использование: ру.ехе mcb.pyw save <ключевоеслово> # Сохраняет буфер обмена в ключевое слово. # ру.ехе mcb.pyw <ключевоеслово> # Заrружает ключевое слово в буфер обмена. # ру.ехе mcb.pyw list # 3аrружает все ключевые слова в буФер # обмена.
Чтение и запись файлов 247 о import shelve, pyperclip, sys о mcbShelf shelve.open ('тсЬ') # тооо: Сохранить содержимое буфера обмена. # TODO: Сформировать список ключевых слов и заrрузить # содержимое. mcbShelf. close () Общепринятой практикой является размещение общей информации о порядке использования проrраммы, оформленной в виде комментариев в начале файла.. Если вы вдруr забудете, как выполнить сценарий, взrля- ните на комментарий. Затем импортируются необходимые модули .. Для копирования и вставки текста потребуется модуль pyperclip, а для чтения apryMeHToB командной строки модуль sys. Также потребуется модуль shelve: всякий раз, коrда пользователь захочет сохранить НОВЫй фраrмент находящеrося в буфере обмена текста, вы сохраните ero в файле хранили- ща. Далее, если пользователь захочет поместить текст обратно в буфер, вы откроете файл хранилища и заI'рузите ero в проrрамму, Имя файла храни- лища будет содержать префикс тсЬ .. Ш",2. 'Ollt"... 'ОIl'Р...О'О 6уф.р" ОМ''''', ""о"."ру'.ою , оlO...... tJlО.О. Проrрамма выполняет различные действия в зависимости от 1'01"0, чеrо хочет пользоватеJlЬ: сохранить текст, ассоциируя ero с ключевым словом, заrрузить текст в буфер или вывести список всех имеющихся ключевых ('лов. Рассмотрим первый с.лучай. ДОП()}Jните код, как показано ниже. #! руthопЗ # mcb.pyw Сохраняет и заrружает фраrменты текста # в буфер обмена. пропущенный KOД # Сохранение содержимоI'O буфера оБИ8на. О if len(.y..argv) .... 3 and .y..argv[l] .lower() .. '.аув': . DlcbShelf[.y..argv[2]] .. pyperclip.pa.teO elif len(.y..argv) == 2: О # Сформировать список ключевых слов и заrрузить содержимое. mcbShelf.close() Если первым apryMeHToM командной строки (который всеrда будет иметь индекс 1 в списке sys. argv) ЯВJIЯется 'save' О, то вторым aployмeHToM
248 r лава 8 ЯWJяется ключевое слово для текущеrо содержимоrо буфера обмена. Ключе- вое слово будет использоваться в качестве ключа для хранилища mcbShelf, тоrда как значением будет текст, находящийся в данный момент в буфере обмена .. Если предоставлен только один apryMeHT командной строки, то вы пред- полаrаете, что это либо строка' list', либо ключевое слово для заrрузки содеРЖИМОJ'О в буфер обмена. Этот код вы реализуете lIозднее. А пока что оставьте в этом месте кодасоответ(:твующий комментарий TODO.. Ш", 3. Сn.,ок KIlIO".WX ело. . J""YJK" 'Oll"...oro, "ccoq..pO."HHOro t KIlIO".". '.0.0. Ilаконец, реализуем два оставшихся пункта: зarpузка в буфер обмена TeK ста, ассоциированноrо с определенным ключевым словом, и вывод списка всех доступных ключевых слов. Дополните код, как показано ниже. #! python3 # mcb.pyw Сохраняет и заrружает фраrменты текста # в буфер обмена. пропущенный KOД # Сохранение содержимоrо буфера обмена. if len(sys.argv} == 3 and sys.argv{l] .lower() == 'save': mcbShelf[sys.argv[2]] = pyperclip.paste() elif len(sys.argv) == 2: * Формирование cnиска КJDI)Чеаых с.пов и зaжpvзка СОДepDlМOl'O. О if &y..arqv[l].lower() == 'li.t': О руреrсliр.cqpy(.tr(li.t(шdbShelf.keys(»» elif .y..argv[l] in mcbShelf: О pyperclip. cqpy (шсЬShеlf [.У.' arqv[ 1] ] ) mcbShelf.close(} Если имеется только один арryмент командной строки, то прежде вcero необходимо проверить, является ли ИМ строка' list' .. Если это действи- тельно так, то в буфер обмена копируется строковое представление списка ключей хранилища .. Пользователь может вставить этот список в окно oт KpblToro TeKcтoBoro редактора и прочитать ero. В противном случае можно полaraть, rro apryMem командной строки яв- ляется ключевым словом. Если это ключевое слово существует в виде ключа хранилища mcbShelf, можно заrpузить соответствующее значение в буфер обмена .. Вот и все! В зависимости от установленной на компьютере операцион- ной системы эта проrрамма может запускаться Шrразному. Детали запуска ПрОI'рамм в различных операционных системах описаны в приложении Б.
Чтение и запись файлов 249 Вспомните проrpамму парольной защиты, сохраняющую пароли в сл<r варе, которую мы создали в rлаве 6. Обновление паролей требовало изме- нения исходноro кода проrраммы. Это далеко не идеальный вариант, по- скольку пользователи не MOryr чувствовать себя комфортно, если для об- новления проrpаммы им приходится самостоятельно вносить изменения в код. Кроме Toro, всякий раз, коrда приходится изменять исходный код I1pOrpaMMbI, существует риск случайноrо внесения в нее новых ошибок. ('.о- храняя данные для проrpаммы не в коде, а в дpyroM месте, вы облеrчаете использование проrраммы друrими людьми и снижаете вероятность появ ления в ней новых ошибок. РеЗlOме Файлы ОРl'анизуются в папки (друroе название каталоrи), и их расп<r ложение описывается пyrями доступа. Каждая проrpамма, выполняющая ся на вашем компьютере, имеет текущий рабочий каталоr, что позволяет указывать пyrи относительно текущеrо расположения вместо Toro, чтобы всеrда задавать полный (или абсолютный) путь. Модуль 05. path содержит множество функций, предназначенных для манипулирования пyrями досту- па к файлам. Ваши проrраммы имеют возможность непосредственно взаимодейстВ<r вать с содержимым текстовых файлов. Функция open () позволяет OTKpЫ вать эти файлы для чтения их содержимоI'О в виде одной длинной строки (с помощью метода read () или в виде списка строк (с помощью метода readline5 () ). Функция open () может открывать файлы в режиме записи или присоединения для создания новых текстовых файлов или добавления TeK ста в конец существующих файлов соответственно. В предыдущих rлавах вы использовали буфер обмена в качестве сред- ства, позволяющеrо вставлять в проrрамму rотовый текст, а не вводить eI'o вручную. Теперь же вы научились читать необходимые проrрамме данные непосредственно с жесткоrо диска, что является большим дости' жением, поскольку хранить данные в файлах I'ораздо надежнее, чем в бу- фере обмена. Из следующей rлавы вы узнаете о том, как обрабатывать сами файлы, Т.е. выполнять такие операции, как копирование, удаление, переименова- ние, перемещение файлов и мноrие ДРУI'ие. Контропьные вопросы 1. Относительно чеrо задается относительный пyrь? 2. С чеrо начинается абсолютный путь? 3. Каково назначение функций 05. getcwd () и 05. chdir ()?
250 4. 5. 6. 7. 8. 9. r лава 8 Что собой представляют папки . и . .? Какие части пути С:Ьасоnеggssраm.tхtпредставляют имя папки и базо- вое имя? Назовите три возможных значения apryMeHTa, задающие режим o крытия файла, которые MOryт передаваться функции open ( ) . Что происходит при открытии существующеrо файла в режиме запи си? Чем ра:шичаюп:я методы read () и readlines ()? Какую структуру данных напоминает орrанизация хранилища, созда- BaeMoro с помощью модуля shelve? Учебные проекты Чтобы закрепить полученные знания на практике,напишите проrрам- мы для предложенных ниже задач. Р"сш.рен.е возможносте. 6уфер" .е.", Р""'."'''''ОIO н" Р"'ОТУ , не,"о.,,,,,.,, зн",ен".." Расширьте возможности проrраммы для работы <: не(:колькими значени ями через буфер обмена таким образом, чтобы она допускала использова- ние apryMeHTa командной строки ВИда delete <КЛ1Очевоес.лово>, обеспечи вающеrо удаление ключевоro слова из хранилища. Затем добавьте apryмeHT командной строки delete, позволяющий удалить все ключевые слова, пporp".M" II"d L;b$ Создайте проrрамму Mad Libs, которая читает текстовые файлы и пре доставляет пользователю возможность добамять собственный текст в лю бом месте файла, rде встречаются слова ADJECTIVE (прилаrательное), NOUN (существительное), ADVERB (наречие) и VERB (rлаrол). Например, содержи мое TeKcToBoro файла может иметь следующий вид: ТЬе ADJECTIVE panda walked to the NOUN and then VERB. А nearby NОИN was unaffected Ьу these events. Проrрамма найдет вхождения перечисленных слов и предложит пользо вателю заменить ИХ. Введите имя прилаrательное: silly Введите имя существительное: chande1ier
Чтение и запись файлов 251 Введите rлаrол: screamed Введите имя существительное: pickup truck в результате будет еоздан следующий текстовый файл: The silly panda walked to the chandelier and then screamed. А nearby pickup truck was unaffected Ьу these events. Результаты должны выводиться на экран и сохраняться в новом TeKCTO вом файле. по",к , пОМОЩ61D pery..p""X ."ра.е",,;; Напишите проrрамму, которая открывает все файлы с расширением. txt, находящиеся в папке, и выполняет поиск строк, соответствующих предо ставленному пользователем реryлярному выражению. Результаты должны ВblВОДИТЬСЯ на экран.
VПРАвпЕНИЕ ФАйпАМИ в предыдущей rлаве вы научились создавать и записывать на жесткий диск новые файлы в Python. Но ваши проrрам- мы MOryr реорrанизовывать и файлы, которые уже суще- ствуют на жестком диске. Возможно, вам приходилось рабо- тать с папками, хранящими десятки, сотни и даже тысячи файлов, которые нужно было копировать, переименовы- вать, перемещать или сжимать вручную. Задачи подобноrо рода MOryr быть самыми разнообразными, включая, например, следующие: . создание копий всех РDF-файлов (и moJlЪ"'О РIН'-файлов) во всех под папках заданной папки; . удаление ведущих нулей из имен всех файлов наподобие spaтOOl.txt, spaт002.txt, sратООЗ.tхt и так Далее, сохраненных в некоторой папке в количестве, исчисляемом сотнями; . сжатие содержимоrо нескольких папок в один ZIР-файл (что может быть использовано для создания простейшей системы для создания резервных КОПИй файлов). Подобные рутинные задачИ так и просятся, чтобы их автоматизировали с помощью Python. Проrpаммируя свой компьютер для выполнения Taкoro рода задач, вы леrко превратите ero в расторопноrо, безошибочно функ- ционирующеrо офисноrо клерка. Начав работать с файлами, вы вскоре поймете, насколько удобно иметь возможность непосредственно видеть, какое расширение (.txt, .pdj, .jрgи др.) имеет тот или иной файл. В операционных системах 05 Х и Lint1x обо- зреватель файлов в большинстве случаев автоматически отображает pac ширения имен. В случае же Windows расширения имен файлов по YMOk чанию MoryT скрываться. Чтобы отобразить их, выберите ПУНКТЫ меню ПускПанель управленияОформление и персонализацияПараметры папок. Пере- йдите в открывшемся окне на вкладку Вид и снимите флажок Скрывать расши- рения для зареrистрированных типов файлов в разделе Дополнительные параметры.
254 r лава 9 Модупь shutil Модуль shutil (от аllI'Л. "shell utilities" утилиты командной оболочки) содержит функции, позволяющие копироваТI>, перемещать, переименовы вать и удалять файлы с IIОМОЩЬЮ проrpамм на PytllOll. Прежде чем исполь зовать эти утилиты, необходимо выполнить инструкцию import shutil. Ко""ров"н", ф"..08 " п""ок Модуль shutil предоставляет функции для копирования как файлов, так и целых папок. Вы:юв shut il. сору (исходный путь, путь назначения) приведет к КОПИр<r ванию файла, расположение KOToporo определяется пyrем ИСХОДНЫЙ путь, В папку, определяемую пyrем путьназначения. (Параметры исходныйпуть и путьнаэначения являются строками.) Если apryMeHT путьназначения это имя файла, то оно будет использовано 8 качестве HOBoro имени скопи- pOBaHHoro файла. Эта функция возвращает строку, (одержащую пyrь к CK<r пированному файлу. Чтобы посмотреть, как работает функция shutil. сору (), введите в ИIIТC'" рактивной оболочке следующие команды. >>> import .hutil, о. »> 08.chdir('C:\') О >>> shutil. сору ( 'С: \spam. txt', I С: \deliciou. ' ) 'C:\delicious\spam.txt' О >>> .hutil.copy('egq..txt', 'C:\delicious\eqq.2.txt') 'C:\delicious\eggs2.txt' Первый вызов функции shutil. сору () копирует файл C:fpaт.lxt В папку C:VJelicious. Возвращаемым значением является пyrь к скопированному фай- лу. Обратите внимание на то, что в качестве объекта назначения указана папка., а имя копии файла совпадает с именем исходноrо файла spaт. txt. Второй вызов shutil. сору () . также выполняет копирование файла C:. txt в папку C:VJeliciou..s, но копии файла присваивается имя eggs2. txt. В то время как функция sh и til. сору () копирует одиночный файл, функ- ция shutil.copytree () копирует папку вместе со всеми папками и файлами, которые в ней содержатся. В результате вызова shutil. copytree (исходный путь, путьназначения) папка, находящаяся в расположении исходный путь, копируется вместе со всеми находящимися в ней файлами и под' папками в раПlOложение путьназначения. Параметры исходныйпуть и путьназначения являются строками. Функция возвращает строку, пред- ставляющую путь к копии папки.
Управление файлами 255 Введите в интерактивной оболочке следующие команды. »> iшроrt _hutil, оз »> оз.chdir('С:\') »> зhutil.соруtrее('С:\Ьаооn', 'C:\baconbackup') 'C:\baconbackup' в результате вызова shutil. copytree () создается новая папка baconbackup с таким же содержимым, как и содержимое исходной папки Ьасоn. Теперь у вас есть резервная копия вашей бесценной папки Ьасоn. п.р.....,."". " "'Р'".'''08''''''' фаЙЛ08 " """ОК Вызов функции shutil.move (исходныйпуть, путьназначения) пере- Мещает файл или папку из расположения исходныйпуть В расположение путьназначенияи возвращает строку, представляющую абсолютный пyrь к новому расположению. Если l1араметр путь назна чения представляет папку, исходный файл пе-- ремещается в папку назначения и сохраняет свое текущее имя. Например, введите в интерактивной оболочке следующие команды. »> iшроrt _hutil »> зhutil.ШОV8('С:\Ьасоn.txt', 'C:\egqs') 'C:\eggs\bacon.txt' в предположении, что папка eggs существует в каталоre c: вызов shutil. тоуе () в словесной формулировке означает следующее: "Переме(ТИ1Ъ файл C:'/Jacon. txt в папку C: "(:ggs" , Если в папке C:"(:ggs уже существует файл Ьасоп, txt, то он будет заменен. Поскольку таким образом можно очень леl"КО случайно потерять нужный файл, вы должны проявлять определенную осторожность, коrда используе- те функцию move ( ) . Параметр путь назна чения также может задавать имя файла. в следую- щем примере исходный файл перемещает(:я и переименовывастся. »> _hutil.шоvе('С:\Ьасоn.txt', 'C:\egqs\new bacon.txt') 'C:\eggs\newbacon.txt' Эта строка кода имеет следующий смысл: "Переместить файл C:'.pacon.tx/ в папку C:"(:ggs и присвоить перемещенному файлу bacoп.txt новое имя nеш Ьасоn. txf, Оба предыдущих примера работают в предположении, что в каталоrс с: существует папка eggs. Но если это не так, то функция move () переименует файл Ьасоn. txt в файл eggs.
256 r лава 9 »> Shutil.шove('С:\Ьасоn.txt', 'C:\eggs') 'с: \eggs , Здесь функция move ( ) , не найдя папку eggs в каталоre c: npедполаrает, что путь назначения ДОJlжен обозначать имя файла, а не папки. Поэтому текстовый файл bacon.txt переименовывается в файл eggs (текстовый файл, но без расширения. txt) вероятно, вразрез с вашими намерениями! Разrля- деть подобную ошибку в проrрамме очень трудно, поскольку вызов move ( ) может беспрепятственно сделать то, чею вы совершенно не ожидали. Это еще одна из причин, по которым работа с функцией move () требует осто- рожности. Наконец, задаваемые в качестве nyти назначения папки должны суще ствовать, иначе PythOIl сreнерирует исключение. Введите в интерактивной оболочке следующую команду. »> 8hutil.шоve('spаш,txt', 'C:\d088 not ехi8t\egg8\haш') Traceback (most recent call last): File "C:Python34libshиtil.py", line 521, in move os.rename(src, realdst) FileNotFoиndError: [WinError 3] The system cannot find the path specified: 'spam.txt' > 'c:\doesnotexist\eggs\ham' В процессе обработки вышеприведенноro исключения возникает друroe исключение. Traceback (most recent call last): File "<pyshell#29>", line 1, in <modиle> shиtil.move('spam.txt', 'c:\does поt exist\eggs\ham') File "C:Python34libshиtil.py", lIne 533, in move copy2(src, real dst) File "C:Python34libshutil.py", line 244, in сору2 copyfile(src, dst, fo11ow symlinksfollow symlinks) File "C:Python34libshutil.py", line 108,in copyfile with open(dst, 'wb') as fdst: FileNotFoиndError: [Errno 2] No such file or directory: 'c:\does not exist\ eggs\ham' Python ищет eggs и haт в каталоrе doe.snoCexist. Поскольку найти несуще- ствующие каталоrи ему не удается, он не может псреместить файл spaт.txt по указанному пyrи.
Управление файлами 257 В".о'.р"'.о, УII".'..' ф".nо. . п"пок Если для удаления одиночною файла или одиночной пустой папки мож но воспользоваться функциями, содержащимися в модуле 05, то для уда- ления папки вместе со всем ее содержимым следует использовать модуль 5hutil. . Вызов 05. unlink (путь) удаляет файл, расположенный 110 указанному пyrи. . Вызов 05. rmdi r (путь) удаляет папку. расположенную по указанному пути. Эта папка должна быть пуста. Т.е. не должна содержать никаких друrих папок и файлов. . Вызов 5hutil. rmtree (путь) удаляет папку, расположенную по указан ному пyrи. вместе со всеми содержащимися в ней друrими папками и файлами. Испольэуя эти функции в своих проrpаммах, соблюдайте осторожность! Часто имеет смысл предварительно запустить версию проrраммы, в KOT<r рой эти вызовы "закомментированы" . а контроль тою, какие файлы будут удаляться, осуществляется с помощью функции print (). Ниже приведен фраrмент проrраммы на языке Python. предназначенный для удаления файлов с расширением. txt, но из-за допущенной опечатки (выделена п<r лужирным шрифтом) удаляющий файлы с расширением. пct. import 05 for filename in 05.1i5tdir(): if filename.endswith(' .rxt'): 05.unlink(filename) Если бы У вас были важные файлы, имена которых заканчиваются pac ширением . пct, то все они были бы безвозвратно удалены. Вместо этоrо сле довало бы сначала запустить проrрамму с измененной версией этоrо фрar- мента. import 05 for filename in 05.1istdir(): if filename.end5with(' .rxt'): #05.unlink(filename) print(filename) Теперь вызов 05 . unlink () не будет выполняться, поскольку он включен в текст комментария ("закомментирован"), и в силуэтоrо Python проиrнори- рует ero. В таком случае вместо фактическою удаления файла будет лишь выведено имя файла, подлежащеrо удалению. Предварительно выполнив эту версию проrраммы, вы сразу же обнаружите, что в проrрамме имеется
258 r лава 9 ошибка, из-за которой она будет ошибочно удалять не текстовые файлы с расширением. txt, а файлы с расширением. rxt. Убедившись в том, что проrрамма работает так, как запланировано, уда- лите строку print (имяфайла), а также символ комментария в строке с ин- струкцией 05. иnlink (имя файла), После этоrо вновь запустите проrрамму для удаления файлов. COxp".,'re реJер..ые ко".. УII".е..ых Ф"'.О. . п"пок , пOItlO",,,O _оду.' seпd2trash Поскольку встроенная функция Python shиtil. rmtree () необратимо удаляет файлы и папки, ее использование связано с определенным ри- ском. HaмHoro лучший способ удаления файлов и папок предлаrает модуль send2tra5h от независимых разработчиков. Этот модуль можно установить, выполнив команду pip install send2trash в окне терминала. (Более под- робная информация о Ilорядке установки модулей, разработанных сторон- ними компаниями, приведена в приложении А.) Модуль send2trash HaMHoro безопаснее в использовании, чем обычные функции Pyt}lOn, выполняющие операцию удаления, поскольку он отправ. ляет удаляемые файлы и папки в корзину компьютера или в специальную корзину, а не удаляет их безвозвратно. Если из-за ошибок в проrрамме будут удалены файлы, которые вы не собирались удалять, но при этом исполь:ю- вался модуль send2trash, то впоследствии у вас будет возможность восстано- вить их из <:пециальной корзины. После Toro как вы установите модуль send2trash, введите в интерактив- ной оболочке следующие команды. »> iшроrt send2tra_h »> baconFile. open('bacon.txt', 'а') * cosдает файп >>> baconFile. wri t8 ( 'Васоn is not а veqetable. ') 25 »> baoonFile.close() »> send2tra_h.send2trash('bacon.txt') Вообще roворя, целссообраано Bcerдa использовать функцию send2trash. 5end2trash () для удаления файлов и папок. Однако, несмотря на то что от- правка файлов в специальную корзину оставляет вам возможность их по- следующеrо восстановления, размер свободноrо дисковоrо пространства при этом не увеличивается, как в случае безвозвратноrо удаления файлов. Если у вас возникает потребность в освобождении ДИСКОВОI'О пространства, используйте для удаления файлов и папок функции модулей 05 и shиtil. Об- ратите внимание на то, что функция send2trash () может лишь отправлять файлы в корзину, но не извлекать их из нее.
Управление файлами 259 Обход дерева KaTaпoroB Предположим, вы хотите переименовать все файлы, находящисся в не- которой папке, а также во всех ее подпапках. Следовательно, вам необходи мо выполнить обход Bcero дерева каталоrов, обрабатывая при этом каждый файл. К сожалению, написание соответствующей проrраммы задача не- тривиальная, и потому Python предоставляет функцию, орrанизующую этот процесс вместо вас. Рассмотрим папку C:'ylelicioиs и все ее содержимое (рис. 9.1). с: L delicioиs cats [ catnaтes.txt zophie.jpg walnиt L L waffles bиtter. txt spam. txt Рис. 9. 1. Пример попки, содержащей три друrие папки и четыре файла Ниже приведен при мер проrpаммы, в которой для обхода дерева катал<r rOB, представлеНIIОro на рис. 9.1, используется функция 05. walk ( ) . import 05 for folderName, 5ubfolder5, filenames in os.walk('C:\delicious'): рriпt('Текущая лапка , + folderName) for subfolder in 5ubfolder5: рriпt('ПОДПАПКА ПАПКИ I + folderName + 1. , + subfolder) for filename in filename5: рriпt('фАЙЛ В ПАПКЕ' + folderName + 1. '+ filename) print ( , , )
260 r лава 9 Функции 05. walk () передается единственное строковое значение пyrь к папке. Вы можете использовать функцию 05. walk () в цикле for для обхо- Да дерева каталOl'ОВ во MHorOM примерно так, как функцию range () можно использовать для перебора всех целых чисел, принадлежащих некоторому диапазону. Однако, в отличие от функции range () , функция 05. walk () воз- вращает три значения на каждой итерации цикла: 1) строку, содержащую текущее имя папки; 2) список строк, представляющих имена папок, которые содержатся в текущей папке; 3) список строк, представляющих имена файлов, которые содержатся в текущей папке. (Под текущей папкой я подразумеваю папку, используемую в текущей итерации цикла. Применение функции 05. walk () не приводит к смене те- кущеrо рабочеrо каталоrа проrраммы.) Подобно тому как допускается выбрать имя переменной i в коде for i in range ( 1 О) :, можно выбирать имена перемеНIIЫХ для трех вышеперечис- ленных значений. Обычно в качестве таковых я использую соответственно имена folderпame, sиbfolders и filename.5. Если вы выполните эту проrрамму, то ее вывод будет выrлядеть так. 'Текущая папка C:delicioиs ПОДПАПКА ПАПКИ C:delicioиs: cats ПОДПАПКА ПАПКИ C:delicious: walnиt ФАЙЛ В ПАПКЕ C:delicioи5: spam.txt 'Текущая папка C:delicioиscats ФАЙЛ в ПАПКЕ C:delicioиscats: catnames.txt ФАЙЛ В ПАПКЕ C:deliciouscats: zophie.jpg 'Текущая папка C:delicioиswalnиt ПОДПАПКА ПАЛКИ C:delicioиswalnиt: waffles 'Текущая папка C:delicioиswalnиtwaffles ФАйЛ в ПАЛКЕ C:delicioиswalnиtwaffles: bиtter.txt. Поскольку функция 08. walk () возвращает списки строк для переменных 5иbfolder и filename, можно использовать эти списки в их собственных циклах for. Замените вы30вы функции print () собственным пользователь- ским кодом. (Или же удалите цикJIы for, если вам не нужен какой-то один из этих циклоВ или оба цикла.)
Управление ф айлами 261 Сжатие файпов с ПОМОЩblO модуп. zipfile Вероятно, вы знакомы с ZIР-файлами (имеющими расширение .zip), в ко- торых В сжатом виде может храниться содержимое мноrих друrих файлов. В результате сжатия файла ero размер уменьшается, что немаловажно при передаче файлов через Интернет. А поскольку один ZIР-файл может coдe жать множество файлов и папок, эта возможность очень удобна для упаков ки нескольких файлов в один. Этот единственный файл (так называемый архU8'Н:ЫЙ файл) можно, например, присоединить к сообщению электрон- ной 1I0ЧТЫ. Ваши PythОI1проrраммы MOryr как создавать, так и открывать (или расnа- "'О8'ШJШnЪ) ZIР-файлы с помощью функций из модуля zipfile. Предположим, у вас имеется ZIР-файл exaтple.zip, содержимое KOToporo представлено на рис. 9.2. cats [ catnames.txt zophie.jpg spam. txt Рис. 9.2. Содержимое файла examp/e.zip Можете скачать этот файл по aдpecyhttp://nostarch.com/aиtomatestиff/ или просто использовать один из ZIР-файлов, которые уже имеются на ва- шем компьютере. Чrение 1"-ф"..о. Чтобы прочитать содержимое ZIР-файла, прежде Bcero необходимо соз- дать объект ZipFile (обратите внимание на использование прописных букв "Z" и "F" в имени объекта). Объекты ZipFile концептуально напоминают объекты Fi 1 е, возвращаемые функцией open ( ) . с которой вы познакоми- лись в предыдущей rлаве: они представляют собой значения, посредством которых проrрамма взаимодействует с файлами. Для создания объекта ZipFile следует вызвать функцию zipfile. ZipFile ( ) , передав ей строку с именем .ziр-файла. Обратите внимание на то, что zipfile это имя модуля РytlЮIl, а ZipFile () имя функции. Введите в интерактивной оболочке следующие команды. »> import zipfile, 08 >>> 08. chdir ( 'с: \ 1) t пepeй.rи . папку, оодержащYJO 8Хашрlе. zip
262 r лава 9 »> exampleZip = ziрfilе.ZiрFilе('ехашрlе.ziр') »> exampleZip.naaeli8t() [ 'зрат. txt', · cats/', 'cats/ catnames. txt " 'cats/zophie. jpg' ] >>> spaIILInfo = exaвapleZip. чеtinfо ( , 8pa1D. txt' ) »> spaIILInfo.file 8ize 13908 »> spaIILInfo.oampre88 8ize 3828 О »> 'Са.'NЙ файл . %8 раза меныIQ'' % (round(spaIILInfo.file8ize / spaIILInfo.coapres8 8ize, 2» 'Сжатый файл в 3.63 раза меньше!' »> eZip.alose() Объект ZipFile имеет метод namelist (), который возвращает список строк для всех файлов и папок, содержащихся в ZIР-файле. Эти строки можно передать методу getinfo () объекта ZipFile, который возвратит объ- ект Ziplnfo, содержащий информацию о данном файле. Объекты Ziplnfo имеют собственные атрибуты, такие как filesize и compresssize, KOT<r рые содержат соответственно целочисленные значения размера исходноrо и OKёtToro файлов, выраженные в байтах. В то время как объект ZipFile представляет весь архивный файл, объект Ziplnfo хранит полезную инфор- мацию относительно отдельною файла в сжатом архиве. Команда. рассчитывает степень сжатия файла example. zip пyrем деле- ния размера ИСХОДНОI'О файла на размер сжатоrо файла и выводит эту ин- формацию, используя строку форматирования с описателем формата %з. ИI.."'''", ф"'1I0. ", Z,p."px"." Метод extractall () объектов ZipFile извлекает Все файлы и папки И:J ZlP-файла в текущий рабочий каталоr. »> iшроrt zipfile, 08 »> o8.chdir('C:\') * перейти 8 папку, сод8р*аЩуа ехашрlе.ziр »> exampleZip. zipfile.ZipFile('example.zip') О >>> exampleZip.extractall () »> exampleZip.cl08e() После выполнения этоrо кода содержимое файла exaтple.zip будет из- влечено в каталоr C: Методу extractall () можно передать имя папки в качестве необязательноrо параметра, позволяющеrо извлекать файлы в папку, не являющуюся текущим рабочим каталоrом. Если переданная MeT<r ду extractall () папка не существует, то она будет создана. Например, если вызов. заменить вызовом exampleZip.extractall ('с: delicioиs,), код из- влечет файлы из файла exaтple.zip во вноВь созданную папку С: "(ielicious.
Управление файлами 263 Метод extract () объектов ZipFile извлекает одиночный файл из ZIp файла, Продолжите выполнение примера в интерактивной оболочке. »> exampleZip.extract('_pam.txt') 'C:\spam.txt' »> exampleZip.extract('_pam.txt', 'C:\some\new\folders') 'C:\some\new\folders\spam.txt' »> exampleZip.clo_e() Передаваемая методу extr act () строка должна соответствовать одной из строк в списке, возвращаемом методом namelist (). Методу extract () TaK же можно передавать необязательный второй параметр, обеспечивающий возможность извлечения файлов в папку, не являющуюся текущим рабочим каталоrом. Если заданная вторым параметром папка не существует, PytllOn создаст ее. Метод extract () возвращает абсолютный путь, по которому был распакован данный файл. Создан.е Z".фа'.08 . 1I06аиен.е 8 Н.Х H08WX фаiiЛ08 Чтобы (оздать собственный ZIР-Файл, необходимо открыть объект ZipFile в режuмезаnиси посредством передачи строки 'w' в качестве BT<r рО1'О apryMeHTa. (Это аналоrично открытию TeKcToBol'O файла в режиме заIlИСИ путем передачи строки 'w' методу apen () в качестве BToporo пара метра. ) Коrда вы передаете пyrь методу wri te () объекта ZipFile, PytllOl1 сжима ет файл, расположенный по указанному пути, и добавляет ею в ZIР-файл. Первый apryмeHT метода write () это строка, содержащая имя добавляе MOI'O файла. Второй apryмeHT это параметр типа сжатия, сообщающий компьютеру о том, какой алюритм следует использовать для сжатия фай лов; вы всеrда можете установить для этоro параметра значение zipfile. ZIPDEFLATED. (Этому значению соответствует алroритм сжатия без nотеръ, который хорошо работает со всеми типами данных.) Введите в интерактив ной оболочке следующие команды. »> import zipfile »> newZip = zipfile.ZipFile('new.zip', 'w') »> newZip.write('spam.txt', сошрres_type=ziрfilе.ZIРDЕFLAТЕD) »> newZip.close() Данный код создает новый ZIР-файл new.ир, включающий сжатое содер- жимое файла spam.txt.
264 r лава 9 Имейте в виду, что, как и в случае обычной записи файлов, все существу- ющее содержимое ZIР-файла в режиме записи удаляется. Если вы хотите просто добавить файлы в существующий ZIР-файл, псредайте строку 'а' в качестве BToporo параметра методу zipfile. ZipFile () , чтобы открыть ZIP- файл в режиме присосдинения. Проект: пере именование файпов с заменой американскоrо формата дат европейским Предположим, начальник перебросил вам по электронной почте тыся- чи файлов с просьбой переименовать их с заменой американскоro формата дат (mm-м-rrrт) в их именах европейским (M-MM-rrrr). Выполняя это порученис вручную, вы можете потратить на Hero целый день! Не лучше ли написать проrpамму, которая сделает всю работу за вас? Вот что должна делать эта проrрамма: . выполнять в текущем рабочем каталоre поиск всех файлов, имена ко- торых содержат дату в американском формате; . при нахождении каждоrо TaKoro файла переименовывать ero, меняя местами обозначения даты и месяца, чтобы привести стиль ука:щния даты в соответствие с европейским форматом. Это означает, что код должен выполнить следующие действия: . создать реryлярное выражение для распознавания образцов текста, соответствующих дате, заданной в американском стиле; . вызвать функцию 05 .lis tdir () для создания списка всех файлов, со- держащихея в рабочем каталоrе; . орrани:ювать про(мотр всех имен файлов в цикле, определяя, со- держат ли они даты, с помощью соответствующеrо реryлярноrо вы- ражения; . если в имя файла входит дата, изменить ero с помощью функции shutil.move (). Приступая к работе над данным проектом, откройте новое окно в фай- ловом редакторе и сохраните ею в файле renaтeDates.py. Ша, ,. 'OJДII".e pery.'p"OtD 8..ра.,".' /111' .о.,ка lIar, указа""..х 8 а_ер.ка",ко_ фор_аrе в первой части проrpаммы потребуется импортировать необходимые модули и создать реryлярное выражение, (пособное распознавать даты в формате MM-,lUJ.-rrrr. Комментарии ТОНА будyr напоминать о проrрамм- ном коде, который еще предстоит написать. Использование общепринятоro
Управление файлами 265 обозначения ТООО (ЧТО СДЕЛАТЬ) дЛЯ этих комментариев упрощает их поиск путем нажатия комбинации клавиш <Ctl'l+F> при работе в IDLE. BBe дите в файл следующий код. #! руthопЗ # renameDate5.py Переименовывает файлы, имена которых включают # даты, указанные в американском формате (ммддrrrr), приводя # их в соответствие с европейским форматом дат (ддммrrrr). о import 5hиtil, 05, re # Создание реrулярноrо выражения, которому соответствуют имена # файлов, содержащие даты в американском формате. О datePattern re.compile(r'"""'(.*?) # весь текст перед датой ((Oll)?d) # одна или две цифры месяца ((ОI112IЗ)?d) # одна или две цифры числа ((19120)dd) # четыре цифры rода (.*?)$ # весь текст после даты О """, re. VERBOSE) # TODO: Орrанизовать цикл по файлам в рабочем каталоrе. # тооо: Пропустить файлы с именами, не содержащими дат. # TODO: Получить отдельные части имен файлов. # тооо: Сформировать имена, соответствующие европейскому стилю # указания дат. # ТОDO: Получить полные абсолютные пути к файлам. # ТООО: Переименовать файлы. Из этой rлавы вы уже знаете о том, что для переименования файлов мож- но использовать функцию shиtil.move (), арryментами которой служат ис- ходное и НОВое имена файла. Поскольку эта функция содержится в модуле shиtil, ero необходимо импортировать.. Однако, прежде чем переименовывать файлы, НУЖНО идентифициро- вать те из них, которые подлежат переименованию. Переименовывать сле- дует файлы, в именах которых содержатся даты, например spaт441984.txt или 01.03.2014eggs.zip, тоща как имена таких файлов, как littlebrother.epub, не содержащие дат, можно иrнорировать. Для распознавания этоrо шаблона можно использовать реryлярные выражения. Импортировав модуль re в начале файла, вызовите функцию re . сотрНе () для создания объекта Regex .. Передача этой функции KOH станты re. VERBOSE в качестве BToporo apryMeHTa . разрешает использовать пробелы и комментарии в строке реryлярноrо выражения для повышения ее удобочитаемости.
266 r лава 9 Строка реryлярноrо выражения начинается символами л (. *?), которым соответствует любой текст в имепи файла, предшествующий дате. Iруппе ( (О 11) ?d) соответствует цифровое обозначение месяца. Первой цифрой может быть как О, так и 1, так что реryЛЯРIIОС выражение совпадет как с 06о:шачением 12 в (Jlучае декабря, так и с обозначением 02 8 случае февра ля. Кроме 1'01'0, эта цифра задана как необязательная, поэтому, например, апрель будет раСlIознан независимо от TOI'O, как он обозначен: 04 или 4, Обозначениям дней соответствует rруппа ( (О 111213) ?d), которая с.ледует аналоrичной лоrике; 3, 03 и 31 каждый из этих вариантов является ДOIIY стимым для обозначения дней. (Если вы отличаетесь наблюдательностью, то заметите, что данному реryлярному выражению будут соответствовать и некоторые недопустимые даты, такие как 4312014, 2292013 или 015 2014. При работе с датами следует учитывать множество топких моментов, которые MOryт леrко ускользнyrь от вашеl'О внимания. Одпако для большин ства простых случаев это реryлярное выражение может считаться вполне приемлемым для нашей проrpаммы.) Несмотря па то что 1885 корректное обозначение rода, вы можете оrраничиться юдами, относящимися к хх И XXI столетиям. Тем самым вы И:Jбавитесь от с.лучайноrо пере именования тех файлов, в именах которых встречаются цифровые обо:Jначения, лишь нохожие на даты, такие как 10-10-1000.txt. Части (. *?) $ реryЛЯРНОI'О выражения соответствует любой текст, KOT<r рый следует за датой в имени файла. Ш",2. Иllе",нФнк"qн. ,,,ае. н.е" ф"'.08, '00'8еrа8УIOЩНХ 11"'''. После этою проrрамма должна просмотреть в цикле строки имен фай лов из списка, возвращенноI'О функцией 05 .listdir () , для их сравнения с реryлярным выражением. Любые файлы, имена которых не включают дату, должны иrнорироваться. Для имен, содержащих дату, совпавший с шаБЛ<r 110М текст должен быть сохранен в нескольких переменнЬ1Х. Замените пер- вые три комментария ТООО в вашей проrрамме следующим кодом. #! python3 # renameDates.py Переименовывает файлы, имена которых включают # даты, указанные в американском формате (ммддrrrr), приводя # их в соответствие с европейским форматом дат (ддммrrrr). пропушенный KOД t ОрI'aНИЗ8ЦИЯ цикnа по файлам . рабочем 1C&'l'aJIOI'e. for amerFilename in o..li_tdir('.'): шо - datePattern..earch(amerFilename)
Управление файлами 267 # IlponyCJC Ф&ЙJ10. с именами, Не содержащими да'1'. О if 110 == None: . continue о # Ilолучение o'1'дeпнwx час'1'8Й имен файло.. beforePart . 1I0.group(1) DlonthPart · mo.group(2) daypart = шо.grоup(4) yearpart .. 1D0.qroup(6) afterPart - 1D0.qroup(8) пропущенный KOД Если метод search () возвращает значение None ., значит, строка имени файла, содержащаяся в переменной amerFilename, не соответствует pery лярному выражению. Инструкция continue . Иl'норирует оставшуюся часть цикла и осуществляет переход к следующему имени файла. В противном случае строки, соответствующие отдельным rруппам в pe ryлярном выражении, сохраняются в l1еременных beforePart, monthPart, day?art, yearpart и afterPart .. Эти строки будут использоваться при BЫ полнении следующеro шаr'а для формирования имен файлов с датами в ев- ропейском формате. Для поддержания сквозной нумерации rрупп попробуйте прочитать ре-- ryлярное выражение с caMoro начала, ведя отсчет посредством прибавле- ния единицы всякий раз, коrда вам встречается открывающая круrлая скоб- ка. Не думайте о коде и просто наметьте каркас реryлярноrо выражения. Возможно, так вам будет леrче визуализировать rpуппы. datePattern'" re.compile(r""""(l) (2 (З) ) (4 (5) ) (6 (7) ) (8) $ """, re.VERBOSE) # весь текст перед датой # одна или две цифры месяца # одна или две цифры числа # четыре цифры rода # весь текст после даты Здесь числа от 1 до 8 представляют rpуппы в реryлярном выражении, K<r торое вы написали. ПреДLтавление структуры реryлярноro выражения с ис- пользованием лишь крyrлых скобок и номеров rрупп поможет вам понять ero смысл, прежде чем вы перейдете к созданию остальной части проrраммы. Ш", 3. Фор..ро."н.е но.о,о ..ен. ф".." . "'ре..е.о."н.е ф,,;;.о. Последнее, что осталось сделать, это конкатенировать строки, co храненные в переменных на предыдущем шаrе, для I1риведения даты к
268 rпaBa 9 европейскому формату, в соответствии с которым число предшествует ме- сяцу. Замените три оставшихся комментария ТООО кодом, как показано ниже. ft! руthоnЗ ft renameDates.py Переименовывает файлы, имеиа которых включают ft даты, указанные в американском формате (ммддrrrr), приводя # их в соответствие с европейским форматом дат (ддммrrrr). пропущенный KOД * Формиро_ание имен, COO'l'Se'l'C'1'_YJQIIOa европeйcJcому 0'l'И.JII) * ука$8НИJII да'l'. О euroFilename = beforePart + dayPart + ,, + lDonthPart + ,' + yearpart + aft8rPart * По.пучение по.пнwx aCScOJDOll'НWX n}"1'8Й IC файпаи. ab81forkinqDir = o..path.abspath('. ') amerFilename . 08 .path. join (ab.WorkinqDir, amerFilename) euroFilename - 0..path.join(ab81forkingDir, 8UroFilename) * Переимено_ание файпо_. о print( 'ЗаиеНR8М ИМR "%8" именем "%."...' % (amerFi18name, euroFilename» О *.hutil.lDоve(ашеrFilепamе, euroFilename) * раСКОММ8Нтиро8атъ * поспе _ыпопнения ft 'l'8С'l'иро_аниR Конкатенированная строка сохраняется в переменной eиroFilename ... Затем исходное и новое имена файлов, сохраненные в переменных amerFilename и eиroFilename, передаются функции shиtil.move () для окон. чательноrо переименования файла .. В данной версии проrраммы вызов shиtil.move () отключен с помощью комментария и имена файлов, подлежащих переименованию, просто выво. дятся на экран .. Запуск проrраммы в таком режиме позволяет еще раз убе- диться в корректном переименовании файлов. После этою можно удалить символ комментария в строке с вызовом shиtil.move () и вновь выполнить проrрамму для фактическоrо переименования файлов. Иllе. о",0,.rеЛ6ИО ,03ll"И.. "и"лоr""И6lХ "porp".. Необходимость в персименовании БОЛЬUlOrо количества файлов может возникать по целому ряду друrих причин: . для добавления стандартною префикса в начале имен файлов; напри- мер, файл eggs. ехе переименовывается в файл spa1l'ceggs. txt;
Управление файлами 269 · для преобразования дат в именах файлов из европейскоrо формата в американский; · для удаления ведущих нулей из имен таких файлов, как spaт0042.txt. Проект: создание резервной копии папки в виде ZIР-файяа Предположим, вы работаете над просктом, файлы KOTOpOro хранятся в папке C:l/s.lsPythonBook. Вас волнует сохранность результатов вашей работы, и вам хотелось бы периодически создавать "моментальные снимки" про- екта, сохраняя всю папку в одном ZIРфайле. Для вас было бы желательно хранить различные версии проекта в файлах с именами, содержащими номер резервной копии, который увеличивается всякий раз, коrда соз- дается новый ZIР-файл, например AlsPythonBookl.zip, AlsPythonBook2.zip, АlsРуthопВооkЗ.z.iр и т.д. Это можно было бы делать и вручную, 110 такой под- ход чреват тем, что номера ZIР-файлов MOryr быть случайно перепутаны. lЪраздо проще написать проrрамму, которая будет выполнять эту рутинную работу вместо вас. При ступая к работе над данным проектом, откройте новое окно в фай- ловом редакторе и сохраните ero в файле backup1bZip.py. Шаr 1. Опреllеле".е ..е"., KOrOpOe ,леllУет .р.,.о." Z/Р.фаjлу Код этой проrраммы будет помещен в функцию backupToZip (). Это унростит ero копирование и вставку в друrие проrраммы на Python, нужда- ющиеся в этой функциональности. В конце проrраммы эта функция будет вызываться для создания резервной копии содержимоrо папки. Введите следующий код. ft! руthопЗ ft backupToZip.py Копирует папку вместе со всем ее содержимым ft в ZIРфайл с инкрементируемым номером копии в имени файла. о import zipfile, 05 def backupToZip(folder): ft Создание резервной копии Bcel'o содержимоI'О папки "folder" # в виде ZIРфайла. folder os.path.abspath(folder) # убедиться в том, что ft задан абсолютный путь ft к файлу # Определить, какое имя файла должен использовать этот код, # исходя из имен уже существующих файлов.
270 r лава 9 о numЬer = 1 О while True: zipFilename = os.path.basename(folder) + I , + str(numЬer) + '.zip' if not os.path.exists{zipFilename): break number = number + 1 о # TODO: Создать ZIРфайл. # тооо: Обойти все дерево папки и сжать файлы, содержащиеся # в каждой папке. print ( I rOTOBO. 1) backupToZip('C:\delicious') Мы начинаем с элементарных вещей: добавляем "мarическую" строку за пуска PytllOn (с символами #!), описываем назначение проrpаммы и импор- тируем модули zipfile и 05 .. Далее определяется функция backupToZip ( ) , принимающая Bccro один парамстр folder. Этот параметр строка, содержащая пyrь к папке, ре- зервную копию содержимOI'О которой следует создать. Сначала функция определяет имя, которое следует npисвоить создаваемому ZIР-файлу; затем она создает сам файл, совершает обход содержимоrо папки folder и добав ляет все ее подпапки и файлы в ZIР-файл. Включите в исходный код KOM ментарии TODO для этих шarов как напоминание о том, что необходимо сделать в дальнейшем .. В первой части ПрОl'раммы, ответственной за присвоение имени ZIP файлу, используется базовое имя абсолютноrо пути к папке. Если резерви руется содержимое папки C:VJelicious, то именем ZIР-файла будет deliciQUsN. zip, rде N = 1 при первом запуске проrpаммы, N = 2 при втором и т.Д. Вы можете определить, каким должно быть значение N, проверив, существуют ли уже файлы deliciousl.zip, delicious2.zip и Т.д. Значение N xpa пится в переменной пшnbеr . и инкрементируется в цикле, в котором с п<r мощью функции 05.path.exi5t5 () проверяется существование файлов.. Как только обнаружен несуществующий файл, цикл прерывается, ПОСКОЛlr ку нам уже известно, какое имя следует присвоить новому ZIР-файлу. Ш", 2. COJII"""e 1108010 l"ф";;.,, Следующим шаroм является создание ZIР-файла. Дополните проrpамму новым кодом, как показано ниже. #! python3 # backupToZip.py Копирует папку вместе со всем ее содержимым # в ZIРфайл с инкрементируемым номером копии в имени файла.
Управление файлами 271 пропущенный KOД while True: zipFilename = os.path.basename(folder) + ' , + str(number) + '.zip' if not os.path.exists(zipFilename): break number = number + 1 . Создание ZIРфайла. рrint('Соада"ся файл %з.... % (zipFilenaDle)) О backupZip = zipfil..ZipFile(zipFilename, 'w') # Тооо: Обойти все дерево папки и сжать файлы, содержащиеся # в каждой папке. print ('rOTOBO.') backupToZip('C:\delicious') 1еперь, КОl'да имя НОВОI'O ZIР-файла сохранено впеременной zipFilename, можно вызвать функцию zipfile. ZipFile () для фактическоrо создания ZIP файла.. Не забудьте передать ей строку 'w' в качестве BToporo apryмeHTa, чтобы открыть ZIР-файл в режиме записи. Ш", 3. Обход дере." K",,,.oro. .1I0'"..ен.е "'Rep.."oro . 1"-ф,,'. Наконец, для обхода всех файлов и подпаrюк, содержащихся в данной IIапке, используется функция оз. walk (). Дополните ПрОI'рамму новым ко- дом, выделенным ниже полужирным шрифтом. #! руthопЗ . backupToZip.py Копирует лапку вместе со всем ее содержимьш . в ZIРфайл с инкрементируемым номером копии в имени файла, лролущенный KOД * Обход BCВI'O дерева палки и сжатие файлов, оодержащихся . в каждой палКе. О for foldername, 8ubfolders, filename8 in оз. walk (folder) : рrint('Добаапение файлов из папки %8...' % (foldername)) * Добааит в ZIРфайл екущую папку. О backupZip.write(foldername) * Добавить в ZIРфайл все файлы из данной папки. О for filename in filename8: neWВa8e / 08.path.basename(folder) + ' , if filenam..8tart8with(newBa8e) алd filename.endswith('.zip') continu. t Не создаваь резервные копии * ZIРфaй.nов backupZip.write(08.path.join(foldernam., filename))
272 r лава 9 bactupZip.clo.8() print ( 'rOTOBO. ' ) backupToZip('C:\deliciou5') Функцию 05. walk () можно использовать в цикле for .. и на каждой ите- рации ЦИJUIа она будет возвращать имя текущей папки, а также имена всех подпапок И файлов, содержащихся в данной папке. В теле цикла for папка добавляется в ZIРфайл .. Обход всех файлов, имена которых содержатся в списке filenarne5 ., осуществляется во вло- женном цикле for. Каждый из них, за исключением ранее созданных ZIP- архивов, добавляется в ZIР-файл. Выполнив эту проrрамму, вы получите примерно следующий вывод. Создается файл delicioи51.zip... Добавление файлов из папки C:delicious... Добавление файлов из папки C:deliciouscat5... Добавление файлов из папки C:deliciou5waffles... Добавление файлов из папки C:delicioи5walnut... Добавление файлов из папки C:delicioиswalnutwaffle5... rOTOBO. в результате BToporo вызова проrpaммы все файлы' содержащиеся в пап- ке C:Vl.eliciиus, будут архивированы в ZIР-файле d.elicious2.zip и т.Д. ИII'. or.o,,,,,..,,O ""11"".. """.О"'."61Х "ро",,_ Вы сможете использовать обход дерева каталоrов и добавление файлов в сжатые zIp-архивы в целом ряде друrих проrрамм. Например, можно на- писать проrраммы для выполнения следующих задач: . обход дерева каталоrов и архивирование лишь файлов с определен- ными расширениями, например . txt или .ру. и никакими друrими; . обход дерева каталоrов и архивирование всех файлов, за исключени- ем тех, которые имеют расширение .tхtили .ру; . поиск в дереве каталоrов папки, содержащей наибольшее количество файлов, или папки, занимающей наибольший объем дисковоrо про- странства. PeSIOMe Даже если вы опытный пользователь, вы, вероятнее вcero. работаете с файлами вручную с помощью мыши и клавиатуры. Современные файловые менеджеры упрощают работу с небольшим количеством файлов. Но ино-
Управление файлами 273 rда возникают задачи, для выполнения которых с помощью проводника по- требуется несколько часов работы. Модули 05 и 5hutil предлаrают функции, позволяющие осуществлять ко- пирование, перемещспие, переименование и удаление файлов. Удаляя фай- лы, вы, возможно, захотите восполь;юваться модулем send2tra5h, который предоставляет возможность не удалять файлы безвозвратно, а перемещать их в корзину. При написании проrрамм, предназначенных для обработки файлов, целесообразно использовать символы комментария для отключе- ния кода, выполняющеrо непосредствешюе копирование (перемещение, переименование, удаление) файлов, добавляя вместо Hero вызовы функции print () , которые позволяют убедиться в том, что nporpaMMa делает именно то, что вам необходимо. Операции подобноrо рода приходится выполнять не только над фай- лами, хранящимися в данной папке, но и над файлами, хранящимися во вложенных папках, а также в папках BToporo и всех последующих уровней вложенности. Функция 05. walk () может выполнить обход всей структуры папок вместо вас, позволяя сосредоточить все внимание на том, что долж- на сделать nporpaмMa в отношении хранящихся в этих папках файлов. С помощью модуля zipfile можно сжимать и извлекать файлы, хра- нящиеся в .ziр-архивах. В сочетании с предоставляемыми модулями 05 и shutil функциями для обработки файлов модуль zipfile упрощает упаков- ку нескольких файлов, хранящихея в любой папке на вашем жестком диске. Такие .ziр-файлы rораздо леrче выrружать на веб-сайты или пересылать в виде вложений в сообщения электронной почты, чем множество отдель- ных файлов. В предыдущих I'лавах вам предлаraлся rотовый код, который достаточно было просто копировать. Однако при разработке cotkTBeHHbIx nporpaMM вы столкнетесь с тем, что они не щ:сrда правильно работают с nepBoro раза. Следующая rлава посвящена некоторым модулям Руthоп, облеrчаю- щим анализ и отладку IIporpaмM, которые nOMOryr вам быстрее добиться их правильной работы. Контрояьные вопросы 1. Чем отличаются функции 5hutil. сору () и 5hutil. copytree ()? 2. Какая функция используется для lIереименования файлов? 3. Чем отличаются функции для удаления файлов, предлarаемые модуля- ми 5end2tra5h И 5hutil? 4. Метод cl05e () имеют как объекты ZipFile, так и объекты File. Какой метод объектов ZipFile эквивалентен методу open () объектов File?
274 r лава 9 Учебные проекты Чтобы закрепить полученные знания на практике, напишите проrрам мы для предложенных ниже задач. Вы60РОЧНО' ко".ро."н.. Напишите проrрамму, выполняющую обход дерева каталоrов с целью отбора файлов с заданным расширением (например, . pdf или . j pg). Скопи- руйте эти файлы из их текущеrо расположения в новую папку. Уд"л.н.. н,нужных фа..о. Не так уж редки ситуации, коrда несколько ненужных файлов или папок orpoMHoro размера занимают существенную часть дисковою пространства. Для освобождения места на жестком диске наилучший эффект будет достиr- нyr в том случае, если удалить самые крупные из ненужных файлов. Однако сначала их необходимо найти. Напишите проrрамму, которая обходит дерево папок, выполняя поиск исключительно больших по своим размерам папок и файлов, скажем, та- ких, размеры которых превышают 100 Мбайт. (В(:помните, что размер фай ла можно определить с помощью функции os.path.getsize () из модуля os.) Выведите абсолютные пyrи доступа к этим файлам на экран. J""олн.н.. "РО"У'КО. . .ум."""." ф"..о. Напишите проrрамму, которая ищет в папке все файлы с именами, c<r держащими заданный префикс, такими как spaтOOl.txt, spaт002.txt и Т.д., и обнаруживает любые пропуски в нумерации файлов (например, имеются файлы sратООl.tхtи spaт003.txt, но OTcyrcTBYeт файл spaт002.txt). Проrрам- ма должна изменять имена файлов с большими номерами таким образом, чтобы ликвидировать имеющиеся про пуски. В качестве дополнительноrо задания напишите дрyryю проrрамму, спо собную создавать пропуски в нумерации файлов, тем самым создавая CB<r бодные позиции для добавления новых файлов.
от ПАДКА Тех знаний, которые вы к этому времени успели приобре сти, вполне достаточно для Toro, чтобы приступить к напи санию более сложных проrpамм. Однако вы должны быть rотовы К тому, что в вашем HporpaMMHoM коде будут нстре-- чаться ошибки, найти которые не так уж и просто. В этой I'лаве обсуждаются инструменты и методики, нозволяющие ЭКОНОМИТI. время и усилия, затрачиваемые на обнаружение и устранение лоrических ошибок в проrраммах. Перефразируя расхожую шутку ПРOl'раммистов, я сказал бы так: "Про- rраммирование на 90 процентов состоит из написания кода. Остальные 90 процентов приходятся на отладку". Ваш компьютер сделает лишь то, что вы ему прикажете; оп не способен исполнять ваши 1UJ,м,ерe1tШl, читая мысли, и нуждается в соответстнующем проrраммном коде, ответственность за IЮДI'ОТОВКУ KOTOpOI'O возлаraется на проrраммистов. Однако даже ПрОфсССИОПaJlЫIЫС lIрOl'раммисты иноrда д<r пускают серьезныe проrраммные ошибки, поэтому пс стоит падать духом, если в работе вашей НрOl'раммы обнаруживаются неполадки. К счастью, существует целых ряд инструментов и методик, с помощью которых вы сможете точно опрсделить, что имснно делает ваш код и в кa ком месте проrраммы чтото пошло не так, как надо. Во--первых, мы pac смотрим протоколирование операций и утверждсния два средства, облеl'чающие раннее обнаружснис ошибок, Вообще I'оворя, чем раньше об- наружена ОlПибка, тем проще ее исправить. Во--вторых, вы позпакомитссь с отладчиком. Отладчик это средство IDLE, обсспсчивающее пошаrовое ВЫllOлпеllие lIpOrpaMMbI, по одпой ин струкции за раэ, что дает возможность иш:псктировать значения перемен пых и отслеживать их изменсния в ходс вынолнения проrраммы. При этом lIpOrpaMMa выполняется значительно медленнее, чем обычно, но зато вы получаете возможность наблюдать за реальными значениями переменных, а не оценивать их на основе умозаключений, анализируя исходный код.
276 rлава 10 Возбуждение искпlOчениi Python возбуждает (reнерирует) исключение всякий раз, коrда предпри' нимается попытка выполнить недопустимый код. Из rлавы 3 вы узнали о том, как обрабатывать исключения PythOIl с помощью инструкций try и except, позволяющих избежать преждевременноrо прекращения работы проrраммы при возникновении исключительных ситуаций, которые вы предвидели. Но у вас есть возможность возбуждать в своем коде собствен- ные исключения. Возбуждение исключения о:шачает для компьютера сле- дующее: "Прекратить выполнение кода данной функции и передать управ- ление инструкции except". Исключения возбуждаются с помощью инструкции raise, синтаксис ко. торой включает следующие элементы: . ключевое слово raise; . вызов функции Exception (); . строка с описательным сообщением об ошибке, передаваемая функ- ции Exception ( ) . Например, введите в интерактивной оболочке следующую команду. »> raia8 ЕхоерtiСn('ЭТО сообщение об о-ибке.') Traceback (most recent са!1 !ast): File "<pyshell#O>", line 1, in <module> raise Ехсерtiоп('Это сообщение об ошибке.'} Exception: Это сообщение об ошибке. в отсyrствие инструкций try и except, охватывающих инструкцию raise, которая rенерирует исключение, выполнение проrраммы завершается aвa рийно с выводом соответствующеro сообщения. Часто обработка исключения, которое может возникнyrь в функции, вы- полняется не самой функцией, а вызывающим ее кодом. ПО:iТОМУ инструк- ция raise нередко встречается в теле функции, а связанные с ней инструк- ции try И except в коде, вызывающем эту функцию. Например, откройте новое окно в файловом редакторе, введите в Hero следующий код и сохра- ните проrрамму в файле boxPrint.py. def boxPrint(symbo!, width, height}: if !en(symbo!} != 1: О raise Ехсерtiоп('Переменная symbol должна быть односимвольной строкой.'} if width <= 2: О raise Ехсерtiоп('Значение width должно превыmать 2.') if height <= 2: О raise Ехсерtiоп('Значение height должно превыmать 2.') print(symbol * width}
Отладка 2" for i in range(height 2}: print(symbol + (' , * (width 2}) + symbol} print(symbol * width) for sym, w, h in (('*',4, 4), ('0',20, 5), ('х', 1, З), ('ZZ', 3, З)}: try: boxPrint(sym, w, h} О except Exception as err: О рriпt('Возникло исключение: ' + str(err)} Здесь мы определили функцию boxprint () , которая принимает параметр symbol, определяющий символ или rруппу символов, а также параметры width и height, и использует заданный символ (символы) для создания не- БОЛЬШОl'О изображения, ширина и высота KOTOpOro определяются осталь- ными двумя l1араметрами. Полученная прямоуrольная фиrypа выводится на консоль. Предположим, мы хотим, чтобы параметр symbol Mor быть только оди- ночным символом, а значения параметров width и height превышали 2. для этоrо мы добамяем инструкции Н, которые возбуждают исключения, если эти требования не удовлетворяются. Впоследствии, коrда вызывается функ- ция boxPrint () с различными арryментами, конструкция try /except обрабо- тает недопустимые apl'YMeHTbI. В этой проrрамме в инструкции except используется и(ключение в форме Exception as err О. В случае возврата объекта Exception функцией boxPrint () ... инструкция except сохранит ero в переменной err. Затем объект Exception можно преобразовать в строку посредством передачи ero функции str () для вывода сообщения об ошибке в понятной для пользова- теля форме .. В результате выполнения nporpaMMbI boxPrint. ру вы долж- ны получить следующий вывод. **** * * * * **** 00000000000000000000 о о о О о О 00000000000000000000 Возникло исключение: 'Значение width должно превышать 2. Возникло исключение: Переменная symbol должна быть одно символьной строкой. Применение инструкций try и except обеспечипает элеraнтную обрабо..... ку ошибок, исключающую возможность неконтролируемоrо аварийноro за- вершения nporpaмMbI.
278 rЛQВQ 10 Попучение обратной трассировки стека вызовов в виде строки Коrда возникает ошибка, Python предоставляет весьма ценную информа- цию о ее природе в виде так называемой обратной трассировки Сте1Са въtз0вов. Эта информация включает текст сообщения об ошибке, номер строки в ис ходном коде, ЯWJяющейся причиной неполадок, и последовательность вы- зовов функций, I1риведшую к ошибке. Эта последовательность называется Сте1СО./Ч вшооов. Откройте новое окно в IDLE, введите приведенный ниже код проrрам- мы и сохраните ero в файле eпvrExample.py. def зраm () : bacon ( ) def bacon(): raise Ехсерtiоп('Это сообщение об ошибке.') spam() Выполнив проrpамму eпvrExample.py, вы получите следующий вывод. Traceback (most recent call last): File "errorExample.py", line 7, in <module> spam() File "errorExample.py", line 2, in spam Ьасоn () File "errorExample.py", line 5, in bacon raise Ехсерtiоn('Это сообщение об ошибке.') Exception: Это сообщение об ошибке. На основании информации о стеке вызовов можно yrверждать, что ошибка возникла в строке 5, Т.е. в коде функции bacon () . Эта функция была вызвана в строке 2, Т.е. в коде функции spam(), которая, в свою очередь, бьта вызвана в строке 7. В тех случаях, коrда одна и та же функция может вызываться в нескольких местах проrраммы, с.тек вызовов позволяет опре-- делить, какой именно вызов l1рИВОДИТ к ошибке. Python отображает стек вызовов всякий раз, Коrда возбужденное исклю- чение остается необработанным. Но ero также можно получить в виде (тра- ки, вызвав функцию traceback. forma t ехс ( ) . Эта функция приroдится вам в тех случаях, коrда вы хотите обработать ИСJUIючение, но одновременно с этим получить информацию о стеке вызовов. Прежде чем вызывать эту функцию, необходимо импортировать модуль traceback Python.
Отладка 279 Например, вместо Toro чтобы просто позволить проrрамме завершить- ся аварийно сразу же после возникновения исключения, можно записать информацию о стеке вызовов в журнал отчетов и предоставить проrpамме воаможность дальнейшеrо выполнения. Впоследствии, коrда вы будете 1"0- товы приступить К отладке, вы сможете обратиться к записанной в журнале информации. Введите в интерактивной оболочке следующий код. >>> iDlport traceback »> try: rai_e Exception ( 'э.rо сообщение об ошибке. I ) except: errorFile · ореn ('errorInfo. txt' , 'w') errorFile..rite(traceback.format8Xc(» errorFile.clo&e() print( 'Ииформация о снке 8Ы80808 бып& 8anисана 8 файл errorInfo . txt. ' ) 113 Информация о стеке вызовов записана в файл errorInfo.txt. Число 113 это значение, возвращенное методом wri te () , которое равно количеству символов, записанных в файл (ВJUIючая символы новой строки). Записанная в файл error/nfo. txt информация должна выrлядеть примерно так. Traceback (most recent call last): File "<pyshell#l1>", Нпе 2, in <module> Exception: Это сообщение об ошибке. УтвеР)I(дения Уmвef)ждеиue это профилактический механизм, позволяющий убедить- ся в I1равильности выполнения кода проrраммы. Этот механизм основан на выполнении проверок с помощью инструкций assert. Если критерий проверки не выдержив'аt.'Тся, то возникает исключение AssertionError. Ин- струкция assert имеет следующий синтаксис: . ключевое слово assert; . условие (т.е. выражение, вычисление KOTOpOl'O дает значение True или False); . запятая; . строка, которая отображается в том случае, если условие оказываt..'Тся ложным.
280 rлова 10 в каче<:тве примера введите в интерактивной оболочке следующий код. >>> podВayDoorStatu& = 'открыто' > > > a&&8rt podВayDoorSta tU& .. ' OТIepbl'l'O', 'Дверь Ж'РУSО80ro О'1'сеха дo.mrнa нахОди'1'Ь . СОС'1'ОRНИИ "открыто".' >>> podВayDoorStatu& · 'Мне -М18, Дейв. SOIOOIo, R ничеro Н8 мory сд..пать. ' , >>> a&&ert podВayDoorStatu& == 'OTIepbl'l'O', 'Двер18 :t'pYS080r0 О'1'сеха дo.mrнa нахОди'1'18CJ1 8 СОC'rОRНИИ "открыто".' Traceback (most recent call last): File "<pyshell#10>", line 1, in <module> assert podBayDoorStatиs == 'открыто', 'Дверь РРУЗ0ВОРО отсека должна находиться в состоянии "открыто".' AssertionError: дверь РРУЗ0ВОРО отсека должна находиться в состоянии "OT крыто". Здесь мы УLтанавливаем для переменной podBayDoorStatus значение 'OT крыто' И рассчитываем на то, что данная переменная всеrда будет иметь именно это значение. Предположим, что выполнение этоl'О условия имеет существенное значение для 1'01"0, чтобы код работал так, как ожидается. По- этому мы добавляем утверждение, которое позволяет УДОLТОвериrься в том, ЧТО в lIеременной podВayDoorSta tus действиrельно хранится значение 'OT крыто' . в данном случае дЛЯ TOI'O чтобы сразу увидеть, rде возникает сбой, если утверждение не выполняется, мы включаем в операцию присваивания строку сообщения 'Дверь I'PY30BOI'O отсека должна находиться в состоянии "открыто" . ' . Допустим, что впоследствии мы совершаем досадную ошибку, при сваи- вая переменной podBayDoorStatus дрyrое значение, но не замечаем этоrо orpexa среди множества строк кода. Утверждение обнаружит эту ошибку и недвусмысленно информирует нас о том, что именно произошло. В переводе на обычный язык утверждение сообщает нам следующее: "Я убеждаюсь в том, что данное условие выполняется; в противном случае можно утверждать, что rдe-тo в проrрамме скрывается ошибка". В отличие от исключений ваш код не должен обрабатывать инструкции assert с помо- щью конструкции try/except. Если утверждение оказывается ложным, то ваша проrрамма должна завершиться аварийно. Такой способ paHHero вы- явления потенциальных ошибок сокращает промежуток времени, отделяю- щий момент возникновения первопричины ошибки от момента, коrда вам впервые становится известно о наличии ошибки в ПрОI'рамме. Тем самым сокращается объем кода, который пришлось бы написать для выявления фраrментов OCHOBHoro кода, 110рождающих ошибку. Утверждения предназначены для нахождения ошибок проrраммиста, а не пользователя. Для обнаружения ошибок, которые допускают элеrантное (или, как rоворят, амортизированное) восстановление работоспособноrо
Отладка 281 СОСТОЯНИЯ проrраммы, таких как ввод пользователем некорректных дaH ных или lIевозможность обнаружить указанный файл, используйте возбуж- дение исключений, а не инструкции a55ert. И'"О."О."".' р.'ржд.".. . .porptl_., ..."'Р,IOIII'. ра60ry ,..1Офор" ПреДIIОЛОЖИМ, требуется создать проrpамму, имитирующую работу све- тофора. В качестве структуры данных, предстаВJIяющей сю'налы свето- фора на перекрестке, выбираем словарь с ключами 'n5' и 'ew' для ламп, ориентированных вдоль направлений "северюl''' и "востокзапад" COOT ветственно. Каждому из этих ключей MOryr соответствовать значения в виде строк' green', 'yellow' и 'red' (красный). Соответствующий код бу дет выrлядеть так. market 2nd = {'ns': 'green', 'ew': 'red') mission 16th = {'ns': 'red', 'ew': 'green'} Эти две переменные отвечают за перекрестки, образуемые парам и улиц "Mal'ket Stl'eet2nd Street" и "Mission Street16th Stl'eet". Приступая к про- екту, целесообразно написать функцию swi tchLights () , которая будст пере- ключать еветофор, принимая указанный словарь в качестве apryMeHTa. Первое, что может прийти вам на ум, это то, что функция switch Lights () Bcero лишь должна поеледовательно переключать цвета сиrналов ( одноrо на друrой: за любым значением I green' (зеленый) должно следовать значение' yellow' (желтый), которое будет заменяться значением' red' (красный), в свою очередь сменяющимся значением' green' и Т.д. КОД, pea лизующий эту идею, может выrлядеть примерно так. def switchLights(stoplight): for key in stoplight.keys(): if stop1ight[key] 'green': stop1ight[key] 'yellow' elif stoplight[key) 'yellow': stoplight[key] = 'red' elif stoplight[key] == 'red': stoplight[key] 'green' switchLights(market2nd) Возможно, вам уже удалось заметить проблему, связанную с этим KO ДОМ, но все же давайте представим, что вы уже написали остальную часть кода ПрOl'раммы имитации светофора, насчитывающую тысячи строк, И указанная проблема ускользнула от вашеrо внимания. Коrда вы запустите
282 rлаВQ 10 проrpамму, она не потерпит крах, чеrо нелЬ.зя будет сказать о ваших ВИРТУ- альных автомобилях! Поскольку, как мы предположили, остальная часть проrpаммы уже напи- сана, вы не имеете ни малейшеrо представления о том, в каком ее разделе может скрываться ошибка. Возможно, она затаилась в коде, имитирующем движение машин, или, может быть, в коде, имитирующем действия вирту- альных водителей. Прежде чем вы доrадаетесь, что следы ошибки ведут к функции swi tchLights (), может пройти немало времени. Но если бы в процессе написания функции switchLights () вы добавили yrверждение, проверяющее, что в любой момент времени по ",райnеи. .мере одии cuеuал светофора ",раcu'Ый, то поместили бы в конце функции следую- щий код. assert 'red' in stoplight.values(), 'Ни один из сиrиалов не является красным! ' + str(stoplight) Проrрамма, содержащая это утверждение, завершится аварийно с выда- чей следующеI'О сообщения об ошибке. Traceback (most recent саll last): File "carSim.py", Нпе 14, in <module> switchLights(market2nd) File "carSim.py", line 13, in switchLights assert 'red' in эtорlight.vаluеs(}, 'Ни один из сиrналов не является красным! ' + str(stop1ight) О АssеrtiопЕrrоr:Ни один из сиrналов светофора не является красным! {'ns': 'yell0W', 'ew': 'green'} Здесь очень важна строка AssertionError О. Несмотря то что решение, ПРИВОДЯlцее к аварийному завершению работы проrраммы, далеко не иде- ально, оно позволяет незамедлительно известить вас о нарушении прове- ряемоrо условия: ни в одном из направлений не rорит красный сиrнал, а это означает, что движение разрешено одновременно по двум пересекаю- щимся проезжим частям. Заблаrовременно обнаруживая этот факт уже на ранних стадиях выполнения проrраммы, вы обеспечиваете значительную ЭКОНОМИЮ своих усилий по ее отладке в будущем. Откn",.'".' yr..рq.".й Механизм утверждений можно отключить, указав в командной строке флаr o при запуске Python. Эта мера вполне уместна, коrда этап написа- ния и отладки проrраммы завершен и вы не хотите, чтобы работа проrрам- мы замедлялась за счет выполнения профилактических проверок с помо- ЩЬЮ утверждений (хотя в большинстве случаев наличие yrверждений не
Отладка 283 оказывает существенноrо влияния на скорость выполнения проrраммы). Утверждения предназначены для проrрамм, находящихся на стадии раз работки, но не для rOToBoro продукта. К тому времени, Korдa вы переда дите проrрамму в эксплуатацию потребителям, она должна быть свободна от ошибок и не должна нуждаться в проверке соблюдения оrраничений. Более подробно о том, как запустить с флarом o проrpамму, которая, как ожидается, уже отлажена, речь идет в приложении Б. Протокояирование Если вы уже ис.пользовали инструкции print () для вывода значений интересующих вас. lIеременных в ходе выполнения проrраммы, то може те считать, что с одной из форм nроmtЖолuроваnия вы уже знакомы. Cpeд ства протоколирования позволяют получать ценнейшую информацию о том, что именно и в какой последовательности происходит в ПРOI'рамме. Модуль logging в PyttlOI1 упрощает создание и ведение журнала отчетов на основе ПОДl'отовленных вами сообщений. Сообщения протоколирования включают описание Toro, коrда именно проrрамма достиrла вызова функ ции протоколирования, а также список значений указанных вами перемен. ных в данный момент времени. С ДРУl'ОЙ стороны, отсутствие сообщения свидетельствует о том, что данная часть кода была пропущена и не выпол- нялась. И,,,ОЛ6Jо.,,н.е -оду.. loggiпg Чтобы активизировать модуль logging для вывода сообщений Проток<r лирования на экран в процессе выполнения проrраммы, скопируйте приве- денный ниже код в начало своей проrpаммы (но после "мarической" строки запуска PytllOIl, начинающейся символами #!). import logging logging.basicConfig (level=logging. DEBUG, format=' % (asctime)s %(levelname)s %(message)s') Детали Toro, как все это работает, для вас несущественны, но вам будет полезно знать, что Python каждый раз, коrда протоколирует событие, соз- дает объект 1ogRecord, в котором хранится информация о данном событии. Функция basicConfig () модуля logging позволяет конкретизировать, какую именно информацию об объекте 1ogRecord и в какой именно форме вы хо- тите видеть. Предположим вы написали функцию для расчета факториала числа. Ha пример, факториал числа 4 равен произведению 1 )( 2 )( 3)( 4, Т.е. 24. Факт<r риал числа 7 равен 1 )( 2 х 3 х 4 х 5 )( б )( 7, или 5040. Откройте новое окно в
284 rлава 10 файловом редакторе и введите приведенный ниже код. В этом коде содер- жится ошибка, но вы дополнительно предусмотрите несколько сообщений, предназначенных для записи в журнал, которые помоryт вам выяснить, rде именно чт<yfо идет не так, как нужно. Сохраните проrрамму в файле factoriaLLog. ру. import logging logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s %(levelname)s %(message)s') lоggiпg.dеЬug('Начало проrраммы') def factorial(n): lоggiпg.dеЬug('Начало factorial(%s%%)' % (n)) total = 1 for i in range(n + 1): tota1 *= i logging.debug('i = , + str(i) + " total '+ str (total) ) logging.debug('KoHeu factorial(%s%%) I % (п)) return tota1 print(factorial(5)) logging.debug('KoHeu проrраммы') в этом коде для вывода протоколируемой информации используется функция logging. debug ( ) . Она вызывает функцию basicConfig () , которая выводит строку информации. Формат вывода будет соответствовать тому формату, который задан в функции basicConfig () , и включать сообщения, переданные функции debug (). Вызов print (factorial (5)) является частью исходной проrраммы, поэтому результат будет отображаться даже в том случае, если средства протоколирования отключены. Результирующий вывод будет иметь примерно следующий вид. 2015O523 16:20:12,664 DEBUG Начало проrраммы 20150523 16:20:12,664 DEBUG Начало factoria1 (5) 20150523 16:20:12,665 DEBUG i О, total = О 20150523 16:20:12,668 DEBUG i 1, total О 20150523 16:20:12,670 DEBUG i 2, total О 20150523 16:20:12,673 DEBUG i 3, tota1 О 2015052З 16:20:12,675 DEBUG i 4, total О 2015052З 16:20:12,678 DEBUG i 5, total О 2015052З 16:20:12,680 DEBUG Конец factoria1(5) О 2015052З 16:20:12,684 DEBUG Конец проrраммы Функция factorial () возвращает значение О для факториала числа 5, что неверно. В цикле for значение переменной total должно умножаться
Отладка 215 на числа от 1 до 5. Однако сообщения, отображаемые функцией 10gging. debug ( ) , указывают на то, что начальным значением переменной i является О, а не 1. Поскольку результатом умножения любоrо числа на О всеrда будет О, оставшаяся часть итераций также дает неверный результат для nepeMeH ной total. Сообщения журнала отчетов являются теми самыми "хлебными крошками", идя ПО которым, вам будет леrче выяснить, в каком месте np<r rраммы чт<rто пошло не так, как надо. Замените строку for i in range (n + 1): строкой for i in range (1, n + 1): и вновь выполните npOl'paMMY, Результат должен выrлядеть примерно так. 20150523 17:13:40,650 DEBUG Начало проrраммы 20150523 17:13:40,651 DEBUG Начало factoria1(5) 20150523 17:13:40,651 DEBUG 1, t ot а 1 1 20150523 17:13:40,654 DEBUG i 2, tota1 2 20150523 17:13:40,656 DEBUG i 3, tota1 6 20150523 17:13:40,659 DEBUG i 4, total 24 20150523 17:13:40,661 DEBUG i 5, total 120 20150523 17:13:40,661 DEBUG Конец factorial(5) 120 20150523 17:13:40,666 DEBUG Конец ;-;роrраммы Теперь вызов factorial (5) возвращает корректное значение 120. Соо& щения протоколирования показывают: именно то, что происходит в ци кле, непосредственно IIрИВОДИТ к ошибке. как видите, вызовЫlоgging .debug () выводят не только передаваемые им строки, но и временные метки, а также слово DEBUG. Не ...IIM",.,e О,.III1КУ , 110_0"..10 .""РУК".. priп t ( ) Подход, основанный на импортирован ии модуля logging и вызове функ- цииlоggiпg.ЬаsiсСопfig(lеvеl=lоgging.DЕВUG, format = '%(asctime)s : (levelname) s % (message) s' ) , может выrлядеть несколько rpомоздким. Не исключено, что у вас возникнет желание использовать вместо Hcro обыч- ные вызовы print (), однако не поддавайтесь этому искушению! 3акончив отладку, вы потратите массу времени на удаление из кода вызовов print () .1.ЛЯ каждоrо сообщения. Более 1'01'0, при этом не исключено, что вы слу- чайно удалите полезные вызовы print ( ) , никак не связанные <: сообщения- :IofИ протоколирования. Работа с сообщениями протоколирования удобна тем, что вы можете предусмотреть в своей проrpамме столько сообщений, сколько пожелаете, а 8последствии в любой момент сможете отключить их, добавив единственный вызов logging. di sable (logging . CRITICAL) . в от- .1ичие от функции print () , модуль logging упрощает переключение между режимами отображения и сокрытия сообщений протоколирования.
286 r лава 1 О Сообщения протоколирования предназначены для I1роrраммистов, а не для пользователей. Пользователя не интересует содержимое слова- ря, за которым вы хотите наблюдать в процессе отладки; для подобных целей используйте сообщения протоколирования. Для вывода сообще- ний, предназначенных для пользователя, таких как "Файл не найден" или "Недопустимый ввод; пожалуйста, введите число", следует исполь:ювать вызовы print (). Ведь не захотите же вы лишить пользователя доступа к полезной информации после Toro, как отключите вывод сообщениЙ жур- нала отчетов. У,о..'" Kp.r.."o,,,, ,006щ.".. В системе протоколирования так называемые уровни критичности (уров- ни журналирования) сообщений позволяют классифицировать сообщения по степени важности. Существует пять уровней критичности сообщений протоколирования, описанных в табл. 10.1 в порядке возрастания их важ- ности. На каждом уровне сообщения MOryr выводиться с использованием различных функций протоколирования. Та6nмца 10.1. Уровни критичности сообщений в Руthоп Уронн.. ФунКЦИ. протоковировани. Описани. DEBUG logging . debug ( ) Самый ни ,кий уровень. Испопьэуетсl Дnl вывода подробных сведении техническоrо характера. Обычно вы 'аинтересованы в этих сообщеНИIХ В процессе диаrНОСТМРОВОНИI пробnем INFO logging. info () ИспоnьэуетCl Дnl ,описи информации об обычных соБЫТИllх, ПРОИСХOДlщих в проrpaмме, или Дnll подтверждеНИI HOpмOllbHoro ходо роботы проrраммы WARN ING logg ing . wa rning ( ) ИспоnьзуетСI дnя индикации потенциanьно опасных ситуации, которы. не препlIТСТВуюТ работе проrраммы, но мoryт привести к пому , будущем ERROR logging. error () ИспоnыуетСIДnI ,аписи информации об ошиБК8, котораl привела к тому, что проrромма не cмarna IbIПОnНИТЬ некоторые деИСТВИI CRITICA1 logging. cri tical () Наивысший уровень. ИспопыуетСIДnllиндикацни фатальных ошибок, которые привели иnи MOryт привести к полному прекращениlO работы проrраммы Ваши сообщения передаются этим функциям в виде строк. Приведен ные описания это не более чем рекомендации. В конечном счете ЛИшь вы решаете, к какой катеrории следует отнести то или иное предупреждающее сообщение. Введите в интерактивной оболочке следующие команды.
Отладка 287 >>> illport loqqinq »> logqinq.ba8icConfiq(level-loqqinq.DЕВUG, fоrшаt=' %(asctille). %(lвvelname)s %(1I888аЧ8)8') »> loqqinq. debuq ( 'О'l'Л8ДочиаR информации. ' ) 20150518 19:04:26,901 DEBUG Отладочная информация. »> lоqqinq.infо('Работает модуль logqinq. ') 20150518 19:04:35,569 INFO Работает модуль logging. »> lоqginq.wаrninq('Риск получеНИR сообщения об ошибке. ') 20150518 19:04:56,843 WARNING Риск получения сообщения об ошибке. »> lоqqinq.8rror('произошnа ошибка. ') 20150518 19:05:07,737 ERROR Произошла ошибка. »> lоqqinq.сritiсаl('Восотаиоanение работоспособности nporpaммw не80ЗИОЖНО ' ) 20150518 19:05:45,794 CRITICAL Восстановление работоспособности про rраммы невозможно Преимуществом использования уровней критичности сообщений яв- ляется То, что они позволяют задавать rраничный приоритет сообщений, подлежащих отслеживанию. Передача значения 10gging. DEBUG в качестве именованноrо apryмeHTa функции basicConfig () приведет к тому, что 01'0& ражаты:я будут сообщения всех уровней критиtШо(:ти (из которых DEBUG самый низкий уровень). После Toro как разработка I1porpaMMbl пройдет определенные этапы, вас будет интересовать лишь вывод информации об ОlUибках. В этом случае вы (:можете задать для функции basicConfig ( ) в качестве apryMeHTa, определяющеrо уровень критичности сообщений, значение 10gging. ERROR. В результате будут отображаться лишь сообщения катеroрий ERROR и CRITICAL, а сообщения катеrорий DEBUG, INFO и WARNING будyr иrнорироваться. OrUIO.'II'" "pOrOKO."pO."II". Завершив отладку nporpaмMbI, вы, вероятно, захотите избавиться от на- зойливых сообщений, захламляющих экран. Вам не придется удалять все вызовы функций протоколирования вручную, поскольку вместо вас это может сделать функция logging. disable (). Вам остается лишь передать ей желаемый уровень критичности ошибок, и она подавит вывод сообщсний, относящихся К этому И более низким уровням. Следователr>но, если вы за- хотите полностью отключить средства протоколирования, вам буд<.->т доста- точно добавить в свою проrpамму вызов logging. disable (logging. CRITICAL) . Например, введите в интерактивной оболочке следующие команды. »> import logqinq »> logqinq.ba8icConfiq(level-loqqinq.INFO, format=' % (a8ctiJlle) 8 % (levelnaDle) 8 % (1IIe88aqe) 8 1) >>> logqinq.critical ('I(pИ'1'ИЧ8СКая ошибка! Кpm.ич8СICU OIIИбха!')
288 rлава 10 20150522 11:10:48,054 CRITICAL Критическая ошибка! Критическая ошибка! »> loqqinq.di_able(loqqinq,CRITICAL) >>> loqqing. cri tical ( 'Критическая OIIIИбка! Критическая ошибка! ') >>> loqging. error ( , Ошибка! Ошибка!') Поскольку вызов logging. disable () отключает все следующие за ним ин- СТРУКlии протоколирования, вы, вероятно захотите добавить ero 8 свою проrрамму за строкой import logging. Блаrодаря этому вы сможете быстро находить этот вызов, чтобы при необходимости "закомментировать " или "раскомментировать" ero ДJШ аКТИВИзации или отключения (:редств прото- колирования. 3а""" ,оо6",ен,,' "poroKo."po8aH". 8 фаl. .урна.а Вместо Toro чтобы отображать сообщения протоколирования на экране, их можно записывать в текстовый файл журнала отчетов (жарr. лое-файл). Для этоrо следует воспользоваться функцией logging .basicConfig (), кото- рая принимает именованный apryмeHT filename. .import logging lоggiпg.ЬаsiсСопfig(filenaше='ауProqraaLoq.txt', level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s') Здесь сообщения протоколирования сохраняются в файле тyProgramLog. txt. Какими бы полезными ни были сообщения протоколирования, они мо- ryr захламлять экран и затруднять чтение вывода проrраммы. Запись этих сообщений в файл журнала позволяет освободить экран, а ознакомитЬ(:я с тек(:том сообщений можно будет уже после завершения выполнения про- rpaMMbI. для открытия файла журнала отчетов можно воспользоваться лю- бым текстовым редактором, таким как Блокнот или TextEdit. Отпадчик IDLE Отлаичu'К это средство IDIE, которое предоставляет возможность по- шаrовоro выполнения проrраммы по одной строке за раз. При этом отлад- чик выполняет одну строку кода, а затем переходит в состояние ожидания, пока не получит команду продолжить выполнение. Выполнение проrрам- мы под управлением отладчика позволяет наблюдать за значениями перс-- менных в любом заданном месте проrpаммы на протяжении ее срока сущес- твования. Orладчик нсзаменимое средство для обнаружения лоrических ошибок 8 проrрамме.
Отладка 289 Чтобы активизировать отладчик IDLE, щелкните на пунктах меню Debug Debugger (Отладка Отладчик) в окне интерактивной оболочки для открытия окна Debug Control (Управление отладкой), как показано на рис. 10.1. Коrда откроется окно Debug Control, установите все четыре флажка, Stack (Стек), Locals (Локальные переменные), Source (Исходный код) и Globals (rлобальные переменные), для отображения полноrо набора разделов от- ладочной информации. Всякий раз, кш'да вы будете запускать проrрамму из окна файловоrо редактора при открытом окне Debug Contral, отладчик бу дет приостанавливать выполнение проrраммы перед первой инструкцией и отображать следующую информацию: . строку кода, которая будет выполнена следующей; . список всех локальных lIеременных и их значения; . список всех rлобальных переменных и их значения. r,-'''''' ;:;, .-::--'- .- DёugСontroI (.""I LI. . . I .,.. J ". .1. ", ! ,'. : J;;i StlCk ! SoLlI(R j...' .....:.:...J.:J....::_.j ';; Lo"ls ;'. Glob.Is (Non) ..J Loc.ls Non , ..) ..J Рис. 10. 1. Окно Debug Contro/ Позже вы увидите, что в списке rлобальных переменных присутствуют такие имена, как as Ьиiltiлs, doc, file и друrие, которые вы не определяли. Эти переменные PythOl1 автоматически устанавливает вся- кий раз, коrда вы запускаете проrpамму. Их рас<м()трение выходит за рамки данной книrи, и вы можете смело их иrнорировать. Проrрамма будет оставаться в состоянии ожидания до тех пор, пока вы не щелкнете на одной из следующих пяти кнопок в окне Debug Control: Go, Step, Over, Out или Quit.
290 rЛQВQ 10 Кнопка 'О Щелчок на кнопке Go (Выполнить) запускает обычный процесс ВЫПОk пения ПрOl'раммы до ее полноrо завершения или до тех пор, КOI'да будет достиrнyrа mО'ч/Ка остаnова. (О точках останова речь пойдет далее.) Если вы завершили отладку и хотите, чтобы проrрамма продолжила выполнение в обычном режиме, щелкните на кнопке Go. Кнопка 51ер Щелчок на кнопке Step (Шаr с заходом) приводит К выполнению следую- щей строки кода и приостановке проrраммы. Если значения локальных и l'Лобальных переменных изменились, то их список в окне Dcbug COI1tl'ol обновится. Если следующей строкой кода является вызов функции, то OT ладчик выполнит "шаr с заходом", Т.е. выполнит вызов функции и приоста новится на первой строке ее кода. Кнопка Over Щелчок на кнопке Over (Шаr с обходом) при водит к выполнению сле дующей строки кода, как и при щелчке на кнопке Step. Но если следующей строкой кода является вызов функции, то отладчик выполнит "Шаr с обхо дом", Т.е. будет выполнен не только вызов функции, но и весь ее код, и пос ле возврата из функции дальнейшее выполнение приостановится. Напри мер, если следующей строкой кода является вызов функции print () , то вас интересует лишь вывод переданной ей строки на экран, а не сам код функ ции. Поэтому обычно кнопкой Over пользуются чаще, чем кнопкой Step. Кнопка Ои' IЦелчок на кнопке Out (Шаr с выходом) приводит К выполнению строк кода в обычном режиме до тех пор, пока не будет выполнен возврат из те- кущей функции. Если перед этим вы выполнили шаr с заходом в функцию с помощью кнопки Step, а теперь просто хотите продолжить выполнение инструкций вплоть до возврата из функции, щелкните на кнопке Out для выполнения "шаrа с выходом" из текущеrо вызова. Кнопка Ои;' Если вы хотите полностью прекратить отладку без дальнейшеrо выпол- нения остальной части проrраммы, щелкните на кнопке Quit (Выход), что приведет к немедленному прекращению выполнения проrраммы. Если же вы хотите возобновить работу проrраммы в обычном режиме, то отключи- те отладчик, повторно выбрав пункты меню DebugDebugger.
Отладка 291 ОrЛ"IIК" пpOrp"MM6I /111- Ulоже",,_ '''''л Откройте новое окно в файловом редакторе и введите следующий код. рriпt('Введите первое слаrаемое:') first = inpиt () рriпt('Введите второе слаrаемое: ') second = inpиt ( ) рriпt('Введите третье слаrаемое: ') third = inpиt() print('Cyммa равна' + first + second + third) Сохраните текст ПрOl'раммы в файле buggyAddiпgPrograт.py и выполните ее сначала при отключенном отладчике. Вы должны ПОЛУЧИ1'Ь следующий вывод. Введите первое слаrаемое: 5 Введите второе слаrаемое: 3 Введите третье слаrаемое:: 42 Сумма равна 5342 Проrрамма не завершилась аварийно, однако результат явно неверный. АКтивизируйте окно Debug Control и вновь выполните проrрамму, на этот раз под управлением отладчика. Нажмите клавишу <F5> или выберите пункты меню RunqRun Module (предварительно активизировав отладчик ( помощью пунктов меню DebugqDebugger и установив все четыре флажка в окне Debug Control), что приведет к запуску ПрОl'раммы и ее приостановке на строке 1. Отладчик всеrда приостанавливается на той строке кода, которая будет ВЫIIOЛНЯТЫ::Я следующей. Окно Debug Control примет вид, показанный на рис. 10.2. Щелкните на кнопке Over один раз для выполнения первоrо ны:юва функции print (). В данном случае необходимо использовать кнопку Over, а не Step, поскольку в "заходе" в код функции print () нет необходимости. Содержимое окна Debug Control обновится, и теперь оНО будет отображаТI. информацию, относящуюся к строке 2, а сама строка 2 в окне файловою ре- дактора будет подсвечена (рис. 10.3). Тем самым вы сможете леrко опреде- лить, какая именно строка кода выполняется в данный момент. Вновь щелкните на кнопке Over, чтобы выполнился вызов функции inpиt (); при этом на все то время, пока IDLE будет ожидать вашею ввода в окне интерактивной оболочки 8 ответ на вызов функции inpиt ( ) , кнопки в окне Debug Control деактивизируются. Введите 5 и нажмите клавишу <Enter>. После этоrо кнопки в окне Debug Control вновь активизируются.
292 rлава 10 . . jt Debug Conbol . J 1 J Jti1St1Ck oJ2:P ?:,e l .2' [О; LOCIII ":.;:.:"':> ?{:(.:. '..,.. :-<,.<><:.';'"!. (":.: > ';l;;t(rBi" 1" Source ;;; Globlll bugg)'Addingprogr.m,PJ"l: < modult> О # 1; ;i;; I! I l I I I I I i I ,,,''""" ..... ....... ""'_''_"'''__'''_'''''''_.., ................L LOCIIs Nont , ....J Globlls builtin. doc filt Iolder < module 'bu,ltins' (built,in» Nont '5:\'.' Project'l, ', ру\ \buggyAddingProgram.py' < ,1155 '}rozen.importlib,Builtinlmporter" J neml'..... 'main...: .....P"klge None None ) Рис. 10.2. Внд окна Debug Coпtro/ при пеР80начальном запуске про- rpаммы под управлением отлодчика f,'C_ ..' ","," ..<',.'..>., ,. . ба: : о... ; Ф". QI& I ,.J bdr. Saura .",",",,-..:.. .';.., !v Lo(»S __ 01,*_ bugg';'AddIngPnIgf,"'.l; ' medult)O 'i3 fiiiil ЪCI> .tun(l. I4l'1ea. "K( I.I .t':.:t!;;:;t..t"'J"Z .. ...tp..,."'; . s.;J ; 'i;';Ci;; :. .. Е'" FOnNt '" opoiom ......... ...., LOaIS . ;П' (' ') .. ' IP] 1т (.i'.iat (' .. .".- .. ') second = ;',pt () .! I:;t ( . . .., : --." с ) third :r.1=ut () (,".! : :it ( , . .... + firt + sec.,nd + third) Но.. GlotI.S tluiti"l 1"I'Юd,1t bUI".r'l ':b'ln:' dc( Ыe... ....otdv ..... ':'.:... P':.tf<I' .;,';.Ьu9ФДddi"gPf09""" р,' oJс18и '.frol.lmpoftlф 8uilttn&.npoltu'" ...оцМ..... m.ln ............. None Ln; 1 (01 Н .....)I:8'!: None Рис. 10.3. Вид окиа Debug Сопtrol после щелчка на кнопке Over
Отладка 293 Продолжая щелкать на кнопке Over, введите 3 и 42 в качестве следующих ДI}УХ слаl'аемых, пока отладчик не достиrнет строки 7, Т.е. последнеrо BЫ зова функции print () в ПрOl'рамме. В этот момент окно Debug Control долж но выrлядеть так, как 110казано на рис. 10.4. Как можно увидеть в разделе Globals, первая, вторая и третья переменные содержат строковы е значения , 5 1, '3' и '42', а не целочисленные значения 5, 3 и 42. При достижении п<r следней строки кода вместо сложения трех чисел выполняется KOHкaTeHa ция указапных строк, что приводит К ошибке. (jc.. , I ' ; , i;; 5tlck ..? 1ep ._r_! .?ut:Q.uJ r.; LOClls buggyAddingProgrlm,py:7: < modulR> О C;;=-iС ii), j Source '" Globals 'bdb',runO, lin 431: exc(cmd, glob.ls, locels) .- ...: ;'1J!'::j'.:.-r:::::UХ.:J ; ...J Loc.ls Non. .1 ! ..... Glob.ls builtinl doc fjll!.... loadRr <modulR 'builtlns' ibuilt.in» Nona '5:\ \Projws", ру" o,buggyAddingProgram.py' . ,1'55 'Jrozn.importlib,Builtinlmpollar'> I .J ...nam!".... .....m8In.....' .jIlclclgL Nona spac Nona fir5t '5' sacond '3' third '42' Рис. 10.4. Вид окна Debиg Coпtro/ при достижении последней строки кода. Перемениые принимают строковые значения, что приводит к ошибке Пошaroвое выполнение проrpаммы с помощью отладчика чрезвычайно информативно, однако замедляет работу проrраммы. Во мноrих случаях вам хотелось бы, чтобы проrрамма выполнялась в обычном режиме, пока не достиrнет определенной строки кода. Можно сконфиrypировать отлад чик для TaкOI'O режима работы, используя точки останова.
2М rлава 1 О То.к" ona"о.. Точка остапова, у(тановленная для определенной строки кода, сообща- ет отладчику, что по достижении данной строки выполнение nporpaMMbl должно нриостановиться. Откройте новое окно файловоrо редактора и введите следующую nporpaMMY, имитирующую подбрасывание монсты 1000 раз. Сохраните nporpaмMY в файле coinFlip.py. import random heads = О for i in range(l, 1001): О if random.randint(O, 1) 1: heads = heads + 1 if i == 500: О print ( I Полпути пройдено!') рriпt('Орел выпал' + str(heads) + ' раз.') Вызов random.randint (О, 1). в половине случаев будет возвращать зна- чение О, а в половине 1. Этот факт можно использовать для имитации подбрасывания монеты с равными вероятностями выпадения "орла" и "решки", причем в нашем случае "орлу" соответствует значение 1. Выпол- нив nporpaMMY без отладчика, вы очень быстро получите примерно следую- щий вывод. Полпути пройдено! Орел выпал 490 раз. Если вы будете выполнять проrрамму под управлением отладчика, то к тому моменту, коrда npOI'paMMa закончит выполнение, вам придется щел- кнуть на кнопке Over не одну тысячу раз. Если вас интересует, сколько раз выпала решка, коrда nporpaMMa выполнилась наполовину, Т.е. при совер- шении 500 "подбрасываний" монеты из 1000 возможных, можете просто задать точку останова в строке print ( 'Полпути пройдено! ') .. Чтобы задать TOtlKY останова, щелкните правой кнопкой мыши на этой строке в окне файловоrо редактора и выберите во всплывающем меню пункт Set Breakpoint (Задать точку останова), как показано на рис. 10.5. Задавать точку останова для строки с инструкцией if не следует, по- скольку данная инструкция выполняется на каждой итерации цикла. Если задать точку останова в коде инструкции i f, то отладчик будет прерывать выполнение только в тех случаях, коrда поток управления будет входить в тело этой инструкции. Строка, для которой задана точка останова, подсвечивается в файловом редакторе желтым цветом. Коrда вы запускаете проrрамму под управлени- ем отладчика, ее выполнение начинается с состояния ожидания в нервой
Отладка 295 строке. Но если вы щелкнете на кнопке Go, то проrpамма станет ВblПОЛНЯТЬ инструкцию за инструкцией до тех пор, пока не достиrнет строки, дЛЯ KOT<r рой задана точка останова. Далее можете щелкать на кнопках Go, Over, Step и Out, чтобы продолжать выполнение в соответствующем режиме. L_ coinFIip.py - S:Projects1I)'colnFflp.py (Э';5.11) Eile Edit F,Qrmat Bun Qptions. indow, J:ielp random heads = О i rarJg,,, (1, 1001): random.randint(O, 1) head5 = heads + 1 '. i == 5.00: print (", ,'"...-::, -,;:..:';,::,:, I' + tr (heads (,- I-'':i '&fOJI8 ..] 1: print (' .....:;, l -;" '. ""'''''.....''f(; ';""'.-'.""kfF Clear Вreakpoint Ln: 7Co 31 Рис. 10.5. Задание точки останова Чтобы удалить точку останова, щелкните правой кнопкой мыши на COOT ветствующей строке в окне файловоrо редактора и выберите в открывшем- ся меню пункт Clear Breakpoint (Отменить точку останова). Желтая подсветка строки исчезнет, и в будущем выполнение проrраммы на ЭТОй строке не будет прерываться. РеЗlOме Утверждения, ИСIUlючения, протоколирование и отладка незаменимые инструменты, предназначенные для обнаружения и устранения лоrических ошибок в проrраммах. Механизм утверждений, обеспечиваемый инструк цией assert в Python, отличное средство реализации "профилактических проверок" , обеспечивающее раннее обнаружение потенциальных ошибок, если не ВЫIЮЛШIЮТСЯ необходимые условия. Утверждения прсдназначены для выявления только тех ошибок, при возникновении которых проrрам- ма не будет пытаться восстановить работоспособное состояние и должна лишь быстро завершиться аварийно. В противном случае следует исполь- зовать исключения. Исключения можно перехватывать и обрабатывать (: помощью инструк- ций try и except. Модуль logging, обеспечивающий возможность учета уровней критичности ошибок при записи сообщений о них в текстовый
296 rлава 10 файл журнала, предоставляет неплохие возможности для наблюдения за состоянием ПрOl'раммы во время ее выполнения и I'ораздо более удобен в использовании, чем функция print (). Отладчик обеспечивает пошаl'овое выполнение I1porpaMMbl по одной инструкции за раз. Альтернативной возможностью является выполнение IIporpaMMbl с обычной скоростью с возможностью ее прерывания отлад чиком при достижении строк КОДа, для которых заданы точки останова. Используя отладчик, можно наблюдать за значениями любых переменных во время выполнения ПрОI'раммы на протяжении вcero срока ее существо вания. Использование перечисленных средств и методик отладки облеrчит вам написание правильно работающих проrpамм. Непреднамеренное внесение лоrических ошибок в код неотъемлемый атрибут работы проrраммиста, от KOToporo не помоrает полностью избавиться даже мноrолетний опыт проrраммирования. KOHTpOnbHbl8 BonpOCbI 1. Напишите инструкцию assert, которая возбуждает исключение AssertionError, если :шачение переменной эрат представляет собой целое число, меньшее 10. 2. Напишите инструкцию assert, которая возбуждает исключение AssertionError, если переменные eggs и bacon содержат одинаковые LТрОКИ, при этом реrистр букв не учитывается (например, они содер- жат строки I hello I И 'hello' или же строки' goodЬye' и I GOODbye' , K<r торые также считаются одинаковыми). 3. Напишите инструкцию assert, которая всеrда возбуждает ип<люче- ние AssertionError. 4. Какие две строки кода должна иметь ваша проrрамма для TOro, чтобы вызывать функцию logging. debug () ? 5. Какие две строки кода должна иметь ваша проrpамма для TOro, что бы вызывать функцию logging. debug ( ) , записывающую сообщение в файл журнала prograтLog. txt? 6. Назовите пять уровней критичности сообщений. 7. Какую строку кода вы можете добавить для Toro, чтобы отключить все сообщения протоколирования в своей проrpамме? 8. Почему для отображения одноrо и Toro же сообщения лучше испол зовать сообщения протоколирования, чем вызов функции print ( ) ? 9. Чем различаются команды, выполняемые после щелчков на кнопках Step, Over и Out в окне Debug Control?
Отладка 297 10. Коrда отладчик прервет выполнение nporpaMMbl, если вы щслкнете на кнопке Go в окне Debug Control? 11. Что такое точка останова? 12. Как задать точку останова для строки кода в IDIE? Учебный проект Чтобы закрепить полученные знания на практике, реализуйте предл<r женную ниже задачу. Or."IIK" "porfICJ_6I, II."'IIPYIOIII" 1I01l6p"'''.''IIII' .011',61 Привсденная ниже nporpaMMa предназначена для имитации иrры в yra дывание Toro, какой стороной будет обращена вверх упавшая после 1I0Д брасывания монета. В распоряжении иrрока имеются два варианта ОТВета (это простая иrра). Однако в nporpaмMe содержатся лоrические ошибки. Запустите nporpaMМY несколько раз для тою, чтобы обнаружить ошибки, препятствующие ее правильной работе. import random guess = " while guess not in ('орел', 'решка'): рriпt('Уrадайте результат подбрасывания монеты! .Введите орел или ретка:') gиess = input () toss = random.randint(O, l} # о решка, 1 орел if toss == gиess: print ( 'Есть! 1) else: print('YBbl! Попробуйте снова!'} guesss = input () if toss == guess: print ( 'Есть! I ) else: print('HeT. Вам действительно не везет в этой иrре.')
АВТОМАТИЧЕСКИЙ С&ОР ДАННЫХ ВИНТЕРНЕТЕ в те редкие пуrающие минуты, коrда остаешься без WHi, отчетливо осознаешь, насколько то, что ты делаешь с IЮм<r щью компьютера, связано с Интернетом. Я часто замечаю, что по устоявшейся привычке меня так и тянет про верить электронную почту, прочитать сообщения друзей в Твит тере или ответить на вопросы типа "Были ли у Кертвуда Смита l'Лавные роли дО TOI'O, как ero приrласили ПIЯТЬСЯ в ориrинальном фильме РобlЖОn, выпущенном в 1987 rоду?"1 Поскольку значительная часть работ, выполняемых с помощью компькr тера, связана с выходом в Интернет, было бы замечательно, если бы ваши nporpaMMbI моrли обеспечивать это подключение. Авто,м,атический сбор даn- nых в иnтерnете (жарr. веб-скраnиnz) это термин, обозначающий исполь зование проrраммы для зarрузки и обработки содержимоrо из Интернета. К примеру, Google выполняет множество таких nporpaMM, индексируя веб- страницы для своей поисковой системы. В этой rлаве вы познакомитесь с несколькими модулями, которые облеrчают автоматический сбор данных с веб-страниц с помощью Python. Ниже приведен перечень этих модулей. . webbrowser. Поставляется вместе с Pytholl и предназначен для OТKpЫ тия браузера на определенной веб-странице. . Requests. Заrружает файлы и веб-страницы из Интернета. . Beautiful Soup. Предназначен для синтаксическоrо анализа кода HTML языка, на котором написаны веб-страницы. . Selenium. Запускает веббраузер и управляет ero работой; способен заполнять формы и имитировать щелчки мышью в браузере. I Orвeт нет.
300 rЛQва 11 Проект: nporpaMMa map't.py с модупем webbro"ser ФуНКЦИЯ open () модуля webbrowser запускает браузер с использованием указанноrо URL-aдpeca. Введите в интерактивной оболочке с.ледующие ко- манды: »> iшроrt w8bbrowaer »> w8bbrowaer.open('http://inventwithpython.com/') В бра узе ре откроется веб-страница, расположенная по URL-адресу http://inventwithpython. сот/. Это почти единственное, что может делать модуль webbrowser. Тем не менее функция ореп () позволяет делать интерес- ные вещи. Например, вставка lIочтовоrо адреса в приложение Google Maps через буфер обмена довольно хлопотная задача. Вы можете сэкономить на этом некоторое время, написав простой сценарий, который будет авто- матически запускать карту в вашем браузере, используя содержимое буфера обмена. Блаroдаря этому вам останется лишь скопировать адрес в буфер об- мена и запустить сценарий, который и заrpузит карту. Вот что должна делать такая проrpамма: . получать почтовый адрес из командной строки или буфера обмена; . открывать браузер на странице Google Maps, соответствующей yкa занному адресу. Это означает, что ваш код должен выполнять следующие операции: . читать apryмeнты командной строки из списка sys . argv; . читать с.одержимое буфера обмена; . вызывать функцию webbrowser . open () для открытия браузера. Orкройте новое окно файловоrо редактора и сохраните ero в файле map/t.py. Ш", ,. О"Р'II'.'''.' U'I."IIP"" Руководствуясь инструкциями, приведенными в приложении В, настрой- те файл map/t.py таким образом, чтобы при ero запуске, например, с помо щью команды С:> mapit 870 Valencia St, San Francisco, СА 94110 сценарий использовал в качестве почтовоrо адреса apryмeHTЫ командной строки, а не содержимое буфера обмена. Если же apryмeнты командной стро- ки не заданы, то проrрамма будет знать, что в этом случае нужно использо- вать буфер обмена.
Автоматический сбор данных вИнтернете 301 Прежде BCeI'O необходимо определить, какой URLaдpec следует исполь- зовать для указанноrо почтовоrо адреса. Если вы заrрузите в браузер стра- ницу http://maps . google. сот/ и выполните поиск по интересующему вас по- чтовому адресу, то URL-aдpec, отображаемый в адресной строке браузера, будет выrлядеть примерно так: https://www.google.com/maps/place/870+Valencia+st/@37.7590311, 122.421509б,17z/dаtа=!3m1!4bl!4m2!3m1!1s0х808f7е3dаdс07аЗ7: Охс8БЬОЬ2ЬЬ9ЗЬ73d8 URL-aдpec содержит почтовый адрес, но, кроме Hero, включает также дополнительный текст. Веб-сайты часто вставляют дополнительные дан- ные в URL-адреса, которые используются для отслеживания при вы чек посетителей или адаптации сайта к запросам пользователей. Если вы ис- ключите эти данные и попытаетесь выполнить в браузере переход по упро- щенному адресу https: / /www. google. com/maps/place/870+Valencia+St+San+ Francisco+CA/, то обнаружите, что вам по-прежнему доставляется нужная страница. Следовательно, нам достаточно настроить проrрамму на страни- цу 'https: / /www.gооglе.сот/тарs/рlасе/вашаадреснаясока', rде ваша адресная строка это почтовый адрес, в соответствии с которым должна быть открыта карта. Ш", 2. 06р"60nr,, "P,.,.'II108 КО."II11110. "рок. Введите следующий код. #! python3 # mapIt.py Запускает карту в браузере, извлекая почтовый адрес # из командной строки или буфера обмена. import webbrowser, sys if len(sys.argv) > 1: # Получение почтовоrо адреса из командной строки. address = , '.join(sys.argv[1:]) # TODO: Получить почтовый адрес из буфера обмена. Первая строка сценария "мarическая" (начинается символами # ! ). Да- лее мы импортируем модули webbrowser (необходим для запуска сценария) и sys (необходим для чтения возможных apryMeHToB командной строки). Впеременной sys. argv хранится список, включающий имя файла проrpам- мы и apryмeHTЫ командной строки. Если этот список содержит не только имя файла проrpаммы, то вызов len (sys. argv) возпратит значение, боль- шее 1, указывающее на то, что в командной строке действительно предо- ставлены арryменты.
302 rлава 11 Обычно apryMeHTbI командной строки разделяются пробелами, но в дан- ном случае мы желаем интерпретировать все apryMeHTbI как одну строку. Поскольку sys . argv это СПИСОК строк, ero можно передать методу j oin ( ) , который возвратит результат в виде одной строки. Имя проrраммы в этой строке для нас не представляет интереса, поэтому мы передаем методу j oin () не весь список sys. argv, а ero срез sys. argv [1: ] , тем самым исклю- чая ненужный нам элемент списка. Результирующая строка сохраняется в переменной address. Если для запуска проrраммы использовать команду Taкoro вида: mapit 870 Va1encia St, San Francisco, СА 94110 то впеременной sys. argv будет содержаться следующий список: ['maplt.py', 1870', 'Valencia', 'St, " 'San', 'Francisco, , 'СА', '94110'] в то время как 8 переменной addre s s будет содержаться строка '87 О Valencia St, San Francisco, СА 94110'. Шаr 3. 06работка 'Оllер...оro 6уфера 06.."а . ,ап"к 6рорера Добавьте в проrpамму код, выделенный ниже полужирным шрифтом. #! python3 # maplt.py Запускает карту в браузере, извлекая почтовый адрес # из командной строки или буфера обмена. import webbrowser, sys, pyperc1ip if len(sys.argv) > 1: # Получение почтовоrо адреса из командной строки. address = I '.join(sys.argv[l:]) 8188: * Ilo.nyч8ние ПОЧ'l'О801'O a,qpeca на буфера обмена. addr888 = pyperclip.pa8t8() w8ЬЬrOW88r.open('httpа://www.gоog18.oam/шар8/р1аое/' + addr8aa) Если apryMeHTbI командной строки не предоставлены, проrpамма пред- полаrает, что адрес хранится в буфере обмена. Мы можем получить содер- жимое буфера обмена с помощью функции pyperclip. pas te () и сохранить ero в переменной address. Наконец, для запуска браузера с URL-адресом Google Maps вызывается функция webbrowser. open () . Несмотря на то что некоторые из проrрамм, которые вы напишете, сэ- кономят вам MHoro часов, выполняя вместо вас трудоемкие задачи, не стоит
Автоматический сбор данных вИнтернете 303 недооценивать nporpaMMbI, позволяющие сэкономить хотя бы несколько секунд вашеro времени каждый раз, Korдa вы выполняете даже такие np<r стые задачи, как отображение интересующеl'О вас места на карте Google. В табл. 11.1 сравниваются шarи, которые приходится выполнять для отоб- ражения карты с помощью и без помощи nporpaMMbl mllp/t.py. Та6nмца 11.1. Попучеиие карты местности с помощыо и без помощи nporpoммы map't.py Попучение KQpnoI8PY'lflYlO 8ЫД8J1ение адреса Копирование адреса Открытие браузера Переход по адресу httр://шарs .google.com/ Щ8J1'40К в попе Д/lII ввода адреса Вставка адреса Нажатие клавиши <Enter> ИCIКNIltlO8ClНие nPOrPaММIOI mapl,.py Выдепение адреса Копирование адреса Запуск проrpаммы шар!t . ру Вы сами можете оценить, насколько автоматизация этой задачи позв<r ляет СЭКОIЮМИТЬ ваши усилия И время. Иllе" OrHO,,,re.tHO '0111""". """.о,,,.,,,.,х "ро',о_ Коль скоро известен нужный URLaдpec, модуль webbrowser избавит от необходимости самостоятельно открывать браузер и переходить на нуж ный веб-сайт. Вы можете встроить эту функциональность в друrие проrрам мы для решения следующих задач: . открытия всех ссылок, Ilриведенных на странице, в отдельных вклад ках браузера; . открытия браузера на странице с проrнозом поroДЫIlО вашему реrиону; . открытия нескольких сайтов социальных сетей, которые вы реryляр но посещаете. Эаrруэка файяов И3 Интернета с помощыо модупя Requests Модуль Requests упрощает заrрузку файлов из Интернета, позволяя не задумываться о таких сложных вопросах, как ошибки сети, проблемы noд ключения и сжатие данных. Этот модуль не поставляется вместе с Python и нуждается в предварительной установке. Выполните в командной строке команду pip in.tall request.. (Подробное описание процесса установки модулей, разработанных третьими сторонами, ПРИ8едено в приложении А.)
304 rлава 11 Необходимость в написании модуля Requests была продиктована тем обстоятельством, что модуль Python urllib2 СJIИШКОМ сложен в использова нии. Фактически вам следует наrлухо вымарать маркером весь этот абзац. Забудьте вообще о том, что я коrдалибо упоминал о модуле urllib2. Е(ли вам нужно заrрузить информацию из Интерllета, используйте для этоro ис ключителыю модуль Requests. Вашим следующим шarом должно быть выполнение простоrо теста, КО- торый позволит убедитьс.я в корректности установки модуля Requests: »> import requests Оп:утствие (:ообщений об ошибке будет свидетельствовать о том, что установка модуля Requests была успешной. 311rpY3KII ..6."р"..,," п"'рв..том фУНК".. requests . get (J Функция requests. get () принимает строку с URLaдpecoM, по которо- му должна осуществляться зarрузка. Вызвав функцию type () для значения, возвращеННОl'О вызовом requests .get (), вы увидите, что этот вызов возвра щает объект Response, в котором содержится ответ с.ервера на ваш запрос. Мы еще поrоворим об объекте Response, а пока что введите в интерактив ной оболочке следующие команды, предварительно убедившись в том, что компьютер подключен к Интернету. »> iщport requests О>>> res ... requests.get( 'http://www.gutenberg.org/ cache/вpub/1112/pg1112 . txt' ) »> type (res) <class 'requests.models.Response'> О>>> res.statuscode -- requests.codes.ok True »> len (res . text) 178981 »> print(res.text[:250]) The project Gutenberg EBook of Romeo and Juliet, Ьу William Shakespeare. This eBook is for the use of апуопе anywhere at по cost and with almost по restrictions whatsoever. Уои тау сору it, give it away or reuse it under the terms of the proje По указанному URLpccy находится веб-странип.а, содержащая полный текст lIьссы"Ромео и Джульстта", который предоставляется бесплатно в рамках IIроскта "Iyrенберr" О. Проверить успеШНОСТI. выloлненияя запр<><:а можно с помощью атрибyrа sta tus code объекта Response.
Автоматический сбор данных вИнтернете 305 Значение requests. codes. ok этоrо атрибута rоворит о том, что запрос был успешно выполнен О. (Кстати, в протоколе HТfP успешному запросу ("ОК") соответствует код (остояния 200.) Возможно, вам уже приходилось сталкиваться с кодом состояния 404 ("Not Found" "Не найдено"). В слу чае успешноrо запроса заrpуженная страница сохраняется в виде строки в переменной text объекта Response. В этой переменной хранится полный текст пьесы в ВИДе одной длинной строки; результат вызова len (res. text) rоворит о том, что эта СТрОКа содержит более 178000 символов. Наконец, вызов print (res. text [ : 250] ) отображает лишь первые 250 символов. Про.еРКII ОШllбок Как вам уже известно, объект Response имеет атрибут statuscode, cpaв нение KOToporo со значением requests. codes. ok позволяет (:удить о том, была ли заrрузка успешной. Однако для такой проверки существует более простой способ, который состоит в том, чтобы вызвать метод raise for. status () для объекта Response. Данный метод возбуждает исключение, если в процессе заrрузки файла произошла ошибка, и не совершает никаких действий в случае успешной заrрузки. Введите в интерактивной оболочке следующие команды. »> r8S = r8quеsts.gеt('httр://inventwithруthоn.сош/ Н.С)'Щ.С.У"" C!l'p8НJllIfa') »> r8s.raise fortatus() Traceback (most recent call 1ast): File "<руэЬеll#13В>", line 1, in <modu1e> res.raise for status() File "С:РуthопЗ4liЬsitерасkаgеsrеquеstsmоdеls.ру", line 773, in raise for status raiseHTTPError(httperrormsg, response=self) requests.exceptions.HTTPError: 404 Client Error: Not Found Метод raise for status () ЭТО эффективный СIIОСоб rарантированной остановки проrpаммы в случае неудачной зarpузки. Конечно же, это непл хо, ведЬ вы сами заинтересованы в том, чтобы при возникновении неоЖИ данных ошибок выполнение проrpаммы было llрекращено. Если же Hey дачная заrрузка не является критическим препятствием для ДaJIьнейшеrо выполнения IIporpaMMbI, можно обернуть строку кода raiseforstatus () конструкцией try/except, чтобы обработать эту ошибку, не допуская ава. рийной остановки проrpаммы. import requests res : requests.get('http://inventwithpython.com/ неС.YIЦествующая страница' ) try:
306 rлаВQ 11 res.raiseforstatus() except Exception as ехс: рriпt('Возникла проблема: %s' % (ехс)) в результате данноrо вызова метода raiseforstatus () проrрамма вы- ведет следующую информацию: Возникла проблема: 404 Client Error: Not Found Целесообразно всеrда вызывать метод raisefor status () после функ- ции requests. get () . Это позволяет убедиться в том, что зarрузка действи- тельно была осуществлена, прежде чем предоставить проrрамме возмож- ность ДaJIьнейшеrо выполнения. Сохранение эаrруженных файпов на жестком диске в этом разделе будет рассказано о том, как сохранить веб-страницу в файле на жестком диске с помощью стандартной функции ореп () и ието- да wri te ( ) . Однако есть некоторые нюансы. Прежде Bcero, необходимо открыть файл в режиме двоичной записи, передав функции ореп () строку 'wb' в качестве BToporo apryмeнтa. Даже если страница представляет собой простой текст (как, скажем, заrpуженный ранее текст "Ромео и Джульет- ты"), для поддержки кодировки Unicode необходимо записывать двоичные данные, а не текстовыс. Кодировка Unicode Рассмотрение кодировки Unicode выходит за рамки донной книrи. Для получения более подробной информации по зтому вопросу можете воспользоваться следую- щими ресурсами вИнтернете: . )ое' оп Software: The Abso/ute M;n;mum Every Software Developer Abso/utely, Pos;- tive/y Must Kпow Аbou, Un;code and Character Sets (No Excusesl): http://www . joelonsoftware.cam/articles/Unicode.html . Pragmatic Unicode: http://nedЬatchelder . com/text/unipain. ьш Записать веб-страницу в файл можно с помощью цикла for по возвращае- мому значению метода i ter content () объекта Response. »> iщроrt requests »> res = r8quеsts.get('http://1f1Пf.gutenberg.оrg/ cache/epub/1112/pg1112 . txt' ) >>> res.raiseforstatus ()
Автоматический сбор данных вИнтернете 307 > > > playFile = оpen ( 'RoDLeoAndJuliet. txt', '.Ь I ) »> for сЬunk in re8.itercontent(lOOOOO) : playFile.write(chunk) 100000 78983 »> playFile.close() Метод itercontent () возвращает порции содержимоrо на каждой CTa дии цикла. Каждая порция данных ЭТО данные байтовоrо типа, и оам предоставляется возможность указать, сколько байтов должна содержать каждая порция. В общем случае одна сотня тысяч байтов ЭТО вполне 1I0Д ходящий размер, ПОЭТОМУ мы передаем функции i ter conten t () значение 100000 в качестве арryмепта. Теперь в рабочем каталоrе существует файл RoтeoA пdjuliet. txt. Обрати те внимание на то, что ЭТО имя, под которым файл сохраняется на жест ком диске, отличается от имени файла, используемоrо вебстраницей (pgll12.txt). Заrрузкой содержимоro веб-страниц управляет модуль Requests. Как только страница заrружена, она становится простыми данными в Ba шей проrрамме. Даже если соединение с Интернетом нарушится после за rрузки страницы, ее содержимое останется в вашем компьютере. Метод wr i te () возвращает количество байтов, записанных в файл. В предыдущем при мере первая порция включала 100000 байт, так что для оставшейся части файла понадобилосьтолько 78983 байта. Ниже приведен перечень отдельных стадий процесса заrрузки и coxpa нения файла. 1. Вызов функции requests. get () для заrрузки файла. 2. Вызов функции ореп () С apryмeHToM 'wb' для создания HOBOro файла в режиме записи двоичных данных. 3. Цикл по возвращаемому значению метода i t е r со n ten t () объекта Response. 4. Вызов метода write () на каждой итерации для записи содержимоrо файла. 5. Вызов метода close () для закрытия файла. Это все, что вам следует знать о модуле requests! Возможно, использ вание цикла for и метода itercontent () покажется вам несколько более сложным по сравнению с технолоrией, основанной на применении цепоч ки вызовов ореп () /wri te () /close () , которую мы использовали для записи теКСТовых файлов. Однако такой подход rарантирует, что модуль Requests не будет потреблять слишком MHoro памяти даже в случае заrpузки крупных файлов. Более подробную информацию относительно дрyrих возможностей модуля Requests можно найти на сайте http://requests .readthedocs .org/.
308 rлава 11 HTML Прежде чем приступить к анализу веб-страниц, следует познакомиться с основами HTMI.. Вы также узнаете о том, как воспользоваться мощными средствами разработки, предоставляемыми браузером, которые значитель- но упростят процесс веб-скрапинrа. Ре,ур,,,, дл. ",учен". НТМ! Язык rипертекстовой разметки (Hypertext Markup Lапguаgе HTML) это формат, используемый для записи веб--страниц. В этой rлаВе предпо лаrается, что вы уже владеете некоторыми навыками работы с HTMI.., но если вы нуждаетесь в практическом руководстве для начинающих, можно порекомендовать следующие сайты: . http://htmldog.com/guides/html/beginner/ . http://www.codecademy.com/tracks/web/ . https://developer.mozilla.org/enUS/learn/html/ IРllтк"е tвeдeH". по ЮМL Если до этоrо вы сталкивались с НТМI..докумснтами лишь эпизоди чески, то вам будет полезно ознакомиться с приведснным ниже кратким обзором основ HTML. НТМLайл это обычный текстовый файл с pac ширением .html. Текст в таких файлах окружается mezaмu (дескрипторами). Каждый Ter представляет собой слово, заключеННОе в yrловые скобки. Теrи сообщают браузеру, как следует форматировать веб-страllИЦУ. Начальный и конеЧный (открывающий и закрывающий) теrи MOryт окружать некоторый текст, вместе с которым они образуют э.лe.мeum. Текст (или в'Нутрениее НТМLсодержu.м,ое) это содержимое, заключен- ное между открывающим и закрывающим теrами. Например, следующий НТМLдокумент отображает в браузере фразу Здравствуй, мир!, в которой слово Здравствуй выделено полужирным шрифтом: <strопg>Здравствуй</strопg>, мир! Вид этой НТМL-разметки в браузере показан на рис. 11.1. Открывающий Ter <strong> указывает на то, что заключенный между этими теrами текст должен отображаться .полужирным шрифтом. За- крывающий Ter </ strong> обозначает, rде заканчивается полужирный текст. В HTML существует множество TerOB. Некоторые из них имеют допол- нительные свойства в виде атрибутов, записываемых в yrловых скобках. Например, Ter <а> содержит тек(:т, создающий rиперссылку. URL-aдpec, на
Автоматический сбор данных вИнтернете 389 который с(:ылается этот текст, определяется атрибутом href. Вот Соответ- ствующий пример: <а hrеf="httр://iпvепtwithруthоп.соm">Книrи по Python</a>, бесплатно предоставляемые на сайте Эла. r'i i,..,dr-:':"!Jr! со .' f;;@;I/.;5:.;Pr',j@ctS/PY/I"d@x.I':' :J.::Jравств)'й. шр! ц ч,QI:iI: Рис. J '. '. Фраза ЗдРавствуй, мир!, визуализированная в браузере Вид этой НТМL-разметки, визуализированной в браузере, показан на рис. 11.2. i. t ; In:lt. "ФJ";'" Х с: fIIe;ij/;fPI'C'j@ct>ipy(i"d@x,llt",1 (. vt t1 r3 k ИllfП по P y tllOI1. беСП.1атно пре.:Iоста8.1яемые на сшlrе 'Эла. Рис. 11.2. Ссылка, визуализированная в браУЗ8ре Некоторые элементы имеют атрибут id, который используется в ка- честве уникалыюrо идентификатора элемента на странице. В своих про- I'paMMax вам придется часто выполнят!> поиск элемента по ero атрибyry id, поэтому определение ДаННоrо атрибута с использованием инструментов разработчика, предоставляемых браузером, является одной из самых рас- пространенных задач при написании I1pOrpaMM для веб-скраllинrа. Про,.отр "'XOIIHOrO HTIIL-KOIIO "'."РОН",,'" Так или иначе вам понадобится просматривать исходный НТМL-код веб-страниц, с которыми будут работать ваши проrраммы. Чтобы это сде- лать, щелкните правой кнопкой мыши (или выполните щелчок мышью
310 rлаВQ 11 при нажатой клавише <Ctrl> в OS Х) на любой всб-страпиП,е в своем брау зере и выберите в (у)'крывшемся контекстном меню пупкт Просмотреть код (View Source) или Просмотр кода страницы (View page source), как показано на рис. 11.3. Таким образом вы сможете просмотреть текст, который в дей ствительности получает ваш браузер. Браузеру известно, как отображать. или, как roворят. визуа.лuзuровamъ. веб-страницу па основе I1ТМI4-кода. Вestullers I . :., . ,...... 1tIeМak&r'sGuldetolhe ., ",. lOmbIe Apocalypse " '0" }_ ,"olr.', .'..,.0....",. ...э;. j ['PYTHONi В , 08,.'D,! 1 :. >.,}. ,....... ",.- pe::-. 3:': ::.оч::.....IТI::I .3"" "d !"""i!'...1't'_ ;' ' с ) , , '..у,; " .' ) "_ : . , :'lt;.. ;):''.I.:,"'::T ..;.; . . - 1] t. "".t ! 1"1 -;I r . (. r.; SUr,.., р,..,. "'- if. ... -:c,_",.:httpo.' , ' )t "l.}::':: с -.,}-,.; ,'i'JV,l)оst",'СI,,',:оп'1 Щ " 13t g CIЗ '," 0.1>.1 , ," --::. t I : -:.::: ,,.-tt ,"-" ,. ; t!..,:. ",'::'. i:)("'g/ 1 ):(.I / .;<1'1-t::1" ... (": F---'," er"'" "' '. ",r. ",,.. >..; .:.;.;i;':: E:rl ;J ,т..:' ;1:!. '. -'. 1 ::;."C.r::t17_.TYDe" '::'',:еп1;::::I't€:уt. J,tтl.1 ':').:1P'5t=,.tf .jI/. .' t 1: 1.; '.1.10 Stdl'ch Press' ,'т:: t::'€:> '.т..::.Э" ,.,;_,.. ..... :..i ..-:=.f'(.:.п:I1--=hТуре" ..(.:tе(;t='I.tе-хt/"tml; Сl(.:н"':::еt::-.tf.i3",:> <::,. "l--: :.. ,:""'t-=-,-.:t/...1/d>:,"':"pt"j' I!<! [0::'-:-::'[ trv{if ('indow,CloudFlare) {var CloudFlare= [ {"e,'bose: ", р: 1457666483, Ьус: 0, c.,oJlid: .. С f" ,bag2: 1, mirage2: 0, oracle: е. patll s :{cloudflare;";cdn сgi/пехр/dоkЗV=161ЗаЗа185/.,.tоk:.d7е3а5а139184g1еа8cdb6983c3069..,peto k:Кblf0ЬfЬd31dс8lа8ЬЗ2dfБЬ9еI8аI140328а6dБ-1458141163 18130", Zane: "nostarch . со"," ,I'ocket: "О., apps: {"gakey": {.ua.: .UA 5e17625 Рис. 11.3. Просмотр исходноrо кода ве6страницы я настоятельно рскоменяую вам ИССJlедовать 1 П'П.код понравИВПlихся сайтов. ЕсJlИ нри просмотрс НТМLкода вам не все будет понятно, не orop- чайтесь. Чтобы писать простые проrpаммы для ве{Н:крапинrа. не нужно быть искусным мастером HTML в конце концов, речь не идет о создании
Автоматический сбор данных вИнтернете 311 собственноrо веб-сайта. Вам достаточно лишь знать, как орraнизовать сбор данных с существующих сайтов. О,кр"""е ОКНII "н"ру.енro. рllЗрll60"'"КII . 6Рllузере Кроме исходноrо кода веб--страницы, можно просматривать HTML- разметку страницы с помощью инструментов разработки, предоставляе- мых браузером. В браузерах Chrome и Iпtеrпеt Explorer для Windows ин- струменты разработчика уже установлены, и для начала работы с ними вам остается лишь нажать клавишу <F12> (рис. 11.4). Повторное нажатие клавиши <F12> приводит к закрытию окна инструментов разработчика. В браузере Chrome вам предоставляется дополнительная возможность д ступа к инструментам разработчика пyrем выбора пунктов меню Настройка и управление Google Chrome (кнопка в конце адресной строки)t::>Дополнительные инструментыt::>Инструменты разработчика. В OS Х для этоrо следует нажать ком- бинацию клавиш <X+Option+I>. -;; :.;:: ar.;" "oe +- с (J ",." . '". !...: .!..;< ,lhww.nos!arch.::om С, ' " .... :-'I-I":I' ! I .. ... I ___,О ,. а, . I '" I ."...,. ...... ".,..,.. .... ..:... --.; зr-j More u!inV;J .:r.,""':.I1'1 ;:!:-"'.1!1!: 1..'. '1181 Y11IO" '......1:.'.... Pyll10n Cr..h Cour.. .5 3 ::JUI( ')C.MI1Sn'S& Q!.'JE: {О pf.').;!:"al"1f"lI".!J i P).tfIOn. I ш,сЬ, а""..', 'WhOdul':;t trto ::i1j.lb! э;I-i!'. in'rod"tln ,h. :00'.." ...: . (" . ;; t$!'I'J:t startrl 31,:";З . .<.''" а"::iМР(lI1.э!'1t::i9ltjj - :',';7.." 'S'riJсtlrtи t!'НЦ1h Ule rletIM о' "" !ТIj;orj "о:...&! I .... Арот ""'" -, " ') >« ",,/f!; .. С. r 1/ l' . '1'; "'"..:t,< I'' .:: . ":::-:. ,., :I'IH . '.,< l' .' .:. х . '9 .. 'v . .::. :;; rI :., .;.1. .. ,.:' .,.; lo:t...,;:.;1 '>_1 Й ..; ":,-::: :r,:,:,a:.:':- .Z ....:!"'!1 .:; ..E-.o! - .C ..;...-:- ..:. .:' ..;: ,;",..' :''' E-S... .!l.e-.,:;' .3 B -- !I<! '::'';' . .-;..: .>: r,:,. : :;., , (,E ca "Щ ",'d,....;57 ..i..6 .- tB 0:-''';''. : .=>#. ''''', , -: ':: ,,> lE "":' i :. .' <.. , ....):.: :' : .... Р" '.- '9 .!' ,jt' ....: lB -:=.' :j е - <5;. ,у, ."" "'" ;ш. =::.T;;; j.(i:-t';;, J:: .:. r 6:i.:';-;' "7..41:' ;. '.:' _ ':.:I ::i !=Iп:::.h.:' 6:. 5 : :y)r...1C :.п!E;tL'::"..1J_ .31;:. ; ..:..:,:). 2.:: : Рис. 11.4. Окно инструментов разработчика в браузере Chrome в браузере Mozi11a Firefox для открытия окна Инспектор можно нажать комбинацию клавиш <Ctrl+Shift+C> в Windows и Lillux или комбинацию клавиш <X+Option+C> в OS Х. в обоих случаях окна инструментов разра- ботчика будyr выrлядеть почти так же, как в Сhroше. В браузсрс Safari следует открыть окно Preferences (Установки) и YCTaн вить флажок Show Develop menu in the menu bar (ПОКа3ывать меню инструментов
312 rлава 11 разработчика в строке меню) панели Advanced (Дополнительно). После это- 1"0 можно вызвать окно инструментов разработчика, нажав комбинацию клавиш <X+Option+I>. Активизировав или установив инструменты разработчика в своем брау- зере, можете щелкнyrь правой кнопкой мыши в любой части ве6-страницы и выбрать в открывшемся контекстном меню пункт Inspect Element (Исследо- вать элемент), чтобы выделить НТМL-размстку, ответственную за данную часть страницы. Такая возможность придется вам кстати, коrда вы присту- пите к синтаксическому анализу (парсинry) НТМL-документов в своих про- rраммах для автоматическоrо сбора данных вИнтернете. Не пытайтесь испопЬЗ0вать реryпярные выражения дnя НТМL-парсинrа Казалось бы, что может быть ффективнее применения реryлярных выражений, если речь идет о нахождении определенных HTML-лемеНТ08 в строке. Однако я катеrорический противник TaKoro подхода. HTML-РОЗМ8тка может осуществляться множеством самыХ раэ.nичных способов, Кaжд1Jму из которых будет cootbetCT-О8ать действительный НТМL-документ, и любые попытки охватить все возможные 8ариан ты с помощью реryЛЯрНIIIХ 8ыражений потребуют больwих усилий И будут чреsaты ошибками. Вероятность появления оwибок уменьwится, если использовать модуль Beautiful Soup, специOJ'lЬНО разработанный дnя НТМL-парсинrа. Дополнительную арryментацию против испопьзования реryлярных выражений АЛЯ парсинrа НТМL-разметки можио найти по адресу http://stackoverflow. соm/а/1732454/18931б4h "'ПОЛ'308"нне .Hnpy.ell108 р"з,,,60,,,.,,,, RЛ. по.,,,,, иrlIl-.е.енro8 Как только ваша проrpамма заrрузит веб..страницу с помощью модуля Requests, вы будете иметь в своем распоряжении ее НТМL--содержимое в виде одной строки. Ваши дальнейшие действия заключаются в том, чтобы определить, какая часть НТМL--содержимоrо соответствует объекту вашеrо интереса. И здесь вам На IJОМОЩЬ придyr инструменты разработчика, предостав- ляемые браузером. ПреДllОЛОЖИМ, 8Ы хотите написать проrpамму, осущест- вляющую сбор данных о проrнозах поroды на сайте http://weather.gov/. Прежде чем браться за написание кода, проведите небольшой эксперимент. Если вы посетите этот сайт и выполните поиск 110 ZIP-KOДY (почтовому ин- дексу) 94105, то сайт откроется на странице, представляющей информацию о поrоде для этой местности.
Автоматический сбор данных вИнтернете 313 Что если ва(: интересует сбор температурных данных по местности с данным ZIР-кодом? Щелкните правой кнопкой мыши в соответствующем месте страницы (или щелкните мышью при нажатой клавише <Сопtrоl>, если работаете в 05 Х) и выберите в открывшемся контекстном меню пункт Inspect Element (Исследовать элемент). Это приведет к открытию окна инструментов разработчика, в котором будет отображаться НТМL-код, от- ветственный за создание данной части страницы. На рис. 11.5 показано окно инструментов разработчика, открытое на НТМL-коде, отображающем температуру. ,!y; ] . 7-0ау Forecast 10" . .. 'I'f% .. с ,forecos1.weat'l."90V -:.;..;" .(" ;,'-;' "'!.- : ';':,.::.:: :. .:'J.:. о fI'!!! о,. -,7J 5, Ф. NATIONAL WEATHER SERVICE НО"" FOII:CAST РАЯ WБOIНtR v.l:АТ"'А SAfE1Y IЧFORМАТIOI4 CEIm:R ,.,ws 5tAROI AIЮUТ Lоиtfortlеl.tЬ, Clti. 61.or%llII.:o" ао Un...oonaЫy COId ........."'...1. .... с........ ond ...t8m U,5, "''''Utlh WodnиdlУ А pow..... cold 'ont has ulht8td in ......нonebly cdd l,m",.8Iu,fS 10 ch. С.nI,aI and ..,t,fII U S for.iiП)o thI& w"" F'rHllIIg t8mptr1tures \О. drJt into tht Онр SOUIh IIКI tc tht f.tId.A1IInIic thtouglt ':.:tdPnd8y п-oming 1'bis tiI tt drmaging to иn8lt1. 'leQ8lallOI'I '........OUI Frost Мi80nи вnd 'rtN. v".mingI "е in tlect Current Condltlons "'-1IWOI I 59°F ....."'" 65.. '/ind SOn<I ,!Д ........... IIД .,.,.,.... rFt1I.C: ......... IIД . ;...,.,.,c.:JO*",_ S'IN FRAНCI5CO DOWNTOWN (SfOC1) LaI э., !?'О56т Lon 122.ue.s=w [.1..... 1508 11- ip.",:?,:'.<!.-':"':_ :'::.:"::'E :::'.. 47;:J .:::.I ''''iI HI;r.li I.:,::;; Q. EIe'ncnts NetI'lo'Ork 5otlfc Tlffldlnl Profills Resourlto Au6ib ConнIe: " ,"=. о. . !) t..,,."tI!X" e....s'. pt. ._. .......:s- pt. ..:JIQ't t.. tl!.''.II'.II:'ipt. .........!&.-:йiDt..t:..i "а11 ir.'...$".jpt, .,,., ,:_. 1".QI!!S t . l - o( 111'11:1 !:cl."t о", . !t;'le"cllsPll1ilnOMJ' . . а1' : ., -'::11 - Jll -= '1!C1t .se.:tJ.cn cuent -.:cnаJ.t1cns' '.' id1- '., .'.,1..' .11 __::: -'".. ..e;8":':;'CC:.:::;> -:'J'_,,-,'cr.aitll'; ,. ..(11,' .;al.'dj'.-J1.l ,;u'.eM.(..Ii=.1t1on, . ...,J". <:1,-'"ne-thid.1.lt. ., <:I,.. 'J а: " 011. .hI1"'- . . ,1l<.' :, ..:o.-e'"c'-:.....,,,t. .r..., . j;:' .. 5t)1es' CcmpuIIId E1mt Lnt8*! -, .l"ent.stle { :. :.; . j .., '" -_ ... ",т. -,1" .s; : .(. : .ItU .tnt.c:tadltions Р I"'I;;' -'_I':' ",(. I.р.....". ,:41';' "a.l..' .. :11'. 'l"H" t;nt.thi"..,;1.lt .':-11,. ".:11'. .:].H..anl!.th1-d.t.st......:ri,. . . !J..: :li'. :11',,-' .c'''.st 'SoI!.:,..to.. .< '111'. . hlmI bod)' dlv.иnlf1 Clto.ct"ttr-tonttnt сР.dtf".'ull.WfJInt-с;опcИthИlS .Io;.M,4hi1d4Irst dlv.dIY.tI'" .rl'ec'5t"U.,,,,t. c.:';(t..:-.!t.. :';' 11 : ::: ;;;. Cid; I:: :; -.e4 .. .1I'Y"е(.:.t-..:....I"'mt- .:.1l 1". f Рис. 11.5. Инспектирование элемента, в котором храннrСII код, ответственный за отображение температуры, с помощью инструментов разработчика Окно инструментов разработчика позволяет увидеть, что за отображе- ние температуры отвечает следующий код: <р class="myforecastcurrent lrg">59°F</p>. Это именно то, что вы искали! Нетрудно заметить, что ин. формация о температуре содержится в элементе <р>, которому присвоен класс myforecastcurrentlrg. Теперь, коrда вы уже знаете, что именно He обходимо искать, модуль BeautifulSoup поможет вам найти искомый эле- мент в строке.
314 rлава 11 Синтаксическиii анапиз HTML с помощыо Beautiful Soup Beautiful Soup это модуль, предназначенный для извлечения инфор- мации из НТМL-cтраницы (причем roраздо более приспособленный для этой цели, чем реryлярные выражения). Имя модуля Beautiful Soup Ьв4 (от "Beautiful Soup, версия 4"). Для установки этоro модуля необходимо BЫ ПОЛНИть команду pip install beautifulsoup4 в командной строке. (Более подробные инструкции относительно установки модулей, разработанных третьими сторонами, приведены в приложении А.) Несмотря на то что при установке этоrо модуля используется имя beautifulsoup4, он импортируе ся с помощью инструкции import bs4. Приведенные в этой rлаве примеры использования модуля Beautiful Soup охватывают синтаксический анализ (т.е. исследование и идентифика цию отдельных элементов) НТМLайла, хранящеrося на Жестком диске. Откройте новое окно в файловом рсдакторе IDLE, введите приведенный ниже текст и сохраните ero в файле exaтple. htтl. Этот файл также достуйен на сайте http://nostarch . com/automatestuff/. <! Это файл примера example.html. > <html><hеаd><titlе>Заrоловок вебсайта</titlе></hеаd> <body> <р>Заrрузите мои книrи по <strong>Python</strong> на моем сайте <а href=''http://inventwithpython.com''></a>.</p> <р Сlаss="slоgап">Простой подход к изучению Python!</p> <р>Автор <span id="аuthоr">Эл Свейrарт</sрап></р> < /body></html > Как можно видеть, даже простой НТМLфайл включает MHOI'O ра:шич ных TeroB и атрибyrов, количество которых быстро увеличивается при переходе к сложным веб.сайтам. К счастью, модуль Beautiful Soup значи тельно упрощает работу с НТМL-разметкой. COIAIIHlle 06.еКТll Вeauti:f'ulSoup НII о,но.е НТМl Вам потребуется вызвать функцию bs4. BeautifulSoup () , передав ей строку, которая содержит НТМLкод, подлежащий анализу. Функция Ьв4. BeautifulSoup () возвращает объект BeautifulSoup. Введите в ИlIтерактив ной оболочке следующие Команды, предварительно убедившись в том, что ваш компьютер подключен к Интернету. »> import reque8t8, Ь84 »> re8 = reque8t8.qet('http://no8tarch.aoa') »> re8. rai88 for 8и tU8 ()
Автоматический сбор AaHHblX вИнтернете 315 »> noStarchSoup = bs4.ВeautifulSoup(res.text) »> type(noStarchSoup) <class 'bs4.BeautifulSoup'> Этот код сначала заrружает основную страницу веб--сайта No Starch Press с помощью вызова requests. get () , а затем передает атрибyr text oт вета функции Ьв4 . BeautifulSoup () . Возвращенный этой функцией объект BeautifulSoup сохраняется в переменной noStarchSoup. Вы также можете заrрузить НТМL-файл со cBoero жесткоrо диска, пере- дав функции bs4 . BeautifulSoup () объект File. Введите в интерактивной оболочке следующие команды (предварительно убедившись в том, что в рабочем каталоrе находится файл ехатрю. htтl). >>> 8X8J11.pleFile = open ( 'exaaple. htll1l' ) »> ехашрlеSоup = Ьs4.ВеаutifulSоup(ехашрlеFilе) >>> type(exaapleSoup) <class 'bs4.BeautifulSoup'> Получив объект BeautifulSoup, вы можете использовать ero Мt.'1'оды для поиска отдельных частей HTML-докумеНl'а. ПОН'К Jле_ен,,, , ПО_ОЩ61О _етод" select () Для получения элемента веб-страницы из объекта BeautifulSoup можно вызвать метод select () и передать ему строку CSS-cелектора, выбирающеrо искомый элемент. Селекторы напоминают реryлярные выражения: в обоих случаях задается шаблон поиска, но в случае селекторов поиск осуществля- ется в НТМL-страницах, а не в обычных текстовых строках. Полное обсуждение синтаксиса СSS-селекторов выходит за рамки дан- ной книrи (хорошее практическое руководство по использованию селекто- ров вы найдете на сайте http://www.nostarch.com/automatestuffresources). поэтому ниже мы оrраничимся лишь кратким их обзором. Примеры наи- более часто используемых селекторов нриведены в табл. 11.2. Та6nица 11.2. Примеры CSS-С8пекторов Сen8кrор,передаеаемыА методу select () Соответствие 'div' '#author' , . notice' 'div span' 'div > span' Все ал.менты <div> Элем.нт, атрнбут id KOToporo им.ет значение author Все алементы, атрибут class которых имеет значение notice Вс. ал.менты <sраП>,вnож.нны.ввnементы <di v> Вс. ал.м.нты <span>, вложенны. непосредственно в JЛементы <di v >, без каких бы то ни было ал.ментов между ними
316 rлава 11 (}кон/ча1luе там.. 11.2 С.,.8КТОР, передаваемый методу se18ct () 'input[пame] , СоответстВИ8 'input[type="button"] , Все элементы <input>, имеlOщие атрибут пате, независимо от era значеНИI Все элементы <input>, имеlOщие атрибут type со значением button Ра.зличные селекторы MOryr с.очетаться, обра:JУЯ сложные шаблоны. На- пример, вызову soup. select ( 'р #author') будет соответствовать любой эл мент, атрибут id Koтoporo имеет значение author, при условии, что этот элемент вложен в элемент <р>. Метод select () возвращает список объектов Tag, представляющих в Beautiful Soup НТМL-элементы. Этот список будет представлять по одному объекту Tag для каждоrо найденноrо совпадения в НТМL-коде объекта BeautifulSoup. Объекты Tag можно передавать функ ции str () для отображения TeroB HTML, которые они представляют. Значения типа Tag имеют атрибyr attrs, преДСl'авляющий все HTM1.- атрибуты данноrо Tera в виде словаря. Используя предыдущий файл exaтple. html, введите в интерактивной оболочке следующие команды. >>> import Ь.4 »> 8xamp18Fi18 - open('examp18.html') »> examp18Soup . Ь.4.ВеаutifulS0uр(ехащрleFi18.read(» > > > 818111. = exup18S0Up. .818ct ( , 'author' ) >>> type (818111.) <class 'list'> »> 1en(818111.) 1 »> type(818111.[0) <class 'bs4.element.Tag'> »> 818111.[0).q8tT8xt() 'Эл Свейrарт' »> .tr(818111.[0) '<span id="аlthоr">Эл СверТ'айт</sрап>, »> 818111.[0) .attr. { 'id': 'author' I Этот код извлекает элемент с id="author" из нашеrо примера HTML разметки. Вызов select ( '1tauthor' ) возвращает список всех элементов, удо- влетворяющих данному условию. Мы сохраняем этот (:писок объектов Tag в переменной elerns, и значение len (elems) указывает на то, что в списке име- ется только один такой элемент. Вызов метода getText () для этоrо элемеН- та возвращает содержащийся в нем текст, Т.е. внутреннюю НТМIJРазметку. Текст элемента это содержимое, заключенное между ero открывающим и закрывающим тсrами. В данном случае это строка' Эл СвейТ'арт I .
Автоматический сбор данных вИнтернете 317 При передаче функции str () элемента она возвращает строку, включаю- щую открывающий и закрывающий теrи вместе с текстом элемента. Haк нец, переменная attrs предоставляет словарь, содержащий имя атрибyrа, , id 1, и ero значение, 'author'. Также можно извлечь из объекта BeautifulSoup все элементы <р>. Введи- те в интерактивной оболочке следующие команды. »> pElems = examp!eSoup.8e18ct('p') »> 8tr(pElem8[0) '<р>Заrрузите мои книrи по <strong>Python</strong> на сайте <а href=''http://inventwithpython.com''></a>.</p>' >>> рЕ!8II18 [О) . getText О 'Заrрузите мои книrи по python на моем сайте.' »> 8tr(pElem8[1) '<р сlаss="slоgап">Простой подход к изучению Python!</p>' >>> рЕ 181118 [ 1] . getText () 'Простой подход к изучению Python! ' »> 8tr(pE18ll18[2]) '<р>Автор <span id="аuthоr">Эл СвеЙI'арт</sрап></р>' »> рЕ!8II18 [2) . q8tText() 'Автор Эл Свейrарт' На этот раз вызов метода select () предоставляет СIIИ(ОК с тремя сов- падениями, который мы сохраняем в переменной pElems. Последователь- но передавая функции str() элементы pElems[O], pElems(l] и pElems(2], мы отображаем каждый из этих элементов в виде строки, а вызов метода getText () для элементов позволяет отобразить их текстовое содержимое. ПолучеНllе AIIHHWX 113 "'pll6yro. Iле.е",II Метод get () объекта Tag упрощает доступ к значениям атрибyrов соот- ветствующеrо элемента. Метод принимает строку с именем атрибyrа и воз- вращает cro значение. Используя файл exaтple.html, введите в интерактив- ной оболочке следующие команды. »> import Ьв4 >>> 80UP == Ьв4. ВeautifulSoup (open ( 'example. htJD1' » »> 8panElem = 8oup.8elect('span') [О) >>> str (apanEl8ll1) '<span id="аuthоr">Эл СвеЙI'арт</sрап>, »> apanElem. q8t ( , id ' ) 'author' »> врanE!еш. qet ( 'несущес'l'В)'ЩИЙ c') == None True »> spanElem.attr8 {'id': 'author'}
318 r лава 11 Здесь мы используем метод select () ДЛЯ нахождения элементов <span> и сохранения первоrо из них в lIеременной spanElem. Передав методу get () имя атрибута, 'id t , мы получаем соответствующее значение, I author'. Проект: кнопка "Мне повезет" поисковика 600g18 Выполнив поиск в Google, я никоrда не начинаю сразу же просматри вать все полученные ссылки ОДНа :Ja дрyroй. Вместо этоrо я щелкаю средней кнопкой мыши (или левой кнопкой мыши при нажатой клавише <Ctrl» на нескольких первых ссылках для открытия каждой из них на новой вкладКе, чтобы просмотреть их позже. Поскольку поисковиком Google я пользуюсь довольно часто, описанный порядок поиска открытие браузера, поиск по заданному поисковому термину и ПОСJIедующие щелчки средней кнопкой мыши на нескольких ссылках оказывается достаточно трудоемким. Было бы неплохо, ес.ли бы Я Mor просто ввести поисковый термин в комаlЩIlОЙ строке, а компьютер автоматически открыл бы браузер с первыми несколь- кими результатами поиска, размещенными на отдельных вкладках. Давайте напишем сценарий, реализующий ЭТО пожелание. Вот что должна делать Такая I1porpaмMa: . получать из apryMeHToB командной строки ключевые слова, по кот<)- рым должен быть выполнен поиск; . извлекать страницу с результатами поиска; . размещать каждый результат на отдельной вкладке браузера. Это означает, что ваш код должен выполнять следующие действия: . читать apryMeHTbI командной строки из списка sys. argv; . извлекать страницу с результатами поиска с помощью МОДУЛЯ Req uests; . находить ссылки на каждый результат поиска; . вызывать функцию webbrowser . open () для открытия браузера. Откройте новое окно в файловом редакторе и сохраните ero в файле lucky.py. ШII' ,. Полученне "р'у.енfO' КО.IIНДНОЙ ,Т РОК. . Зllпро, пОНСIlО'ОН СТРllН."'" Прежде чем приступить к написанию кода, необходимо определить URL адрес страницы с результатами поиска. Взrлянув на адресную строку брау зера после выполнения поиска в Google, вы увидите там URIjaдpec вида https: / /www.gооglе.соm/sеаrсh?q=поисковыйтЕрмин.. Модуль Requests мо- жет заrрузить эту страницу, после чеrо вы сможете использовать Beautiful Soup для нахождения ссылок на результаты поиска в HTML. Наконец, вы
Автоматический сбор данных вИнтернете 319 используете модуль webbrowser для открытия этих ссылок в отдельных вкладках браузера. Введите в созданный файл следующий код. #! руthопЗ # lucky.py Открывает несколько результатов поиска с помощью # Google. import requests, зуз, webbrowser, Ьз4 рriпt('rуrлим...') * отображается при заrрузке страницы Google res = requests.get('http://google.com/search?q=' + , '.join(sys.argv[l:])) res.raiseforstatus() # ТООО: Извлечь первые несколько найденных ссылок. # ТООО: Открыть отдельную вкладку для каждоrо результата. Поисковые термины будут предостааляться пользователем с помощью apryMeHToB командной строки при запуске проrpаммы. Эти apryмeHTЫ бу дут сохраняться в виде строк в списке зуз. argv. Ш", J. ПОН'К .еех peJY.""'O' А теперь настал черед использовать модуль Beautiful Soup для Извле- чения нескольких первых ссылок на результаты поиска из заrруженноrо НТМLдокумента. Однако как определить, какой селектор следует исполь- зовать для выполнения этой работы? Например, вы не можете просто ото- брать все теrи <а>, поскольку в полученном НТМI_документе будет присyr- ствовать множество ссылок, не представляющих для пас интереса. Вместо ЭТОI'О вы должны инспектировать страницу с результатами поиска с по мощью инструментов разработчика, предоставляемых браузером, чтобы определить (електор, который отберет лишь нужные вам ссылки. Используя для поиска в Google строку Beautiful SQUp в качестве поисково- 1"0 термина, вы можете открыть окно инструментов разработчика и иссле- довать некоторые из элементов, содержащих ссылки. Эти элементы MOryr выrлядеть невероятно устрашающе, например так. <а href="/url?sa=t&ampirct=j&amp;q=&amp;esrc=s&amp;source=web&amp; :d=1&amp;cad=rja&amp;uact=B&amp;ved=OCCgQFjAA&amp;url=httр%ЗА%2F .:2Fwww.сrummу.соm%2Fsоftwаrе%2FБеаutifulSоuр%2F&аmр;еi= :HBVUXDD9КVyAShmYDwCw&amp;usg=AFQjCNHAxwplurFOBqg5cehWQEVKiTuLQ&a :!p;sig2=sdZu6WVIBIVSDrwhtworМA" onmousedown="return rwt(this,' ',' " ", '1', 'АFQjСNНАхwрlurFОБqg5сеhWQЕVКiТuLQ', 'sdZuБWVIВIVSDrwhtwоrМА', 'JCCgQFjAA',' ',' ',event)" datahref=..http://www.crummy.com/software/ EeautifulSoup/"><em>BeautifulSoup</em>: We called him Tortoise :есаизе he taught из.</а>
320 rnaBQ 11 Пусть вас не смущает вид элемента. Вам достаточно опредслить лишь шаблон, который яwшется общим для всех ссылок. Однако в этом :лементе <а> нет ничеrо, что позволило бы леrко отличить ero от элементов <а>, не ИМеющих никакоrо отношения к поиску. Добавьте в llporpaммy код, выделенный ниже полужирным шрифтом. #! руthопЗ # lucky.py Открывает несколько результатов поиска с помощью # Google. import requests, зуз, webbrowser, bs4 Пропущенный KOД f ИзВJIечеИИ8 первых несКOJJЪJCИX найденных ссыпок. 80UP = Ье4. ВeautifulSoup (res . text) . ОтlCpW'1'И8 О'l'дem.ной вкпa,цJCИ дпя: кurдoro peaYJlЬ'l'a'l'a. linkElems · soup.select('.r а') Однако, оrлядевшись в окрестностях элемента <а>, вы заметите элемент наподобие <hЗ class="r">. Просмотр оставшейся части I-IТМI..-кода позв ляет предположить, что класс r используетСЯ исключительно для ссылок на результаты поиска. Вам необязательно знать, что собой представляет CSs. класс r и что он делает. Вы просто будете использовать ero в качестве марке- ра элемента <а>, который ищете. Вы можете создать объект BeautifulSoup из HTMLoТeKcTa заrруженной страницы, а затем использовать селектор , . r а' ДЛЯ нахождения всех элементов <а>, которые вложены в элемент, имеющий С5S-класс r. Ш"r 3. Qr"pw'IIe 6р"узер" Р. "".Aoro 11' ре',.""'о, пOll'"'' Наконец, нам необходимо, чтобы проrрамма открыла в браузере отдель ные вкладки ДЛЯ каждоrо из результатов поиска. Добавьте в конце nporpaM мы следующий код. #! руthопЗ # lucky.py Открывает несколько результатов поиска с помощью # Google. import requests, sys, webbrowser, Ьз4 пропущенный KOД # Открытие отдельной вкладки для каждоrо результата. linkElems = soup.select('.r а') nwaOpen - IВin(S, len(linkElems»
Автоматический сбор данных вИнтернете 321 for i in range (numOpen) : webbrowser.open('http://google.coa' + linkEl&ms[i].get('href'» По умолчанию вы открываете с помощью модуля webbrowser новые вкладки для пяти результатов поиска. Однако пользователь Mor выполнить поиск, дающий менее пяти результатов. Вызов soup. se lect () возвращает список всех элементов, соответствующих селектору , . r а', поэтому коли чество открываемых ВКЛадок либо будет равно 5, либо будет определяться длиной указанноrо списка (в зависимости от Toro, что меньше). Встроенная функция Python min () возвращает наименьшее из передан ных ей целых или вещественных чисел. (Также существует встроенная функция шах ( ) , которая возвращает наибольшсе из чисел, которые были ей переданы.) Можно использовать функцию min () для Toro, чтобы выяснить, содержит ли СIlИСОК менее няти ссылок, и сохранить количество ссылок, подлеЖащих открытию, в переменной nurnOpen. После этоro можно ВЫПОk нить цикл for, вызвав функцию range (nurnOpen). На каждой итерации цикла вы открываете новую вкладку в браузере с по-- мощью вызова webbrowser . open () . Обратите внимание на то, что значение атрибута href в возвращеНIIЫХ элементах <а> не содержит начальную часть U RLaдpeca http://google.com, поэтому вы должны конкатснировать ее со строкой значения атрибyrа h re [. Теперь вы можете сразу открыть псрвые пять результатов поиска, выпол- неШ-lOrо, скажем, для ПОИСКО80rо термина Python prograттing tutarials, введя в командной строке команду lucky python progranuning tutorials! (Более Iюдробная информация о простом способе запуска проrрамм в разлИlНых операционных системах приведена в приложении Б.) ИАе. OT.ot.reIl6.0 tOJAII.". II.IIlIor,,'.6IX пРО"II"" Преимуществом описанноrо подхода к выполнению НОШ' ка является то, что он позволяет леrко открывать ссылки в новых вкладках для Toro, чтобы просмотреть их ПОЗДнее. Проrрамма, автоматически открывающая сразу несколько ссылок, ПОМОЖL>Т вам сэкономить время, ВЫIlОЛНЯЯ следующие операции: . открытие страниц всех заинтересовавших вас товаров после выпол- нения поиска на <:айте какоrо-либо интернет-маrазина, например Ашazоп; . открытие ссылок на все обзоры, посвященные одному и тому же пp<r дукту; . открытие рсзультирующих ссылок на фотоrpафии по<:ле выполнения поиска на каком-либо фотосайте, таком как Flickr или Iшgur.
322 rлава 11 Проект: эаrруэка всех комиксов на сайте XKCD в блоrах и на друrих реryлярно обновляемых веб-сайтах часто есть за- rлавная страница с последней публикацией и кнопка Previous (Предыдущая), которая ПрИ80ДИТ к предыдущей публикации. Предпоследний пост также снабжен аналоrичной кнопкой previous и Т.д., что обеспечивает возмож- ность последовательноrо прос.мотра публикаций на данном сайте в направ- лении от последней к первой. Если вы хотите скопировать содержимое сайта, чтобы прочесть ero позднее в автономном режиме, то можете вруч- ную пройтись по всем страницам и сохранить их. Но это занятие довольно скучное, поэтому поручим nporpaMMe выполнить эту работу вместо вас. XKCD это популярный веб-комикс с сайтом, структура KOToporo впи- сывается в эту схему (рис. 11.6). На заrлавной странице по адресу http: / / xkcd.com/ есть кнопка Prev (Предыдущий), щелкая на которой пользователь может переходить к предыдущим комиксам. Заrpузка всех комиксов вруч- ную длилась бы целую вечность, но вы можете написать сценарий, кото- рый выполнит эту работу за пару минут. Вот какие действия должна выполнять ваша проrрамма: . заrружать rлавнуlO страницу XKCD; . сохранять изображение комикса, отображаемоrо на данной странице; . выполнять переходы по ссылке Previous Comic (Предыдущий комикс); . повторять описанные действия, пока не будет ДОСТИПI}Т первый комикс. I .... . 1 П n . I 16 ют А ; :1: 1 CAtII ТEU.. PМtSICS, 1'00 :r IJORICED OUТ7JfE ktNl<S '''' CQlNn.IМ NIC.S АМ) IlSJImVIТY. AL . TOOk. А LDТ М ПfIМ<ING. EkJТ 1)ffS PtN::E fIWER $ 1JOfAN А SoIIS6 ""ТОЛ' CIFFЮ!:. il 1k9E's PWП'V OF' 1IМЕ' fOR "tИ,*-NG our НERE. 1;'VE RU'ltR.lVE> ЖIEQМ """" IN r.WO ..... ..." .... O .- '":'.::' fW E1'ERt.JIJY, R!)IЦ.Y. ANOТНEн $OrIE шн Nt'W ROU RX.I.OWED FRDМ ТНЕ IA$Т 'н '" 5fI'IPЦ: P1mI:RfI. о d.. o о о : . о o..Q t> о О А "'1'1-'1)4& 5u OF fIID ENouGtI 5М'Е, :I м МLE. 1\ 8UtLD А Щ"'VIВ. ;: i:Ч:;;i;t:"Н "EIOi' N&i'PA. o,r . SТ0N6 IS Ч tIEItr '1"IRRrQI rJf 1к. N. ONE OIW t sт.-.m:o l.IМt.IO OOWN of RCUS. о о о o - ...... ; ..:: ..:. : . ..: .:...::: ........ -..- .. .- -ь. SURt', IrS Roats .....SТE:AD OF' ШaRIafY, &п"'В Т14!:. Рис. 11.6. ХКСО "веб-комикс о романтике, сарказме, математике и языке"
Автоматический сбор данных вИнтернете 323 Это означает, что ваш код должен выполнять следующие операции: . заrружать страницы с помощью модуля requests; . находить URIaдpec изображения комикса для страницы, используя возможности Beautiful Soup; . заrружать и (охранять изображение комикса на жестком диске, ис пользуя метод itercontent (). . находить URLaдpcc ссылки Previous Comic и повторять действия. Откройте новое окно в файловом редакторе и сохраните ero в файле downloadXkcd.py. ш", ,. npoeKrllpO'''.IIe про,р"..'" Открыв в браузере окно инструментов разработчика и ПРОИНСllектиp<r вав элементы (траницы, вы обнаружите следующее: . URL-aдpec файла изображения комикса предоставляется атрибyrом href элемента <img>; . элемент <img> вложен в :лемент <div id="comic">; . кнопка Prev имеет НТМI,-атрибут rel со значением prev. . С кнопкой Prev nepBoro комикса связан URLaдpec http://xkcd. сот/#, указывающий на отсyrствие дрyrих предыдущих страниц. Введите в подrотовленный ранее файл следующий код. #! python3 # down1oadXkcd.py 3аrружает все комиксы хксо. import reque5t5, 05, Ь54 url = 'http://xkcd.com' # начальнь URLадрес os.makedirs('xkcd', existok=True) # сохраняем комикс в # папке. /xkcd while not url.endswith('#'): тооо: 3аrрузить страницу. .;. тооо: Найти URI.аДрес изображения комикса. тооо: 3аrрузить изображение. = тооо: Сохранить изображение в папке ./xkcd. тооо: Получить URLадрес кнопки Prev. rint ('rOTOBO. 1) у вас будет персменная url с начальным значением 'http://xkcd.com' , которое будет реryлярно обновляться (в цикле for) значением URL-aдpeca ссылки на кнопку Prev текущей страницы. На каждом шаrе цикла вы заrру жаете комикс, находящийся по адресу, который хранится впеременной url. Вы будете знать, что цикл следует завершить, коrда встретится значение ..:.::1, заканчивающееся (имволом '#'.
324 rлова 11 Файлы изображений будут заrружаться в папку xkcd, находящуюся в Te кущем рабочем каталоrс. Вызов os .rnakedirs () rарантирует существование этой папки, а именованный apryмeHT exist ok=True предотвращает возбуж- дение исключения в том случае, если эта папка уже существует. ОСТaJll>Ная часть КОДа это просто комментарии, описывающие оставшуюся часть проrраммы. ШII,2. 311'РРКII ,,'.аРIIН",,'" Приступим к реализации кода для заrрузки страницы. Добавьте в про- rpaMМY код, выделенный ниже полужирным шрифтом. #! руthопЗ # downloadXkcd.py Заrружает все комиксы хксо. import requests, os, bs4 url = 'http://xkcd.com' # начальный URLадрес os.makedirs('xkcd', exist ok=True) # сохраняем комикс в папке ./xkcd while not url.endswith('#'): * ЗаrpУ8ка страницы. рrint('Заrpужаеся страница %в...' , url) res · requests.qet(url) r8s.rais8forstatuB() soup . bs4.B8autifulSoup(res.text) # Тооо: Найти URLадрес изображения комикса. # тооо: Заrрузить изображение. # тооо: Сохранить изображение в папке ./xkcd. # TODO: Получить URLадрес кнопки Prev. print (IrOTOBO. 1) Здесь прежде Bcero выводится значение url, информирующее пользова- теля о том, по какому URL-aдpecy сейчас будет осуществляться зarpузка; для последующей заl'РУЗКИ изображения используется функция request. get ( ) модуля requests. Как вcerдa, если в процессе заrрузки что-то пойдет не так, вы немедленно сможете вызвать метод raise for status () объекта Response для возбуждения исключения и прекращения работы проrраммы. В пр тивном случае нужно будет создать объект BeautifulSoup на основе текста заrруженной страницы.
Автоматический сбор данных вИнтернете 325 Шаr 3. ПО"'1l . JarpYJlla .Jо6ражен". ком.",а Добавьте в nporpaMМY код, выделенный ниже полужирным шрифтом. #! руthопЗ # downloadXkcd.py Заvружает все комиксы хксо. import reque5t5, 05, bs4 пропущенный KOД f ПОИСК tЛU.а,цреса иеобрu8НИJI ХOМИJCса. comioElem. 8oup.8elect('_comic iшq') if comicElem == [): print('He удалось найти изображение ХOМИJCса. ') е188: comicUrl = comicElem[O).get('8rc') _ Эаrpуеиь иеображение. рrint('Эаrpужаеся иеобрв-ение %8... I % (comicVrl» А8 = reque8t8.get(comicUrl) re8.rai8efor8tatu8() # TODO: Сохранить изображение в папке ./xkcd. # тооо: Получить URLадрес кнопки Prev. print ( I rOTOBO. I ) Как показывают результаты инспектирования rлавной страницы XКCD с помощью инструментов разработчика, элемент <img> для изображения K микса помещен в элемент <di v>, атрибyr id KOToporo имеет значение сооос. Поэтому для извлечения нужноrо элемента <img> из объекта BeautifulSoup следует использовать селектор' #сооос img I . Некоторые страницы XKCD имеют специальное содержимое, не явля- ющееся простым файлом изображения. Никаких сложностей это не пред- ставляет вы просто пропускаете эти страницы. Если селектор вообще не найдет никаких элементов, то вызов 50ир. select (' #comic img') вернет пу- стой список. В этом <:лучае nporpaMMa может просто вывести сообщение об ошибке и продолжить выполнение, опустив зarpузку изображения. В противном случае селектор возвратит список, содержащий один эле- мент <img>. Вы можете получить значение атрибyrа src этоrо элемента <img> и передать ею функции requests. get () для заrpузки файла изображе- ния комикса.
326 rЛQва 11 Ш", 4. 'охр"..... .,06р"...". . по.,,, np'A"'AYIII'ro "о."",,, Добавьте в проrрамму код, выделенный ниже полужирным шрифтом. #! руthопЗ # downloadXkcd.py Заrружает все комиксы ХКСО. import rеquезtз, оз, Ьз4 ПрОПущеннЫЙ KOД f Сохранение иеображеНИR в пanке ./xkod. imaqeFile . open(08.path.join('xk.cd' ,o8.path. Ьа8еnаше(oomiсUrl», 'wb') for chunk in r88.iter content(lOOOOO): imaqeFile.write(Chunk) imageFile.clo8e() * Пo.nучени8 URLaдp8ca lCНопlCИ Prev. prevLink = 8oup.8elect('a[r81-"pr8v")') [О] url = 'http://xkod.CIOIII' + prevLink.qet( 'href') print ( I rOTOBO. ' ) К этому моменту файл изображения комикса хранится в переменной res. Вам следует записать данные изображения в файл на жестком диске. Функции ореп () необходимо передать имя локальноrо файла изображе- ния. Переменная comicUrl будет иметь значение наподобие' http: 11 imgs. xkcd. com/comics/heartbleedexplanation.png', которое, как вы, наверное, заметили, во мноroм напоминает путь к файлу. И действительно, вы може- те вызвать функцию os.path.basename () с comicUrl в качестве apryмeнтa. и она возвратит лишь последнюю часть URI" I heartbleed explana tion. png' . эту часть можно использовать в качестве имени файла при сохранении и бражения на жестком диске. Полученное имя можно соединить с именем вашей папки xkcd с помощью функции os.path.join () (ваша проrpамма ис пользует в обозначениях пyrи символы обратной косой черты при работе в Windows и косой черты при работе в 05 Х и linux). Получив имя файла, вы можете вызвать функцию open () для открытия HOBoro файла в режиме I wb' (запись двоичноrо файла). Как уже rоворило<ъ, для (охранения заrруженных файлов с помощью модуля Requests следует орrанизовать цикл по возвращаемому значению метода iter content () . Содержащийся в цикле код записывает порции дан- ных изображения (не более 100000 байт в каждой порции) в файл, после чеro вы закрываете файл. Теперь изображение сохранено на вашем жест. ком диске.
Автоматический сбор данных вИнтернете 327 Затем селектор' а [rel="prev"] , выбирает элемент <а>, атрибyr rel кото- poro имеет значение prev, и атрибyr href этоrо элемента используется для получения URL-aдpeca предыдущеrо комикса, который сохраняется в пере- мен ной url. После этоro ЦИIUI while вновь начинает весь процесс зarpузки для Данноrо комикса. Вывод nporpaмMbl выrлядит так. Заружается страница http://xkcd.com... Заружается изображение http://imgs.xkcd.com/comics/phonealarm.png... Заружается страница httр://хkсd.соm/lЗ58/... Заrружается изображение http://imgs.xkcd.com/comics/nro.png... Заружается страница http://xkcd.com/1357/... Заружается изображение http://imgs.xkcd.com/comics/free speech.png... Заrружается страница httр://хkсd.соm/135б/... Заrружается изображение http://imgs.xkcd.com/comics/ orbitalmechanics.png... Заrружается страница http://xkcd.com/1355/... Заrружается изображение http://imgs.xkcd.com/comics/ airplane message.png... Заrружается страница httр://хkсd.соm/lЗ54/... Заrружается изображение http://imgs.xkcd.com/comics/ heartbleedexplanation.png... пропущенный KOД Этот проект представляет собой неплохой пример nporpaмMbI, которая может автоматически выIIлнятьb переходы по ссылкам для сбора больших объемов данных в Интернете. О друrих возможностях модуля Beautiful Soup можно узнать из ero документации, которая находится по адресу http://www.crummy.com/software/BeautifulSoup/bs4/doc/. ИАе" оrио,,,rеn6ИО tОJАIIИИ. IIИllnоrИ9И6lХ ироrpll" Заrрузка страниц и переходы по ссылкам составляют основу мноrих про- rрамм, собирающих данные вИнтернете. Анмоrичные им npOl'paмMbI мо- ryr выполнять также (ледующие задачи: . резервное копирование Bcero сайта путем обхода всех ero ссылок; . копирование в(ех сообщений, опубликованных на форуме; . дублирование каталоrа товаров для продажи через интернет- маl'3.ЗИИ. Модули requests и BeautifulSoup сослужат вам отличную службу при условии, что вы можете определить URL-aдpec, который необходимо пере- дать функции request s. get () . Однако иноrДа сделать это довольно неле1'- ко. Кроме 1'01"0, может оказаться так, что сайт, навиrацию по которому вы хотите выполнить с помощью своей проrраммы, требует предварительно- 1"0 выполнения процедуры входа. Средством, которое предоставит вашим
328 rЛQВQ 11 проrраммам больше возможностей ДЛЯ решения таких сложных задач, Я& ляется модуль SeleniuJll. Управпение 6рауэером с помощыо модуп. Selenium Модуль SеlепiuПl предоставляс'r Pytllon возможность непосредственно управлять браузером пyrем IIроrраммной имитации щелчков на ссылках и прохождения процедуры входа, как если бы сам пользователь взаимодей ствовал со страницей. Этот модуль обеспечивает rораздо более rибкие ме- тоды взаимодействия с вебстраницами, чем модули Requests и Beautiful Soup. Но поскольку модуль Selenium запускает браузер, он работает нем но- ro медленнее, и орrанизация фоновой обработки с ero помощью требует больших хлопот, если, скажем, вам нужно Bcero лишь зarpузить некоторые файлы из Интернета. Процедура установки модулей, разработанных сторонними лицами, опи сана в приложении А. 3апуек 6раузера, yпpa...eMoro моду.е. Se/eпiиm Для работы с при мерами в этом разделе вам понадобится браузер Firefox. Это и есть браузер, которым вы будете управлять. Если Firefox у вас еще не установлен, можете бесплатно заrрузить ero, посетив сайт http: / / get firefox. сот/. Импортирование модулей для Selenium выполняется не совсем стандарт- Ным способом. Вместо команды import selenium необходимо выполнить ко- манду from selenium import webdriver. (Объяснение истинных причин тoro, почему ДЛЯ установки МОДУЛЯ SelепiUIl1 выбран подобный способ, выходит за рамки книrи.) После этою вы сможете запустить браузер Firefox с помощью Selenium, Введите в ИJперактивной оболочке следующие команды. »> froDl 8eleniwa iJDport "ebdriver »> brO"8er - webdriver.Firefox() »> type(brow8er) <class 'selenium.webdriver.firefox.webdriver.WebDriver'> »> bro"8er.get('ht://invent"ithpython.OODI') Вы увидите, что вызов webdriver. Firefox () приводит К запуску браузе- ра Firefox. Вызов функции type () с передачей ей значения, возвращенною вызовом webdriver. Firefox (), показывает, что типом данных это('() значе- ния является WebDriver. Л п()(:ледующий вызов метода browser.get ('http: / / inventwithpython.com') перенаправляет браузер на страницу http: // inventwi thpython. сот/. Окно вашеro браузера должно выrлядеть примерно так, как показано на рис. 11.7.
Автоматический сбор данных вИнтернете 329 . . ". .i.": '.:"':".'2!.::и-_.':,:,. ." . ''''''''' r......-"PI'thon.Сomi ," + . :..,"" ,7,':Iii!ii!I .,'. '!. ,)i'iI..ae vent with Python M , , . .._._............. ... ".,... J.5.O ShOII FlIt fdC SIIIII Dnvg opboos ...оn..... .... Python 3.5.0 (v3.5.0:374f501f45б7, sep 13 201 02:27:37) [МSC v.1900 б4 bit (AМDб4)] оп win3 Туре "copyright", "сrеditз. or "1iсеnзе()" fo ore information. »> 5elenium webdriver »> Ьrоwзеr = webdriver.Firefox() »> Ьrоwзеr.gеt(' »> .... ) Рис. 11.7. Выполнение вызовов webdriver. Firefox () и get () в IDLE приводнт к запуску 6раузера Firefox По"ек элементо. н" "р""""е Объекты WebDri ver имеют ДОВОЛЬНО большое количество методов, пред" l-tазначенных для поиска элементов на странице. Эти методы условно де- лятся на Две rруппы: findelement * () и findelernents * (). МетОДЫ rpуп" пы findelernent* () возвращают одиночный объект WebElement, который представляет первый из найденных на странице элементов, соответствую- щих запросу. Методы rруппы findelernents* () возвращают список объек" то в WebElement *, который представляет все элементы, соответствующие запросу. В табл. 11.3 ноказано несколько примеров вызова методов find element* () и findelements* () для объекта WebDriver, coxpaHeHHoro в пе- ременной browser. Табnица 11.3. Примеры ИСПОПЬЭО8аНИI методов об'Ьекта WebDri ver Дnl поиска !леМ8НТОВ Во,вращаемый 06ьект (список 06ьеКТОВ) W.bIlement Им. метода browser.find element Ьу class пamе(имя) Ьrоwsеr.fiпd=еlеrnеntsЬусlаssпamе(имя) browser.find element Ьу css sеlесtоr(селектор) Ьrоwsеr.fiпd=еlemепtsЬусsssеlесtоr(селектор) browser.find element Ьу id(id) browser.find=elementsbyid(id) Элементы, имеющие С55. lCЛасс с указанным именем Элементы, соответствующие указанному селектору С55 Элементы, соответствующие указанному значению атрибутаid
330 rлово 11 Oк01l: l taииe тafiя. 11.3 И.. метода browser.find elernent Ьу link text(TeKcT) browser.find=elernentsbylinktext(TeKcT) browser.find elernent Ьу partial link text(TeKcT) browser.findelernents Ьу partiaI link text (текст) browser.find elernent Ьу пarnе(имя) Ьrоwsеr.find=еlеrnепtsЬуnarnе(имя) browser.find elernent Ьу tag пarnе(имя) Ьrоwsеr.fiпd=еlеrnепtsЬуtаgnarnе(имя) Воавращaeмwi о6ьект (СПIlCOК 06мкr0в) weыement Элементы <а>, ПOJIноаыо совпадающие с указанным текстом Элементы <а>, содержащие указанный тека ЭлеМ8нты,СОАеР*Dе атрибут с указанным нменем Элементы с указанным нмен.м T.ra (реrистр н. УЧИТЫ80.теl)i нменам I а ' и , А' будет eooТleтcr8080ТЬ тer <а> За исключением rруппы методов * Ьу tag паrnе ( ) , apryMeHTbl всех м(...... ТОДов нечувствительны к реrистру, Если элементы, являющие(:я объектом поиска метода, отсутствуют на странице, модуль selenium возбуждает ис ключение NoSuchElernent. Если аварийное завершение работы проrpаммы в результате возбуждения исключения не входит в ваши планы, добавьте в код инструкции try и except. Получив объект WebElernent, вы сможете найти более полную инфор мацию о нем, читая атрибуты или вызывая методы, приведенные в табл. 11.4. Табnица 11.4. Атрибуты и MeTOДbI об8КТО WebElemen t Атрибут Иllи метод Описани. tagnarne gеtаttriЬutе(имя) text clear() is di splayed ( ) is enabled () is selected () location Имя Terai например, 'а' в случае !лемента <а> Значение атрибута с указанным нменем АПI AaHHoro !Аементо Т.ка, еодеРJI(QЙClВ мемеН18; например, 'hello' в случае !л.мента <span>hello</span> YAOIIleY Т8ка, введенный в текаовом пол. иnи текаовой облаCJИ Возвращает знач.ни. True, .сли мемент видимый, инач. False Возвраща.т знач.ние True Д/II м.меНТОВВ80да, еслн м.мент аКТИlИзирован. иначе False Возвращает значение True АПI флажка или переключатеЛI, .сли !л.мент выбран. иначе False CIIовар.. с ключами 'х' и 'у' АПI позиции !лем-нто на аранице
Автоматический сбор данных вИнтернете 331 Например, откройте новое окно в файловом редакторе и введите сле- ДУЮЩИЙ код. from 8e1enium import webdriver brOW8er = webdri ver. Fir8fox () brow8er.qet('http://inventwithpython.coa') иу: е1еа = brow8er.find e1ement ьу с1а88 nаше('Ьооkсоver') print ( 'Найден эпемен'l' <%8> с даннwи Именем кпасса l' % (elem.taqname» except: print('He удалось найти эпемен'l' с даннwи именем класса.') Этот код открывает Firefox и пере направляет ero по заданному URL- адресу. На открывшейся странице выполняется поиск элементов с классом 'bookcover' , и если такой элемент обнаружен, то на экран выводится имя Tera, определяемое атрибyrом tag пате. В противном случае выводится дру- rое сообщение. Для данной проrраммы вывод будет таким: Найден элемент <img> с данным именем класса! Мы нашли элемент с именем класса' bookcover' и именем Tera 'img'. Щелчок н" "р"н""е Объекты WebElement, возвращаемые методами findelement* () и find elements * () , имеют метод click () , имитирующий щелчок мышью на эле- менте. Этот метод можно использовать для перехода по ссылке, выбора с помощью переключателя, щелчка на кнопке Submit (Отправить) или ини- циирования любоl'О друrоrо действия, которое может быть запущено щелч- ком на элементе. Ilапример, введите в интерактивной оболочке следующие команды. »> from 8818nium import webdriver »> brow88r · W8bdriver.Firefox() »> brow8er.qet('http://inventwithpython.cam') »> 1inkE1em · brow8er.find e1ement ьу link teхt('Чи'l'а'l'Ь онпайн') >>> type (linkE1em) <class 'seleniиm.webdriver.remote.webelement.WebElement'> >>> 1inkE1em. c1ick () * перей'1'и по ссыпке "Чи'l'а'l'Ь онпайн"
332 rлава 11 В Данном случае мы открываем Firefox на странице h t tp : / / inventwithpython. сот/, получаем объект WebElement для элемента <а> с тек- стом Чuтат'ь о'Н.Лайи, а затем имитируем щелчок На этом элементе. Все про- исходит так, как если бы вы сами щелкнули на ссылке, заставляя браузер открыть соответствующую страницу. 311nOnHeHlle " отпр"вк" ФОР. Отправка нажатий клавиш при нахождении фокуса ввода в текстовом поле сводится к нахождению на странице элемента <input> или <textarea>, соответствующсrо данному полю, и последующему вызову меТОДа send keys ( ) . Например, введите в интерактивной оболочке следующий код. »> from 8eleniuz import webdriver »> brow8.r . w8bdriver.Firefox() »> brow8.r.qet('http://gmail.com') »> emailElem . browBer.find elem.nt Ьу id('Email') »> emailEl.m.8end kеУ8('фИКТИВНЫЙ адрес email@gmail.com') »> pa88wordElem =bro"8er.find elSment ьу id('Pa88"d') »> pa88"ordElem.8.nd key8 (' 12345') »> pa88wordElem.8uЬmIt() Если только служба Gшаil не изменила идентификаторы id текстовых полей Username (Имя пользователя) и Passworcl (Пароль) с момента выхода в свет данной Кllиrи, этот код заполнит указанные текстовые поля пред ставленным текстом. (Для проверки значения id вы всеrда можете вое. пользоваться средством инспектирования элсментов, предоставляемым браузером. ) Вызов метода submi t () для любоro элемента будет иметь тот жс эффект, что и щелчок На кнопке Submit (Отправить) формы, содержащей данный элемент. (Вы также моши вызвать emailElem. subrnit () , и код выпол. нил бы то же самое.) Ornpll.KII КОДОВ ,ne"HIIII6HWX КЛII.IIШ SelcniUln включает модуль для обработки нажатий клавиш, которые нельзя ввести в виде строки, функционирующий во мноroм подобно экра- нированным символам. Соответствующие значения хранятся в модуле selenium. webdr i ver. соттоп. keys. Поскольку это довольно длинное имя, ro- раздо проще выполнить в начале проrраммы инструкцию from sеlепiшn. webdriver. common. keys import Keys. После этоrо вы сможете использовать имя Keys везде, rде обычно вы использовали бы запись selenium.webdriver. соттоп. keys.
Автоматический сбор данных вИнтернете 333 Та6nица 11.5. Часто иcnаnьэуемые "еременные модуn...lеnium.webdrivеr.сошmоn.kеУ8 Атри'Ут Keys.DOWN, Keys.UP, Keys.LEFТ, Keys . RIGHT Keys.ENTER, Keys.RETURN Кеуз.НОМЕ, Keys.END, Keys.PAGE DOWN, Keys.PAGEUP Keys.ESCAPE, Keys.BACKSPACE, Keys.DELETE Onисаи... l<JIаlИWИ со ар8llками l<JIаlИWИ <Enter> и <R.tum> l<JIаlИWИ <Ноте>, <End>, <PageDown> и <Pag.Up> l<JIаlИWИ <Ек>, <Backspace> и <D.lete> Keys.Fl, Keys.F2, . . Keys.F12 Кеуз.ТАВ l<JIаlИWИ от <F 1> АО <F2> l<JIаlИwa <ТаЬ> Например, если курсор в данный момент не находится в текстовом поле, нажатие клавиш <Ноше> и <End> выполняет прокрyrку в начало или конец страницы соответственно. Введите в интерактивной оболочке следующий код и обратите внимание на то, как вызовы метода send keys () приводят К прокрyrке страницы. »> from 8elenium import webdriver »> from sеlеnium.webdriver.сошmоn.kеУ8 import КеУ8 >;> > bro,,8er · "ebc:iri ver . Firefox () »> bro''8er.gвt('http://n08tarch.com') »> htmlElem = bro"8er.find element Ьу tag name('html') >>> htmlElem.8end key8 (КeY87END) # ПроRpУ'l'U В конец >>> htmlElem. 8snd:key8 (КеУ8. НОМЕ) * npoRPy'l'I<& В начало Ter <html> основной в НТМL-файлах: все содержимое НТМLайла за ключено между теrами <html> и </:-tt.!'!tl>. Вызов browser. findelementbytag пате ( , h tml') обеспечивает удобную возможность отправки кодов клавиш целой всб-странице. Это приrодится вам, если, например, новое содержи Мое заrружается сразу же, как только вы прокручиваете страницу до конца. Щ'ЛЧКII НII кнопКIIХ 6,IIУI'," Модуль Selenium также может имитировать щелчки на различных кноп ках брау