Книга Изучаем Python
Скачать 4.68 Mb.
|
ПРИМЕЧАНИЕ Для запуска этой программы (и многих других примеров, приведенных ниже) необходимо загру- зить ресурсы по адресу https://www .nostarch .com/pythoncrashcourse/ . 192 Глава 10 • Файлы и исключения Проверка дня рождения Меня всегда интересовало, не встречается ли мой день рождения среди цифр числа «пи»? Воспользуемся только что созданной программой для проверки того, входит ли запись дня рождения пользователя в первый миллион цифр. Для этого можно записать день рождения в виде строки из цифр и посмотреть, присутствует ли эта строка в pi_string : filename = 'pi_million_digits.txt' with open(filename) as file_object: lines = file_object.readlines() pi_string = '' for line in lines: pi_string += line.rstrip() birthday = input("Enter your birthday, in the form mmddyy: ") if birthday in pi_string: print("Your birthday appears in the first million digits of pi!") else: print("Your birthday does not appear in the first million digits of pi.") В точке программа запрашивает день рождения пользователя, а затем в точке проверяет вхождение этой строки в pi_string . Пробуем: Enter your birthdate, in the form mmddyy: 120372 Your birthday appears in the first million digits of pi! Оказывается, мой день рождения встречается среди цифр «пи»! После того как данные будут прочитаны из файла, вы сможете делать с ними все, что сочтете нужным. УПРАЖНЕНИЯ 10-1 . Изучение Python: откройте пустой файл в текстовом редакторе и напишите несколько строк текста о возможностях Python . Каждая строка должна начинаться с фразы: «In Python you can…» Сохраните файл под именем learning_python .txt в каталоге, использованном для примеров этой главы . Напишите программу, которая читает файл и выводит текст три раза: с чтением всего файла, с перебором строк объекта файла и с сохранением строк в списке с последующим выводом списка вне блока with . 10-2 . Изучение C: метод replace() может использоваться для замены любого слова в строке другим словом . В следующем примере слово ‘dog’ заменяется словом ‘cat’: >>> message = "I really like dogs." >>> message.replace('dog', 'cat') 'I really like cats.' Прочитайте каждую строку из только что созданного файла learning_python .txt и замените слово Python названием другого языка, например C . Выведите каждую измененную строку на экран . Запись в файл 193 Запись в файл Один из простейших способов сохранения данных — запись в файл. Текст, записанный в файл, останется доступным и после закрытия терминала с вы- водом вашей программы. Вы сможете проанализировать результаты после завершения программы или передать свои файлы другим. Вы также сможете написать программы, которые снова читают сохраненный текст в память и ра- ботают с ним. Запись в пустой файл Чтобы записать текст в файл, необходимо вызвать open() со вторым аргументом, который сообщает Python, что вы собираетесь записывать данные в файл. Чтобы увидеть, как это делается, напишем простое сообщение и сохраним его в файле (вместо того чтобы просто вывести на экран): write_message.py filename = 'programming.txt' with open(filename, 'w') as file_object: file_object.write("I love programming.") При вызове open() в этом примере передаются два аргумента . Первый аргумент, как и прежде, содержит имя открываемого файла. Второй аргумент 'w' сообщает Python, что файл должен быть открыт в режиме записи. Файлы можно открывать в режиме чтения ( 'r' ), записи ( 'w' ), присоединения ( 'a' ) или в режиме, допуска- ющем как чтение, так и запись в файл ( 'r+' ). Если аргумент режима не указан, Python по умолчанию открывает файл в режиме только для чтения. Если файл, открываемый для записи, еще не существует, функция open() автома- тически создает его. Будьте внимательны, открывая файл в режиме записи ( 'w' ): если файл существует, то Python уничтожит его данные перед возвращением объекта файла. В точке метод write() используется с объектом файла для записи строки в файл. Программа не выводит данные на терминал, но, открыв файл programming .txt , вы увидите в нем одну строку: programming.txt I love programming. Этот файл ничем не отличается от любого другого текстового файла на вашем ком- пьютере. Его можно открыть, записать в него новый текст, скопировать/вставить текст и т. д. ПРИМЕЧАНИЕ Python может записывать в текстовые файлы только строковые данные . Если вы захотите сохра- нить в текстовом файле числовую информацию, данные придется предварительно преобразовать в строки функцией str() . 194 Глава 10 • Файлы и исключения Многострочная запись Функция write() не добавляет символы новой строки в записываемый текст. А это означает, что если вы записываете сразу несколько строк без включения символов новой строки, полученный файл может выглядеть не так, как вы рассчитывали: filename = 'programming.txt' with open(filename, 'w') as file_object: file_object.write("I love programming.") file_object.write("I love creating new games.") Открыв файл programming .txt , вы увидите, что две строки «склеились»: I love programming.I love creating new games. Если включить символы новой строки в команды write() , текст будет состоять из двух строк: filename = 'programming.txt' with open(filename, 'w') as file_object: file_object.write("I love programming.\n") file_object.write("I love creating new games.\n") Результат выглядит так: I love programming. I love creating new games. Для форматирования вывода также можно использовать пробелы, символы табу- ляции и пустые строки по аналогии с тем, как это делалось с выводом на терминал. Присоединение данных к файлу Если вы хотите добавить в файл новые данные вместо того, чтобы перезаписывать существующее содержимое, откройте файл в режиме присоединения. В этом слу- чае Python не уничтожает содержимое файла перед возвращением объекта файла. Все строки, выводимые в файл, будут добавлены в конец файла. Если файл еще не существует, то Python автоматически создаст пустой файл. Изменим программу write_message .py и дополним существующий файл programming .txt новыми данными: write_message.py filename = 'programming.txt' with open(filename, 'a') as file_object: file_object.write("I also love finding meaning in large datasets.\n") file_object.write("I love creating apps that can run in a browser.\n") В точке аргумент 'a' используется для открытия файла в режиме присоединения (вместо перезаписи существующего файла). В точке записываются две новые строки, которые добавляются к содержимому programming .txt : Исключения 195 programming.txt I love programming. I love creating new games. I also love finding meaning in large datasets. I love creating apps that can run in a browser. В результате к исходному содержимому файла добавляется новый текст. УПРАЖНЕНИЯ 10-3 . Гость: напишите программу, которая запрашивает у пользователя его имя . Введенный ответ сохраняется в файле с именем guest .txt . 10-4 . Гостевая книга: напишите цикл while, который в цикле запрашивает у пользователей имена . При вводе каждого имени выведите на экран приветствие и добавьте строку с со- общением в файл с именем guest_book .txt . Проследите за тем, чтобы каждое сообщение размещалось в отдельной строке файла . 10-5 . Опрос: напишите цикл while, в котором программа спрашивает у пользователя, по- чему ему нравится программировать . Каждый раз, когда пользователь вводит очередную причину, сохраните текст его ответа в файле . Исключения Для управления ошибками, возникающими в ходе выполнения программы, в Python используются специальные объекты, называемые исключениями. Если при возникновении ошибки Python не знает, что делать дальше, создается объект исключения. Если в программу включен код обработки исключения, то выполне- ние программы продолжится, а если нет — программа останавливается и выводит трассировку с отчетом об исключении. Исключения обрабатываются в блоках try - except . Блок try - except приказывает Python выполнить некоторые действия, но при этом также сообщает, что нужно де- лать при возникновении исключения. С блоками try - except ваши программы будут работать даже в том случае, если что-то пошло не так. Вместо невразумительной трассировки выводится понятное сообщение об ошибке, которое вы определяете в программе. Обработка исключения ZeroDivisionError Рассмотрим простую ошибку, при которой Python инициирует исключение. Ко- нечно, вы знаете, что деление на ноль невозможно, но мы все же прикажем Python выполнить эту операцию: division.py print(5/0) Из этого ничего не выйдет, поэтому на экран выводятся данные трассировки: Traceback (most recent call last): File "division.py", line 1, in ZeroDivisionError: division by zero 196 Глава 10 • Файлы и исключения Ошибка, упоминаемая в трассировке — ZeroDivisionError , — является объектом исключения. Такие объекты создаются в том случае, если Python не может выпол- нить ваши распоряжения. Обычно в таких случаях Python прерывает выполнение программы и сообщает тип обнаруженного исключения. Эта информация может использоваться в программе; по сути вы сообщаете Python, как следует поступить при возникновении исключения данного типа. В таком случае ваша программа будет подготовлена к его появлению. Блоки try-except Если вы предполагаете, что в программе может произойти ошибка, напишите блок try - except для обработки возникающего исключения. Такой блок приказывает Python выполнить некоторый код, а также сообщает, что нужно делать, если при его выполнении произойдет исключение конкретного типа. Вот как выглядит блок try - except для обработки исключений ZeroDivisionError : try: print(5/0) except ZeroDivisionError: print("You can't divide by zero!") Команда print(5/0) , порождающая ошибку, находится в блоке try . Если код в бло- ке try выполнен успешно, то Python пропускает блок except . Если код в блоке try порождает ошибку, то Python ищет блок except с соответствующей ошибкой и выпускает код в этом блоке. В этом примере код блока try порождает ошибку ZeroDivisionError , поэтому Python ищет блок except с описанием того, как следует действовать в такой ситу- ации. При выполнении кода этого блока пользователь видит понятное сообщение об ошибке вместо данных трассировки: You can't divide by zero! Если бы за кодом try - except следовал другой код, то выполнение программы продолжилось, потому что мы объяснили Python, как обрабатывать эту ошибку. В следующем примере обработка ошибки позволяет программе продолжить вы- полнение. Использование исключений для предотвращения аварийного завершения программы Правильная обработка ошибок особенно важна в том случае, если программа должна продолжить работу после возникновения ошибки. Такая ситуация часто встречается в программах, запрашивающих данные у пользователя. Если програм- ма правильно среагировала на некорректный ввод, она может запросить новые данные после сбоя. Создадим простой калькулятор, который выполняет только операцию деления: Исключения 197 division.py print("Give me two numbers, and I'll divide them.") print("Enter 'q' to quit.") while True: first_number = input("\nFirst number: ") if first_number == 'q': break second_number = input("Second number: ") if second_number == 'q': break answer = int(first_number) / int(second_number) print(answer) Программа запрашивает у пользователя первое число first_number , а затем, если пользователь не ввел q для завершения работы, запрашивает второе число second_number . Далее одно число делится на другое для получения результата answer . Программа никак не обрабатывает ошибки, так что попытка деления на ноль приводит к ее аварийному завершению: Give me two numbers, and I'll divide them. Enter 'q' to quit. First number: 5 Second number: 0 Traceback (most recent call last): File "division.py", line 9, in ZeroDivisionError: division by zero Конечно, аварийное завершение — это плохо, но еще хуже, если пользователь увидит данные трассировки. Неопытного пользователя они собьют с толку, а при сознательной попытке взлома злоумышленник сможет получить из них больше информации, чем вам хотелось бы. Например, он узнает имя файла программы и увидит некорректно работающую часть кода. На основании этой информации опытный хакер иногда может определить, какие атаки следует применять против вашего кода. Блок else Для повышения устойчивости программы к ошибкам можно заключить строку, выдающую ошибки, в блок try - except . Ошибка происходит в строке, выполняющей деление; следовательно, именно эту строку следует заключить в блок try - except Данный пример также включает блок else . Любой код, зависящий от успешного выполнения блока try , размещается в блоке else : print("Give me two numbers, and I'll divide them.") print("Enter 'q' to quit.") while True: first_number = input("\nFirst number: ") if first_number == 'q': 198 Глава 10 • Файлы и исключения break second_number = input("Second number: ") try: answer = int(first_number) / int(second_number) except ZeroDivisionError: print("You can't divide by 0!") else: print(answer) Программа пытается выполнить операцию деления в блоке try , который включает только код, способный породить ошибку. Любой код, зависящий от успешного выполнения блока try , добавляется в блок else . В данном случае, если операция деления выполняется успешно, блок else используется для вы- вода результата . Блок except сообщает Python, как следует поступать при возникновении ошибки ZeroDivisionError . Если при выполнении команды из блока try происходит ошибка, связанная с делением на 0, программа выводит понятное сообщение, которое объясняет пользователю, как избежать подобных ошибок. Выполнение программы продолжается, и пользователь не сталкивается с трассировкой: Give me two numbers, and I'll divide them. Enter 'q' to quit. First number: 5 Second number: 0 You can't divide by 0! First number: 5 Second number: 2 2.5 First number: q Блок try - except - else работает так: Python пытается выполнить код в блоке try В блоках try следует размещать только тот код, при выполнении которого может возникнуть исключение. Иногда некоторый код должен выполняться только в том случае, если выполнение try прошло успешно; такой код размещается в блоке else Блок except сообщает Python, что делать, если при выполнении кода try произо- шло определенное исключение. Заранее определяя вероятные источники ошибок, вы повышаете надежность своих программ, которые продолжают работать даже при вводе некорректных данных или при недоступности ресурсов. Ваш код оказывается защищенным от случайных ошибок пользователей и сознательных атак. Обработка исключения FileNotFoundError Одна из стандартных проблем при работе с файлами — отсутствие необходимых файлов. Тот файл, который вам нужен, может находиться в другом месте, в имени файла может быть допущена ошибка, или файл может вообще не существовать. Все эти ситуации достаточно прямолинейно обрабатываются в блоках try - except Исключения 199 Попробуем прочитать данные из несуществующего файла. Следующая програм- ма пытается прочитать содержимое файла с текстом «Алисы в Стране чудес», но я не сохранил файл alice .txt в одном каталоге с файлом alice .py : alice.py filename = 'alice.txt' with open(filename) as f_obj: contents = f_obj.read() Прочитать данные из несуществующего файла нельзя, поэтому Python выдает исключение: Traceback (most recent call last): File "alice.py", line 3, in FileNotFoundError: [Errno 2] No such file or directory: 'alice.txt' В последней строке трассировки упоминается FileNotFoundError : это исключение выдается в том случае, если Python не может найти открываемый файл. В данном примере функция open() порождает ошибку, и, чтобы обработать ее, блок try на- чинается перед строкой с вызовом open() : filename = 'alice.txt' try: with open(filename) as f_obj: contents = f_obj.read() except FileNotFoundError: msg = "Sorry, the file " + filename + " does not exist." print(msg) В этом примере код блока try выдает исключение FileNotFoundError , поэто- му Python ищет блок except для этой ошибки. Затем выполняется код этого блока, в результате чего вместо трассировки выдается доступное сообщение об ошибке: Sorry, the file alice.txt does not exist. Если файл не существует, программе больше делать нечего, поэтому код обработ- ки ошибок почти ничего не добавляет в эту программу. Доработаем этот пример и посмотрим, как обработка исключений помогает при работе с несколькими файлами. Анализ текста Программа может анализировать текстовые файлы, содержащие целые книги. Многие классические произведения, ставшие общественным достоянием, доступны в виде простых текстовых файлов. Тексты, использованные в этом разделе, взяты с сайта проекта «Гутенберг» (http://gutenberg.org/). На этом сайте хранится под- 200 Глава 10 • Файлы и исключения борка литературных произведений, не защищенных авторским правом; это превос- ходный ресурс для разработчиков, которые собираются работать с литературными текстами в своих программных проектах. Прочитаем текст «Алисы в Стране чудес» и попробуем подсчитать количество слов в тексте. Мы воспользуемся методом split() , предназначенным для построения списка слов на основе строки. Вот как метод split() работает со строкой, содер- жащей только название книги: >>> title = "Alice in Wonderland" >>> |