ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Python编程进阶:函数、切片、字典与文件读写实战全流程

Python编程进阶:函数、切片、字典与文件读写实战全流程 如果你已经跟着这个系列一路走到第六篇我想先提醒你一件事从这一篇开始学Python的方式要变了。前五篇我们搞定了环境搭建、变量、数据类型、条件判断、循环这些基础语法能写一些小脚本但这离“Python人工智能编程”真正的样子还差着一层关键的东西——把代码组织成可以复用、可以组合、可以交给别人维护的工具。很多人学到这一步会卡住语法都认识但一写项目就发蒙。这篇文章要解决的正是这个卡点。我会用函数、切片、字典、文件读写、一个小型数据分析实战把“零散语法”串成“解决问题的套路”。如果你没有读过前五篇也没关系只要你会写print、能分清if和for就可以直接跟读。这一篇的代码我全部贴出来了跟着敲一遍比只看不练强十倍。1. 从语法到工程思维第六篇应该怎么学如果说前五篇是在教你“认识单词和语法”那从第六篇开始你要学的是“怎么把话说清楚、把事办成”。这个转变我称之为“从写代码到写程序”。1.1 为什么学了基础语法还是不会做项目我经常看到新手卡在一个现象上单看每个知识点都懂for循环会写列表会切片字典会取值但真给一个需求——“把这份成绩单读进来算平均分画个图”——就懵了。问题不在某个语法不会而在于脑子里没有“流程地图”不知道第一步该读文件还是该清洗数据不知道哪些功能该拆成函数不知道数据在每一步长什么样子。第六篇的核心任务就是建立这张地图。我会刻意把知识点都挂到一个真实流程上数据从哪里来文件→ 怎么整理切片、去重、字典统计→ 怎么反复使用函数→ 怎么把结果变成看得见的东西可视化。这一套流程不是AI编程的专属但它是所有人工智能项目的通用底座。说白了后面你想做机器学习、爬虫、量化策略前半段工作百分之八十都是这类数据整理和流程编排。1.2 这一篇的学习目标和预期收获明确一下第六篇的目标免得你学完觉得“好像都看了又好像没学到啥”能写出三五个参数、有返回值、带文档说明的函数并且知道为什么不能随便用全局变量能用切片和推导式把原来要写五六行的数据筛选压缩成一两行还不牺牲可读性能理解字典和集合的底层逻辑顺手用它们去重、统计频次、表达“图”这类复杂结构能读写 CSV 和 JSON 文件让程序的数据真正落地能独立完成一个“读文件→清洗→统计→画图→保存”的分析小项目。一个很重要的心态调整这一篇开始不要再用“眼睛学”了。我贴出的代码请务必亲手敲一遍。不是复制粘贴是敲。敲的过程中你会碰到缩进报错、中文引号、变量名拼写这些极其琐碎但必须跨过去的坎。踩过这些坎的人和没踩过的人写出来的代码完全不是一回事。1.3 建议的学习节奏和配套工具我建议你把这篇拆成三天来看。第一天只看函数和切片动手写“李白打酒”小例子第二天学字典、集合、邻接矩阵重点是理解哈希是怎么回事第三天做综合实战把前面所有内容串起来。工具我就推荐最简单的Python 3.8 以上版本 一个顺手的代码编辑器VSCode 装了 Python 插件就够了不需要一上来就搞虚拟环境、远程开发那些花活等你的项目真的需要隔离依赖的时候再说。如果你还没装好 Python 环境先去官网下载安装包安装时记得勾选“Add Python to PATH”这是大部分环境配置问题的根源。装完后在命令行输入python --version能输出版本号就说明基础环境OK了。2. 函数封装让自己写过的代码能“复用一万次”先说一个判断标准当你发现自己开始在代码里复制粘贴同一段逻辑的时候就是该写函数的时候。函数不是Python的高级特性它就是把一段代码“打包命名”然后随用随调。但就这么一个简单的动作决定了你能不能从“脚本小子”变成“写程序的人”。2.1 函数的骨架参数、返回值、文档字符串最简单的函数长这样def add(a, b): 返回两个数的和。 return a bdef是关键定义符add是函数名a和b是参数return是返回值。调用的时候直接写add(3, 5)就能拿到8。这里我要重点说几个新手容易忽略的点。第一函数不是必须要有return比如只打印日志的函数就可以只干活不返回这时它返回的是None你如果把这个结果赋给变量再打印会得到一个None这很正常别慌。第二参数的传递方式有很多种最常用的是位置参数和关键字参数def introduce(name, age, city未知): return f{name}今年{age}岁来自{city} introduce(小明, 18) # 用默认值city introduce(小红, 17, city上海) # 关键字参数顺序可以不按定义来默认参数要放在非默认参数的后面这个语法规则记一下。第三函数名本身要能“读出声来”。def add(a, b)比def f(x, y)好得多因为三个月后的你看到代码时不需要靠注释也能猜到它在干嘛。至于文档字符串就是在函数第一行用三引号写的说明。别小看它后面你用IDE补全代码的时候悬停在函数名上就能看到这段说明对团队协作和回忆旧代码都极其有用。2.2 作用域为什么函数里改不动外面的变量代码里最常见的一个坑是以为在函数里写了count count 1就能改掉全局的count结果一运行报错“局部变量在赋值前被引用”。这背后是Python的作用域规则函数内部默认只能读取全局变量不能直接修改。要修改必须用global声明但我不建议新手用。更好的做法是让函数接收参数、返回结果也就是“输入输出都在接口上完成”。total 0 def add_to_total(x): total x # 这是函数内部的total不是外面的total return total此时外面的total还是0。如果你真的需要累加某个外部计数正确姿势是def add_to_total(total, x): return total x total add_to_total(total, 5) total add_to_total(total, 3)这样写函数只负责“计算”状态的管理交给了调用方逻辑清晰得多也方便测试。2.3 小实战用函数解“李白打酒”“李白街上走提壶去买酒。遇店加一倍见花喝一斗。三遇店和花喝光壶中酒。试问壶中原有多少酒”这道题用数学能解但用程序来做更能体现“递推思维”。我们已知过程是遇到店酒量翻倍遇到花喝掉一斗。一共三次“遇店见花”最后刚好喝光。正向推算太麻烦不如从最后一步逆推最后一次见花之后是0斗见花前就该是1斗遇店前呢遇店前只有一半即0.5斗。往前倒推三次即可。def libai_wine(rounds3): 李白打酒已知经过rounds轮“遇店加倍、见花喝一斗”后喝光反推初始酒量。 wine 0.0 for _ in range(rounds): wine 1.0 # 逆推见花喝一斗喝之前多一斗 wine / 2.0 # 逆推遇店加倍进店之前只有一半 return wine def verify(wine, rounds3): 正向验证按题目过程走一遍看最后是否为0。 for _ in range(rounds): wine * 2.0 wine - 1.0 return wine result libai_wine() print(result) # 0.875 print(verify(result)) # 0.0 说明没算错运行输出0.875和0.0。也就是说李白出门时壶里有七分半斗——不对是零点八七五斗。这个例子虽然小但把函数的三样东西都练了定义、传参、返回值顺带还练了循环和递推。你试着改rounds的值为4或5看看初始酒量怎么变这比背语法强得多。2.4 关于“定义函数”的一些实战心得最后说点容易踩的经验。我见过很多初学者把那种“只执行一次的脚本逻辑”也硬包进函数结果函数定义三行、调用三行反而显得绕。函数的意义在于复用和组合如果你确定一段代码只跑一次直接写反倒更直白。反过来只要这段逻辑你在不同地方用了两次以上就值得抽成函数哪怕参数只有一两个。判断标准就一条会不会重复。另外函数里避免用print作为“返回结果”。打印只能看不能参与后续计算。要让调用方拿到结果并继续处理一定用return。你可以把函数理解为“黑箱加工厂”原料参数进去产品返回值出来至于里面怎么加工调用方不该也不需关心。3. 切片和推导式用半行代码搞定数据筛选如果你处理过真实数据一定遇到过这类需求取列表前10个、隔一个取一个、把列表里所有数都乘2、筛选出大于某个阈值的元素。这些需求在Python里有两件趁手的兵器切片和推导式。它们不是性能优化的重点但能极大减少你的代码量让意图“一眼可见”。3.1 切片start、stop、step三步走切片的基本写法是序列[start:stop:step]取左不取右不含stop位置。比如nums [10, 20, 30, 40, 50, 60, 70, 80] nums[:3] # [10, 20, 30]从头取到索引2 nums[3:] # [40, 50, 60, 70, 80] nums[::2] # [10, 30, 50, 70]每隔一个取一个 nums[::-1] # 倒序[80, 70, 60, 50, 40, 30, 20, 10]字符串也能切片python教编程[::2]就取出奇数位置的字符。列表、字符串、元组都支持切片这是Python特别统一的一点。新手阶段最容易犯的错是忘记“stop不包含”比如nums[1:3]你以为去的是第1和第3个其实是索引1和2对应的元素。这个规则记住一次以后所有切片都不迷糊。切片还有个方便之处可以给列表的一部分赋值。nums[2:4] [100, 200]这会把第2、第3个元素替换成新值。写这种代码要注意长度变化初学者建议还是用“新建列表”的思路别在原列表上乱改不然排错会头疼。3.2 列表推导式循环、条件、变换一体完成推导式的模板是[表达式 for 元素 in 可迭代对象 if 条件]读法也很顺对每个元素如果满足条件就做一遍表达式的变换。nums [1, 2, 3, 4, 5, 6] squares [n * n for n in nums] evens [n for n in nums if n % 2 0] big [n * 10 for n in nums if n 3]三行代码分别完成“求平方”“筛偶数”“先筛后乘”。如果不用推导式这些至少要写三到五行的临时列表和循环。推导式最大的好处是它“声明式”——你告诉程序“我要什么”而不是“怎么一步步构建”。这也符合人工智能编程里数据处理非常常见的“变换-过滤”模式。除了列表推导式字典也有推导式names [小明, 小红, 小刚] name_len {name: len(name) for name in names} # {小明: 2, 小红: 2, 小刚: 2}如果你想要“既能去重、又保留出现顺序”的数据结构可以配合 Python 3.7 的有序字典特性来做。3.3 去重set、字典键名、推导式三种思路对应热搜里那个“python筛选一样的”去重是数据处理的高频操作。Python里最简单的去重是set(nums)直接转集合。但集合不保序有时候你希望保留原始顺序可以用dict.fromkeysnums [3, 1, 3, 5, 1, 7] unique_ordered list(dict.fromkeys(nums)) # [3, 1, 5, 7]原理是利用字典键的唯一性fromkeys用列表元素生成键键天然不重复。还有一个常见的变体是“筛选出重复元素”而不是去重。你可以用列表推导式配合计数from collections import Counter nums [1, 2, 2, 3, 3, 3, 4] counter Counter(nums) duplicates [k for k, v in counter.items() if v 1] # [2, 3]记住一条实用路线纯去重用set保序去重用dict.fromkeys要统计次数用Counter规则复杂的筛选用推导式自己写条件。3.4 画图横坐标太密集一个真实的切片应用热搜词里有一个“python画图横坐标太密集”这其实是数据处理里的常见麻烦。比如你有1000天的数据想在横轴上全部标出来图片根本没法看全挤成一团黑。一个简单的处理思路是只显示一部分坐标刻度。你可以用切片抽样import matplotlib.pyplot as plt days list(range(1000)) values [x ** 0.5 for x in days] plt.figure(figsize(10, 4)) plt.plot(days, values) step 100 plt.xticks(days[::step], days[::step]) plt.tight_layout() plt.savefig(sample.png, dpi150) plt.show()也就是说横轴刻度只取第0、100、200、…… 900个位置。这个思路本质就是切片步长抽样和上面讲的nums[::2]是一个东西。很多“画图丑”的问题不是图不好而是数据预处理没做到位。学会切片你就多了一个预处理工具。4. 字典、集合与邻接矩阵把图结构装进Python在人工智能编程里数据不会永远是一维列表很多时候你需要表达“元素之间有关系”。比如社交网络里两个人是不是好友城市之间有没有直达航班知识图谱里概念之间的关联。这类关系在数学上叫“图”而Python里最常见的图表达方式之一就是邻接矩阵。这一章我们把它彻底说透。4.1 字典和集合的底层哈希表的直觉先补一个底层直觉。字典dict和集合set之所以查询极快是因为它们基于哈希表把键映射成一个哈希值直接定位存储位置不需要像列表那样挨个比较。这就是为什么name in my_dict几乎是瞬时的而name in my_list在列表很长时会变慢。这种“直接用键找值”的模型特别适合表达结构化数据。比如一个人档案person { name: 小明, age: 18, courses: [语文, 数学], score: {语文: 88, 数学: 92}, }你看字典可以嵌套列表、嵌套字典。几乎所有从文件读进来的结构化数据最终都是字典的形态。4.2 邻接矩阵用二维列表表示节点间关系举个例子。现在有五个城市北京、上海、广州、成都、武汉。如果两个城市之间有直达航班我们记为1没有记为0那么邻接矩阵就是一个5x5的方阵行表示出发城市列表示到达城市第i行第j列是1就表示第i个城市能直达第j个城市。对角线上的值一般是0因为城市到自身没有“直达”的意义有自环另说。Python里构建邻接矩阵的第一步是把城市名称映射成索引cities [北京, 上海, 广州, 成都, 武汉] city_index {city: i for i, city in enumerate(cities)} n len(cities) matrix [[0 for _ in range(n)] for _ in range(n)]然后有一条边列表每条边包含出发城市和到达城市edges [(北京, 上海), (北京, 广州), (上海, 武汉), (成都, 北京), (广州, 武汉)] for start, end in edges: i city_index[start] j city_index[end] matrix[i][j] 1这样一个邻接矩阵就建好了。你可能注意到我用了[[0 for _ in range(n)] for _ in range(n)]而不是[[0] * n] * n这是个很容易踩的坑。后者看起来也能生成n行n列的数组但当你修改matrix[0][1] 1时会发现每一行的第1列全变了。原因是[0] * n生成了同一个数组引用外层乘法只是把这个引用复制了n份。用列表推导式每次新建列表才能避免这种“浅拷贝”带来的神坑。打印矩阵看效果for row in matrix: print(row)输出就是一个标准的0/1方阵这已经是很多图算法的输入格式了。后面你要写最短路径、深度优先搜索只要把数据变成这种矩阵剩下的就是套算法的问题。4.3 为什么要用邻接矩阵和邻接表对比邻接矩阵的优点是查询直观判断两个节点是否相邻只需访问matrix[i][j]复杂度是O(1)。缺点是空间浪费节点一多比如一万个点矩阵就是上亿个格子大部分是0很浪费内存。这时候可以用邻接表也就是用字典来记录每个节点的邻居列表adj {city: [] for city in cities} for start, end in edges: adj[start].append(end) # {北京: [上海, 广州], 上海: [武汉], 成都: [北京], 广州: [武汉], 武汉: []}结构上更紧凑遍历邻居也方便。这两种选哪个我的建议是点数少几百以内或边稠密用邻接矩阵好写、好调试点数多或边稀疏用邻接表。后面你接触真实AI项目里的图数据会发现十个里有八个是稀疏的所以邻接表更常用。但邻接矩阵的概念一定要懂因为很多中间表示比如特征矩阵、距离矩阵长得就是邻接矩阵的样。4.4 集合在关系处理里的另一面快速判断“在不在”处理图关系时集合也很有用。比如你想知道某个城市有没有被边记录到或者哪些城市没有任何出边直接集合运算就行all_cities set(cities) cities_with_edge set(start for start, _ in edges) isolated all_cities - cities_with_edge这里涉及到集合的差集运算得出的就是“没有作为出发城市出现过”的城市也就是没有出边的城市。这类写作方式平铺直叙却又非常高效。5. 文件读写与结构化数据让程序连上真实世界学了前面这些你的程序还都活在内存里关掉就没了。要让数据持久化让程序能和Excel表、爬虫抓下来的数据、量化行情的日志产生交集必须掌握文件读写。5.1 open和with读写文件的基本姿势最推荐的文件读取方式是with open(...)因为with会在代码块结束时自动关闭文件不用你记着写f.close()。基本模式with open(data.txt, r, encodingutf-8) as f: text f.read() print(text)r是读模式w是写模式a是追加模式。第二个经常踩的坑是编码。Windows系统下文件默认可能是GBK如果你读文件不指定encodingutf-8经常会出现中文乱码。我现在的习惯是无论读写都显式带上encodingutf-8省得换台电脑就乱码。写文件也一样with open(output.txt, w, encodingutf-8) as f: f.write(你好Python\n) f.write(这是第二行\n)注意w模式会覆盖原有内容如果不想覆盖用a追加。5.2 CSV文件Python标准库一条龙搞定CSV是最常见的表格存储格式Excel也能直接打开。Python标准库里有csv模块读写都很方便。比如读取import csv with open(students.csv, r, encodingutf-8) as f: reader csv.DictReader(f) rows list(reader) print(rows[0])csv.DictReader会把第一行当表头后面每行变成一个字典字典的键就是表头。假设表头是“姓名,语文,数学”那rows[0]就是{姓名: 小明, 语文: 88, 数学: 92}。写入CSV则是这样with open(output.csv, w, encodingutf-8, newline) as f: writer csv.DictWriter(f, fieldnames[姓名, 语文, 数学]) writer.writeheader() writer.writerow({姓名: 小华, 语文: 90, 数学: 85})加newline是为了避免在Windows下写入时出现多余空行这也是踩过才知道的细节。注意CSV读出来的数字全是字符串你要计算之前必须int(row[语文])转一下或者用float()我自己经常在这里忘记转换然后跑出类型错误。5.3 JSON文件程序之间交换数据的通用语言和CSV比JSON更灵活能表达嵌套结构而且几乎所有编程语言都支持。Python读写JSON非常简单import json data { name: 小明, scores: [88, 92, 85], address: {city: 北京, district: 海淀}, } with open(data.json, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse, indent2) with open(data.json, r, encodingutf-8) as f: loaded json.load(f) print(loaded[scores])ensure_asciiFalse特别重要不设置的话中文字符会被存成\uXXXX转义序列人眼根本没法看。indent2让文件有缩进便于阅读。学了JSON你会发现Web后端返回的数据、爬虫抓到的接口数据、现在很多配置文件的格式全是JSON。当你把“字典 ↔ JSON字符串”这个通道打通Python就等于接入了互联网世界的通用语。5.4 和爬虫、量化交易预处理如何衔接为什么会有人在热搜里搜“python爬虫”“python量化交易策略代码”因为很多真实项目的第一步都是“拿数据、存数据、整理数据”。爬虫抓下来的原始数据往往是一大堆嵌套JSON或HTML你需要提取关键字段转成CSV或JSON落盘稍后分析。量化交易更是典型你回测一个策略前提是把历史行情数据读进来、清洗掉停牌或异常值、按日期排序然后才能计算移动平均线之类的指标。我举一个非常简单的行情数据读取和均线计算例子import csv with open(price.csv, r, encodingutf-8) as f: rows list(csv.DictReader(f)) # 列date, close closes [float(row[close]) for row in rows] ma5 sum(closes[-5:]) / 5 ma20 sum(closes[-20:]) / 20 print(ma5, ma20)这段代码虽然短但已经踩到了量化数据处理的核心动作读文件、取序列、按窗口聚合。后面你学习pandas会发现整个pandas就是把这些小操作做得更极致、更高效而已。所以别觉得基础文件读写没用它是所有数据类项目的根。5.5 matplotlib保存图片别被show挡住“python绘图保存图片”也是热搜词。Matplotlib保存图片最常见的坑是先plt.show()再plt.savefig()结果保存出来的是一张白图。原因很简单show()执行后会清空当前图形后面的savefig已经无图可存了。正确顺序是先savefig再show。此外保存高清图片建议加dpi参数比如plt.savefig(chart.png, dpi150, bbox_inchestight)bbox_inchestight会尽量把图片周围的留白裁掉让图占满画布。这些经验都是被坑过才记住的写入你的本子或记忆里都行反正下次用到就直接用。6. 综合实战CSV成绩单清洗、分析、可视化全流程前面的知识点都是独立的零件。现在把它们组装成一个完整的小程序。这个实战全程贴代码我建议你新建一个analyze_students.py文件把代码一段段敲进去运行。它解决的是一份格式不太干净的CSV成绩单。6.1 准备一份带有脏数据的成绩单我模拟了一个students.csv内容如下姓名,语文,数学,英语 小明,88,92,85 小红,95,85,88 小刚,76,80,90 小明,88,92,85 小丽,,88,92 小刚,76,80,90 ,85,90,80肉眼能看到的问题有重复记录小明和小刚各出现了两次、缺失数据小丽的语文空着、无名空行最后一行的姓名是空的。真实项目里的数据往往比这个脏十倍但清洗思路是一样的。6.2 数据读取和清洗代码第一步读取原始数据过滤掉没有姓名的行并把数字字段转成整数。用函数封装每步工作import csv from collections import defaultdict def read_raw(path): 读取CSV文件返回字典列表。 with open(path, r, encodingutf-8) as f: return list(csv.DictReader(f)) def clean_row(row): 清洗单行过滤无姓名、补齐缺失数字字段。 name row[姓名].strip() if not name: return None try: return { 姓名: name, 语文: int(row[语文]) if row[语文] else 0, 数学: int(row[数学]) if row[数学] else 0, 英语: int(row[英语]) if row[英语] else 0, } except ValueError: return None raw_rows read_raw(students.csv) cleaned [] for row in raw_rows: processed clean_row(row) if processed: cleaned.append(processed)这里的strip()是去掉字符串首尾空格防止表格里带上不可见字符。而缺失值我用了“填0”策略这只是清洗策略之一真实项目里也可能是删除该行或插补平均值。6.3 去重和聚合统计第二步依据“姓名”字段去重。同一门考试里同一个人的重复记录理应只留一条这里继续用dict.fromkeys配合姓名构造去重后的列表def deduplicate(rows): 依据姓名去重保留第一次出现的完整记录。 unique list(dict.fromkeys(r[姓名] for r in rows)) seen {name: row for name, row in zip(unique, rows)} return list(seen.values())这种写法稍显取巧但能跑通。更稳妥的写法是显式遍历并维护一个seen_names集合def deduplicate(rows): seen_names set() result [] for row in rows: name row[姓名] if name not in seen_names: seen_names.add(name) result.append(row) return result这个版本的逻辑更直白也不容易出幺蛾子。我实际项目中常用这个写法因为它对“保留第一条还是最后一条”的控制更直观。第三步按学科求平均分和最高分def analyze(rows): 统计各科平均分与最高分。 subjects [语文, 数学, 英语] stats {} for subject in subjects: scores [row[subject] for row in rows if subject in row] stats[subject] { avg: sum(scores) / len(scores), max: max(scores), } return stats stats analyze(deduplicate(cleaned)) print(stats)运行后你会看到一个嵌套字典比如语文平均分约86.33数学约87.67英语约88.75。这里的核心是列表推导式把某个学科的所有成绩取出来再交给内置函数sum/max处理。6.4 把统计结果画成图并保存第四步为了让结果直观用matplotlib画一个三门课程的“平均分-最高分”对比柱状图import matplotlib.pyplot as plt subjects list(stats.keys()) avg_values [stats[s][avg] for s in subjects] max_values [stats[s][max] for s in subjects] plt.figure(figsize(8, 5)) plt.bar(subjects, avg_values, label平均分, alpha0.7) plt.bar(subjects, max_values, label最高分, alpha0.4) plt.legend() plt.title(成绩分析) plt.ylabel(分数) plt.tight_layout() plt.savefig(score_analysis.png, dpi150) plt.show()中文标题可能默认字体显示成方框这是因为matplotlib默认字体不含中文字形。一个可用的解决办法是在代码开头设置支持中文的字体plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, Arial Unicode MS] plt.rcParams[axes.unicode_minus] False第二个设置是防止负号显示成方块。频率很高建议直接存成自己的模板。6.5 这个小项目到底练了什么复盘一下整个流程只有一百行左右的代码但它把所有关键技能都串起来了函数封装read_raw、clean_row、deduplicate、analyze、列表推导式提取成绩序列、字典嵌套统计结果、集合去重判断、CSV读写、文件持久化、可视化输出。这就是一个完整的数据分析流水线也是后续接触pandas、numpy这些人工智能编程必备库时你已经能用自己的代码理解每一步在干嘛。7. 这一篇最容易踩的坑与自查清单最后分享一批我在带人和自己写码过程中反复见到的坑。每个坑背后都有一句“我之前也是这样”的恍然大悟希望能帮你少走弯路。7.1 可变默认参数你以为的初始值其实会“记住上一次”这是Python函数一个著名的坑。比如def add_item(item, lst[]): lst.append(item) return lst连续调用add_item(a)再add_item(b)第二次的结果是[a, b]因为这个默认列表在函数定义时就创建了后续每次调用如果没有传lst用的都是同一个列表。正确做法是默认参数设为None内部再新建列表def add_item(item, lstNone): if lst is None: lst [] lst.append(item) return lst不要纠结“为什么默认参数不被重置”这是面试题常客但实践中记得这个写法就行。7.2 列表的浅拷贝直接赋值不是复制new_list old_list并不会生成新列表它只是给同一个列表贴了新标签。所以当你new_list.append时old_list也会变。想要真正复制用old_list[:]或old_list.copy()。二维列表的“你以为你改了第1行结果每一行都变了”也是同一个坑的变体在构建二维矩阵、邻接矩阵时尤其容易爆发建议写完矩阵后print一下再往下走。7.3 文件编码中文乱码的根源不指定encodingutf-8就打开CSV或TXT尤其在Windows下很大概率会撞上GBK乱码。另外用Excel新存的CSV文件有时是带BOM头的UTF-8也叫“utf-8-sig”读的时候可以试一下encodingutf-8-sig不然你会发现第一列表头名多了一个看不见的字符。我自己处理这类问题时习惯先快速打印前两行确认字段名干净再继续。7.4 过度使用print调试却从不看“类型”新手排错第一反应是到处加print这没错但只print变量还不够要print它的类型和长度。比如读进来一个CSV单元格你以为它是数字print出来也是88但它是字符串。此时row[语文] 1直接报错。我的习惯是写一句print(type(x), repr(x))这能同时看清类型和值里的隐藏字符。repr会把换行、制表符显示成转义序列很多疑难杂症就是这么揪出来的。7.5 不敢拆函数一长串代码堆在main里还有一种非常常见的现象是所有逻辑全写在一个脚本里从第一行到第一百行不加任何函数。在项目很小的时候无所谓但一旦逻辑复杂起来你定位问题会非常痛苦。我建议的拆分粒度是凡是能自圆其说的“步骤”比如读取、清洗、去重、统计、画图都做成函数。哪怕每个函数开始时只有三五行代码这个习惯也会让你的程序结构迅速清晰后面要改某个环节只需要改函数内部不用动其他地方。7.6 一个适合贴在屏幕边的自查清单我在写这类代码时最后会过一遍清单每个函数是否只做一件事有没有返回值而不是光print有没有用列表推导式简化循环去重是用set还是fromkeys有没有考虑顺序文件读写有没有指定编码保存图片时savefig是不是在show之前中文显示设置了字体没有默认参数有没有写成可变类型这些检查大概花两分钟能让代码质量和运行稳定性提升一个档次。学到这里你已经不是“只能跟着教程跑demo”的新手了。函数、容器、文件、可视化这四样东西组合起来足够支撑你去独立完成一些小工具比如把一个杂乱日志文件整理成表格比如抓取网页数据后自动生成报表比如把自己手算的量化指标脚本化。我的建议是别急着进入下一章拿一个自己手边真实的数据文件用今天的方法折腾一遍。编程能力的提升从来不是靠读文章读出来的是靠一次次“跑通一个属于自己的小工具”累积出来的。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表