ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

零基础入门python09:模块、包、类型注解与日志调试

零基础入门python09:模块、包、类型注解与日志调试 零基础入门python09模块、包、类型注解与日志调试一、上一篇课后练习讲解按月份统计可以给Record增加occurred_on日期字段再在summary(month)中比较年月。不要用字符串模糊匹配日期日期应该使用datetime.date这样跨月份计算不会出错。可执行验收答案fromdataclassesimportdataclassfromdatetimeimportdatedataclassclassRecord:amount:floatoccurred_on:datedefsummary(records:list[Record],year:int,month:int)-float:returnsum(r.amountforrinrecordsifr.occurred_on.yearyearandr.occurred_on.monthmonth)records[Record(10,date(2025,1,31)),Record(20,date(2025,2,1))]assertsummary(records,2025,1)10assertsummary(records,2025,2)20print(month summary passed)一月 31 日和二月 1 日的断言能暴露startswith(2025-01)等脆弱实现。输入层负责把 ISO 字符串转换成dateRepository 负责持久化统计函数只接收已经验证的对象。二、项目为什么要拆模块一个文件超过几百行后查找和测试都会变困难。我们把项目拆成模型、存储、业务和入口ledger_app/ ├─ models.py # Record数据结构 ├─ repository.py # JSON读写 ├─ service.py # 添加和统计规则 └─ main.py # 命令行入口三、类型注解和日志importloggingfrompathlibimportPath loggerlogging.getLogger(__name__)defload_records(path:Path)-list[dict]:读取记录并声明输入输出类型日志只记录路径和数量不记录敏感账目。logger.info(开始读取账本文件%s,path)# 具体读取代码放在 repository 中业务层只依赖这个接口。return[]类型注解不会替 Python 自动阻止所有错误但会让 IDE、静态检查和读代码的人知道函数契约。日志使用占位符参数避免在不需要输出时提前拼接字符串。四、验收从项目根目录运行python-m ledger_app.main python-m compileall ledger_app故意把 JSON 文件改坏日志应该记录读取失败并返回友好错误不要在日志中输出密码、JWT 或完整账单内容。课后练习增加一个--verbose参数只有开启时才输出 DEBUG 日志。深入实验模块化任务管理器前面的账单函数已经把输入和业务计算分开。本篇把同一思路扩展成一个真正的命令行任务管理器数据保存到 JSON命令行负责解析参数服务函数负责新增、完成和查询任务。fromdataclassesimportdataclass,asdictfrompathlibimportPathimportjsondataclassclassTask:title:strdone:boolFalseclassTaskStore:def__init__(self,path:Path):self.pathpath self.tasks:list[Task][]self.load()defload(self)-None:ifnotself.path.exists():returndatajson.loads(self.path.read_text(encodingutf-8))self.tasks[Task(**item)foritemindata]defadd(self,title:str)-Task:titletitle.strip()ifnottitle:raiseValueError(任务标题不能为空)taskTask(titletitle)self.tasks.append(task)self.save()returntaskdefcomplete(self,index:int)-None:try:taskself.tasks[index]exceptIndexErrorasexc:raiseValueError(任务编号不存在)fromexc task.doneTrueself.save()defsave(self)-None:payload[asdict(task)fortaskinself.tasks]self.path.write_text(json.dumps(payload,ensure_asciiFalse,indent2),encodingutf-8)这里的 Store 负责数据生命周期命令行层不应该直接拼 JSON。以后把 JSON 换成 SQLite只替换 Store用户操作仍然保持不变。验收结果执行新增、完成、重新启动三个场景确认 JSON 文件存在、完成状态持久化、空标题和越界编号得到可读错误。课后练习增加优先级和截止日期先写两个失败测试再实现下一篇将把日志和配置放进模块而不是继续把所有代码堆在一个文件里。本篇结束完整模块文件本节不是代码片段而是本篇结束时该模块的完整版本。请先备份旧文件再整体替换替换后重新运行本篇命令和测试。阅读时重点看本篇新增的函数、事务边界和错误处理未涉及的代码先不要自行删减。main.pyfromtypingimportOptionaldeffind_user(user_id:int,users:list[dict])-Optional[dict]:returnnext((uforuinusersifu[id]user_id),None)print(find_user(1,[{id:1,name:Alice}]))
返回列表