7. 输入和输出¶
有几种方法可以显示程序的输出;数据可以以人类可读的形式打印,或写入文件以供将来使用。本章将讨论一些可能性。
7.1. 更高级的输出格式¶
到目前为止,我们遇到了两种写入值的方法: 表达式语句 以及 print()
功能。(第三种方法是使用 write()
文件对象的方法;标准输出文件可以引用为 sys.stdout
.有关详细信息,请参阅“库参考”。)
通常,您需要对输出的格式进行更多的控制,而不是简单地打印以空格分隔的值。有几种方法可以格式化输出。
使用 formatted string literals ,以字符串开头
f
或F
在左引号或三引号之前。在这个字符串中,您可以在{{
和}}
可以引用变量或文字值的字符。>>> 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. 格式化字符串文本¶
Formatted string literals (也称为f-strings,简称)通过在字符串前面加上 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'.
有关这些格式规范的参考,请参阅 格式规范小型语言 .
7.1.2. 字符串格式()方法¶
的基本用法 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 = {'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. 旧字符串格式¶
%运算符(模)也可用于字符串格式。鉴于 '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()
返回A file object ,并且最常用于两个参数: open(filename, mode)
.
>>> f = open('workfile', 'w')
第一个参数是包含文件名的字符串。第二个参数是另一个字符串,其中包含一些描述文件使用方式的字符。 mode 可以是 'r'
当只读取文件时, 'w'
仅用于写入(具有相同名称的现有文件将被删除),以及 'a'
打开文件进行附加;写入文件的任何数据都将自动添加到末尾。 'r+'
打开文件进行读写。这个 mode 参数是可选的; 'r'
如果省略,则假定为。
通常,文件在 text mode 也就是说,您从文件中读取和写入字符串,这些字符串以特定的编码进行编码。如果未指定编码,则默认值取决于平台(请参见 open()
) 'b'
附加到模式后,打开文件 binary mode :现在以字节对象的形式读取和写入数据。此模式应用于不包含文本的所有文件。
在文本模式下,读取时的默认值是转换特定于平台的行尾 (\n
在UNIX上, \r\n
在Windows上)仅 \n
. 以文本模式写入时,默认值是转换 \n
返回到特定于平台的行尾。这种对文件数据的幕后修改对于文本文件来说是可以的,但是会破坏二进制数据,就像 JPEG
或 EXE
文件夹。在读写这样的文件时,要非常小心地使用二进制模式。
使用 with
处理文件对象时使用关键字。其优点是,即使在某个时刻引发异常,文件在其套件完成后也会正确关闭。使用 with
也比写同等的短得多 try
\ finally
阻碍::
>>> with open('workfile') as f:
... read_data = f.read()
>>> # We can check that the file has been automatically closed.
>>> f.closed
True
如果您没有使用 with
关键字,则应调用 f.close()
关闭该文件并立即释放其使用的任何系统资源。
警告
Calling f.write()
without using the with
keyword or calling
f.close()
might result in the arguments
of f.write()
not being completely written to the disk, even if the
program exits successfully.
关闭文件对象后,通过 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)
写入的内容 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)
. 位置是通过添加 抵消 到参考点;参考点由 何处 参数。一 何处 0的值从文件开始测量,1使用当前文件位置,2使用文件结束作为参考点。 何处 可以省略,并默认为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数据层次结构,并将其转换为字符串表示形式;此过程称为 serializing . 调用从字符串表示形式重新构造数据 deserializing . 在序列化和反序列化之间,表示对象的字符串可能存储在文件或数据中,或者通过网络连接发送到某个远程计算机。
注解
现代应用程序通常使用JSON格式来进行数据交换。许多程序员已经熟悉它了,这使得它成为一个很好的互操作性选择。
如果你有一个物体 x
,您可以使用一行简单的代码查看其JSON字符串表示:
>>> import json
>>> json.dumps([1, 'simple', 'list'])
'[1, "simple", "list"]'
的另一个变体 dumps()
函数,调用 dump()
,只需将对象序列化为 text file . 所以如果 f
是一个 text file 打开对象进行写入,我们可以执行以下操作:
json.dump(x, f)
再次解码对象,如果 f
是一个 text file 已打开进行读取的对象::
x = json.load(f)
这种简单的序列化技术可以处理列表和字典,但是在JSON中序列化任意类实例需要额外的工作。参考文献 json
模块包含对此的解释。