7. Вход и выход¶
Существует несколько способов представления выходных данных программы; данные могут быть напечатаны в удобочитаемом виде или записаны в файл для дальнейшего использования. В этой главе будут рассмотрены некоторые из возможностей.
7.1. Более удобное форматирование выходных данных¶
До сих пор мы сталкивались с двумя способами записи значений: операторами expression и функцией print()
. (Третий способ заключается в использовании метода write()
для файловых объектов; на стандартный выходной файл можно ссылаться как на sys.stdout
. Более подробную информацию об этом смотрите в справочной библиотеке.)
Часто вам требуется больше контроля над форматированием выходных данных, чем просто выводить значения, разделенные пробелами. Существует несколько способов форматирования выходных данных.
Чтобы использовать formatted string literals, начинайте строку с
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%'
Наконец, вы можете выполнять всю обработку строк самостоятельно, используя операции разбиения и объединения строк для создания любого макета, который только можете себе представить. Тип string имеет несколько методов, которые выполняют полезные операции для заполнения строк до заданной ширины столбца.
Если вам не нужен сложный вывод, а просто требуется быстрое отображение некоторых переменных в целях отладки, вы можете преобразовать любое значение в строку с помощью функций 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. Форматированные строковые литералы¶
Formatted string literals (для краткости также называемые f-строками) позволяют включать значения выражений Python в строку, добавляя к строке префикс f
или F
и записывая выражения в виде {expression}
.
Необязательный спецификатор формата может следовать за выражением. Это позволяет лучше контролировать способ форматирования значения. В следующем примере число pi округляется до трех знаков после запятой:
>>> 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'
Смотрите self-documenting expressions для получения дополнительной информации о заданном формате =
. Для получения информации об этих спецификациях формата смотрите справочное руководство для Мини-язык спецификации формата.
7.1.2. Метод String 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()
используются ключевые слова arguments, то ссылка на их значения производится с использованием имени аргумента.
>>> 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.
Если у вас действительно длинная строка формата, которую вы не хотите разделять, было бы неплохо, если бы вы могли ссылаться на переменные, которые будут отформатированы по имени, а не по позиции. Это можно сделать, просто передав dict и используя квадратные скобки '[]'
для доступа к ключам.
>>> 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()
для объектов string выравнивает строку по правому краю в поле заданной ширины, заполняя ее слева пробелами. Существуют аналогичные методы 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. Старое форматирование строк¶
Оператор % (по модулю) также может использоваться для форматирования строк. При заданном '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()
возвращает file object и чаще всего используется с двумя позиционными аргументами и одним ключевым словом: open(filename, mode, encoding=None)
>>> f = open('workfile', 'w', encoding="utf-8")
Первый аргумент - это строка, содержащая имя файла. Второй аргумент - это еще одна строка, содержащая несколько символов, описывающих способ использования файла. режим может быть 'r'
, когда файл будет доступен только для чтения, 'w'
только для записи (существующий файл с таким же именем будет удален), и 'a'
открывает файл для добавления; любые записанные данные чтобы файл автоматически добавлялся в конец. 'r+'
открывает файл как для чтения, так и для записи. Параметр mode необязателен; 'r'
будет приниматься, если он опущен.
Обычно файлы открываются в text mode, это означает, что вы читаете и записываете строки из файла и в сам файл, которые закодированы в определенной кодировке. Если кодировка не указана, значение по умолчанию зависит от платформы (см. open()
). Поскольку UTF-8 является современным стандартом де-факто, рекомендуется использовать encoding="utf-8"
, если только вы не знаете, что вам нужно использовать другую кодировку. Добавление 'b'
к режиму открывает файл в виде binary mode. Данные в двоичном режиме считываются и записываются в виде объектов 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 опущено или отрицательно, все содержимое файла будет прочитано и возвращено обратно; это ваша проблема, если размер файла в два раза превышает объем памяти вашего компьютера. В противном случае будет считано и возвращено не более символов размера (в текстовом режиме) или байтов размера (в двоичном режиме). Если достигнут конец файла, 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
Другие типы объектов должны быть преобразованы - либо в строку (в текстовом режиме), либо в байтовый объект (в двоичном режиме) - перед их записью:
>>> value = ('the answer', 42)
>>> s = str(value) # convert the tuple to string
>>> f.write(s)
18
f.tell()
возвращает целое число, указывающее текущее положение файлового объекта в файле, представленное в виде числа байт от начала файла в двоичном режиме и непрозрачного числа в текстовом режиме.
Чтобы изменить положение файлового объекта, используйте f.seek(offset, whence)
. Положение вычисляется путем добавления offset к опорной точке; опорная точка выбирается с помощью аргумента where. Значение where, равное 0, отсчитывается от начала файла, 1 использует текущее положение файла, а 2 использует конец файла в качестве точки отсчета. where может быть опущено и по умолчанию равно 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()
, или равны нулю. Любое другое значение offset приводит к неопределенному поведению.
Файловые объекты имеют некоторые дополнительные методы, такие как isatty()
и truncate()
, которые используются реже; обратитесь к библиотечному справочнику для получения полного руководства по файловым объектам.
7.2.2. Сохранение структурированных данных с помощью json
¶
Строки могут быть легко записаны в файл и считаны из него. С числами требуется немного больше усилий, поскольку метод read()
возвращает только строки, которые нужно будет передать функции типа int()
, которая принимает строку типа '123'
и возвращает ее числовое значение 123. Когда вы хотите сохранить более сложные типы данных, такие как вложенные списки и словари, ручной разбор и сериализация усложняются.
Вместо того чтобы заставлять пользователей постоянно писать и отлаживать код для сохранения сложных типов данных в файлах, Python позволяет использовать популярный формат обмена данными под названием JSON (JavaScript Object Notation). Стандартный модуль, называемый json
, может использовать иерархии данных Python и преобразовывать их в строковые представления; этот процесс называется serializing. Восстановление данных из строкового представления называется deserializing. Между сериализацией и десериализацией строка, представляющая объект, могла быть сохранена в файле или данных или отправлена по сетевому соединению на какой-либо удаленный компьютер.
Примечание
Формат JSON широко используется современными приложениями для обмена данными. Многие программисты уже знакомы с ним, что делает его хорошим выбором для обеспечения взаимодействия.
Если у вас есть объект x
, вы можете просмотреть его строковое представление в формате JSON с помощью простой строки кода:
>>> import json
>>> x = [1, 'simple', 'list']
>>> json.dumps(x)
'[1, "simple", "list"]'
Другой вариант функции dumps()
, называемый dump()
, просто преобразует объект в text file. Итак, если f
- это объект text file, открытый для записи, мы можем сделать это:
json.dump(x, f)
Чтобы снова расшифровать объект, если f
является binary file или text file объектом, который был открыт для чтения:
x = json.load(f)
Примечание
Файлы JSON должны быть закодированы в UTF-8. Используйте encoding="utf-8"
при открытии файла JSON как text file как для чтения, так и для записи.
Этот простой метод сериализации позволяет обрабатывать списки и словари, но сериализация экземпляров произвольных классов в формате JSON требует дополнительных усилий. Ссылка на модуль json
содержит объяснение этого.
См.также
pickle
- модуль pickle
В отличие от JSON, pickle - это протокол, который позволяет выполнять сериализацию сколь угодно сложных объектов Python. Как таковой, он специфичен для Python и не может использоваться для взаимодействия с приложениями, написанными на других языках. Это также небезопасно по умолчанию: десериализация данных pickle, поступающих из ненадежного источника, может привести к выполнению произвольного кода, если данные были созданы опытным злоумышленником.