Ввод и вывод

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

Удобное форматирование вывода

На данный момент мы выяснили два способа вывода значений: операторные выражения (expression statements) и функция print(). (Третий способ — использование метода write() объектов файлов; на файл стандартного вывода можно сослаться как на sys.stdout. Более подробную информацию по этому пункту смотрите в Справочнике по библиотеке.)

Часто возникает желание иметь больший контроль над форматированием вывода, чем обычная печать значений разделённых пробелами. Есть два способа форматирования вашего вывода. Первый способ — выполнять самостоятельно всю работу над строками: используя срезы строк и конкатенацию вы можете создать любой шаблон, какой пожелаете. Тип string содержит много полезных операций для выравнивания строк по определённой ширине колонки (скоро мы их кратко рассмотрим). Второй способ — использование форматированных строковых литералов, или метода 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...
>>> # The repr() of a string adds string quotes and backslashes:
... hello = 'hello, world\n'
>>> hellos = repr(hello)
>>> print(hellos)
'hello, world\n'
>>> # The argument to repr() may be any Python object:
... 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=' ')
...     # Note use of 'end' on previous line
...     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!"

Скобки с символами внутри (их называют полями форматирования (format fields)) заменяются на объекты, переданные методу 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'.

После имени поля может следовать необязательный спецификатор формата ‘:’. Это позволяет лучше управлять тем, как форматируются значения. Следующий пример оставляет у числа Пи только три цифры после десятичного разделителя.

>>> 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() описано в разделе Синтаксис строк форматирования.

Форматирование строк в старом стиле

Для форматирования строк можно использовать и операцию %. Она интерпретирует левый операнд как строку форматирования в стиле sprintf, которую следует применить к правому операнду, и возвращает строку, получившуюся в результате этого преобразования. Например:

>>> import math
>>> print('The value of PI is approximately %5.3f.' % math.pi)
The value of PI is approximately 3.142.

Больше информации можно найти в разделе Операции форматирования строк в старом стиле.

Чтение и запись файлов

Функция open() возвращает объект файла и в большинстве случаев используется с двумя аргументами: open(имя_файла, режим).

>>> f = open('workfile', 'w')

Первый параметр — строка, содержащая имя файла. Второй — другая строка, содержащая несколько символов, описывающих способ использования файла. Значение параметра режим может быть символом 'r', если файл будет открыт только для чтения, 'w' — открыт только для записи (существующий файл с таким же именем будет стёрт) и 'a' — файл открыт для добавления: любые данные, записанные в файл автоматически добавляются в конец. 'r+' открывает файл и для чтения, и для записи. Параметр режим необязателен: если он опущен — предполагается, что он равен 'r'.

В обычном случае файлы открываются в текстовом режиме (text mode) — это значит что вы читаете из файла и записываете в файл строки в определённой кодировке. Если кодировка не указана явно, то используется кодировка по умолчанию, которая зависит от платформы (см. open()). Если добавить к режиму файла символ ‘b’, файл открывается в двоичном режиме (binary mode): теперь данные считываются и записываются в виде двоичных объектов. Этот режим следует использовать для всех файлов, которые не содержат текст.

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

Методы файловых объектов

В примерах ниже подразумевается, что заранее создан файловый объект с именем f.

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

>>> f.write('This is a test\n')
15

Чтобы записать в файл нечто отличное от строки, предварительно это нечто нужно в строку сконвертировать:

>>> value = ('the answer', 42)
>>> s = str(value)  # convert the tuple to string
>>> f.write(s)
18

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

Чтобы изменить позицию объекта-файла, используйте f.seek(смещение, откуда). Позиция вычисляется прибавлением смещения к точке отсчёта; точка отсчёта выбирается из параметра откуда. Значение 0 параметра откуда отмеряет смещение от начала файла, значение 1 применяет текущую позицию в файле, а значение 2 в качестве точки отсчёта использует конец файла.

>>> f = open('workfile', 'rb+')
>>> f.write(b'0123456789abcdef')
16
>>> f.seek(5)      # Go to the 6th byte in the file
5
>>> f.read(1)
b'5'
>>> f.seek(-3, 2)  # Go to the 3rd byte before the end
13
>>> f.read(1)
b'd'

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

Сохранение структурированных данных с json

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

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

Замечание

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

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

>>> json.dumps([1, 'simple', 'list'])
'[1, "simple", "list"]'

Другой вариант функкции dumps(), называемый dump(), просто сериализует объект в текстовый файл. Так что если f это объект текстовый файл, открытый для записи, мы можем сделать это:

json.dump(x, f)

Для декодирования объекта обратно, если f это объект текстовый файл, открытый для чтения:

x = json.load(f)

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

Смотрите также

pickle - модуль "засолки"

Вопреки JSON, pickle есть протокол, который позволяет сериализацию сколь угодно сложных объектов Python. Как таковой, он специфичен для Python и не может быть использован для коммуникации с приложениями, написанными на других языках. Он также небезопасен по умолчанию: десериализация данных pickle, поступающих из ненадежного источника может выполнить произвольный код, если данные были смастерены опытным злоумышленником.