Документация Python неофициальный перевод
Содержание страницы

7. Ввод и выводInput and Output

Есть несколько способов отображения вывода программы: данные можно выводить в удобочитаемой форме или записывать в файл для последующего использования. В этой главе рассматриваются некоторые из возможностей.

7.1. Более изощрённое форматирование выводаFancier Output Formatting

До сих пор мы встречали два способа записи значений: инструкции-выражения и функция print(). (Третий способ – использовать метод write() файловых объектов; стандартный файл вывода можно указать как sys.stdout. Подробнее об этом см. в Справочнике по библиотеке.)

Часто требуется более гибкое управление форматированием вывода, чем просто вывод значений, разделённых пробелами. Есть два способа форматирования вывода: первый – обрабатывать строки самостоятельно, используя срезы и конкатенацию, чтобы создать любой макет. У строкового типа есть методы, которые помогают выравнивать строки до заданной ширины столбца; о них будет сказано далее. Второй способ – использовать f-строки (отформатированные строковые литералы) или метод 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()) можно использовать для преобразования значения перед форматированием:

>>> contents = 'eels'
>>> print('My hovercraft is full of {}.'.format(contents))
My hovercraft is full of eels.
>>> print('My hovercraft is full of {!r}.'.format(contents))
My hovercraft is full of 'eels'.

После имени поля могут следовать необязательные ':' и спецификатор формата. Это позволяет более гибко управлять форматированием значения. В следующем примере значение 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. Этот режим следует использовать для всех файлов, не содержащих текст.

В текстовом режиме при чтении по умолчанию концевые символы строки, зависящие от платформы (\n в Unix, \r\n в Windows), преобразуются в простой \n. При записи в текстовом режиме по умолчанию вхождения \n преобразуются обратно в концевые символы, зависящие от платформы. Эта фоновая модификация данных файла нормальна для текстовых файлов, но повредит двоичные данные, например, в файлах JPEG или EXE. Будьте очень осторожны и используйте двоичный режим при чтении и записи таких файлов.

Рекомендуется использовать ключевое слово with при работе с файловыми объектами. Преимущество в том, что файл корректно закрывается после завершения своего блока, даже если в какой-то момент возникло исключение. Использование with также намного короче, чем написание эквивалентных блоков try-finally:

>>> with open('workfile') as f:
...     read_data = f.read()
>>> f.closed
True

Если ключевое слово with не используется, следует вызвать f.close(), чтобы закрыть файл и немедленно освободить все занятые им системные ресурсы. Если файл не закрыть явно, сборщик мусора Python в конечном счёте уничтожит объект и закроет открытый файл, но файл может оставаться открытым некоторое время. Ещё один риск – разные реализации Python могут выполнять эту очистку в разное время.

После закрытия файлового объекта – с помощью инструкции with или вызова f.close() – попытки использовать файловый объект автоматически завершатся неудачей.

>>> f.close()
>>> f.read()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file.

7.2.1. Методы файловых объектовMethods of File Objects

Остальные примеры в этом разделе предполагают, что файловый объект с именем f уже создан.

Чтобы прочитать содержимое файла, вызовите f.read(size), который считывает некоторое количество данных и возвращает их в виде строки (в текстовом режиме) или объекта bytes (в двоичном режиме). размер – необязательный числовой аргумент. Если размер опущен или отрицателен, будет прочитано и возвращено всё содержимое файла; если файл в два раза больше оперативной памяти – это ваша проблема. В противном случае считывается и возвращается не более размер байт. Если достигнут конец файла, f.read() вернёт пустую строку ('').

>>> f.read()
'This is the entire file.\n'
>>> f.read()
''

f.readline() читает одну строку из файла; символ новой строки (\n) остаётся в конце строки и опускается только в последней строке файла, если файл не заканчивается переводом строки. Это делает возвращаемое значение однозначным: если 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

Объекты других типов необходимо преобразовать – либо в строку (в текстовом режиме), либо в объект bytes (в двоичном режиме) – перед записью:

>>> value = ('the answer', 42)
>>> s = str(value)  # преобразовать кортеж в строку
>>> f.write(s)
18

f.tell() возвращает целое число, указывающее текущую позицию в файле для данного файлового объекта, представленную как количество байтов от начала файла в двоичном режиме и непрозрачное число в текстовом режиме.

Чтобы изменить позицию файлового объекта, используйте f.seek(offset, from_what). Позиция вычисляется прибавлением смещение к опорной точке; опорная точка выбирается аргументом 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 приводит к неопределённому поведению.

Файловые объекты имеют некоторые дополнительные методы, такие как isatty() и truncate(), которые используются реже; обратитесь к Справочнику библиотеки для получения полного руководства по файловым объектам.

7.2.2. Сохранение структурированных данных с помощью jsonSaving structured data with json

Строки можно легко записать в файл и прочитать из файла. С числами немного сложнее, поскольку метод read() возвращает только строки, которые затем нужно передать такой функции, как int(), принимающей строку вида '123' и возвращающей её числовое значение 123. Если требуется сохранять более сложные типы данных, например вложенные списки и словари, ручной разбор и сериализация становятся сложными.

Чтобы избавить пользователей от постоянного написания и отладки кода для сохранения сложных типов данных в файлы, Python позволяет использовать популярный формат обмена данными, называемый JSON (JavaScript Object Notation). Стандартный модуль json может обрабатывать иерархии данных Python и преобразовывать их в строковые представления; этот процесс называется сериализацией. Восстановление данных из строкового представления называется десериализацией. Между сериализацией и десериализацией строка, представляющая объект, может быть сохранена в файл или данные, либо передана по сетевому соединению на удалённую машину.

Примечание

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

Если у вас есть объект x, вы можете увидеть его строковое представление в JSON одной строкой кода:

>>> import 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 из ненадёжного источника может выполнить произвольный код, если данные были подготовлены опытным злоумышленником.