7.输入输出

有几种方法可以呈现程序的输出; 数据可以以人类可读的形式打印,或写入文件以供将来使用。

7.1。Fancier输出格式

到目前为止,我们遇到了两种写值的方法:表达式语句print()函数。(第三种方法是使用write()文件对象的方法;标准输出文件可以引用为sys.stdout。有关详细信息,请参阅库参考。)

通常,您需要更多地控制输出的格式,而不仅仅是打印空格分隔的值。有几种格式化输出的方法。

  • 要使用格式化的字符串文字,请在开始引号或三引号之前fF之前开始一个字符串。在这个字符串中,你可以在{} 字符之间写一个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%'
  • 最后,您可以使用字符串切片和连接操作自己完成所有字符串处理,以创建您可以想象的任何布局。字符串类型有一些方法可以执行将字符串填充到给定列宽的有用操作。

当您不需要花哨的输出但只是想快速显示某些变量以进行调试时,您可以将任何值转换为带有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字符串)允许您通过在字符串前加上f或者F将表达式写为 字符串,在字符串中包含Python表达式的值{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。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()方法中使用关键字参数,则使用参数的名称引用它们的值。

>>>

>>> 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 = {'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。旧字符串格式

%运营商还可以用于字符串格式化。它将左参数解释为非常类似于sprintf()要应用于右参数的格式字符串,并返回由此格式化操作产生的字符串。例如:

>>>

>>> 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)

>>>

>>> f = open('workfile', 'w')

第一个参数是包含文件名的字符串。第二个参数是另一个字符串,其中包含一些描述文件使用方式的字符。 模式可以是'r'仅读取文件时,仅'w' 用于写入(将擦除具有相同名称的现有文件),并 'a'打开文件进行追加; 写入文件的任何数据都会自动添加到最后。 'r+'打开文件进行读写。所述模式参数是可选的; 'r'如果省略则将被假定。

通常,文件以文本模式打开,这意味着您从文件读取和写入文件,这些文件以特定编码进行编码。如果未指定编码,则默认值取决于平台(请参阅参考资料 open())。'b'附加到模式后以二进制模式打开文件 :现在以bytes对象的形式读取和写入数据。此模式应该用于所有不包含文本的文件。

在文本模式下,读取时的默认设置是将平台特定的行结尾(\n在Unix上,\r\n在Windows上)转换为just \n。在文本模式下写入时,默认设置是将事件的发生转换\n为特定于平台的行结尾。这幕后的修改文件数据精细的文本文件,但会喜欢,在破坏了二进制数据 JPEGEXE文件。在读取和写入此类文件时要非常小心地使用二进制模式。

with在处理文件对象时,最好使用关键字。优点是文件在套件完成后正确关闭,即使在某个时刻引发了异常。使用with也比写相当于短得多tryfinally块:

>>>

>>> with open('workfile') as f:
...     read_data = f.read()
>>> f.closed
True

如果您没有使用该with关键字,那么您应该调用 f.close()以关闭该文件并立即释放它使用的任何系统资源。如果您没有显式关闭文件,Python的垃圾收集器最终会破坏该对象并为您关闭打开的文件,但该文件可能会暂时保持打开状态。另一个风险是不同的Python实现将在不同的时间进行清理。

通过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() 返回一个整数,给出文件对象在文件中的当前位置,表示为二进制模式下文件开头的字节数和文本模式下的不透明数字。

要更改文件对象的位置,请使用。通过向参考点添加偏移来计算位置; 参数点由from_what参数选择。甲from_what从文件的开头0措施值,1使用当前文件位置,和2使用该文件作为参考点的端部。 from_what可以省略,默认为0,使用文件的开头作为参考点。f.seek(offset, from_what)

>>>

>>> 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数据层次结构,并将它们转换为字符串表示形式; 这个过程叫做序列化。从字符串表示重建数据称为反序列化。在序列化和反序列化之间,表示对象的字符串可能已存储在文件或数据中,或通过网络连接发送到某个远程机器。

注意

 

JSON格式通常被现代应用程序用于允许数据交换。许多程序员已经熟悉它,这使其成为互操作性的良好选择。

如果您有一个对象x,则可以使用一行简单的代码查看其JSON字符串表示:

>>>

>>> import json
>>> json.dumps([1, 'simple', 'list'])
'[1, "simple", "list"]'

dumps()调用该函数的另一个变体dump(),只是将对象序列化为文本文件。因此,如果f是一个 文本文件,对象为写入打开,我们可以这样做:

json.dump(x, f)

要再次解码对象,if f是一个已打开以供阅读的文本文件对象:

x = json.load(f)

这种简单的序列化技术可以处理列表和字典,但是在JSON中序列化任意类实例需要额外的努力。该json模块的参考包含对此的解释。