
到《python第四天》这篇更新为止你已经坚持了72小时的Python学习。第一天装解释器、第二天配VSCode环境、第三天跑通第一个Hello World——说实话愿意认真走到这一步的人已经超过一半了。但真正让你和“看看教程党”区分开来的恰恰是从今天开始的内容变量、类型转换、条件循环、函数这些基础语法里的核心骨架我希望用一篇能把“为什么”讲透的文章帮你一次性跨过从“看懂代码”到“写出代码”之间的坎。本文适合已经跑通Python环境、会运行.py文件的初学者也适合那些学了几天总觉得自己只会抄代码的朋友。今天不聊爬虫、不聊数据分析就专心把地基打牢。学完之后你应该能独立写出一个带输入校验、有逻辑分支、可复用函数的小程序——这个标准比背100个语法点都重要。1. 第四天到底学什么先把今天的“锚点”定住1.1 三天之后先做个能力盘点每个学Python的人都会经历一个错觉前几天猛看教程感觉什么都会真让自己写代码脑子一片空白。这不是你笨而是前三天你接触的全是零散知识点还没有形成“解决问题”的路径。我用Python教学经常把前三天安排成这样第1天装Python解释器、跑通print(Hello World)理解“代码从哪写到哪”。第2天配置编辑器VSCode或PyCharm学会运行脚本、切换解释器弄懂终端和编辑器的关系。第3天了解变量、字符串、数字这些“名词”但只是认识没有深入。能坚持到第4天的人大概率不是三分钟热度。你缺的不是资料而是一种“把这些碎片焊成完整技能”的刻意练习。所以第四天的任务很清晰把变量、数据类型、类型转换、条件判断、循环、函数这六件事串成一个整体。如果说前三天是认识单词今天就是学语法从今天开始你才真正具备“说一句完整的话”的能力。1.2 给第四天定一个可验收的标准我特别反对只列知识点不列验收标准的学习方式。知识点背得再多不如一小时内独立写出一段能跑、能处理用户输入、能输出不同结果的程序。今天结束时你先别急着学新内容拿下面这个清单自测一下能否说出int、float、str、bool、list、dict这六种类型的区别并各写两行例子出来。能否解释为什么input()读进来的数字必须用int()转换才能做算术运算。能不能用if/elif/else写一个多分支判断而不出现缩进报错。能否用for循环遍历列表同时打印出每个元素的下标和值。能否写一个带return的函数并在主程序里调用它而不是只会用print在函数里输出。看到Traceback (most recent call last)时能不能在十秒内定位到出错的文件和行号。如果这些问题你都能点头恭喜你已经达到Python入门第一个小里程碑了。下面每一节我都会把对应的“为什么”和“坑在哪”给你展开讲。2. 动手前先配好环境解释器、venv与VSCode2.1 为什么从第四天开始我建议你认真用venv很多教程只教一句话装完Python就能写代码配好环境就能跑第三方库。但真有读者来问我“我按教程pip安装了numpy为什么运行还报ModuleNotFoundError”时我一看截图就发现他电脑里装了不止一个Python。这种情况太常见了。系统里可能有官网装的Python 3.10有Anaconda自带的Python 3.9还有VSCode插件默认拉到的另一个解释器。你在终端里执行pip install装的是A环境VSCode运行代码用的却是B环境两边根本不是同一个Python。所以要养成一个习惯每个项目都创建独立的虚拟环境venv。虚拟环境相当于给当前项目建了一个独立的小房间你在这个房间里装的包不会污染全局环境不同项目之间也不会因为依赖版本冲突打架。创建命令很简单在项目目录下执行python -m venv .venv创建完成后Linux或macOS用source .venv/bin/activateWindows用.venv\Scripts\activate激活后你会看到终端提示符前面多了一个(.venv)这就说明当前已经处于虚拟环境内。之后再pip install任何包都只会装到这个项目的.venv里不会再出现“我明明装了却找不到”的问题。2.2 VSCode里选择解释器比想象中更关键如果你用的是VSCode请务必确认一件事命令面板里选中的Python解释器到底是不是你项目虚拟环境里的那个Python。操作路径我再说一遍按这个走基本不会错用VSCode打开你的项目文件夹。按CtrlShiftPmacOS是CmdShiftP打开命令面板。输入Python: Select Interpreter并回车。在弹出的列表里选择带有.venv标识的那个解释器路径。很多人忽略这个步骤结果导致明明在终端激活了虚拟环境VSCode左下角显示的解释器却是全局Python。代码能跑起来是运气跑不起来才是常态。如果你需要断点调试可以在左侧“运行和调试”面板里点击“创建launch.json”选择Python调试器。VSCode会自动把当前选中的解释器写进调试配置里后面按F5就能进入调试状态。这一步建议今天就做别等到写长程序才临时学调试是每天都要用的基本功。2.3 “安装缺失节点”“缺少xxx包”的报错先查环境再查代码这个坑我要单独拎出来讲。最近不少人在跑开源项目时会看到类似的提示当前工作流引用了某些未安装的Python模块建议先执行pip install补全依赖。很多人第一反应是“那我全部pip install一遍”结果越装越乱甚至把系统环境搞得一团糟。以我排查几百个环境问题的经验来说九成以上的“缺包”其实不是包真的没装而是当前激活的环境和项目运行时用的环境不是同一个。正确的排查顺序是先确认当前是否在虚拟环境里命令行执行where pythonWindows或which pythonmacOS/Linux看路径里有没有.venv。再确认包装到了哪里执行pip list看目标包是否在列表里。如果没装就激活虚拟环境后再执行pip install 包名。如果项目有requirements.txt直接执行pip install -r requirements.txt一步到位。我见过最夸张的例子是有人为了让一个项目跑起来在全局环境里装了200多个包最后连Python自己都启动异常。记住环境问题不是靠硬装解决的是靠隔离和梳理解决的。在项目里主动维护requirements.txt也是个好习惯。每次成功运行一个第三方库前别偷懒顺手执行一次pip freeze requirements.txt以后再换电脑、换环境只要装一遍这个文件里的依赖就够了。这个习惯越早养成后续踩坑越少。3. 变量与基本数据类型重新认识“赋值”这件事3.1 变量不是一个盒子而是一张标签很多入门书喜欢说“变量是存放数据的盒子”这个比喻在C语言里勉强能用但在Python里会严重误导你。Python的变量赋值本质上是把一个名字绑定到一个对象上。a 10这句话的意思是把标签a贴到对象10身上。之后你再写a 20不是把a这个盒子里的10倒掉换成20而是把标签a从10身上撕下来改贴到20这个对象上面。这个区别在列表操作里特别明显。一个刚学Python的人很容易写出这样的代码种下隐患a [1, 2, 3] b a b.append(4) print(a) # 你猜会打印什么如果a是“盒子”你会觉得b复制了一份a改b不应该影响a。但Python的答案是[1, 2, 3, 4]因为b a只是把b这个新标签贴到了同一个列表对象上a和b指向同一个内存地址改b等于改a。用id()函数可以验证这一点a [1, 2, 3] b a print(id(a) id(b)) # True所以理解Python的变量请务必建立“名字绑定对象”的心智模型。这个模型越早建立之后学函数传参、学可变对象与不可变对象时就越轻松。3.2 六种想绕都绕不开的基本类型回到第四天的知识主线。今天不需要去啃所有Python内置类型先把下面六种搞定就已经覆盖了90%的日常使用场景。类型例子一句话说明int100、-3整数没有小数点float3.14、-0.5浮点数主要用来表示小数strhello、你好字符串单双引号都行但别混用boolTrue、False布尔值注意首字母大写NoneTypeNone表示“没有值”常用于函数默认返回list[1, 2, a]列表可修改能装任意类型dict{name: 小明, age: 18}字典以键值对方式存数据我单独把NoneType拎出来提醒是因为很多新手学函数时不知道“函数没有return时其实返回了None”导致后面做逻辑判断时莫名其妙出错。Python里一切对象都有类型None也有自己的专属类型它代表“空”而不是False。这里还有个易混淆点bool在Python里是int的子类。True在算术运算里等价于1False等价于0所以True True结果是2。这个冷知识平时用得少但偶尔在别人代码里见到会觉得很神奇先有个印象就行。3.3 变量命名第四天就养成这三个习惯新手写代码最不在意的一件事就是命名。我见过有人用a、b、c写完整段逻辑三天后回来看自己都看不懂。第一个习惯是变量名用全小写多个单词用下划线分隔比如user_age、total_price这符合Python社区主流的PEP 8风格。第二个习惯是名字要说明用途而不是说明过程。a改成price_with_tax一眼就看懂temp这种名字能不用就不用它除了告诉你“临时”以外什么都没说。第三个习惯是不要用Python内置函数名当变量名。很多人喜欢写list [1, 2, 3]用完这个变量以后内置的list()函数就被你局部覆盖了。之后想执行list(abc)就会报TypeError: list object is not callable百思不得其解。这里有个小技巧编辑器会自动给内置函数名做颜色高亮看到自己的变量名和内置函数高亮一致立刻换掉。磨刀不误砍柴工第四天就应该开始训练“可读性优先”的编码自觉。4. 类型转换动态类型里最容易翻车的区域4.1 为什么类型转换是第四天的必修课动态类型语言有个特点你写代码时不用声明类型解释器运行时才看对象是啥。这带来了极大的便利同时也埋了不少坑而类型转换就是新手遇到的第一片雷区。最经典的场景就是input()。所有从input()读进来的值在Python 3里统一都是字符串哪怕你输入了一串纯数字age input(请输入你的年龄) print(age 1)这段代码会直接报错TypeError: can only concatenate str (not int) to str。原因很简单18 1相当于“把字符串和整数拼在一起”Python不知道该听谁的。解决办法就是显式做类型转换把字符串变成整数age int(input(请输入你的年龄)) print(age 1)为什么Python不让我们直接18 1自动得到整数19因为Python对“字符串”和“数字”的运算边界非常敏感宁可报错也不猜。这种设计初看麻烦实际上保护了我们避免了大量隐式转换导致的诡异Bug。4.2 四个内置转换函数的“脾气”和隐藏规则Python提供了几个以内置函数形式存在的转换工具用法不复杂但坑全藏在细节里。int(x)可以把字符串、浮点数转成整数。但它的规则不是四舍五入而是把小数部分直接砍掉所以int(3.9)结果是3不是4初学者尤其容易误判。如果你希望“四舍五入”应该用round(3.9)。float(x)相对温和浮点数字符串能被正常转换float(3.14)可以得到3.14。但要注意float(abc)会直接抛出ValueError。str(x)是废话最少的一个任何类型都能转成字符串str(123)得到123。它最大的用途是在拼接时兜底比如你想打印年龄是 str(age)没有str()就没法这么干。bool(x)是最容易被误解的转换函数。很多新手以为bool(False)会得到False实际上Python的规则是任何非空字符串都是True不管你里面写的是不是“False”。空字符串、数字0、空列表[]、None才属于“假值”其他都算真。这个规则建议拿小本本记下来后面写条件判断时用到它的频率非常高。4.3 一个能当作业的输入校验小案例把类型转换和输入校验结合起来你就能写出今天第一个像样的实用小程序。比如让用户输入身高和体重计算BMI值。但用户可能乱输、输中文、输小数必须先处理数据合法性。height_str input(请输入身高米) weight_str input(请输入体重千克) try: height float(height_str) weight float(weight_str) except ValueError: print(输入格式错误请输入数字例如1.75) else: bmi weight / (height ** 2) print(你的BMI是, round(bmi, 2))这段代码用到了try/except做异常捕获。第四天不需要把异常机制学透但你至少要知道程序遇到非法输入时不会直接崩溃而是可以优雅地提示用户重来。类型转换是表象健壮性才是目的——能让用户瞎输入还不出Bug的程序才配叫完成了第一步。5. 条件判断与循环第一次让代码“思考”5.1 if/elif/else逻辑分支的语法骨架条件判断是程序有了“智能”的起点。它的核心语法不复杂score 85 if score 90: print(优秀) elif score 60: print(及格) else: print(不及格)Python的条件判断有三个特点新手上路时最容易在这些细节上翻车。第一是缩进决定归属。其他语言用花括号Python用冒号和缩进划分子代码块。同一个if下面的语句必须保持相同的缩进层级多了少了都会出问题。很多人刚学时觉得这个设计很烦但用熟了会发现它强制你写出整齐代码。第二是elif不是“else if”也不是“elseif”。Python没有elseif这种写法也没有switch语法多分支判断全靠elif一层层往下铺。上面这个例子就是标准形态。第三是条件表达式的结果本质上是一个布尔值。你可以写if 3 2也可以直接写if is_ok这里的is_ok就是一个布尔变量。一个常见误区是新手喜欢写if is_ok True其实直接写if is_ok更简洁两个完全等价。另外注意条件判断是从上到下逐个检查的一旦某个条件成立后面的elif和else都不会再执行。写不等价条件时顺序很重要。比如判断身高的区间你务必把“先判断超大范围”放前面否则小范围会提前拦截。5.2 for循环Python的for不是“计数”是“遍历一切”如果你以前接触过C语言一定记得for (i0; i10; i)这种写法。Python的for完全不是这个套路它是一个彻头彻尾的“遍历器”——把序列里的每个元素挨个取出来处理一遍。看这个例子fruits [苹果, 香蕉, 橘子] for fruit in fruits: print(fruit)这里fruit变量会在每次循环时被绑定到列表里的下一个元素你根本不用管下标是多少。这个设计省掉了大量容易出错的索引操作也让代码读起来更像人话。如果你想在遍历时同时拿到下标别自己维护一个计数器用enumerate就行fruits [苹果, 香蕉, 橘子] for index, fruit in enumerate(fruits): print(index, fruit)常见的for遍历对象还有range。range(5)生成0到4这5个整数range(2, 10, 2)生成从2开始、步长为2、小于10的数序列也就是2、4、6、8。用range实现“循环N次”是Python里非常高频的用法for i in range(3): print(第, i 1, 次执行)遍历字典时也推荐掌握一个技巧默认情况下for key in dict拿到的是键而不是值想要值要写for value in dict.values()想要键值对要写for key, value in dict.items()。这些写法第四天先混个眼熟后面用到时直接回来查。5.3 while循环什么是死循环以及怎么防for擅长遍历已知次数的场景while更适合“不知道要循环多少次只知道满足某条件就继续”的情况。比如你要让用户输入一个合法密码直到他输对为止用for就很不方便因为你不知道他第几次能输对password secret123 while True: user_input input(请输入密码) if user_input password: print(密码正确欢迎回来) break else: print(密码错误请重试)这里while True是无限循环好在内部有break语句——当条件满足时主动跳出循环。新手最容易犯的错误是忘记在循环体里更新循环条件导致死循环。比如count 0 while count 5: print(count) # 忘了写 count 1这段代码会永远打印0直到你按CtrlC强制终止。所以用while时心里要有一个声音循环体里有没有哪行代码能让条件最终为假如果没有要么加break要么加计数器更新。对第四天来说理解“死循环”这个概念本身就是收获。很多初学者只知道循环可以让代码重复执行但真正理解循环的终止条件是从会写while True break开始的。5.4 一个把条件和循环串起来的小练习学完分支和循环做个十秒钟就能读懂的练习就够建立手感了从1数到20遇到3的倍数打印“Fizz”遇到5的倍数打印“Buzz”同时是3和5的倍数打印“FizzBuzz”其他情况打印数字本身。这是程序员圈著名的FizzBuzz题目。for i in range(1, 21): if i % 15 0: print(FizzBuzz) elif i % 3 0: print(Fizz) elif i % 5 0: print(Buzz) else: print(i)注意判断15的倍数必须放最前面如果把“3的倍数”放第一15会被Fizz拦截永远不会走到FizzBuzz分支。“条件顺序影响结果”这个意识从这个小例子就能建立起来。6. 函数今天开始做一个“能拆代码”的人6.1 def、参数、返回值一次讲透到第四天你已经能写出几十行的顺序代码了。但随着代码量增长你会发现一个尴尬同样的逻辑可能要复制粘贴好几遍改一处就得改所有地方。函数就是为了解决这个问题而生的。它把一段逻辑封装成一个“积木”起个名字定义好参数和返回值以后在任何地方都能反复调用。最基础的函数长这样def calculate_bmi(weight_kg, height_m): bmi weight_kg / (height_m ** 2) return round(bmi, 1)拆开看有三步def是关键字表示“我要定义一个函数”。calculate_bmi是函数名命名规范和变量一致多用动词才能表明它做了什么。括号里是参数列表weight_kg和height_m是形参相当于函数内部的两个占位符。return把计算结果返回给调用方。调用时print(calculate_bmi(70, 1.75))70会传给weight_kg1.75会传给height_m函数内部算出BMI约22.9并返回回来。这就是“封装”的基本含义。参数还可以设置默认值。比如大多数成年人的海拔高度变化不大你可以给某一个参数设置默认值这样调用时少传一个参数也不会报错def greet(name, greeting你好): return f{greeting}{name} print(greet(小明)) # “你好小明” print(greet(小红, 早上好)) # “早上好小红”6.2 return和print这两个的区别新手必须理清非常多初学者栽在return和print的区别上。简单说print只是把内容显示到控制台让屏幕上能看到return才是真正把结果交给调用方的通道。def add_with_print(a, b): print(a b) def add_with_return(a, b): return a b result1 add_with_print(1, 2) result2 add_with_return(1, 2) print(result1) # None print(result2) # 3第一版函数在控制台打印了3但当你把它的返回值赋给result1时result1得到的却是None。因为函数执行完该打印的打印了却没有任何值返回。这个坑在写逻辑组合时特别致命。你写了total add_with_print(3, 4)以为total等于7结果它是None后面所有基于total的运算全部崩掉。养成一个习惯如果这个函数的结果还要被继续用来计算一定要用return把值传出去print只负责给人看不负责给程序用。6.3 变量作用域函数内和函数外是两个世界新手写函数最常见的疑惑是“我在函数里定义了一个变量为什么外面访问不到”在Python里函数内部创建的变量默认是局部变量它只在函数执行期间存在函数结束后就会被回收。def make_number(): x 10 # x 是局部变量 make_number() print(x) # NameError: name x is not defined反过来函数内部可以读取全局变量但读取前要想清楚。全局变量的本意是“哪里都能用”但它也让函数变得难以理解和测试。比如total 0 def add_one(): global total total 1在函数内部用global声明可以修改全局变量但这种写法我强烈建议新手少用因为会让数据流向变得特别难追踪。更推荐的做法是把需要的数据作为参数传入函数把结果通过return返回由主程序统一管理。这个思路叫“无副作用函数”现在的你体会不了太多但照着写百来行以后就会回来感谢这个纠结过的下午。6.4 综合小项目第五天之前请务必写出这个BMI分类器函数学会以后今天的内容就可以融会贯通了。把前面出现过的基础输入、类型转换、条件判断、循环以及刚学的函数封装组合成一个完整的BMI分类器。def calculate_bmi(weight_kg, height_m): return round(weight_kg / (height_m ** 2), 1) def get_bmi_category(bmi): if bmi 18.5: return 偏瘦 elif bmi 24: return 正常 elif bmi 28: return 偏胖 else: return 肥胖 def main(): while True: try: height float(input(请输入身高米)) weight float(input(请输入体重千克)) break except ValueError: print(输入不合法请重新输入数字) bmi calculate_bmi(weight, height) category get_bmi_category(bmi) print(f你的BMI是{bmi}属于{category}) if __name__ __main__: main()这段代码里藏了一个重要细节每个函数只做一件事。calculate_bmi只负责计算get_bmi_category只负责分类main负责把输入输出串起来。这就是工程上说的“单一职责”也是从“会写代码”走向“写好代码”的第一小步。if __name__ __main__:这个写法暂时不用理解太深先记住它是“Python程序入口”的仪式感代码。等以后你写了能被导入的模块会回来感谢这行保护代码的。7. 读报错与调试新手最该养成的两种习惯7.1 四种高频报错速查看完不再一头雾水学Python第四天报错不可怕可怕的是看到Traceback就慌或者完全不看报错信息直接乱改。我总结了新手阶段出现率最高的四种错误今天先对号入座以后再见面就不陌生了。报错类型典型触发场景解决思路NameError变量名拼写错误或者用了还没定义的变量检查变量名是否写对是否在上一行定义了TypeError对不同类型做不支持的运算比如字符串加整数检查运算符两边对象的类型必要时做类型转换ValueError值本身不合法比如int(abc)检查传入函数的参数内容考虑异常捕获IndentationError缩进层级不一致混用了空格和Tab统一用4个空格缩进不要在同一个文件混用Tab和空格新手经常有一个误区觉得报错是“代码不行”的信号。实际上报错是解释器给你递过来的线索。真正让人崩溃的不是报错而是“一点报错都没有但结果就是不对”——那种情况才需要你会调试。7.2 traceback只看三行定位问题快十倍阅读错误信息是程序员的基本功。当你的代码出错时终端会显示一大段Traceback但新手不需要全看懂。你只需要顺着找这三样东西出错的代码文件路径和行号通常在最下方紧邻错误类型的那一行里例如File test.py, line 5。错误类型通常是最后一行里冒号前面的大写单词例如ValueError、TypeError。错误描述冒号后面的那句人话它往往把原因说明得比想象中清楚。举个例子Traceback (most recent call last): File bmi.py, line 12, in main height float(input(请输入身高米)) ValueError: could not convert string to float: abc这比任何提示都明确bmi.py的第12行把字符串abc转成float时失败了也就是用户输入了非数字内容。定位到第12行再去看输入值是什么问题基本就解开了。7.3 VSCode断点调试每天花十五分钟就能从“盲猜”里解脱初学者写代码报错后最常见的调试手段是到处贴print然后在控制台里拼命看输出。这个方法有效但效率太低。我建议第四天就开始用VSCode的断点调试。操作起来非常简单在代码左侧行号旁边点一下会出现一个红点这就是断点。按F5启动调试程序运行到断点那一行时会自动暂停。左侧面板会显示当前所有变量的值你可以看到程序“此刻”到底处于什么状态。按F10单步执行一行一行地看代码在做什么。断点调试比打印厉害的地方在于你不必事先猜到底该打印什么变量而是可以在任意位置停下来把当前所有变量的值都看一遍。很多“奇怪”的错误都是在盯住变量面板后三秒内现出原形的。今天建议你故意写一段有小Bug的代码然后不开print纯用断点把它揪出来。这样练一次你对“调试”这两个字的理解会上一个台阶。7.4 给第四天的一点学习策略建议最后多说几句心态层面的东西。我见过太多人学Python前三天热情高涨到第四天开始遇到完整的程序题写不出来就挫败然后就去搜“Python全套教程”想再做一遍。其实这是学习路径设计的问题不是你能力的问题。一个特别有用的策略是每天新学内容之前先把昨天的代码不看答案重写一遍。写不出来就翻回去看然后隔一天再重写。这种“提取式练习”比单纯看十遍教程有用得多。我带的很多学生都是这样前期写代码还很卡到了一个月后突然感觉自己“通”了就是因为前面反复重写撑过了最险的入门期。四天前你还在为Python解释器装不上发愁现在你写得出带异常捕获的输入程序写得出封装的函数也看得懂Traceback。这个进度一点都不慢真的。如果你今天能独立把BMI分类器默写出来哪怕花了一小时你也已经超过绝大多数“收藏了等于学会了”的人了。