7. Input and Output输入和输出

There are several ways to present the output of a program; data can be printed in a human-readable form, or written to a file for future use. 有几种方法可以显示程序的输出;数据可以以人类可读的形式打印,也可以写入文件以备将来使用。This chapter will discuss some of the possibilities.本章将讨论一些可能性。

7.1. Fancier Output FormattingFancier输出格式

So far we’ve encountered two ways of writing values: expression statements and the print() function. 到目前为止,我们遇到了两种编写值的方法:表达式语句print()函数。(A third way is using the write() method of file objects; the standard output file can be referenced as sys.stdout. (第三种方法是使用file对象的write()方法;标准输出文件可以引用为sys.stdoutSee the Library Reference for more information on this.)有关这方面的更多信息,请参阅库参考。)

Often you’ll want more control over the formatting of your output than simply printing space-separated values. 通常,您希望对输出的格式进行更多控制,而不是简单地打印以空格分隔的值。There are several ways to format output.有几种方法可以格式化输出。

  • To use formatted string literals, begin a string with f or F before the opening quotation mark or triple quotation mark. 要使用格式化的字符串文字,请在开始引号或三引号前以fF开头字符串。Inside this string, you can write a Python expression between { and } characters that can refer to variables or literal values.在这个字符串中,您可以在{}个字符之间编写一个Python表达式,该表达式可以引用变量或文字值。

    >>> year = 2016
    >>> event = 'Referendum'
    >>> f'Results of the {year} {event}'
    'Results of the 2016 Referendum'
  • The str.format() method of strings requires more manual effort. 字符串的str.format()方法需要更多的手动操作。You’ll still use { and } to mark where a variable will be substituted and can provide detailed formatting directives, but you’ll also need to provide the information to be formatted.您仍将使用{and}来标记变量将被替换的位置,并且可以提供详细的格式化指令,但还需要提供要格式化的信息。

    >>> 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%'
  • Finally, you can do all the string handling yourself by using string slicing and concatenation operations to create any layout you can imagine. 最后,您可以通过使用字符串切片和连接操作来创建您可以想象的任何布局,从而完成所有字符串处理。The string type has some methods that perform useful operations for padding strings to a given column width.字符串类型有一些方法可以执行有用的操作,将字符串填充到给定的列宽。

When you don’t need fancy output but just want a quick display of some variables for debugging purposes, you can convert any value to a string with the repr() or str() functions.当您不需要复杂的输出,只需要快速显示一些变量以进行调试时,可以使用repr()str()函数将任何值转换为字符串。

The str() function is meant to return representations of values which are fairly human-readable, while repr() is meant to generate representations which can be read by the interpreter (or will force a SyntaxError if there is no equivalent syntax). str()函数用于返回人类可读的值的表示形式,而repr()则用于生成可由解释器读取的表示形式(如果没有等效语法,则会强制生成SyntaxError)。For objects which don’t have a particular representation for human consumption, str() will return the same value as repr(). 对于没有用于人类消费的特定表示形式的对象,str()将返回与repr()相同的值。Many values, such as numbers or structures like lists and dictionaries, have the same representation using either function. 许多值(如数字或列表和字典等结构)使用这两个函数都具有相同的表示形式。Strings, in particular, have two distinct representations.特别是字符串有两种不同的表示形式。

Some examples:例如:

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

The string module contains a Template class that offers yet another way to substitute values into strings, using placeholders like $x and replacing them with values from a dictionary, but offers much less control of the formatting.string模块包含一个Template类,它提供了另一种将值替换为字符串的方法,使用$x之类的占位符并用字典中的值替换它们,但对格式的控制要少得多。

7.1.1. Formatted String Literals格式化字符串文本

Formatted string literals (also called f-strings for short) let you include the value of Python expressions inside a string by prefixing the string with f or F and writing expressions as {expression}.格式化字符串文本(也称为f-strings)允许您在字符串中包含Python表达式的值,方法是在字符串前面加上fF,并将表达式写为{expression}

An optional format specifier can follow the expression. 表达式后面可以有一个可选的格式说明符。This allows greater control over how the value is formatted. 这样可以更好地控制值的格式。The following example rounds pi to three places after the decimal:以下示例将pi四舍五入到小数点后三位:

>>> import math
>>> print(f'The value of pi is approximately {math.pi:.3f}.')
The value of pi is approximately 3.142.

Passing an integer after the ':' will cause that field to be a minimum number of characters wide. ':'后面传递一个整数将使该字段的宽度最小。This is useful for making columns line up.这对于使列对齐很有用。

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

Other modifiers can be used to convert the value before it is formatted. 在格式化之前,可以使用其他修饰符转换值。'!a' applies ascii(), '!s' applies str(), and '!r' applies repr():'!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'.

For a reference on these format specifications, see the reference guide for the Format Specification Mini-Language.有关这些格式规范的参考,请参阅格式规范迷你语言的参考指南

7.1.2. The String format() Method字符串format()方法

Basic usage of the str.format() method looks like this:str.format()方法的基本用法如下所示:

>>> print('We are the {} who say "{}!"'.format('knights', 'Ni'))
We are the knights who say "Ni!"

The brackets and characters within them (called format fields) are replaced with the objects passed into the str.format() method. 括号和其中的字符(称为格式字段)将替换为传递到str.format()方法的对象。A number in the brackets can be used to refer to the position of the object passed into the str.format() method.括号中的数字可以用来表示传递给str.format()方法的对象的位置。

>>> print('{0} and {1}'.format('spam', 'eggs'))
spam and eggs
>>> print('{1} and {0}'.format('spam', 'eggs'))
eggs and spam

If keyword arguments are used in the str.format() method, their values are referred to by using the name of the argument.如果在str.format()方法中使用关键字参数,则使用参数的名称引用它们的值。

>>> print('This {food} is {adjective}.'.format(
... food='spam', adjective='absolutely horrible'))
This spam is absolutely horrible.

Positional and keyword arguments can be arbitrarily combined:位置参数和关键字参数可以任意组合:

>>> print('The story of {0}, {1}, and {other}.'.format('Bill', 'Manfred',
other='Georg'))
The story of Bill, Manfred, and Georg.

If you have a really long format string that you don’t want to split up, it would be nice if you could reference the variables to be formatted by name instead of by position. 如果你有一个很长的格式字符串,你不想分裂,如果你可以引用变量的名称,而不是按位置格式化,这将是很好的。This can be done by simply passing the dict and using square brackets '[]' to access the keys.这可以通过简单地传递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

This could also be done by passing the table as keyword arguments with the ‘**’ notation.这也可以通过将表作为带有‘**’记号法的关键字参数传递来实现。

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

This is particularly useful in combination with the built-in function vars(), which returns a dictionary containing all local variables.这在与内置函数vars()结合使用时特别有用,该函数返回一个包含所有局部变量的字典。

As an example, the following lines produce a tidily-aligned set of columns giving integers and their squares and cubes:例如,以下几行生成一组整齐排列的列,给出整数及其平方和立方体:

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

For a complete overview of string formatting with str.format(), see Format String Syntax.有关str.format()字符串格式的完整概述,请参阅格式化字符串语法

7.1.3. Manual String Formatting手动字符串格式化

Here’s the same table of squares and cubes, formatted manually:这是同样的正方形和立方体表格,手动格式化:

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

(Note that the one space between each column was added by the way print() works: it always adds spaces between its arguments.)(请注意,每列之间的一个空格是通过print()的工作方式添加的:它总是在参数之间添加空格。)

The str.rjust() method of string objects right-justifies a string in a field of a given width by padding it with spaces on the left. 字符串对象的str.rjust()方法通过在左边填充空格来右对齐给定宽度字段中的字符串。There are similar methods str.ljust() and str.center(). 有类似的方法str.ljust()str.center()These methods do not write anything, they just return a new string. If the input string is too long, they don’t truncate it, but return it unchanged; this will mess up your column lay-out but that’s usually better than the alternative, which would be lying about a value. 这些方法不写任何东西,只返回一个新字符串。如果输入字符串太长,他们不会截断它,而是返回不变的字符串;这会弄乱你的专栏布局,但这通常比另一个更好,那就是在价值上撒谎。(If you really want truncation you can always add a slice operation, as in x.ljust(n)[:n].)(如果你真的想要截断,你可以添加一个切片操作,比如x.ljust(n)[:n]。)

There is another method, str.zfill(), which pads a numeric string on the left with zeros. It understands about plus and minus signs:还有另一种方法str.zfill(),它在左边用零填充一个数字字符串。它了解加号和减号:

>>> '12'.zfill(5)
'00012'
>>> '-3.14'.zfill(7)
'-003.14'
>>> '3.14159265359'.zfill(5)
'3.14159265359'

7.1.4. Old string formatting旧字符串格式

The % operator (modulo) can also be used for string formatting. %运算符(模)也可用于字符串格式化。Given 'string' % values, instances of % in string are replaced with zero or more elements of values. 给定'string' % valuesstring%的实例将替换为零个或多个值元素。This operation is commonly known as string interpolation. 这种操作通常被称为字符串插值。For example:例如:

>>> import math
>>> print('The value of pi is approximately %5.3f.' % math.pi)
The value of pi is approximately 3.142.

More information can be found in the printf-style String Formatting section.更多信息可以在printf样式字符串格式部分找到。

7.2. Reading and Writing Files读写文件

open() returns a file object, and is most commonly used with two arguments: open(filename, mode).返回一个文件对象,最常用于两个参数:open(filename, mode)

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

The first argument is a string containing the filename. 第一个参数是包含文件名的字符串。The second argument is another string containing a few characters describing the way in which the file will be used. 第二个参数是另一个字符串,其中包含几个字符,用于描述文件的使用方式。mode can be 'r' when the file will only be read, 'w' for only writing (an existing file with the same name will be erased), and 'a' opens the file for appending; any data written to the file is automatically added to the end. 当文件只被读取时,mode可以是'r',而'w'只用于写入(具有相同名称的现有文件将被删除),而'a'打开文件进行附加;写入文件的任何数据都会自动添加到末尾。'r+' opens the file for both reading and writing. 打开文件进行读写操作。The mode argument is optional; 'r' will be assumed if it’s omitted.mode参数是可选的;如果省略,则假定为'r'

Normally, files are opened in text mode, that means, you read and write strings from and to the file, which are encoded in a specific encoding. 通常情况下,文件是以文本模式打开的,也就是说,您可以在文件中读写字符串,这些字符串以特定的编码进行编码。If encoding is not specified, the default is platform dependent (see open()). 如果未指定编码,则默认值取决于平台(请参阅open()'b' appended to the mode opens the file in binary mode: now the data is read and written in the form of bytes objects. 附加到模式的'b'二进制模式打开文件:现在数据以字节对象的形式读取和写入。This mode should be used for all files that don’t contain text.此模式应用于所有不包含文本的文件。

In text mode, the default when reading is to convert platform-specific line endings (\n on Unix, \r\n on Windows) to just \n. 在文本模式下,读取时的默认设置是将特定于平台的行尾(\n在Unix上,\r\n在Windows上)转换为\nWhen writing in text mode, the default is to convert occurrences of \n back to platform-specific line endings. 在文本模式下写入时,默认情况是将\n的出现转换回特定于平台的行尾。This behind-the-scenes modification to file data is fine for text files, but will corrupt binary data like that in JPEG or EXE files. 这种对文件数据的幕后修改对于文本文件来说很好,但会破坏JPEGEXE文件中的二进制数据。Be very careful to use binary mode when reading and writing such files.在读写这样的文件时,要非常小心地使用二进制模式。

It is good practice to use the with keyword when dealing with file objects. 在处理文件对象时,最好使用with关键字。The advantage is that the file is properly closed after its suite finishes, even if an exception is raised at some point. 这样做的好处是,即使在某个时候出现异常,该文件在其套件完成后也会正确关闭。Using with is also much shorter than writing equivalent try-finally blocks:使用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

If you’re not using the with keyword, then you should call f.close() to close the file and immediately free up any system resources used by it.如果未使用with关键字,则应调用f.close()关闭该文件,并立即释放该文件使用的所有系统资源。

Warning

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.write()或调用f.close()可能会导致f.write()的参数未完全写入磁盘,即使程序成功退出。

After a file object is closed, either by a with statement or by calling f.close(), attempts to use the file object will automatically fail.通过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. Methods of File Objects文件对象的方法

The rest of the examples in this section will assume that a file object called f has already been created.本节中的其余示例将假定已经创建了名为f的文件对象。

To read a file’s contents, call f.read(size), which reads some quantity of data and returns it as a string (in text mode) or bytes object (in binary mode). 要读取文件内容,请调用f.read(size),它读取一定数量的数据,并将其作为字符串(文本模式)或字节对象(二进制模式)返回。size is an optional numeric argument. 是可选的数字参数。When size is omitted or negative, the entire contents of the file will be read and returned; it’s your problem if the file is twice as large as your machine’s memory. size被省略或为负数时,将读取并返回文件的全部内容;如果文件的大小是机器内存的两倍,那就是你的问题。Otherwise, at most size characters (in text mode) or size bytes (in binary mode) are read and returned. 否则,最多读取并返回size字符(文本模式)或size字节(二进制模式)。If the end of the file has been reached, f.read() will return an empty string ('').如果已到达文件末尾,f.read()将返回一个空字符串('')。

>>> f.read()
'This is the entire file.\n'
>>> f.read()
''

f.readline() reads a single line from the file; a newline character (\n) is left at the end of the string, and is only omitted on the last line of the file if the file doesn’t end in a newline. 从文件中读取一行;换行符(\n)保留在字符串的末尾,只有在文件没有换行结束时,才会在文件的最后一行省略。This makes the return value unambiguous; if f.readline() returns an empty string, the end of the file has been reached, while a blank line is represented by '\n', a string containing only a single newline.这使得返回值明确无误;如果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 reading lines from a file, you can loop over the file object. 要从文件中读取行,可以在文件对象上循环。This is memory efficient, fast, and leads to simple code:这是一种高效、快速的内存管理方法,并且可以生成简单的代码:

>>> for line in f:
... print(line, end='')
...
This is the first line of the file.
Second line of the file

If you want to read all the lines of a file in a list you can also use list(f) or f.readlines().如果要读取列表中文件的所有行,也可以使用list(f)f.readlines()

f.write(string) writes the contents of string to the file, returning the number of characters written.string的内容写入文件,返回写入的字符数。

>>> f.write('This is a test\n')
15

Other types of objects need to be converted – either to a string (in text mode) or a bytes object (in binary mode) – before writing them:在写入其他类型的对象之前,需要将其转换为字符串(文本模式)或字节对象(二进制模式):

>>> value = ('the answer', 42)
>>> s = str(value) # convert the tuple to string
>>> f.write(s)
18

f.tell() returns an integer giving the file object’s current position in the file represented as number of bytes from the beginning of the file when in binary mode and an opaque number when in text mode.f.tell()返回一个整数,在二进制模式下表示文件对象在文件中的当前位置,在文本模式下表示为距文件开头的字节数,在文本模式下表示为不透明数。

To change the file object’s position, use f.seek(offset, whence). 要更改文件对象的位置,请使用f.seek(offset, whence)The position is computed from adding offset to a reference point; the reference point is selected by the whence argument. 通过向参考点添加offset来计算位置;参考点由whence参数选择。A whence value of 0 measures from the beginning of the file, 1 uses the current file position, and 2 uses the end of the file as the reference point. whence值0从文件的开头开始测量,1使用当前文件位置,2使用文件的结尾作为参考点。whence can be omitted and defaults to 0, using the beginning of the file as the reference point.whence可以省略,默认为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'

In text files (those opened without a b in the mode string), only seeks relative to the beginning of the file are allowed (the exception being seeking to the very file end with seek(0, 2)) and the only valid offset values are those returned from the f.tell(), or zero. 在文本文件(在模式字符串中未使用b打开的文件)中,只允许相对于文件开头的搜索(例外情况是使用seek(0, 2)搜索文件结尾),唯一有效的偏移量值是从f.tell()返回的值,或零。Any other offset value produces undefined behaviour.任何其他偏移值都会产生未定义的行为。

File objects have some additional methods, such as isatty() and truncate() which are less frequently used; consult the Library Reference for a complete guide to file objects.文件对象有一些额外的方法,例如isatty()truncate(),它们使用频率较低;有关文件对象的完整指南,请参阅库参考。

7.2.2. Saving structured data with jsonjson保存结构化数据

Strings can easily be written to and read from a file. Numbers take a bit more effort, since the read() method only returns strings, which will have to be passed to a function like int(), which takes a string like '123' and returns its numeric value 123. 字符串可以很容易地写入和读取文件。数字需要更多的努力,因为read()方法只返回字符串,必须将字符串传递给int()之类的函数,int()接受'123'之类的字符串并返回其数值123。When you want to save more complex data types like nested lists and dictionaries, parsing and serializing by hand becomes complicated.当您想要保存更复杂的数据类型(如嵌套列表和字典)时,手动解析和序列化会变得复杂。

Rather than having users constantly writing and debugging code to save complicated data types to files, Python allows you to use the popular data interchange format called JSON (JavaScript Object Notation). Python允许用户使用流行的数据交换格式JSON(JavaScript对象表示法),而不是让用户不断编写和调试代码以将复杂的数据类型保存到文件中。The standard module called json can take Python data hierarchies, and convert them to string representations; this process is called serializing. 名为json的标准模块可以采用Python数据层次结构,并将其转换为字符串表示形式;这个过程叫做序列化。Reconstructing the data from the string representation is called deserializing. 从字符串表示重构数据称为反序列化Between serializing and deserializing, the string representing the object may have been stored in a file or data, or sent over a network connection to some distant machine.在序列化和反序列化之间,表示对象的字符串可能存储在文件或数据中,或者通过网络连接发送到某台远程机器。

Note

The JSON format is commonly used by modern applications to allow for data exchange. 现代应用程序通常使用JSON格式来进行数据交换。Many programmers are already familiar with it, which makes it a good choice for interoperability.许多程序员已经熟悉它,这使得它成为互操作性的一个很好的选择。

If you have an object x, you can view its JSON string representation with a simple line of code:如果你有一个对象x,你可以用一行简单的代码来查看它的JSON字符串表示:

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

Another variant of the dumps() function, called dump(), simply serializes the object to a text file. dumps()函数的另一个变体称为dump(),它只是将对象序列化为文本文件So if f is a text file object opened for writing, we can do this:因此,如果f是一个文本文件对象,打开进行写入,我们可以这样做:

json.dump(x, f)

To decode the object again, if f is a text file object which has been opened for reading:要再次解码该对象,如果f是一个文本文件对象,已被打开读取:

x = json.load(f)

This simple serialization technique can handle lists and dictionaries, but serializing arbitrary class instances in JSON requires a bit of extra effort. 这种简单的序列化技术可以处理列表和字典,但在JSON中序列化任意类实例需要一些额外的工作。The reference for the json module contains an explanation of this.json模块的参考文件对此进行了解释。

See also

pickle - the pickle modulepickle模块

Contrary to JSON, pickle is a protocol which allows the serialization of arbitrarily complex Python objects. JSON相反,pickle是一种允许序列化任意复杂Python对象的协议。As such, it is specific to Python and cannot be used to communicate with applications written in other languages. 因此,它是Python特有的,不能用于与用其他语言编写的应用程序通信。It is also insecure by default: deserializing pickle data coming from an untrusted source can execute arbitrary code, if the data was crafted by a skilled attacker.默认情况下也是不安全的:如果数据是由熟练的攻击者精心编制的,则对来自不受信任源的pickle数据进行反序列化可能会执行任意代码。