AI编程(二十八):配置文件管理 — 改设置不动代码,程序才像个”正经软件”

作者:

在

为什么专业程序的设置都放在单独的文件里?今天你做出第一个”改配置不改代码”的程序

一、一个改到崩溃的真实场景

先讲一个你大概率亲历过的惨案。

假设你(或者AI)写了一个”每日打卡提醒程序”,里面写着提醒时间是早上8点。用了一周,你想改成7点半——于是你打开代码,找到那行提醒时间 = "08:00",改成"07:30",保存,重新运行。

又过了两周,你想改提醒语;再过一周,你想把打卡记录存到另一个文件夹……每次改动都得:打开代码 → 全文搜索找到那行 → 改 → 提心吊胆别改错别的地方 → 保存重跑。

问题出在哪?程序分成两种东西:逻辑(怎么判断、怎么提醒)和设置(几点提醒、存哪里、说什么话)。逻辑写好之后基本不动,设置却三天两头变。把两种东西混在一起,等于为了换个灯泡把整面墙拆了。

生活里早就有了答案:你家空调的”设置”不在空调的电路板上,而在遥控器和面板上;汽车的座椅记忆不在发动机里,而在设置菜单里。程序也一样——设置应该放在一个单独的文件里,程序每次启动先读它。

这个文件,就叫配置文件。

改配置不改代码,是”玩具程序”和”正经软件”的分水岭之一。你手机上每个App的”设置”页面,背后都对应着一个配置文件。今天,你就给自己的程序装上这道”设置面板”。

好消息是:今天没有全新的语法。配置文件用的就是上一篇的JSON存取(json.load / json.dump),加上你早就会的字典和文件判断。今天学的是一个套路——把学过的招组合成专业做法。

二、配置文件的套路:一个字典 + 一个JSON文件

先看配置文件长什么样。就是个普通的JSON文件,比如config.json:

{
  "提醒时间": "07:30",
  "提醒语": "该打卡啦!",
  "记录文件": "打卡记录.csv",
  "声音提醒": true
}

再看程序怎么用它。完整的”读配置”套路只有三步:

import json

# 第1步:打开配置文件,翻译成字典
with open("config.json", encoding="utf-8") as f:
    配置 = json.load(f)

# 第2步:从字典里取设置项——第8篇的字典招全在这
print(提醒时间 := 配置["提醒时间"])   # 07:30

# 第3步:程序后面全部用变量/字典,不再出现"写死的值"
print(f"每天{配置['提醒时间']}提醒:{配置['提醒语']}")
if 配置["声音提醒"]:
    print("🔔 叮叮叮!")

看到没?套路的核心就一句话:把”写死在代码里的值”搬进JSON文件,程序启动时读进来。以后想改提醒时间?用记事本打开config.json改一下就行,代码一个字都不用碰——就像调空调温度不用拆外机。

反过来,程序要把设置存回去(比如用户在程序里改了设置),就是上一篇的原样招式:

配置["提醒时间"] = "06:45"          # 改字典
with open("config.json", "w", encoding="utf-8") as f:
    json.dump(配置, f, ensure_ascii=False, indent=2)   # 整个存回去

一读一写,配置文件管理的骨架就完了。但直接拿这套骨架上路,会撞上一个大坑——第一次运行时,config.json根本不存在。程序一启动就FileNotFoundError崩给你看。这就引出今天的主角套路。

三、核心套路:没有配置文件?自动创建默认配置

专业程序第一次启动时,如果没找到配置文件,不会崩,而是自动生成一份默认配置,顺便告诉你:”配置文件已创建,改完再运行。”下次启动,就用你改过的了。

这个套路叫”默认配置 + 自动创建“,写出来长这样:

import json
import os

配置文件名 = "config.json"

# 默认配置:程序出厂设置
默认配置 = {
    "提醒时间": "08:00",
    "提醒语": "该打卡啦!",
    "记录文件": "打卡记录.csv",
    "声音提醒": True
}

if os.path.exists(配置文件名):
    # 已经有配置文件 -> 读出来用
    with open(配置文件名, encoding="utf-8") as f:
        配置 = json.load(f)
    print("配置已加载。")
else:
    # 第一次运行 -> 创建默认配置
    配置 = 默认配置
    with open(配置文件名, "w", encoding="utf-8") as f:
        json.dump(配置, f, ensure_ascii=False, indent=2)
    print(f"第一次运行,已创建默认配置文件 {配置文件名},你可以打开修改后再运行。")

print(f"当前设置:每天 {配置['提醒时间']} 提醒——{配置['提醒语']}")

今天唯一的”新面孔”是os.path.exists(文件名)——问操作系统”这个文件存在吗?”,返回True或False。就这一个函数,配一个if/else,三分钟消化。

(为什么用os.path.exists而不是try/except?两种都行,第10篇的try/except也能接住FileNotFoundError。用os.path.exists读起来更直白——”文件在吗?在就读,不在就建”,思路一眼看穿。跟AI说需求时两种都可以,看你顺口。)

跑一遍这个程序感受一下:第一次运行,它自动给你生成一份config.json;你打开把"提醒时间"改成"07:30"存盘;再运行,它就按7点半来了。改设置,全程没碰代码。这就是”正经软件”的手感。

四、进一阶:配置项”缺胳膊少腿”也不崩

再设想一个场景:你给朋友用了这个程序,朋友打开config.json手滑删掉了一行,或者你程序升级加了个新设置项”天气提醒”,而朋友的配置文件还是旧版、没这项——程序一读配置["天气提醒"],KeyError,崩了。

专业做法是”补全“:拿用户配置和默认配置逐项对比,缺哪项就用默认值补上。一个循环搞定:

# 接上一节:读出用户的 配置 之后,逐项检查
for 键, 默认值 in 默认配置.items():
    if 键 not in 配置:
        配置[键] = 默认值          # 缺了就补上默认值
        print(f"配置缺了「{键}」,已自动补上默认值:{默认值}")

# 补完存回去,下次就齐了
with open(配置文件名, "w", encoding="utf-8") as f:
    json.dump(配置, f, ensure_ascii=False, indent=2)

这个套路你以后会反复用,值得起个名字记下来——”默认配置兜底“:不管用户的配置文件残缺成什么样,程序永远有一份出厂设置托底,最多降级,绝不崩溃。你今天装的每个正经软件都是这么干的,只是你今天第一次自己写出来。

顺便送一个更简洁的写法——字典的setdefault(”设默认值”):

for 键, 默认值 in 默认配置.items():
    配置.setdefault(键, 默认值)   # 有则不动,无则补默认值

一行顶四行,效果一样。看不懂没关系,把两段都丢给AI说”帮我解释setdefault和if补全是不是一回事”,它会给你讲清楚。

五、实战:完整的”打卡提醒配置管理器”

把今天所有招揉成一个完整的小工具:能加载配置、改配置、保存配置、重置回出厂设置。这已经是一个正经软件的”设置模块”了:

import json
import os

配置文件名 = "config.json"
默认配置 = {
    "提醒时间": "08:00",
    "提醒语": "该打卡啦!",
    "记录文件": "打卡记录.csv",
    "声音提醒": True
}

def 读配置():
    """加载配置:有文件就读+补全,没文件就创建默认的。永远返回一个完整的字典。"""
    if os.path.exists(配置文件名):
        with open(配置文件名, encoding="utf-8") as f:
            配置 = json.load(f)
        # 默认配置兜底:缺哪项补哪项
        新增 = False
        for 键, 默认值 in 默认配置.items():
            if 键 not in 配置:
                配置[键] = 默认值
                新增 = True
        if 新增:
            存配置(配置)
    else:
        配置 = 默认配置.copy()   # copy一份,免得改字典时把出厂设置也改了
        存配置(配置)
    return 配置

def 存配置(配置):
    """把配置字典存回JSON文件,中文正常、带缩进。"""
    with open(配置文件名, "w", encoding="utf-8") as f:
        json.dump(配置, f, ensure_ascii=False, indent=2)

def 显示配置(配置):
    print("——— 当前设置 ———")
    for 键, 值 in 配置.items():
        print(f"  {键}:{值}")
    print("—————————————")

def 修改配置(配置):
    显示配置(配置)
    键 = input("要改哪一项?(直接回车取消)").strip()
    if 键 == "":
        return
    if 键 not in 配置:
        print("没有这一项哦。")
        return
    新值 = input(f"把「{键}」改成:").strip()
    # 类型跟随原值:原来是布尔就转布尔,原来是数字就转数字
    if isinstance(配置[键], bool):
        配置[键] = 新值 in ("True", "true", "是", "1")
    elif isinstance(配置[键], int):
        配置[键] = int(新值)
    else:
        配置[键] = 新值
    存配置(配置)
    print("已保存。")

def 重置配置():
    存配置(默认配置.copy())
    print("已恢复出厂设置。")

# ---- 主流程 ----
配置 = 读配置()
while True:
    print("n1.查看设置  2.修改设置  3.恢复出厂  4.退出")
    选择 = input("选:").strip()
    if 选择 == "1":
        显示配置(配置)
    elif 选择 == "2":
        修改配置(配置)
    elif 选择 == "3":
        重置配置()
        配置 = 读配置()
    elif 选择 == "4":
        break

跑一下你会发现:第一次运行自动出配置文件;进”修改设置”改个提醒语,退出再进来还在(因为存回文件了);把config.json删掉再运行,它又自动长出来。这个程序已经不怕你折腾了——这就是配置文件给程序上的”保险”。

把整套代码丢给AI,说一句”帮我加一项’每周总结日’的配置,周几生成周报”,你会发现改动极其干净:默认配置加一项、代码里用一项,完事。配置和逻辑分家之后,程序的每处改动都变小了——这就是这套套路真正的回报。

六、四个必踩的坑(先打疫苗)

坑1:config.json不存在,程序直接崩。

就是第二节那个FileNotFoundError。疫苗:永远走”默认配置 + 自动创建”套路,别假设文件一定在。第一次运行的体验很重要——新手用户可不知道要先手动建配置文件。

坑2:改配置时忘了存回文件,改了等于没改。

字典改完只在内存里,程序一退出就没了。疫苗:改完立刻json.dump存回去。判断标准很简单——”我这个改动,明天启动还在吗?”不在,就是忘了存。

坑3:json.dump忘写ensure_ascii=False和encoding="utf-8"。

老朋友了,上一篇刚打过疫苗,今天再强调一次:配置里有中文(提醒语多半有),这两样缺一个,轻则满屏u63d0u9192天书,重则UnicodeDecodeError。当肌肉记忆。

坑4:默认配置字典被程序改动”污染”。

看实战代码里那句默认配置.copy()——如果不copy,程序里改配置["提醒时间"]时,其实改的就是默认配置本身(Python里字典赋值传的是”引用”,两个变量指向同一个字典)。结果”恢复出厂设置”恢复出来的还是你改过的值。疫苗:把默认配置当”模板”用之前,先.copy()一份。这个坑非常隐蔽,今天踩一次,以后少踩十年。

七、跟AI协作的正确姿势(配置特供)

配置文件这种”套路活”,正是AI最擅长一次写对的。三个要点:

  1. 需求里说清”配置项清单”。”帮我写一个打卡提醒程序,配置放config.json,配置项有:提醒时间、提醒语、记录文件、声音提醒(布尔)。第一次运行自动创建默认配置,配置缺项自动补默认值。”——把配置项列全,AI连兜底逻辑都给你写好。说清配置项,AI就不猜
  2. 让它解释”读配置”函数。AI给你写完读配置(),别客气,追问一句:”逐行解释这个函数,特别是copy()和补全那段。”今天的新套路就藏在那几行里,问透了就是你的
  3. 改动配置项时整段重说。加新配置项时,把完整需求再说一遍(”配置项增加’每周总结日’,默认周日”),别只说”加个总结日”——AI可能只改一半(加了默认配置,忘了在补全循环里兜住)。让它”改完自查:新配置项在默认配置、补全、使用三处都接上了吗”

另外送你一个高频句式:”所有可变设置放进config.json,带默认值和自动创建,代码里不许出现写死的设置值“——这一句能让AI直接产出专业水准的程序骨架。

八、今日小练习

练习1:给老程序装配置

翻出你第22篇的简易记账本,把里面的写死值(比如记账文件名”账本.csv”、每月预算)搬进config.json,用”默认配置 + 自动创建”套路,改配置不用改代码。(提示:先列清单——哪些值是”设置”?然后建一个默认配置字典,把代码里那些写死的值换成配置["xxx"]。别忘了os.path.exists那三步。)

练习2:配置体检器

写一个工具:读任意一个config.json,对照一份”期望配置清单”,报告哪些项缺失、哪些项多出来、哪些项类型不对(该是布尔却是字符串),并能一键补齐缺失项。(提示:两个字典逐键对比——键 not in 配置查缺失;反过来遍历配置查多余;类型检查用isinstance(值, bool)。想把”期望清单”写灵活点?让它自己也是个JSON文件,你的工具就通用了。)

练习3:带界面的设置小面板

做一个”程序启动器”:启动时读config.json里的”上次打开的文件””主题(亮/暗)””字号”,用一句f-string把这些设置”应用”出来(打印就行,不用真做界面);支持--set 键 值的方式从命令行改配置。(提示:命令行参数用import sys,sys.argv就是参数列表——sys.argv[1]是--set、[2]是键、[3]是值。看不懂命令行参数?把这段提示原样丢给AI:”帮我实现这个,顺便解释sys.argv每一项是什么。”)

写在后面:你的程序开始”讲规矩”了

盘点今天:没有新语法,全是套路——设置和逻辑分家(配置文件该独立)、默认配置 + 自动创建(第一次运行不崩)、默认配置兜底(缺项不崩、.copy()防污染)、改完即存(改动明天还在)。

这四个套路,是从”会写代码”到”写出像样软件”的第一级台阶。你可能已经发现了:专业软件和你的小工具,语法上没有区别,区别全在这些”讲究”上。

下一篇我们换个方向——数据不总是干干净净的。CSV里的多余空格、大小写混乱、缺胳膊少腿的行……真实世界的数据是”脏”的,数据清洗就是给数据洗澡的第一步。

下篇预告

第二十九篇:数据清洗入门——拿到一份脏兮兮的表格数据,空格、错格式、缺值混在一起,怎么用字符串处理和条件筛选把它洗干净?下一篇见。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注