Ввод и вывод
Существует несколько способов представить вывод программы; данные могут быть напечатаны в удобочитаемой форме или записаны в файл для дальнейшего использования. В этой главе будут рассмотрены некоторые из этих возможностей.
7.1. Форматирование вывода
До сих пор мы встречали два способа записи значений: выражения-операторы и функцию print(). (Третий способ — использование метода write() объектов файлов; стандартный файл вывода можно получить как sys.stdout. Дополнительную информацию по этому поводу можно найти в Справочнике по библиотеке.)
Часто вам потребуется больше контроля над форматированием вывода, чем просто печать значений, разделённых пробелами. Существует несколько способов форматирования вывода.
-
Чтобы использовать форматированные строковые литералы, начинайте строку с
fилиFперед открывающей кавычкой или тройными кавычками. Внутри этой строки вы можете записать выражение Python между символами{и}, которое может ссылаться на переменные или литеральные значения.>>> year = 2016 >>> event = 'Referendum' >>> f'Results of the {year} {event}' 'Results of the 2016 Referendum' -
Метод
str.format()строк требует больше ручного труда. Вы всё ещё будете использовать{и}для обозначения места, где будет заменена переменная, и сможете предоставить подробные директивы форматирования, но вам также потребуется предоставить информацию для форматирования.>>> yes_votes = 42_572_654 >>> no_votes = 43_132_495 >>> percentage = yes_votes / (yes_votes + no_votes) >>> '{:-9} YES votes {:2.2%}'.format(yes_votes, percentage) ' 42572654 YES votes 49.67%' - Наконец, вы можете сами выполнить все операции со строками, используя операции срезов и конкатенации для создания любого желаемого макета. Тип строки имеет некоторые методы, выполняющие полезные операции для выравнивания строк по заданной ширине столбца.
Когда вам не нужно сложное форматирование вывода, а только быстрое отображение некоторых переменных для отладки, вы можете преобразовать любое значение в строку с помощью функций 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'))"
Модуль string содержит класс Template, который предлагает ещё один способ подстановки значений в строки, используя заглушки, как $x, и заменяя их значениями из словаря, но при этом предоставляет гораздо меньше контроля над форматированием.
7.1.1. Форматированные строковые литералы
Форматированные строковые литералы (также называемые f-строками) позволяют включить значение выражений Python в строку, добавив перед строкой f или F и записав выражения как {expression}.
После выражения может следовать необязательный спецификатор форматирования. Это позволяет лучше контролировать способ форматирования значения. В следующем примере значение числа пи округляется до трёх знаков после запятой:
>>> import math
>>> print(f'The value of pi is approximately {math.pi:.3f}.')
The value of pi is approximately 3.142.
Передача целого числа после ':' приведет к тому, что поле будет иметь минимальную ширину в указанном количестве символов. Это полезно для выравнивания столбцов.
>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
>>> for name, phone in table.items():
... print(f'{name:10} ==> {phone:10d}')
...
Sjoerd ==> 4127
Jack ==> 4098
Dcab ==> 7678
Другие модификаторы могут использоваться для преобразования значения перед форматированием. '!a' применяет ascii(), '!s' применяет str(), а '!r' применяет repr():
>>> animals = 'eels'
>>> print(f'My hovercraft is full of {animals}.')
My hovercraft is full of eels.
>>> print(f'My hovercraft is full of {animals!r}.')
My hovercraft is full of 'eels'.
Спецификатор = может использоваться для развёртывания выражения в текст выражения, знак равенства, а затем представление вычисленного выражения:
>>> bugs = 'roaches'
>>> count = 13
>>> area = 'living room'
>>> print(f'Debugging {bugs=} {count=} {area=}')
Debugging bugs='roaches' count=13 area='living room'
См. самодокументированные выражения для получения более подробной информации о спецификаторе =. Для справки по этим спецификациям форматирования см. справочное руководство по Мини-язык спецификаций форматирования.
7.1.2. Метод str.format()
Базовое использование метода 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.
Если у вас очень длинная строка форматирования, которую вы не хотите разделять, было бы удобно, если бы вы могли ссылаться на переменные, которые нужно отформатировать, по имени, а не по позиции. Это можно сделать, просто передав словарь и используя квадратные скобки '[]' для доступа к ключам.
>>> 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 как ключевые аргументы с обозначением **.
>>> 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(), которая возвращает словарь, содержащий все локальные переменные.
В качестве примера, следующие строки создают аккуратно выровненную таблицу столбцов, отображающую целые числа и их квадраты и кубы:
>>> 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
Для получения полного обзора форматирования строк с помощью str.format() см. Синтаксис строк форматирования.
7.1.3. Ручное форматирование строк
Вот та же таблица квадратов и кубов, отформатированная вручную:
>>> 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
(Обратите внимание, что один пробел между каждым столбцом был добавлен способом работы функции 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'
7.1.4. Старое форматирование строк
Оператор % (modulo) также может использоваться для форматирования строк. Учитывая 'string'
% values, экземпляры % в string заменяются нулём или более элементами values. Эта операция обычно называется интерполяцией строк. Например:
>>> import math
>>> print('The value of pi is approximately %5.3f.' % math.pi)
The value of pi is approximately 3.142.
Дополнительную информацию можно найти в разделе Форматирование строк в стиле printf.
7.2. Чтение и запись файлов
open() возвращает объект файла, и чаще всего используется с двумя позиционными аргументами и одним ключевым аргументом: open(filename, mode, encoding=None)
>>> f = open('workfile', 'w', encoding="utf-8")
Первый аргумент — строка, содержащая имя файла. Второй аргумент — другая строка, содержащая несколько символов, описывающих способ использования файла. mode может быть 'r' при чтении файла, 'w' для записи (существующий файл с тем же именем будет удален), и 'a' для добавления; любые данные, записанные в файл, автоматически добавляются в конец. 'r+' открывает файл для чтения и записи. Аргумент mode необязателен; 'r' будет использоваться по умолчанию, если он опущен.
Обычно файлы открываются в текстовом режиме, то есть вы читаете и записываете строки в файл, которые закодированы в определенной кодировке. Если кодировка не указана, используется кодировка по умолчанию, зависящая от платформы (см. open()). Поскольку UTF-8 является современным фактическим стандартом, encoding="utf-8" рекомендуется, если вы не знаете, что вам нужна другая кодировка. Добавление 'b' к режиму открывает файл в бинарном режиме. Бинарные данные читаются и записываются как bytes объекты. Вы не можете указать кодировку при открытии файла в бинарном режиме.
В текстовом режиме по умолчанию при чтении преобразуются платформенно-зависимые символы конца строки (\n на Unix, \r\n на Windows) в просто \n. При записи в текстовом режиме по умолчанию преобразуются вхождения \n обратно в платформенно-зависимые символы конца строки. Эта скрытая модификация данных файла подходит для текстовых файлов, но повредит бинарные данные, как в файлах JPEG или EXE. Будьте очень осторожны, используя бинарный режим при чтении и записи таких файлов.
Хорошей практикой является использование ключевого слова with при работе с объектами файлов. Преимущество состоит в том, что файл корректно закрывается после завершения блока кода, даже если в какой-то момент возникло исключение. Использование with также намного короче, чем запись эквивалентных блоков try-finally.
>>> with open('workfile', encoding="utf-8") as f:
... read_data = f.read()
>>> # We can check that the file has been automatically closed.
>>> f.closed
True
Если вы не используете ключевое слово with, то вы должны вызвать f.close() для закрытия файла и немедленного освобождения всех системных ресурсов, используемых им.
Предупреждение
Вызов f.write() без использования ключевого слова with или вызова f.close() может привести к тому, что аргументы f.write() не будут полностью записаны на диск, даже если программа завершается успешно.
После закрытия объекта файла, либо с помощью инструкции 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. Методы объектов файла
В последующих примерах этой секции предполагается, что объект файла с именем f уже создан.
Для чтения содержимого файла вызовите f.read(size), который считывает определенное количество данных и возвращает его в виде строки (в текстовом режиме) или объекта байтов (в бинарном режиме). size — необязательный числовой аргумент. Когда size опущен или отрицателен, будет считано и возвращено все содержимое файла; это ваша проблема, если файл в два раза больше памяти вашего компьютера. В противном случае будет считано и возвращено максимум size символов (в текстовом режиме) или size байтов (в бинарном режиме). Если достигнут конец файла, 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) записывает содержимое строки в файл, возвращая количество записанных символов.
>>> 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.seek(offset, whence). Позиция вычисляется путем добавления смещения к точке отсчета; точка отсчета выбирается аргументом whence. Значение whence 0 указывает на начало файла, 1 использует текущую позицию файла, а 2 использует конец файла в качестве точки отсчета. whence можно опустить, по умолчанию используется значение 0 — начало файла.
>>> 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(0, 2)) и допустимыми значениями смещения являются те, которые возвращаются из f.tell(), или ноль. Любое другое значение смещения приводит к неопределенному поведению.
Объекты файлов имеют дополнительные методы, такие как isatty() и truncate(), которые используются реже; обратитесь к Справочнику по библиотеке для получения полного руководства по объектам файлов.
7.2.2. Сохранение структурированных данных с помощью json
Строки легко записываются в файл и считываются из него. Числа требуют немного больше усилий, так как метод read() возвращает только строки, которые необходимо передать в функцию, такую как int(), которая принимает строку, например, '123', и возвращает её числовое значение 123. При работе с более сложными типами данных, такими как вложенные списки и словари, ручная обработка парсинга и сериализации усложняется.
Вместо того, чтобы пользователи постоянно писали и отлаживали код для сохранения сложных типов данных в файлы, Python позволяет использовать популярный формат обмена данными под названием JSON (JavaScript Object Notation). Стандартный модуль под названием json может принимать иерархии данных Python и преобразовывать их в строковые представления; этот процесс называется сериализацией. Восстановление данных из строкового представления называется десериализацией. Между сериализацией и десериализацией строка, представляющая объект, может быть сохранена в файле или данных, или отправлена по сетевому соединению на удалённую машину.
Примечание
Формат JSON широко используется современными приложениями для обмена данными. Многие программисты уже знакомы с ним, что делает его хорошим выбором для межпрограммной совместимости.
Если у вас есть объект x, вы можете просмотреть его строковое представление JSON с помощью одной простой строки кода:
>>> import json >>> x = [1, 'simple', 'list'] >>> json.dumps(x) '[1, "simple", "list"]'
Другой вариант функции dumps(), называемый dump(), просто сериализует объект в текстовый файл. Таким образом, если f — объект текстового файла, открытый для записи, мы можем сделать так:
json.dump(x, f)
Для декодирования объекта снова, если f — объект бинарного файла или текстового файла, который был открыт для чтения:
x = json.load(f)
Примечание
JSON-файлы должны быть закодированы в UTF-8. Используйте encoding="utf-8" при открытии JSON-файла как текстового файла для чтения и записи.
Эта простая техника сериализации может обрабатывать списки и словари, но сериализация произвольных экземпляров классов в JSON требует дополнительных усилий. Справочная информация по модулю json содержит объяснение этого.
См. также
pickle — модуль pickle
В отличие от JSON, pickle — это протокол, который позволяет сериализовать произвольно сложные объекты Python. Таким образом, он специфичен для Python и не может использоваться для взаимодействия с приложениями, написанными на других языках. Кроме того, он по умолчанию небезопасен: десериализация данных pickle, полученных из ненадежного источника, может выполнить произвольный код, если данные были составлены опытным злоумышленником.
© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.11/tutorial/inputoutput.html