7. 输入输出

程序输出显示方式数据可以输出阅读形式可以文件备用探讨一些方式
7.1.
复杂输出格式

到目前为止我们遇到方式: 表达式语句 print() 函数。 (方式使用文件对象 write() 方法标准输出文件可以引用 sys.stdout。 相关信息参阅标准参考)。

输出格式控制只是打印空格分隔需要方式格式化输出包括以下方法

使用 格式化字符串面值字符串开头引号/引号添加 f F 。这种字符串可以 { } 字符之间输入引用变量面值 Python 表达式
>>>

year = 2016

event = 'Referendum'

f'Results of the {year} {event}'
'Results of the 2016 Referendum'

字符串 str.format() 方法需要手动操作使用 { } 标记变量替换位置并且可以提供详细格式化指令需要提供格式化信息下面代码格式化变量例子
>>>

yes_votes = 42_572_654

total_votes = 85_705_149

percentage = yes_votes / total_votes

'{:-9} YES votes {:2.2%}'.format(yes_votes, percentage)
' 42572654 YES votes 49.67%'

注意Notice how the yes_votes 填充空格并且负数添加负号这个例子打印 percentage 乘以 100 结果保留 2 数位带有百分号 (参阅 格式规格迷你语言 了解详情)。

最后可以用字符串切片和合操作完成字符串处理操作创建任何排版布局字符串类型支持字符串给定进行填充这些方法有用

如果需要花哨输出快速显示变量进行调试可以 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,通过 {expression} 表达式 Python 表达式添加字符串

格式说明可选表达式后面可以控制格式化方式 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'.

=
说明用于表达式扩展表达式文本等号表达式求值结果形式
>>>

bugs = 'roaches'

count = 13

area = 'living room'

print(f'Debugging {bugs=} {count=} {area=}')
Debugging bugs='roaches' count=13 area='living room'

参阅 说明表达式 了解 = 说明信息有关这些格式说明详情查看针对 格式规格迷你语言 参考指南
7.1.2.
字符串 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.

如果不想分拆格式字符串最好名称引用变量进行格式化不要位置操作可以通过传递字典方括号 '[]' 访问完成
>>>

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

内置函数 vars() 一同使用这种方式非常实用返回包含所有局部变量字典:
>>>

table = {k: str(v) for k, v in vars().items()}

message = " ".join([f'{k}: ' + '{' + k +'};' for k in table.keys()])

print(message.format(**table))
__name__: __main__; __doc__: None; __package__: None; __loader__: ...

例子以下代码产生整齐数据包含给定整数及其平方立方:
>>>

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

#
注意一行 '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

注意之间空格通过使用 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.
旧式字符串格式化方法

%
运算 () 用于字符串格式化给定 format % values (其中 format 字符串), format 中的 % 转换占位符 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()
返回 file object ,使用位置参数关键字参数:open(filename, mode, encoding=None)
>>>

f = open('workfile', 'w', encoding="utf-8")

第一实参文件名字符串第二实参包含描述文件使用方式字符字符串。mode 包括 'r' ,表示文件只能读取;'w' 表示只能现有同名文件覆盖);'a' 表示打开文件追加内容任何数据自动添加文件末尾。'r+' 表示打开文件进行读写。mode 实参可选省略默认 'r'。

通常情况文件是以 text mode 打开也就是说文件读写字符串这些字符串是以特定 encoding 编码如果没有指定 encoding ,默认平台有关 open() )。因为 UTF-8 现代事实上标准除非知道需要使用不同编码否则建议使用 encoding="utf-8" 。模式后面加上 'b' ,可以 binary mode 打开文件二进制模式数据是以 bytes 对象形式读写二进制模式打开文件不能指定 encoding 。

文本模式读取文件默认平台特定结束(Unix \n, Windows \r\n)转换 \n。文本模式数据默认 \n 转换平台特定结束这种操作方式后台修改文件数据文本文件没有问题破坏 JPEG EXE 二进制文件中的数据注意读写此类文件一定要使用二进制模式

处理文件对象最好使用 with 关键字优点子句结束文件正确关闭即便触发异常可以而且使用 with 相比 try-finally 代码简短
>>>

with open('workfile', encoding="utf-8") as f:

read_data = f.read()

#
我们可以检测文件是否自动关闭

f.closed
True

如果没有使用 with 关键字调用 f.close() 关闭文件即可释放文件占用系统资源

警告

调用 f.write() 使用 with 关键字调用 f.close(),即使程序正常退出**可能** 导致 f.write() 参数没有完全磁盘

通过 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 其他读取返回最多 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) #
元组转换字符串

f.write(s)
18

f.tell()
返回整数文件对象文件中的当前位置表示二进制模式文件开始字节数以及文本模式意义不明数字

f.seek(offset, whence)
可以改变文件对象位置通过参考添加 offset 计算位置参考 whence 参数指定。 whence 0 表示文件开头计算,1 表示使用当前文件位置,2 表示使用文件末尾作为参考省略 whence 默认 0,使用文件开头作为参考
>>>

f = open('workfile', 'rb+')

f.write(b'0123456789abcdef')
16

f.seek(5) #
定位文件中的 6 字节
5

f.read(1)
b'5'

f.seek(-3, 2) #
定位倒数 3 字节
13

f.read(1)
b'd'

文本文件模式字符串使用 b 打开文件允许对于文件开头搜索使用 seek(0, 2) 搜索文件末尾例外),唯一有效 offset f.tell() 返回 0。其他 offset 都会产生定义行为

文件对象还有一些额外方法使用频率 isatty() truncate() 有关文件对象完整指南查阅标准参考
7.2.2.
使用 json 保存结构数据

字符串可以容易文件文件读取数字麻烦一些因为 read() 方法返回字符串字符串必须传给 int() 这样函数接受 '123' 这样字符串返回数值 123。 想要保存嵌套列表字典复杂数据类型手动执行解析序列操作将会变得非常复杂

Python
允许使用流行数据交换格式 JSON (JavaScript Object Notation),不是用户持续编写调试代码复杂数据类型存入文件标准库模块 json 可以接受带有层级结构 Python 数据转换字符串表示形式这个过程称为 serializing。 根据字符串表示形式重建数据称为 deserializing。 序列序列之间用于代表对象字符串可以存储文件数据库或者通过网络连接发送远端主机

备注

JSON
格式通常用于现代应用程序数据交换程序员早已耳熟能详可谓交互操作不二

一行简单代码即可查看对象 JSON 字符串表现形式
>>>

import json

x = [1, 'simple', 'list']

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

dumps()
函数还有变体, dump() ,对象序列 text file 。因此如果 f text file 对象可以这样

json.dump(x, f)

再次解码对象如果 f 打开读取 binary file text file 对象

x = json.load(f)

备注

JSON
文件必须UTF-8编码打开JSON文件作为 text file 用于读写使用 encoding="utf-8" 。

这种简单序列技术可以处理列表字典 JSON 序列任意实例需要付出额外努力。json 模块参考包含解释

参见

pickle -
封存模块

JSON 不同,pickle 一种允许复杂 Python 对象进行序列协议因此 Python 特有不能用于其他语言编写应用程序通信默认情况安全如果数据手段高明攻击精心设计这种信任来源 pickle 数据可以执行任意代码