7. 输入和输出 — Python 文档
7. 输入和输出
有几种方法可以显示程序的输出; 数据可以以人类可读的形式打印,或写入文件以备将来使用。 本章将讨论一些可能性。
7.1. 更高级的输出格式
到目前为止,我们遇到了两种写值的方法:表达式语句和print语句。 (第三种方式是使用文件对象的write()
方法;标准输出文件可以引用为sys.stdout
。 有关这方面的更多信息,请参阅库参考。)
通常,与简单地打印空格分隔值相比,您需要更多地控制输出的格式。 有两种方法可以格式化输出; 第一种方法是自己处理所有字符串; 使用字符串切片和连接操作,您可以创建您可以想象的任何布局。 字符串类型有一些方法可以执行将字符串填充到给定列宽的有用操作; 这些将很快讨论。 第二种方法是使用 str.format() 方法。
string 模块包含一个 Template 类,它提供了另一种将值替换为字符串的方法。
当然还有一个问题:如何将值转换为字符串? 幸运的是,Python 有办法将任何值转换为字符串:将它传递给 repr() 或 str() 函数。
str() 函数旨在返回相当人类可读的值的表示,而 repr() 旨在生成可由解释器读取的表示(或将如果没有等效的语法,则强制使用 SyntaxError
)。 对于没有特定表示供人类消费的对象,str() 将返回与 repr() 相同的值。 许多值(例如数字或结构(如列表和字典))使用任一函数具有相同的表示形式。 特别是字符串和浮点数有两种不同的表示。
一些例子:
>>> s = 'Hello, world.'
>>> str(s)
'Hello, world.'
>>> repr(s)
"'Hello, world.'"
>>> str(1.0/7.0)
'0.142857142857'
>>> repr(1.0/7.0)
'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'))"
这里有两种写正方形和立方体表格的方法:
>>> for x in range(1, 11):
... print repr(x).rjust(2), repr(x*x).rjust(3),
... # Note trailing comma 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
>>> 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
(请注意,在第一个示例中,每列之间添加了一个空格 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'
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.
'!s'
(应用 str())和 '!r'
(应用 repr())可用于在格式化之前转换值。
>>> import math
>>> print 'The value of PI is approximately {}.'.format(math.pi)
The value of PI is approximately 3.14159265359.
>>> print 'The value of PI is approximately {!r}.'.format(math.pi)
The value of PI is approximately 3.141592653589793.
可选的 ':'
和格式说明符可以跟在字段名称之后。 这允许更好地控制值的格式。 以下示例将 Pi 舍入到小数点后三位。
>>> import math
>>> print 'The value of PI is approximately {0:.3f}.'.format(math.pi)
The value of PI is approximately 3.142.
在 ':'
之后传递一个整数将导致该字段的字符数最少。 这对于使表格变得漂亮很有用。
>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
>>> for name, phone in table.items():
... print '{0:10} ==> {1:10d}'.format(name, phone)
...
Jack ==> 4098
Dcab ==> 7678
Sjoerd ==> 4127
如果您有一个不想拆分的非常长的格式字符串,那么如果您可以引用要按名称而不是按位置格式化的变量,那就太好了。 这可以通过简单地传递字典并使用方括号 '[]'
访问键来完成
>>> 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() 结合使用特别有用,该函数返回包含所有局部变量的字典。
有关使用 str.format() 进行字符串格式化的完整概述,请参阅 格式字符串语法 。
7.1.1. 旧的字符串格式
%
运算符也可用于字符串格式化。 它将左参数解释为很像要应用于右参数的 sprintf()
样式的格式字符串,并返回由此格式化操作产生的字符串。 例如:
>>> import math
>>> print 'The value of PI is approximately %5.3f.' % math.pi
The value of PI is approximately 3.142.
更多信息可以在 字符串格式化操作 部分找到。
7.2. 读写文件
open() 返回一个文件对象,最常用的有两个参数:open(filename, mode)
。
>>> f = open('workfile', 'w')
>>> print f
<open file 'workfile', mode 'w' at 80a0960>
第一个参数是一个包含文件名的字符串。 第二个参数是另一个字符串,其中包含一些描述文件使用方式的字符。 mode可以是'r'
只读取文件,'w'
只写入(已存在的同名文件将被删除),[ X153X] 打开文件进行追加; 写入文件的任何数据都会自动添加到末尾。 'r+'
打开文件进行读写。 mode 参数是可选的; 'r'
。
在 Windows 上,附加到模式的 'b'
以二进制模式打开文件,因此还有 'rb'
、'wb'
和 'r+b'
等模式。 Windows 上的 Python 区分文本文件和二进制文件; 读取或写入数据时,文本文件中的行尾字符会自动稍微改变。 这种对文件数据的幕后修改适用于 ASCII 文本文件,但它会破坏 JPEG
或 EXE
文件中的二进制数据。 读写此类文件时要非常小心地使用二进制模式。 在 Unix 上,将 'b'
附加到模式并没有什么坏处,因此您可以独立于平台对所有二进制文件使用它。
7.2.1. 文件对象的方法
本节中的其余示例将假设已经创建了一个名为 f
的文件对象。
要读取文件的内容,请调用 f.read(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,
This is the first line of the file.
Second line of the file
如果要读取列表中文件的所有行,也可以使用 list(f)
或 f.readlines()
。
f.write(string)
将 string 的内容写入文件,返回 None
。
>>> f.write('This is a test\n')
要写入字符串以外的内容,需要先将其转换为字符串:
>>> value = ('the answer', 42)
>>> s = str(value)
>>> f.write(s)
f.tell()
返回一个整数,给出文件对象在文件中的当前位置,以文件开头的字节为单位。 要更改文件对象的位置,请使用 f.seek(offset, from_what)
。 位置是通过将 offset 添加到参考点来计算的; 参考点由 from_what 参数选择。 from_what 值 0 从文件开头开始测量,1 使用当前文件位置,2 使用文件结尾作为参考点。 from_what 可以省略,默认为0,以文件开头为参考点。
>>> f = open('workfile', 'r+')
>>> f.write('0123456789abcdef')
>>> f.seek(5) # Go to the 6th byte in the file
>>> f.read(1)
'5'
>>> f.seek(-3, 2) # Go to the 3rd byte before the end
>>> f.read(1)
'd'
处理完文件后,调用 f.close()
关闭它并释放打开文件占用的所有系统资源。 调用f.close()
后,尝试使用文件对象会自动失败。
>>> f.close()
>>> f.read()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file
在处理文件对象时,最好使用 和 关键字。 这样做的好处是文件在其套件完成后会被正确关闭,即使在途中引发异常也是如此。 它也比编写等效的 try-finally 块要短得多:
>>> with open('workfile', 'r') as f:
... read_data = f.read()
>>> f.closed
True
文件对象有一些额外的方法,例如 isatty() 和 truncate() 不太常用; 有关文件对象的完整指南,请参阅库参考。
7.2.2. 保存结构化数据 json
字符串可以很容易地写入文件和从文件中读取。 数字需要更多的努力,因为 read()
方法只返回字符串,必须将其传递给像 int() 这样的函数,该函数接受像 '123'
这样的字符串] 并返回其数值 123。 当您想要保存更复杂的数据类型(如嵌套列表和字典)时,手动解析和序列化变得复杂。
Python 允许您使用流行的数据交换格式 JSON(JavaScript Object Notation),而不是让用户不断编写和调试代码来将复杂的数据类型保存到文件中。 名为 json 的标准模块可以采用 Python 数据层次结构,并将它们转换为字符串表示; 这个过程称为序列化。 从字符串表示中重建数据称为 反序列化 。 在序列化和反序列化之间,表示对象的字符串可能已存储在文件或数据中,或者通过网络连接发送到某个远程机器。
笔记
现代应用程序通常使用 JSON 格式来进行数据交换。 许多程序员已经熟悉它,这使其成为互操作性的不错选择。
如果你有一个对象 x
,你可以用一行简单的代码查看它的 JSON 字符串表示:
>>> import json
>>> json.dumps([1, 'simple', 'list'])
'[1, "simple", "list"]'
dumps() 函数的另一个变体,称为 dump(),简单地将对象序列化为文件。 因此,如果 f
是一个为写入而打开的 文件对象 ,我们可以这样做:
json.dump(x, f)
再次解码对象,如果 f
是一个已经打开读取的 文件对象 :
x = json.load(f)
这种简单的序列化技术可以处理列表和字典,但在 JSON 中序列化任意类实例需要一些额外的努力。 json 模块的参考包含对此的解释。