Содержание страницы
7. Ввод и вывод¶Input and Output
Есть несколько способов отображения вывода программы: данные можно выводить в удобочитаемой форме или записывать в файл для последующего использования. В этой главе рассматриваются некоторые из возможностей.
7.1. Расширенное форматирование вывода¶Fancier Output Formatting
До сих пор мы встречались с двумя способами вывода значений: выражения-инструкции и функция print(). (Третий способ – использовать метод write() файловых объектов; стандартный файл вывода можно указать как sys.stdout. Подробнее об этом см. в Справочнике по библиотеке.)
Часто требуется более тонкое управление форматированием вывода, чем просто печать значений через пробел. Есть два способа форматирования: первый – обрабатывать строки самостоятельно, используя срезы и конкатенацию для создания любой желаемой разметки. Тип str имеет методы для полезных операций, таких как дополнение строк до заданной ширины столбца; о них будет сказано ниже. Второй способ – использовать метод str.format().
Модуль string содержит класс Template, который предлагает ещё один способ подстановки значений в строки.
Остаётся, конечно, один вопрос: как преобразовать значения в строки? К счастью, в Python есть способы преобразовать любое значение в строку: передать его функции repr() или str().
Функция str() предназначена для возврата представлений значений, удобных для человека, а repr() – для генерации представлений, читаемых интерпретатором (или вызовет SyntaxError, если эквивалентного синтаксиса нет). Для объектов, у которых нет специального представления для человека, str() вернёт то же значение, что и repr(). Многие значения, такие как числа или структуры вроде списков и словарей, имеют одинаковое представление в обеих функциях. Строки, в частности, имеют два различных представления.
Несколько примеров:
>>> s = 'Hello, world.'
>>> str(s)
'Hello, world.'
>>> repr(s)
"'Hello, world.'"
>>> str(1/7)
'0.14285714285714285'
>>> x = 10 * 3.25
>>> y = 200 * 200
>>> s = 'The value of x is ' + repr(x) + ', and y is ' + repr(y) + '...'
>>> print(s)
The value of x is 32.5, and y is 40000...
>>> # repr() строки добавляет кавычки и обратную косую черту:
... hello = 'hello, world\n'
>>> hellos = repr(hello)
>>> print(hellos)
'hello, world\n'
>>> # Аргументом repr() может быть любой объект Python:
... repr((x, y, ('spam', 'eggs')))
"(32.5, 40000, ('spam', 'eggs'))"
Вот два способа вывести таблицу квадратов и кубов:
>>> for x in range(1, 11):
... print(repr(x).rjust(2), repr(x*x).rjust(3), end=' ')
... # Обратите внимание на использование 'end' в предыдущей строке
... print(repr(x*x*x).rjust(4))
...
1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
6 36 216
7 49 343
8 64 512
9 81 729
10 100 1000
>>> for x in range(1, 11):
... print('{0:2d} {1:3d} {2:4d}'.format(x, x*x, x*x*x))
...
1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
6 36 216
7 49 343
8 64 512
9 81 729
10 100 1000
(Обратите внимание, что в первом примере один пробел между столбцами добавляется из-за того, как работает print(): она всегда добавляет пробелы между своими аргументами.)
Этот пример демонстрирует метод str.rjust() строковых объектов, который выравнивает строку по правому краю в поле заданной ширины, дополняя её слева пробелами. Есть аналогичные методы str.ljust() и str.center(). Эти методы ничего не выводят, а просто возвращают новую строку. Если входная строка слишком длинная, они не обрезают её, а возвращают как есть; это нарушит разметку столбцов, но обычно это лучше, чем альтернатива, которая заключалась бы во лжи о значении. (Если обрезка действительно нужна, всегда можно добавить срез, например x.ljust(n)[:n].)
Существует ещё один метод, str.zfill(), который дополняет числовую строку слева нулями. Он учитывает знаки плюс и минус:
>>> '12'.zfill(5)
'00012'
>>> '-3.14'.zfill(7)
'-003.14'
>>> '3.14159265359'.zfill(5)
'3.14159265359'
Базовое использование метода str.format() выглядит так:
>>> print('We are the {} who say "{}!"'.format('knights', 'Ni'))
We are the knights who say "Ni!"
Скобки и символы внутри них (называемые полями формата) заменяются объектами, переданными методу str.format(). Число в скобках может использоваться для указания позиции объекта, переданного методу str.format().
>>> print('{0} and {1}'.format('spam', 'eggs'))
spam and eggs
>>> print('{1} and {0}'.format('spam', 'eggs'))
eggs and spam
Если в методе str.format() используются именованные аргументы, их значения указываются по имени аргумента.
>>> print('This {food} is {adjective}.'.format(
... food='spam', adjective='absolutely horrible'))
This spam is absolutely horrible.
Позиционные и именованные аргументы можно произвольно комбинировать:
>>> print('The story of {0}, {1}, and {other}.'.format('Bill', 'Manfred',
other='Georg'))
The story of Bill, Manfred, and Georg.
'!a' (применить ascii()), '!s' (применить str()) и '!r' (применить repr()) можно использовать для преобразования значения перед форматированием:
>>> import math
>>> print('The value of PI is approximately {}.'.format(math.pi))
The value of PI is approximately 3.14159265359.
>>> print('The value of PI is approximately {!r}.'.format(math.pi))
The value of PI is approximately 3.141592653589793.
После имени поля могут следовать необязательные ':' и спецификатор формата. Это позволяет более гибко управлять форматированием значения. В следующем примере значение Pi округляется до трёх знаков после запятой.
>>> import math
>>> print('The value of PI is approximately {0:.3f}.'.format(math.pi))
The value of PI is approximately 3.142.
Передача целого числа после ':' задаёт минимальную ширину поля в символах. Это удобно для красивых таблиц.
>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
>>> for name, phone in table.items():
... print('{0:10} ==> {1:10d}'.format(name, phone))
...
Jack ==> 4098
Dcab ==> 7678
Sjoerd ==> 4127
Если у вас очень длинная строка форматирования, которую не хочется разбивать, было бы удобно ссылаться на переменные по имени, а не по позиции. Это можно сделать, просто передав словарь и используя квадратные скобки '[]' для доступа к ключам.
>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
>>> print('Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; '
... 'Dcab: {0[Dcab]:d}'.format(table))
Jack: 4098; Sjoerd: 4127; Dcab: 8637678
Это также можно сделать, передав таблицу как именованные аргументы с помощью нотации ‘**’ .
>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
>>> print('Jack: {Jack:d}; Sjoerd: {Sjoerd:d}; Dcab: {Dcab:d}'.format(**table))
Jack: 4098; Sjoerd: 4127; Dcab: 8637678
Это особенно полезно в сочетании со встроенной функцией vars(), которая возвращает словарь, содержащий все локальные переменные.
Полный обзор форматирования строк с помощью str.format() см. в Синтаксис строк формата.
7.1.1. Старое форматирование строк¶Old string formatting
Оператор % также можно использовать для форматирования строк. Он интерпретирует левый аргумент как строку формата в стиле sprintf(), которая применяется к правому аргументу, и возвращает строку, полученную в результате этой операции форматирования. Например:
>>> import math
>>> print('The value of PI is approximately %5.3f.' % math.pi)
The value of PI is approximately 3.142.
Дополнительную информацию можно найти в разделе Форматирование строк в стиле printf.
7.2. Чтение и запись файлов¶Reading and Writing Files
open() возвращает файловый объект и чаще всего используется с двумя аргументами: open(filename, mode).
>>> f = open('workfile', 'w')
Первый аргумент – строка с именем файла. Второй аргумент – ещё одна строка из нескольких символов, описывающая способ использования файла. mode может быть 'r' для чтения, 'w' для записи (существующий файл с таким именем будет стёрт), 'a' открывает файл для добавления; любые записанные данные автоматически добавляются в конец. 'r+' открывает файл для чтения и записи. Аргумент mode необязателен; если он опущен, используется 'r'.
Обычно файлы открываются в текстовом режиме, то есть читаются и записываются строки, закодированные в определённой кодировке. Если кодировка не указана, по умолчанию используется платформозависимая (см. open()). Добавление 'b' к режиму открывает файл в двоичном режиме: теперь данные читаются и записываются в виде объектов bytes. Этот режим следует использовать для всех файлов, не содержащих текст.
В текстовом режиме при чтении по умолчанию концы строк, специфичные для платформы ( в Unix, в Windows), преобразуются в просто . При записи в текстовом режиме по умолчанию вхождения преобразуются обратно в концы строк, специфичные для платформы. Эта незаметная модификация данных файла нормальна для текстовых файлов, но испортит двоичные данные, такие как в файлах JPEG или EXE. Будьте очень внимательны и используйте двоичный режим при чтении и записи таких файлов.
7.2.1. Методы файловых объектов¶Methods of File Objects
В остальных примерах этого раздела предполагается, что файловый объект с именем f уже создан.
Чтобы прочитать содержимое файла, вызовите f.read(size), которая читает некоторое количество данных и возвращает их в виде строки или объекта bytes. size – необязательный числовой аргумент. Если size опущен или отрицателен, будет прочитано и возвращено всё содержимое файла; это ваша проблема, если файл вдвое больше памяти вашей машины. В противном случае читается и возвращается не более size байтов. Если достигнут конец файла, f.read() вернёт пустую строку ('').
>>> f.read()
'This is the entire file.\n'
>>> f.read()
''
f.readline() читает одну строку из файла; символ новой строки ( ) остаётся в конце строки и опускается только для последней строки файла, если файл не заканчивается символом новой строки. Это делает возвращаемое значение однозначным: если f.readline() возвращает пустую строку, достигнут конец файла, а пустая строка представляется как '\n' – строка, содержащая только один символ новой строки.
>>> f.readline()
'This is the first line of the file.\n'
>>> f.readline()
'Second line of the file\n'
>>> f.readline()
''
Для чтения строк из файла можно выполнять итерацию по файловому объекту. Это эффективно по памяти, быстро и приводит к простому коду:
>>> for line in f:
... print(line, end='')
...
This is the first line of the file.
Second line of the file
Если нужно прочитать все строки файла в список, можно также использовать list(f) или f.readlines().
f.write(string) записывает содержимое string в файл, возвращая количество записанных символов.
>>> f.write('This is a test\n')
15
Чтобы записать что-то, отличное от строки, его сначала нужно преобразовать в строку:
>>> value = ('the answer', 42)
>>> s = str(value)
>>> f.write(s)
18
f.tell() возвращает целое число, указывающее текущую позицию файлового объекта в файле: в двоичном режиме это количество байтов от начала файла, а в текстовом режиме – непрозрачное число.
Чтобы изменить позицию файлового объекта, используйте f.seek(offset, from_what). Позиция вычисляется как сумма offset и точки отсчёта; точка отсчёта выбирается аргументом from_what. Значение from_what, равное 0, задаёт отсчёт от начала файла, 1 – от текущей позиции, 2 – от конца файла. from_what можно опустить, по умолчанию 0 – начало файла.
>>> f = open('workfile', 'rb+')
>>> f.write(b'0123456789abcdef')
16
>>> f.seek(5) # Перейти к 6-му байту в файле
5
>>> f.read(1)
b'5'
>>> f.seek(-3, 2) # Перейти к 3-му байту от конца
13
>>> f.read(1)
b'd'
В текстовых файлах (открытых без b в строке режима) разрешены только перемещения относительно начала файла (исключение – перемещение в самый конец файла с помощью seek(0, 2)), и единственные допустимые значения offset – это значения, возвращённые f.tell(), или ноль. Любое другое значение offset приводит к неопределённому поведению.
Когда работа с файлом завершена, вызовите f.close(), чтобы закрыть его и освободить системные ресурсы, занятые открытым файлом. После вызова f.close() попытки использовать файловый объект будут автоматически завершаться неудачей.
>>> f.close()
>>> f.read()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: I/O operation on closed file
Хорошей практикой является использование ключевого слова with при работе с файловыми объектами. Его преимущество в том, что файл корректно закрывается после завершения блока, даже если в процессе возникло исключение. Кроме того, это гораздо короче, чем писать эквивалентные блоки try-finally:
>>> with open('workfile', 'r') as f:
... read_data = f.read()
>>> f.closed
True
Файловые объекты имеют некоторые дополнительные методы, такие как isatty() и truncate(), которые используются реже; полное руководство по файловым объектам см. в Справочнике по библиотеке.
7.2.2. Сохранение структурированных данных с помощью json¶Saving structured data with json
Строки легко записываются в файл и читаются из файла. С числами немного больше усилий, так как метод read() возвращает только строки, которые нужно передавать функции вроде int(), которая принимает строку типа '123' и возвращает её числовое значение 123. Когда требуется сохранить более сложные типы данных, такие как вложенные списки и словари, разбор и сериализация вручную становятся сложными.
Вместо того чтобы пользователям приходилось постоянно писать и отлаживать код для сохранения сложных типов данных в файлы, Python позволяет использовать популярный формат обмена данными, называемый JSON (JavaScript Object Notation). Стандартный модуль json принимает иерархии данных Python и преобразует их в строковые представления; этот процесс называется сериализацией. Восстановление данных из строкового представления называется десериализацией. Между сериализацией и десериализацией строка, представляющая объект, может быть сохранена в файле или данных, или отправлена по сетевому соединению на удалённую машину.
Примечание
Формат JSON широко используется в современных приложениях для обмена данными. Многие программисты уже знакомы с ним, что делает его хорошим выбором для обеспечения совместимости.
Если имеется объект x, можно просмотреть его JSON-строковое представление одной строкой кода:
>>> json.dumps([1, 'simple', 'list'])
'[1, "simple", "list"]'
Другой вариант функции dumps(), называемый dump(), просто сериализует объект в текстовый файл. Так, если f – это объект текстового файла, открытый для записи, можно сделать следующее:
json.dump(x, f)
Чтобы снова декодировать объект, если f – это объект текстового файла, который был открыт для чтения:
x = json.load(f)
Этот простой метод сериализации может обрабатывать списки и словари, но сериализация произвольных экземпляров классов в JSON требует дополнительных усилий. Справочная информация по модулю json содержит объяснение этого.
См. также
pickle – модуль pickle
В отличие от JSON, pickle – это протокол, позволяющий сериализовывать произвольно сложные объекты Python. Поэтому он специфичен для Python и не может использоваться для взаимодействия с приложениями, написанными на других языках. Кроме того, по умолчанию он небезопасен: десериализация данных pickle из ненадёжного источника может выполнить произвольный код, если данные были подготовлены опытным злоумышленником.