3. 文件 I/O 操作:读取配置文件与写入日志
文件操作,说白了就是让脚本跟硬盘上的数据打交道。在配方自动切换这个场景里,我们至少需要搞定三件事:读配置、写日志、管好路径。这三件事要是没做好,脚本跑起来就跟没头苍蝇一样。
我个人习惯把文件操作分成两类:一类是脚本启动时一次性读取的静态数据,比如配方参数、设备地址;另一类是运行时持续写入的动态数据,比如切换记录、报错信息。前者用配置文件,后者用日志文件。
3.1 读取配置文件:INI / JSON / CSV
配置文件就像脚本的「记忆卡」。你不想每次改个参数都去翻代码吧?把可变参数抽到配置文件里,改配置就行,代码不用动。
3.1.1 INI 格式:老派但够用
INI 文件结构简单,就是节(Section)+ 键值对。适合存设备参数、通信端口这类固定结构的数据。
; config.ini
[Device]
IP = 192.168.1.100
Port = 502
Timeout = 5
[Recipe]
Default = recipe_001.csv
RetryCount = 3
Python 里用 configparser 模块读取,几行代码搞定:
import configparser
config = configparser.ConfigParser()
config.read('config.ini')
ip = config['Device']['IP']
port = config['Device'].getint('Port')
timeout = config['Device'].getfloat('Timeout')
print(f"设备地址:{ip}:{port},超时:{timeout}s")
getint()、getfloat() 代替直接索引,避免类型转换出错。我在项目里见过有人直接用 config['Device']['Port'] 拿到的字符串去拼 SQL,结果端口号变成了字符串拼接,排查了半天。
3.1.2 JSON 格式:现代工业标准
JSON 支持嵌套结构,比 INI 灵活得多。配方数据往往有多层结构——一个配方包含多个工步,每个工步又有多个参数。这种场景 JSON 是首选。
{
"recipe_name": "recipe_001",
"version": "2.1",
"steps": [
{
"step_id": 1,
"action": "heat",
"target_temp": 180,
"hold_time": 30
},
{
"step_id": 2,
"action": "cool",
"target_temp": 25,
"hold_time": 15
}
]
}
读取 JSON 用内置的 json 模块:
import json
with open('recipe.json', 'r', encoding='utf-8') as f:
recipe = json.load(f)
print(f"配方名称:{recipe['recipe_name']}")
for step in recipe['steps']:
print(f" 工步 {step['step_id']}:{step['action']} 到 {step['target_temp']}℃")
3.1.3 CSV 格式:表格数据的好搭档
CSV 适合存批量数据,比如配方列表、历史记录。每行一条记录,用逗号分隔字段。
recipe_id,recipe_name,target_temp,hold_time
001,recipe_heat_180,180,30
002,recipe_cool_25,25,15
003,recipe_anneal_120,120,60
Python 用 csv 模块读取:
import csv
recipes = []
with open('recipe_list.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
recipes.append(row)
for r in recipes:
print(f"{r['recipe_id']} - {r['recipe_name']}")
| 格式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| INI | 设备参数、固定配置 | 简单直观,人类可读 | 不支持嵌套 |
| JSON | 配方数据、复杂结构 | 支持嵌套,解析快 | 手写容易漏括号 |
| CSV | 批量数据、历史记录 | Excel 可直接打开 | 不支持层级结构 |
3.2 写入日志文件
日志是脚本的「黑匣子」。配方切换出问题了,第一件事就是翻日志。没有日志,你只能靠猜。
Python 内置的 logging 模块足够用,别自己写文件追加逻辑。我见过有人用 open('log.txt', 'a') 手动写日志,结果多线程环境下日志全乱了。
import logging
logging.basicConfig(
filename='recipe_switch.log',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logging.info('配方切换开始:recipe_001')
logging.warning('温度偏差超过阈值:+3℃')
logging.error('配方文件未找到:recipe_999.json')
日志级别从低到高:DEBUG、INFO、WARNING、ERROR、CRITICAL。生产环境一般设 INFO 级别,DEBUG 只在调试时开。
RotatingFileHandler 按大小切分,或者 TimedRotatingFileHandler 按时间切分。我习惯每天一个日志文件,保留 30 天。
from logging.handlers import TimedRotatingFileHandler
handler = TimedRotatingFileHandler(
'recipe_switch.log',
when='midnight',
interval=1,
backupCount=30
)
handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
logger = logging.getLogger()
logger.addHandler(handler)
3.3 文件路径处理
路径处理是文件操作里最容易翻车的地方。Windows 用反斜杠 \,Linux 用正斜杠 /。硬编码路径的脚本,换个环境就崩。
用 os.path 或 pathlib 处理路径,跨平台无压力。我个人更推荐 pathlib,API 更现代。
from pathlib import Path
# 当前脚本所在目录
base_dir = Path(__file__).parent
# 配置文件路径
config_path = base_dir / 'config' / 'device.ini'
# 日志文件路径
log_path = base_dir / 'logs' / 'recipe_switch.log'
# 确保目录存在
log_path.parent.mkdir(parents=True, exist_ok=True)
print(f"配置文件:{config_path}")
print(f"日志文件:{log_path}")
base_dir + '/' + 'config' + '/' + 'device.ini' 这种写法在 Windows 下会出问题。用 os.path.join() 或 pathlib 的 / 运算符。
3.4 知识结构图:文件 I/O 操作全景
3.5 实战建议:把这些串起来
在实际项目中,我通常这样组织文件操作:
- 启动时:用 JSON 读取配方数据,用 INI 读取设备参数
- 运行时:每次切换配方,记录一条 INFO 日志
- 异常时:捕获异常,写入 ERROR 日志,并记录当前上下文
- 路径管理:所有路径用
pathlib动态生成,不写死
- 文件路径不要用相对路径,用
__file__定位脚本所在目录 - 日志文件要轮转,别让一个文件撑爆硬盘
- 配置文件编码统一用 UTF-8,避免跨平台乱码
- 读取文件时加
try-except,文件不存在要有默认值或报错提示
嗯,文件 I/O 这块其实不难,但细节多。你想想看,一个配方切换脚本跑在生产线上,突然因为路径写死找不到配置文件,或者日志文件撑爆了硬盘——这种低级错误,咱们不能犯。