程序的本质是处理数据,而输入与输出(I/O)是程序与外部世界沟通的桥梁。无论是向用户显示信息、从文件中读取配置,还是保存复杂的程序状态,都离不开 I/O 操作。
格式化字符串#
向用户展示数据时,清晰、美观的格式至关重要。Python 提供了多种强大的字符串格式化方法。
F-Strings 格式#
自 Python 3.6 引入,F-strings 已成为最推荐的格式化方式。它简洁、可读性强且性能出色。
- 语法:在字符串字面值前加上
f或F,并在{}中直接嵌入表达式或变量。 - 特性:
{}中的表达式会在运行时被求值。
import math
year = 2025
event = 'Conference'
# 基本用法
print(f'Results of the {year} {event}')
# -> 'Results of the 2025 Conference'
# 表达式求值
print(f'Pi is approximately {math.pi:.3f}.')
# -> 'Pi is approximately 3.142.'
# :.3f 是一个格式说明符,表示保留三位小数的浮点数
# 对齐与填充
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
str.format() 方法#
在 F-strings 出现之前,str.format() 是标准方法。它依然非常强大,尤其是在格式化字符串本身是变量时。
# 位置参数
print('We are the {} who say "{}!"'.format('knights', 'Ni'))
# -> 'We are the knights who say "Ni!"'
# 关键字参数
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.'
repr() vs str()#
Python 中有两个内置函数可以将任何值转换为字符串:
str(): 返回值的“用户友好”表示形式。其目标是可读性。repr(): 返回值的“官方”或“开发者友好”表示形式。其目标是无歧义,理想情况下,eval(repr(obj)) == obj。
s = 'hello, world.'
print(str(s)) # -> hello, world.
print(repr(s)) # -> 'hello, world.' (带有引号,是合法的字符串字面值)
import datetime
today = datetime.datetime.now()
print(str(today)) # -> 2025-11-04 11:01:00.123456
print(repr(today)) # -> datetime.datetime(2025, 11, 4, 11, 1, 0, 123456)
文件读写#
文件处理是 I/O 的核心。Python 提供了简洁而强大的接口来操作文件。
open() 函数#
open() 函数返回一个文件对象,是所有文件操作的起点。
open(file, mode='r', encoding=None)
file: 文件路径字符串。mode: 一个字符串,指定文件的打开模式。'r': 读 (Read) - 默认值。如果文件不存在,则引发FileNotFoundError。'w': 写 (Write) - 文件若存在则清空其内容,若不存在则创建。'a': 追加 (Append) - 在文件末尾写入,若不存在则创建。'r+': 读写模式。'b': 二进制 (Binary) 模式,附加在其他模式后(如'rb','wb')。用于处理非文本文件(如图片、音频)。
encoding: 文本文件的编码格式。强烈推荐始终显式指定encoding='utf-8',以避免在不同操作系统上出现编码错误。
with 语句#
with 语句是处理文件对象的最佳实践。它能确保在代码块执行完毕或发生异常时,文件都会被自动、正确地关闭。
with open('workfile.txt', 'w', encoding='utf-8') as f:
f.write('This is the first line.\\n')
f.write('This is the second line.\\n')
# 此处文件 f 已被自动关闭
文件对象方法#
读取文件:
f.read(size): 读取并返回最多size个字符(文本模式)或字节(二进制模式)的数据。若size省略或为负,则读取整个文件。f.readline(): 读取并返回文件中的一行(直到并包括\\n字符)。for line in f: 遍历文件对象的最高效、最简洁的方式。它逐行读取,内存占用小。
with open('workfile.txt', 'r', encoding='utf-8') as f: for line in f: print(line, end='') # end='' 避免打印额外的换行符写入文件:
f.write(string): 将string的内容写入文件,并返回写入的字符数。注意:write()不会自动添加换行符\\n。- 要写入非字符串数据,必须先将其转换为字符串。
value = ('the answer', 42) with open('results.txt', 'w', encoding='utf-8') as f: f.write(str(value)) # 将元组转换为字符串后写入文件指针:
f.tell(): 返回一个整数,表示文件对象在文件中的当前位置(从文件开头算起的字节数)。f.seek(offset, whence): 移动文件指针。offset是偏移量,whence是参考位置(0: 文件开头,1: 当前位置,2: 文件末尾)。
持久化:JSON 与 Pickle#
当需要保存列表、字典等复杂数据结构时,简单的文本文件就力不从心了。这时需要序列化(Serialization)技术。
JSON: 通用数据交换#
JSON 是一种轻量级、人类可读的数据交换格式,被广泛用于 Web API 和配置文件。Python 的 json 模块提供了完整的支持。
json.dump(obj, fp): 将 Python 对象obj序列化为 JSON 格式并写入文件对象fp。json.load(fp): 从文件对象fp中读取 JSON 数据,并将其反序列化为 Python 对象。json.dumps(obj)/json.loads(s): 分别处理字符串而非文件对象。
import json
data = {
"name": "John Doe",
"age": 30,
"isStudent": False,
"courses": [
{"title": "History", "credits": 3},
{"title": "Math", "credits": 4}
]
}
# 写入 JSON 文件
with open('data.json', 'w', encoding='utf-8') as f:
json.dump(data, f, indent=4) # indent=4 使文件格式更美观
# 读取 JSON 文件
with open('data.json', 'r', encoding='utf-8') as f:
loaded_data = json.load(f)
print(loaded_data['courses'][0]['title']) # -> History
注意:JSON 的数据类型有限,Python 中的元组在序列化后会变成列表,且无法表示自定义的类对象。
Pickle: 专属序列化#
pickle 模块是 Python 特有的序列化解决方案。它使用二进制协议,能够处理几乎所有的 Python 数据类型,包括自定义类的实例。
- 优势: 功能强大,能够序列化复杂的 Python 对象,如函数、类实例,并完整地保留其数据和类型。
- 劣势:
- 安全风险: 绝对不要 unpickle 来自不可信或未经身份验证来源的数据! 反序列化
pickle数据可以执行任意代码。 - 跨语言不兼容:
pickle格式是 Python 专用的,其他语言无法解析。 - 版本问题: 不同 Python 版本间的
pickle协议可能不兼容。
- 安全风险: 绝对不要 unpickle 来自不可信或未经身份验证来源的数据! 反序列化
pickle 模块的接口与 json 模块非常相似:
pickle.dump(obj, fp): 将对象obj封存到文件对象fp。pickle.load(fp): 从文件对象fp中读取并重建一个 Python 对象。
import pickle
# 定义一个自定义类
class Task:
def __init__(self, name, priority):
self.name = name
self.priority = priority
def __repr__(self):
return f"Task(name='{self.name}', priority={self.priority})"
# 创建实例并封存
task1 = Task("Finish report", 1)
task2 = Task("Call Mom", 2)
tasks_list = [task1, task2]
# 使用二进制写模式 'wb'
with open('tasks.pkl', 'wb') as f:
pickle.dump(tasks_list, f)
# 从文件加载并解封
# 使用二进制读模式 'rb'
with open('tasks.pkl', 'rb') as f:
loaded_tasks = pickle.load(f)
print(loaded_tasks)
# -> [Task(name='Finish report', priority=1), Task(name='Call Mom', priority=2)]
print(type(loaded_tasks[0]))
# -> <class '__main__.Task'>
结论:选择合适的 I/O 工具#
- 格式化输出: 优先使用 F-strings,以获得最佳的可读性和性能。
- 文件处理: 始终使用
with open(...)结构,并明确指定encoding='utf-8'。 - 数据持久化:
- 当需要与其它系统(如 Web 前端)交换数据,或需要人类可读的配置文件时,选择 JSON。
- 当需要在 Python 程序间保存和恢复复杂的内部状态,且数据来源绝对安全时,可以使用 Pickle。