Ввод и вывод
Существует несколько способов представить результат работы программы: данные можно вывести в удобочитаемом виде или записать в файл для дальнейшего использования. В этой главе рассматриваются некоторые из этих возможностей.
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 >>> total_votes = 85_705_149 >>> percentage = yes_votes / total_votes >>> '{:-9} YES votes {:2.2%}'.format(yes_votes, percentage) ' 42572654 YES votes 49.67%'Обратите внимание, что перед
yes_votesдобавляются пробелы, а знак минус ставится только перед отрицательными числами. В примере также выводитсяpercentage, умноженное на 100, с двумя знаками после запятой и символом процента в конце (подробности см. в разделе Мини-язык спецификаций формата). - Наконец, можно самостоятельно обрабатывать строки, используя операции среза и конкатенации, чтобы создать любой желаемый макет. Тип строк предоставляет несколько методов для дополнения строк до заданной ширины столбца.
Если сложный вывод не нужен, а требуется лишь быстро показать значения некоторых переменных для отладки, можно преобразовать любое значение в строку с помощью функций 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. Метод 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(), которая возвращает словарь, содержащий все локальные переменные:
>>> table = {k: str(v) for k, v in vars().items()}
>>> message = " ".join([f'{k}: ' + '{' + k +'};' for k in table.keys()])
>>> print(message.format(**table))
__name__: __main__; __doc__: None; __package__: None; __loader__: ...
Например, следующие строки выводят аккуратно выровненные столбцы с целыми числами, их квадратами и кубами:
>>> 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. Старый стиль форматирования строк
Оператор % (остаток от деления) также можно использовать для форматирования строк. Если задано format % values (где format — строка), спецификации преобразования % в строке format заменяются нулём или несколькими элементами из 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'.
Обычно файлы открываются в текстовом режиме: это означает, что из файла читаются и в файл записываются строки, закодированные с использованием определённой кодировки. Если 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()
>>> # 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). Этот метод считывает некоторое количество данных и возвращает их в виде строки (в текстовом режиме) или объекта 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) # convert the tuple to string
>>> 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) # 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)), а допустимыми значениями offset являются только значения, возвращённые методом f.tell(), или ноль. Любое другое значение offset приводит к неопределённому поведению.
У файловых объектов есть и другие методы, например 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–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/tutorial/inputoutput.html