跳过正文
Python I/O指南
  1. Blog/

Python I/O指南

·2132 字·5 分钟
目录
Python - 这篇文章属于一个选集。
§ 6: 本文

程序的本质是处理数据,而输入与输出(I/O)是程序与外部世界沟通的桥梁。无论是向用户显示信息、从文件中读取配置,还是保存复杂的程序状态,都离不开 I/O 操作。

格式化字符串
#

向用户展示数据时,清晰、美观的格式至关重要。Python 提供了多种强大的字符串格式化方法。

F-Strings 格式
#

自 Python 3.6 引入,F-strings 已成为最推荐的格式化方式。它简洁、可读性强且性能出色。

  • 语法:在字符串字面值前加上 fF,并在 {} 中直接嵌入表达式或变量。
  • 特性{} 中的表达式会在运行时被求值。
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 对象,如函数、类实例,并完整地保留其数据和类型。
  • 劣势:
    1. 安全风险: 绝对不要 unpickle 来自不可信或未经身份验证来源的数据! 反序列化 pickle 数据可以执行任意代码。
    2. 跨语言不兼容: pickle 格式是 Python 专用的,其他语言无法解析。
    3. 版本问题: 不同 Python 版本间的 pickle 协议可能不兼容。

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
Python - 这篇文章属于一个选集。
§ 6: 本文