.. include:: ../LINKS.rst .. _tut-io: ***************** 输入和输出 ***************** 有多种方式可以展现一个程序的输出; 数据可以以一种可读的形式输出, 或者是保存于一个文件便于以后使用. 本章就将讨论几种可能. .. _tut-formatting: 输出格式美化 ========================= 至今为止, 我们知道了两种输出值的方式: *表达式语句* 和 :func:`print` 函数. (第三种方式是使用文件对象的 :meth:`write` 方法; 标准输出文件可以用 ``sys.stdout`` 引用. 参考库手册了解更多的信息.) .. index:: module: string 一般来说你会希望更多的控制其输出格式, 而不是简单的以空格分割. 有两种方式格式化你的输出; 第一种方式是由你自己控制; 使用字符串切片和连接操作, 来实现你所想象的外观. 标准模块 :mod:`string` 包含了一些有用的操作, 用以填充字符串至某一给定的宽度; 很快就会讨论这些. 第二种方式是使用 :meth:`str.format` 方法. :mod:`string` 模块包含了一个类模板, 提供了另一种替换字符串的方式. 还有一个问题, 当然了: 如何把值转成字符串? 幸运的是, Python 有多种方式将任何值转为字符串: 将它传给 :func:`repr` 或 :func:`str` 函数. :func:`str` 函数意味着返回一个用户易读的表达形式, 而 :func:`repr` 则意味着产生一个解释器易读的表达形式 (或者如果没有这样的语法会给出 :exc:`SyntaxError` ). 对于那些没有特殊表达的对象, :func:`str` 将会与 :func:`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), end=' ') ... # Note use of 'end' on previous line 注意前一行 '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 >>> 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 (注意在第一个例子中, 每列间的空格是由 :func:`print` 添加的: 它总会在每个参数后面加个空格.) 这个例子展示了字符串对象的 :meth:`rjust` 方法, 它可以将字符串靠右, 并在左边填充空格. 还有类似的方法, 如 :meth:`ljust` 和 :meth:`center`. 这些方法并不会写任何东西, 它们仅仅返回新的字符串. 如果输入很长, 它们并不会对字符串进行截断, 仅仅返回没有任何变化的字符串; 这虽然会影响你的布局, 但是这一般比截断的要好. (如果你的确需要截断, 那么就增加一个切片的操作, 如 ``x.ljust(n)[:n]``.) 有另一个方法, :meth:`zfill`, 它会在数字的左边填充 0. 它知道正负号: :: >>> '12'.zfill(5) '00012' >>> '-3.14'.zfill(7) '-003.14' >>> '3.14159265359'.zfill(5) '3.14159265359' :meth:`str.format` 的基本使用如下: :: >>> print('We are the {} who say "{}!"'.format('knights', 'Ni')) We are the knights who say "Ni!" 括号及其里面的字符 (称作 format field) 将会被 :meth:`~str.format` 中的参数替换. 在括号中的数字用于指向传入对象在 :meth:`~str.format` 中的位置. :: >>> print('{0} and {1}'.format('spam', 'eggs')) spam and eggs >>> print('{1} and {0}'.format('spam', 'eggs')) eggs and spam 如果在 :meth:`~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. ``'!a'`` (使用 :func:`ascii`), ``'!s'`` (使用 :func:`str`) 和 ``'!r'`` (使用 :func:`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. 可选项 ``':'`` 和格式标识符可以跟着 field name. 这就允许对值进行更好的格式化. 下面的例子将 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 变量前使用 '**' 来实现相同的功能. :: >>> 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 在结合新的内置函数 :func:`vars` (这会以字典的形式返回所有的局部变量) 和这个时会特别有用. 要了解更多关于 :meth:`str.format` 的知识, 参考 :ref:`formatstrings`. 旧式字符串格式化 --------------------- ``%`` 操作符也可以实现字符串格式化. 它将左边的参数作为类似 :c:func:`sprintf` 式的格式化字符串, 而将右边的代入, 然后返回格式化后的字符串. 例如: :: >>> import math >>> print('The value of PI is approximately %5.3f.' % math.pi) The value of PI is approximately 3.142. 因为 :meth:`str.format` 很新, 大多数的 Python 代码仍然使用 ``%`` 操作符. 但是因为这种旧式的格式化最终会从该语言中移除, 应该更多的使用 :meth:`str.format`. 更多的信息可以在 :ref:`old-string-formatting` 中找到. .. _tut-files: 读和写文件 ========================= .. index:: builtin: open object: file :func:`open` 将会返回一个 :term:`file object`, 并且一般使用两个参数进行使用: ``open(filename, mode)``. :: >>> f = open('/tmp/workfile', 'w') .. XXX str(f) is >>> print(f) 第一个参数是包含文件名的字符串. 第二个参数是另一个字符串, 包含描述文件如何使用的字符. *mode* 可以是 ``'r'`` 如果文件只读, ``'w'`` 只用于写 (如果存在同名文件则将被删除), 和 ``'a'`` 用于追加文件内容; 所写的任何数据都会被自动增加到末尾. ``'r+'`` 同时用于读写. *mode* 参数是可选的; ``'r'`` 将是默认值. 一般而言, 文件以 :dfn:`text mode` 打开, 这就意味着, 从文件中读写的字符串, 是以一种特定的编码进行编码 (默认的是 UTF-8). 追加到 *mode* 后的 ``'b'`` , 将意味着以 :dfn:`binary mode` 打开文件: 现在的数据是以字节对象的形式进行读写. 这个模式应该用于那些不包含文本的文件. 在文本模式下 (text mode), 默认是将特定平台的行末标识符 ( Unix 下为 ``\n``, Windows 下为 ``\r\n`` ) 在读时转为 ``\n`` 而写时将 ``\n`` 转为特定平台的标识符. 这种隐藏的行为对于文本文件是没有问题的, 但是对于二进制数据像 :file:`JPEG` 或 :file:`EXE` 是会出问题的. 在使用这些文件时请小心使用二进制模式. .. _tut-filemethods: 文件对象的方法 ----------------------- 本节中剩下的例子假设已经创建了一个称为 ``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() '' ``f.readlines()`` 将返回该文件中包含的所有行. 如果给定一个可选参数 *sizehint*, 它就读取这么多字节, 并且将这些字节按行分割. 这经常用于允许按行读取一个大文件, 但是不需要载入全部的文件时非常有用. 只会返回完整的行. :: >>> f.readlines() ['This is the first line of the file.\n', 'Second line of the file\n'] 另一种方式是迭代一个文件对象然后读取每行. 这是内存有效, 快速, 并用最少的代码: :: >>> for line in f: ... print(line, end='') ... This is the first line of the file. Second line of the file 这个方法很简单, 但是并没有提供一个很好的控制. 因为两者的处理机制不同, 最好不要混用. ``f.write(string)`` 将 *string* 写入到文件中, 然后返回写入的字符数. :: >>> f.write('This is a test\n') 15 如果要写入一些不是字符串的东西, 那么将需要先进行转换: :: >>> value = ('the answer', 42) >>> s = str(value) >>> f.write(s) 18 ``f.tell()`` 返回文件对象当前所处的位置, 它是从文件开头开始算起的字节数. 要改变文件当前的位置, 使用 ``f.seek(offset, from_what)``. 这个位置是通过将当前位置加上 *offset* 所得. *from_what* 的值, 如果是 0 表示开头, 如果是 1 表示当前位置, 2 表示文件的结尾. *from_what* 的默认为 0, 即从开头开始. :: >>> f = open('/tmp/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.close()`` 会关闭它, 并释放系统的资源. 在调用完 ``f.close()`` 之后, 尝试使用那个文件对象是会失败的. :: >>> f.close() >>> f.read() Traceback (most recent call last): File "", line 1, in ? ValueError: I/O operation on closed file 当处理一个文件对象时, 使用 :keyword:`with` 关键字是非常好的方式. 在结束后, 它会帮你正确的关闭文件, 即使发生了异常. 而且写起来也比 :keyword:`try` - :keyword:`finally` 语句块要简短: :: >>> with open('/tmp/workfile', 'r') as f: ... read_data = f.read() >>> f.closed True 文件对象有些额外的方法, 如 :meth:`~file.isatty` 和 :meth:`~file.trucate`, 但它们都较少的使用; 更多的信息需要参考标准库手册. .. _tut-pickle: :mod:`pickle` 模块 ------------------------ .. index:: module: pickle 在文件中, 字符串可以很方便的读取写入. 数字可能稍微麻烦一些, 因为 :meth:`read` 方法只返回字符串, 我们还需要将其传给 :func:`int` 这样的函数, 使其将如 ``'123'`` 的字符串转为数字 123. 但是, 如果要保存更复杂的数据类型, 如列表, 字典, 或者类的实例, 那么就会更复杂了. 为了让用户在时常的编程和测试时保存复杂的数据类型, Python 提供了标准模块, 称为 :mod:`pickle`. 这个模块可以将几乎任何的 Python 对象 (甚至是 Python 的代码), 转换为字符串表示; 这个过程称为 :dfn:`pickling`. 而要从里面重新构造回原来的对象, 则称为 :dfn:`unpickling`. 在 pickling 和 unpickling 之间, 表示这些对象的字符串表示, 可以存于一个文件, 也可以通过网络在远程机器间传输. 如果你有一个对象 ``x``, 和一个已经打开并用于写的文件对象 ``f``, pickle 这个对象最简单的方式就是使用: :: pickle.dump(x, f) 有了 pickle 这个对象, 就能对 ``f`` 以读取的形式打开: :: x = pickle.load(f) (还有其他不同的形式, 比如 pickling 很多对象, 或者不想保存至文件; 更多的信息参考 :mod:`pickle` 模块.) :mod:`pickle` 是 Python 中保存及重用对象的标准方式; 标准的属于称为 :dfn:`persistent` 对象 (即持久化对象). 因为 :mod:`pickle` 被广泛使用, 很多写 Python 扩展的作者都会确保, 如矩阵这样的数据类型能被合理的 pickle 和 unpickle. .. seealso:: (^.^) - 原文: http://docs.python.org/py3k/tutorial/inputoutput.html - 初译: `刘鑫`_ - 精译: `DocsPy3zh`_ - 校对: `Zoom.Quiet`_ - 复审: