资讯详情

资讯详情

自学编程第36天:从处理CSV脏数据到建立编程思维闭环

1. 写在第三十六天自学者最真实的阶段你不是第一天打开教程时那个充满新鲜感的人了也还没到三个月后能独立带小项目的状态。第三十六天恰好卡在一个很微妙的节点上基础语法已经见过一轮简单的小练习能写出来但一碰到“自己从头想思路”的需求还是会卡壳会翻文档会在报错面前愣住。这个节点我给它起了个名字叫“新手墙与回生期之间”。第一周的那种兴奋感早就消退了每天打开编辑器更像一种习惯而不是热情驱动。与此同时你开始隐约感觉到知识的轮廓——不是每个名词都陌生了但很多概念只是“见过”离“会用”还有不小的距离。第三十六天真正考验的不是你能背多少API而是你有没有建立起一套属于自己的“遇到问题怎么查、怎么试、怎么记”的闭环。我身边不少人自学IT有的从Python入门有的直接啃Java还有的从JavaScript前端起步。不管你走的是哪条路第三十六天左右都会遇到相似的困惑进度感觉变慢了很多细节记不住写代码时反复刷手机查资料又觉得自己好像什么都没学会。这些症状很正常但它确实需要一套应对策略否则很容易在第40天左右放弃。这篇文章就把我自己第三十六天的状态、踩过的坑、调整后的方法、以及当天实际写的一段代码完整记录下来。里面有时间和进度上的复盘也有非常具体的操作细节。不管你是刚起步的新手还是已经过了这个阶段想回头看看的老手应该都能找到一些可以参考的地方。2. 第三十六天复盘我到底学了什么卡在了哪里先列一下我这三十六天的整体路线方便你对号入座也方便后面理解我当天的决策逻辑。我的路线是Python语法基础 → 文件读写与异常处理 → 函数与模块 → 面向对象初步 → 常用内置模块 → 数据处理小项目。这个路线不算新奇但对自学者来说它有一个好处每一步的产出都可以用一个小脚本验证不会出现“学了半个月还什么都不能跑”的情况。前20天Python基础语法和流程控制学得还算顺手。第21天到第28天开始碰函数、作用域、递归这些概念明显感到难度往上跳了一截。原因也很简单——语法是“背”函数和设计思路是“想”。同样是写一个计算器用if-else堆出来和一个用函数抽象出来后者要求你在脑子里多一层“封装”的视角。这层视角有的人上手快有的人需要多磨一阵子。到了第29天到第34天我开始接触文件读写open、with、csv模块和异常处理try-except顺手还试了试json模块做数据序列化。这一阶段我觉得很有收获因为终于可以把结果写到文件里了而不是每次都在控制台里print完就算结束。但也是从这里开始我发现自己对“数据格式”的理解很薄弱。比如csv文件里逗号分隔、引号嵌套的规则json里嵌套字典和列表的组合光是这些就能写错好几次。到了第三十五天我给自己做了一次摸底测验——不看任何资料独立完成一个小任务读取一个csv文件筛选出符合条件的数据行计算平均值把结果写到一个新的文件里。结果很诚实核心流程能写出来但边界处理漏洞百出。比如文件不存在时怎么提示某一行数据缺了一个字段怎么办日期格式不统一怎么判断这些平时看教程都会一带而过真正独立写的时候全都冒出来了。所以第三十六天我没有继续往新知识推进而是专门做了一件事把“数据处理小任务”重新做一遍这次严格要求自己处理好所有边界情况并顺手把调试过程、踩坑记录、优化思路全部整理成笔记。我越来越觉得自学的进度感不能靠“学了几个新章节”来衡量得靠“能独立完成什么任务”来衡量。哪怕只做一个任务做扎实了比囫囵吞十个知识点有用。3. 当天实际学习过程从需求到代码的一次完整打磨这里把第三十六天上午到下午的学习过程尽可能完整地还原出来。没有加滤镜也没有隐藏那些“笨拙”的尝试。如果你也在自学看到这些过程应该会有很强的代入感。3.1 任务目标与数据准备我给自己设定的任务很简单本地有一个模拟用户访问日志的csv文件字段包括访问时间、用户ID、页面路径、停留时长秒、是否新用户。需求是筛出停留时长大于等于30秒的记录按天分组计算平均停留时长并把结果输出到新的csv文件。我花了十分钟手写了一个demo数据文件大概30行左右故意埋了几个坑进去有一行时间字段格式不统一有一行停留时长是空字符串还有一行整行数据都是空白。这些都是真实场景里最常见的脏数据平时教程练习里几乎不会出现但实际处理数据时躲不开。这个任务难度不夸张正好卡在基础语法和数据处理的交汇处对第三十六天来说是个合适的“能力锚点”。复杂了容易打击信心简单了又试不出真实水平。3.2 第一版代码功能“能用”但很不结实我第一版代码用了三分钟写出来核心逻辑大概长这样import csv from collections import defaultdict with open(visits.csv, r, encodingutf-8) as f: reader csv.DictReader(f) data list(reader) groups defaultdict(list) for row in data: if int(row[停留时长]) 30: date row[访问时间][:10] groups[date].append(int(row[停留时长])) result [] for date, durations in groups.items(): avg sum(durations) / len(durations) result.append({日期: date, 平均停留时长: round(avg, 2)}) with open(result.csv, w, encodingutf-8, newline) as f: writer csv.DictWriter(f, fieldnames[日期, 平均停留时长]) writer.writeheader() writer.writerows(result) print(完成)这段代码很典型地反映了我当前的水平主要流程会写数据结构和基本逻辑没问题但如果直接拿去处理真实数据十次里可能有一半的几率崩掉。问题出在几个地方。第一int(row[停留时长])遇到空字符串会直接抛出ValueError整段程序中断。第二如果某一行数据连停留时长这个键都不存在会抛KeyError。第三访问时间的格式如果是 ‘2025/03/15 08:30:00’[:10]切出来的字符串是 ‘2025/03/15’和后端统计时的 ‘2025-03-15’ 格式不一致按天分组就会出bug。这些坑在教程里基本不会出现因为教程的数据都是干净的、规整的。但现实中的数据尤其是从业务系统导出、或者爬虫抓下来的数据脏到千奇百怪。第三十六天这个阶段我最大的进步不是多写了多少行代码而是开始具备一种“这行数据可能有问题”的警觉意识。3.3 边界条件的处理代码的“结实感”从哪里来意识到上述问题之后我开始有意识地给代码加防护。加防护不等于不管你三七二十一就try-except吞掉所有异常而是明确区分“可控的坏数据”和“不可控的错误”。对于这版代码我把处理逻辑拆成了几层。第一层逐行判断是否存在必要字段用row.get(字段名)而不是直接row[字段名]这样键缺失时能拿到None而不是抛异常。第二层对字段值做类型转换写一个小函数safe_int(value)捕获异常返回一个默认值或标记该行无效。第三层对时间字段做格式化兼容先统一替换分隔符再切片保证按天分组的key一致。改完之后的核心逻辑是这样import csv from collections import defaultdict def safe_int(value): try: return int(value) except (ValueError, TypeError): return None def normalize_date(raw): # 兼容 2025-03-15 和 2025/03/15 两种写法 return raw.strip().replace(/, -)[:10] groups defaultdict(list) with open(visits.csv, r, encodingutf-8) as f: reader csv.DictReader(f) for row in reader: if not row.get(停留时长): continue duration safe_int(row[停留时长]) if duration is None or duration 30: continue date normalize_date(row.get(访问时间, )) if not date: continue groups[date].append(duration) if not groups: print(没有符合条件的数据) else: with open(result.csv, w, encodingutf-8, newline) as f: writer csv.DictWriter(f, fieldnames[日期, 平均停留时长]) writer.writeheader() for date, durations in groups.items(): avg round(sum(durations) / len(durations), 2) writer.writerow({日期: date, 平均停留时长: avg}) print(f完成共处理 {len(groups)} 天数据)这版代码加了safe_int函数、normalize_date函数每一层都在处理真实数据中最常见的问题。我特意留了一个小坑没补如果同一个日期里所有数据都被过滤掉groups里就不会有这个日期。但如果有符合条件但没有任何数据的日期它自然也不会出现。这个逻辑是对的。normalize_date函数还有一个问题没解决如果row.get(访问时间)返回的是一个空字符串strip()之后会是空串[:10]也是空串那date就会是空字符串这个日期会被写进结果里。所以我加了一个if not date: continue把这种情况跳过去。3.4 输出文件的编码与格式陷阱写入结果文件时我踩了一个很经典的坑csv文件用 Excel 打开出现中文乱码。原因很简单Python 的 open 里指定了utf-8但 Excel 默认用gbk或系统本地编码去解析csv文件。解决方案有两种一种是把编码参数改成utf-8-sig这样会在文件开头写入BOM头Excel 能自动识别另一种是干脆写成gbk编码但不推荐因为通用性差。我最终用的是utf-8-sig这也是处理中文数据时的常见做法。with open(result.csv, w, encodingutf-8-sig, newline) as f:同时必须记得newline不写这个参数的话在 Windows 环境下写csv文件每两行之间会多一个空行。这个细节不致命但确实影响观感。两天前我第一次写csv输出文件时就踩过这个坑当时还以为是代码逻辑错了排查了半天才发现只是newline参数没写。3.5 效果验证不能只看输出文件“生成了”代码跑通、文件也生成之后我还做了一步很多人新手阶段不会做的事读回结果文件验证。我写了一个三行的验证脚本重新读取result.csv检查里面的日期数量、平均时长数值范围、以及有没有空行或异常数据。这一步很有必要。实际工作中代码“不报错”和代码“正确”是两码事。生成的csv文件可能内容全对也可能所有数值都是默认值或者日期格式错乱。尤其是当你处理的数据集很大肉眼看不过来的时候读回验证几乎是唯一靠谱的方式。我还尝试了一个更狠的验证思路在demo数据里手算了三天的期望值然后和程序输出的三个值做对比。这个方法很土但非常有效。它能直接暴露两个问题过滤条件写没写对、分组逻辑是不是按预期走。强烈建议大家在自学初期多这么做别嫌麻烦这比任何测试框架都更容易上手。4. 自学者最容易陷进去的三个时间黑洞第三十六天除了任务本身还有一个重要的变化是我开始认真记录“时间都去哪了”。这一天的学习过程不算顺利中间有几次走神也有几次陷入“找教程”的循环。我把这些过程记录下来后发现有几个典型的时间黑洞特别值得说一下。4.1 教程切换症收藏夹里全是半截子课自学者最熟悉的一个场景看到一个知识点看不懂第一反应不是停下来琢磨而是去搜“XX入门教程”然后打开一个新的视频、新的文章看着看着觉得这个讲得也不清楚再换一个。一个小时过去教程换了四五家知识点还是没弄懂。我第三十五天那天就犯了这个毛病。函数递归和装饰器这两个概念我前前后后收藏了七八篇讲解但真正坐下来写代码验证的时间不超过二十分钟。后来我给自己立了一条规矩遇到看不懂的概念先打开编辑器写一个极简例子跑一遍跑通了再回头去看教程。一个概念如果看了三篇文章还搞不懂那问题不在文章在于我没有亲手触碰它。所以第三十六天我对当天的自控要求是不允许在同一个时间段内切换超过两个学习资源。看文档的时候写代码写代码的时候不刷视频。这个简单粗暴的约束确实让学习效率提升了不少。我不确定这是不是最佳方法论但在第三十六天这个阶段它很管用。4.2 报错恐惧症遇到异常就慌忘了报错是引导另一个时间黑洞是“报错恐惧症”。我身边开始学编程的朋友里包括我自己前期都有一个通病一看到红色报错就慌截图发给别人问“这是怎么回事”或者干脆把整个文件删了重写却不仔细读那两三行错误信息。第三十六天我注意到报错信息里其实包含了九成以上的排查线索。最常见的ValueError: invalid literal for int()直接告诉你是int()函数收到一个无法转换的值KeyError: xxx告诉你是某个键不存在FileNotFoundError告诉你路径或文件名错了。每一个报错都是一条精确的定位信息它们不是在“为难你”而是在给你指路。我从这一天开始养成了一个习惯报错出现在屏幕上后先停下来读三遍然后复制错误类型去搜索。大多数情况下前两三条搜索结果就能解决问题。与其说是“报错恐惧”不如说是“陌生感”——对错误信息不熟所以才慌。多读几次、多搜几次这个感觉会很快消退。4.3 假性努力笔记抄了一堆代码一行没跑第三个时间黑洞最隐蔽用做笔记代替写代码。可能是受学生时代的影响我早期自学时会把教程里的代码一行不落抄到笔记本上甚至用不同颜色的笔标注关键词看起来非常用功。但抄完十页之后合上笔记自己动手写一个“从列表里找出最大数”都卡壳。第三十六天我彻底改了做法笔记只记两类东西。一类是自己踩坑的报错和解决方案另一类是“独立写不出来的代码片段”——包括核心思路、关键函数、以及我自己的理解。至于教程里的完整示例一律不抄只把链接存到书签里需要的时候去翻原文。这个转变带来的效果很明显——第二天再看隔了一晚的代码记忆比之前牢固很多。因为笔记里的内容是自己脑子加工过的不是原样复制粘贴的。写笔记的这个动作本身就是在强迫你过一遍逻辑这比抄十遍都有用。5. 关于学习进度和心态的一笔关于“复盘与跟踪”的流水账第三十六天虽然是个里程碑式的数字但它更多是一个“复盘点”而不是“终点线”。那天结束前我把之前三十五天的学习记录翻了一遍做了个简单回顾二十天语法基础八天函数与面向对象六天文件与异常处理两天小项目。这些记录帮我确认了几件事——我的基础语法相对扎实面向对象是短板文件操作才刚刚起步。我把自己的短板拆成了三个方面面向对象的继承与多态的运用场景、异常处理的粒度设计什么时候捕获、什么时候抛出、以及数据处理时的边界条件意识。这三个短板不一定需要马上补但至少我知道下一个学习阶段该往哪个方向用力了。当天晚上我还做了一个决定——把接下来的七天计划定为“小型项目周”。不再是每天学一个新知识点而是每天做一个小任务围绕“文件处理数据清洗简单统计”这个范围但每天换一个不同的数据场景。比如处理课程评价文本、分析商品价格列表、整理日记关键词等等。这样做的好处是把学过的语法在真实场景里反复调用比推着学新知识要管用得多。如果你也正好在自学的第三十天到第四十天之间我的建议是放慢“学新内容”的节奏花两三天时间把之前学过的内容揉成一个小项目做一遍。别追求做完一个项目就“完美”只要它能跑通、能输出正确结果就说明你的基础已经可以支撑下一步了。另外我想分享一个特别细节但特别实用的习惯给每一天的学习留一个“三行日志”。第一行写今天做了什么第二行写遇到了什么问题第三行写明天打算做什么。这个习惯看着不起眼但它能帮你把36天之前、36天之后的学习状态串起来。回头翻的时候你会清楚地看到自己的成长也会看到那些重复犯的错误。这个方法不保证让你变成高手但至少能让你在“学没学进去”这件事上保持清醒。第三十六天结束的时候我关闭编辑器屏幕上还留着白天测试用的result.csv。我看了一眼里面的三行数据脑子里只有一个念头三十几天前我连print(hello)都要查半天资料现在居然能独立写完一个带边界处理、异常控制、编码设置的数据清洗脚本。这不算什么了不起的事但对我自己来说它比背下来十个语法知识点更有意义。自学的路还长第三十六天只是半山腰的一个歇脚点后面还有递归、算法、项目结构、网络请求这些硬骨头等着。慢慢啃总能啃得动。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →