一种全新的思维方式|《Think Python》第13章
13. 文件与数据库
本章讲解如何让程序持久化保存数据,包括文件路径操作、文本读写、f-string、YAML 配置、shelve 键值数据库,以及用哈希值查找重复文件,实现从临时程序到持久程序的升级。
13.1 文件名与路径
文件被组织在目录(文件夹)中,每个程序都有当前工作目录。os 模块提供文件与目录操作函数。
import os# 获取当前工作目录os.getcwd()
运行结果:
'/home/dinsdale'
# 获取文件的绝对路径os.path.abspath('memo.txt')
运行结果:
'/home/dinsdale/memo.txt'
# 列出目录内容os.listdir('photos')
运行结果:
['digests.dat', 'digests.dir', 'notes.txt', 'new_notes.txt', 'mar-2023', 'digests.bak', 'jan-2023', 'feb-2023']
# 判断路径是否存在os.path.exists('photos')
运行结果:
True
# 判断是否为目录os.path.isdir('photos')
运行结果:
True
# 判断是否为文件os.path.isfile('photos/notes.txt')
运行结果:
True
# 跨系统拼接路径(自动适配 / 和 \)os.path.join('photos', 'jan-2023', 'photo1.jpg')
运行结果:
'photos/jan-2023/photo1.jpg'
13.2 f-strings
f-string 用于格式化字符串,可直接嵌入变量、表达式和函数调用。
num_years = 1.5num_camels = 23# 格式化输出字符串f'In {round(num_years * 12)} months I have spotted {num_camels} camels'
运行结果:
'In 18 months I have spotted 23 camels'
# 写入文件writer = open('camel-spotting-book.txt', 'w')writer.write(f'Years of observation: {num_years}\n')writer.write(f'Camels spotted: {num_camels}\n')writer.close()
# 读取文件data = open('camel-spotting-book.txt').read()print(data)
运行结果:
Years of observation: 1.5Camels spotted: 23
13.3 YAML
YAML 是人类与机器都易读的文本格式,常用于存储配置文件。
import yaml# 配置字典config = {'photo_dir': 'photos','data_dir': 'photo_info','extensions': ['jpg', 'jpeg'],}# 写入 YAML 文件config_filename = 'config.yaml'writer = open(config_filename, 'w')yaml.dump(config, writer)writer.close()
# 读取 YAML 文件reader = open(config_filename)config_readback = yaml.safe_load(reader)config_readback
运行结果:
{'data_dir': 'photo_info', 'extensions': ['jpg', 'jpeg'], 'photo_dir': 'photos'}
13.4 Shelve
shelve 提供键值数据库,像字典一样永久存储数据。
import shelve# 创建数据目录os.makedirs(config['data_dir'], exist_ok=True)# 拼接数据库路径db_file = os.path.join(config['data_dir'], 'captions')# 打开数据库(不存在则创建)db = shelve.open(db_file, 'c')
# 存入数据:键为文件路径,值为说明key = 'jan-2023/photo1.jpg'db[key] = 'Close up view of a cat nose'
# 读取数据db[key]
运行结果:
'Close up view of a cat nose'
# 遍历数据库for key in db:print(key, ':', db[key])
运行结果:
jan-2023/photo1.jpg : Close up view of a cat nose
# 关闭数据库db.close()
13.5 存储数据结构
Shelve 可以存储列表、字典等复杂结构,但修改后必须写回。
defsort_word(word):# 对单词字母排序,用于变位词分组return''.join(sorted(word))# 新建空数据库db = shelve.open('anagram_map', 'n')word = 'pots'key = sort_word(word)db[key] = [word]
# 追加单词(必须先取出、修改、再存回)word = 'tops'key = sort_word(word)anagram_list = db[key]anagram_list.append(word)db[key] = anagram_list
db[key]
运行结果:
['pots', 'tops']
13.6 检查文件是否相同
用二进制读取和 MD5 哈希判断文件内容是否一致。
# 二进制读取文件path1 = 'photos/jan-2023/photo1.jpg'data1 = open(path1, 'rb').read()type(data1)
运行结果:
bytes
# 比较两个文件内容defsame_contents(path1, path2): data1 = open(path1, 'rb').read() data2 = open(path2, 'rb').read()return data1 == data2
# 计算文件 MD5 哈希值import hashlibdefmd5_digest(filename): data = open(filename, 'rb').read() md5_hash = hashlib.md5() md5_hash.update(data)return md5_hash.hexdigest()
md5_digest(path1)
运行结果:
'aa1d2fc25b7ae247b2931f5a0882fa37'
13.7 遍历目录
递归遍历目录与子目录,列出所有文件路径。
defwalk(dirname):for name in os.listdir(dirname):# 拼接完整路径 path = os.path.join(dirname, name)if os.path.isfile(path):# 是文件则打印print(path)elif os.path.isdir(path):# 是目录则递归进入 walk(path)
walk('photos')
运行结果:
photos/digests.datphotos/digests.dirphotos/notes.txt...photos/jan-2023/photo1.jpg
13.8 调试
处理文件时常遇到空白字符、换行符、路径大小写等问题,可用 repr 查看不可见字符。
s = '1 2\t 3\n 4'print(repr(s))
运行结果:
'1 2\t 3\n 4'
13.9 术语表
| | |
|---|
| | |
| | |
| | |
| current working directory | | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
13.10 练习
13.10.2 替换文件内容
defreplace_all(old, new, source_path, dest_path):# 读取原文件内容 reader = open(source_path) content = reader.read() reader.close()# 替换字符串 new_content = content.replace(old, new)# 写入新文件 writer = open(dest_path, 'w') writer.write(new_content) writer.close()
13.10.3 变位词存入 Shelve
defadd_word(word, db): key = sort_word(word)if key in db:# 已存在则追加 word_list = db[key] word_list.append(word) db[key] = word_listelse:# 不存在则新建列表 db[key] = [word]