Глава 9. Файлы и исключения
гда это называется «структурным выводом» кода JSON. Если вызов функции dumps включает ключевой аргумент indent, строка содержит символы новой строки и отступы для структурного вывода — аргумент indent также может использоваться с функцией dump при выводе в файл:
In [9]: with open('accounts.json', 'r') as accounts:
...: print(json.dumps(json.load(accounts), indent=4))
...:
{
"accounts": [
{
"account": 100,
"name": "Jones",
"balance": 24.98
},
{
"account": 200,
"name": "Doe",
"balance": 345.67
}
]
}
9.6. Вопросы безопасности: сериализация и десериализация pickle
Модуль pickle стандартной библиотеки Python может сериализовать объекты в формат данных, специфический для Python. Будьте осторожны: в документации Python приводятся следующие предупреждения относительно pickle:
ØØ
«Файлы pickle могут подвергаться несанкционированным изменениям. Если вы получаете необработанный файл pickle по Сети, не доверяйте ему! Он может содержать вредоносный код, который выполнит произвольный код Python при попытке преобразования из формата pickle. С другой стороны, если вы выполняете запись и чтение pickle самостоятельно, то вы в безопасности (разумеется, при условии, что посторонние не имеют доступа к файлу pickle1)»;
ØØ
«Pickle — протокол, позволяющий сериализовать объекты Python произвольной сложности. Соответственно, он специфичен для Python и не может использоваться для взаимодействия с приложениями, написанны1
https://wiki.python.org/moin/UsingPickle.
9.7. Дополнительные замечания по поводу файлов 371
ми на других языках. Он также небезопасен по умолчанию: десериализация данных pickle из ненадежного источника может привести к выполнению произвольного кода, если данные были сгенерированы опытным злоумышленником»1.
Мы не рекомендуем использовать pickle, но этот модуль использовался в течение многих лет, поэтому, скорее всего, вы встретите его в унаследованном коде — старом коде, который часто уже не сопровождается.
9.7. Дополнительные замечания по поводу файлов
В табл. 9.1 приведена сводка режимов открытия текстовых файлов, включая уже описанные ранее режимы для чтения и записи. Режимы записи и присоединения создают файл, если он не существует. Если файл не существует, то режимы чтения выдают ошибку FileNotFoundError. Каждый режим текстового файла имеет соответствующий режим двоичного файла, обозначаемый суффиксом b, например 'rb' или 'wb+'. Эти режимы могут использоваться для чтения или для записи двоичных файлов: графики, аудио, видео, сжатых ZIP-файлов и др.
Таблица 9.1. Основные режимы открытия текстовых файлов
Режим
Описание
'r'
Текстовый файл открывается для чтения. Используется по умолчанию, если режим открытия файла не указан при вызове open
'w'
Текстовый файл открывается для записи. Существующее содержимое файла удаляется
'a'
Текстовый файл открывается для присоединения данных. Если файл не существует, то он создается. Новые данные записываются в конец файла
'r+'
Текстовый файл открывается для чтения и записи
'w+'
Текстовый файл открывается для чтения и записи. Существующее содержимое файла удаляется
'a+'
Текстовый файл открывается для чтения и присоединения данных. Новые данные записываются в конец файла. Если файл не существует, то он создается
1 https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files.
372 Глава 9. Файлы и исключения
Другие методы объектов файлов
Укажем еще несколько полезных методов объектов файлов:
ØØ
Для текстового файла метод read возвращает строку с количеством символов, заданным целочисленным аргументом метода. Для двоичного файла метод возвращает заданное количество байтов. Если аргумент не задан, то метод возвращает все содержимое файла.
ØØ
Метод readline возвращает одну строку текста в строковой форме, включая символ новой строки (если он присутствует). При достижении конца файла метод возвращает пустую строку.
ØØ
Метод writelines получает список строк и записывает его содержимое в файл.
Классы, используемые Python для создания объектов файлов, определяются в модуле io стандартной библиотеки Python (https://docs.python.org/3/library/io.html).
9.8. Обработка исключений
При работе с файлами могут происходить исключения различных типов, например:
ØØ
Исключение FileNotFoundError происходит при попытке открытия несуществующего файла для чтения в режиме 'r' или 'r+'.
ØØ
Исключение PermissionsError происходит при попытке выполнения операции, для которой у вас нет необходимых разрешений. Например, это может произойти при попытке открыть файл, недоступный для вашей учетной записи, или создать файл в каталоге, для которого у вашей учетной записи отсутствует разрешение записи (например, в каталоге, в котором хранится ОС).
ØØ
Исключение ValueError (с сообщением об ошибке 'I/O operation on closed file.') происходит при попытке записи в ранее закрытый файл.
9.8.1. Деление на нуль и недействительный ввод
Вернемся к двум исключениям, которые были описаны ранее в этой книге.
9.8. Обработка исключений 373
Деление на нуль
Вспомним, что попытка деления на 0 приводит к ошибке ZeroDivisionError:
In [1]: 10 / 0
------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
in ()
----> 1 10 / 0
ZeroDivisionError: division by zero
In [2]:
В этой ситуации интерпретатор выдает исключение типа ZeroDivisionError. При возникновении исключения IPython завершает фрагмент, выводит трассировку исключения и последующее приглашение In[] для ввода следующего фрагмента. Если исключение возникает в сценарии, то этот сценарий завершается, а IPython выводит трассировку.
Недействительный ввод
Функция int выдает исключение ValueError при попытке преобразования в строку целого числа, которое не представляет число (например, 'hello'):
In [2]: value = int(input('Enter an integer: '))
Enter an integer: hello
------------------------------------------------------------------------
ValueError Traceback (most recent call last)
in ()
----> 1 value = int(input('Enter an integer: '))
ValueError: invalid literal for int() with base 10: 'hello'
In [3]:
9.8.2. Команды try
Теперь посмотрим, как обработать возникшее исключение, чтобы программа могла продолжить выполнение. Возьмем следующий сценарий и пример выполнения. Его цикл пытается получить два целых числа от пользователя, а затем выводит первое число, разделенное на второе. Сценарий использует механизм обработки исключений для перехвата и обработки любых воз374