Содержание страницы
7. Ввод и вывод¶Input and Output
Есть несколько способов отображения вывода программы: данные можно выводить в удобочитаемой форме или записывать в файл для последующего использования. В этой главе рассматриваются некоторые из возможностей.
7.1. Более изощрённое форматирование вывода¶Fancier Output Formatting
До сих пор мы встречали два способа записи значений: инструкции-выражения и
функция print(). (Третий способ – использовать метод write()
файловых объектов; стандартный файл вывода можно указать как sys.stdout.
Подробнее об этом см. в Справочнике по библиотеке.)
Часто требуется более точный контроль форматирования вывода, чем просто вывод значений, разделённых пробелами. Существует несколько способов форматирования вывода.
Чтобы использовать f-строки, начните строку с
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...
>>> # 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'))"
Модуль string содержит класс Template, который предлагает
eщё один способ подстановки значений в строки, используя плейсхолдеры, такие как
$x, и заменяя их значениями из словаря, но даёт гораздо меньше
контроля над форматированием.
7.1.1. Форматированные строковые литералы¶Formatted String Literals
Форматированные строковые литералы (также называемые 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. Метод format() строк¶The String format() Method
Базовое использование метода 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. Ручное форматирование строк¶Manual String Formatting
Вот та же таблица квадратов и кубов, отформатированная вручную:
>>> 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
(Обратите внимание, что один пробел между столбцами добавляется тем,
как работает 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. Старое форматирование строк¶Old string formatting
Оператор % (модуль) также можно использовать для форматирования строк. При наличии '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. Чтение и запись файлов¶Reading and Writing Files
open() возвращает файловый объект и чаще всего используется с
двумя позиционными аргументами и одним именованным аргументом:
open(filename, mode, encoding=None)
>>> f = open('workfile', 'w', encoding="utf-8")
Первый аргумент – строка, содержащая имя файла. Второй аргумент –
ещё одна строка, содержащая несколько символов, описывающих способ использования файла.
mode может быть 'r' для чтения, 'w'
только для записи (существующий файл с таким же именем будет стёрт), а
'a' открывает файл для дополнения; любые записанные данные
автоматически добавляются в конец. 'r+' открывает файл и для чтения, и для
записи. Аргумент mode необязателен; если он опущен, предполагается 'r'.
Обычно файлы открываются в текстовом режиме, то есть вы читаете и записываете
строки в файл и из файла, которые кодируются в определённой кодировке.
Если encoding не указана, используется значение по умолчанию, зависящее от платформы
(см. open()).
Поскольку UTF-8 – современный де-факто стандарт, рекомендуется encoding="utf-8"
если только вы не знаете, что вам нужна другая кодировка.
Добавление 'b' к режиму открывает файл в двоичном режиме.
Данные в двоичном режиме читаются и записываются как объекты bytes.
Нельзя указывать encoding при открытии файла в двоичном режиме.
В текстовом режиме при чтении по умолчанию концевые символы строки, зависящие от платформы
(\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()
>>> # Можно проверить, что файл автоматически закрыт.
>>> 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. Методы файловых объектов¶Methods of File Objects
Остальные примеры в этом разделе предполагают, что файловый объект с именем
f уже создан.
Чтобы прочитать содержимое файла, вызовите f.read(size), который читает некоторое количество
данных и возвращает их в виде строки (в текстовом режиме) или объекта bytes (в двоичном режиме).
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) записывает содержимое 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, whence). Позиция вычисляется
как добавление offset к опорной точке; опорная точка выбирается аргументом
whence. Значение whence равное 0 отсчитывает от начала
файла, 1 использует текущую позицию файла, а 2 использует конец файла в качестве
опорной точки. whence можно опустить, и по умолчанию оно равно 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. Сохранение структурированных данных с помощью json¶Saving structured data with 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 из ненадёжного источника может выполнить произвольный код, если данные были подготовлены опытным злоумышленником.