☰
暑期实训Python第二次课:条件判断、循环、函数与数据结构核心总结
2026/10/15 5:11:25 网站建设 项目流程

1. 暑期实训第二课到底在练什么

1.1 从标题拆出来的真实需求

"暑期实训python第二次课"这个标题看起来像一份课程记录,但真正有价值的信息藏在"第二次课"这四个字里。第一次课通常解决的是环境搭建、语法速览、变量与数据类型这些入门问题,学生上完第一次课的状态是"能看懂但写不出"。第二次课才是分水岭——它要解决的是"从看懂到能写"的转化问题,同时要把第一次课散落的知识点串成一条可运行的逻辑链。

我带过几期暑期实训,也帮朋友看过他们机构的课程大纲。第二次课普遍会落在三个方向上:流程控制(条件判断与循环)、函数封装、基础数据结构(列表、字典、元组、集合)的综合运用。这三个方向不是随便选的,它们对应着初学者最容易卡住的三个坎:程序不会做判断、代码不会复用、数据不会组织。把这三个坎迈过去,后面学文件操作、异常处理、面向对象才有落脚点。

所以这篇总结不是把课件抄一遍,而是把第二次课里那些"老师讲了但你没记全""当时听懂了回去又忘了"的东西重新捋一遍。适合两类人看:正在上暑期实训、第二次课听得云里雾里的同学;以及自学Python、卡在"语法都会但写不出完整程序"这个阶段的初学者。

1.2 第二次课的知识地图

先把第二次课的知识点画成一张地图,心里有个全局观,后面逐个拆解的时候才不会迷路。

模块核心内容解决什么问题常见坑
条件判断if / elif / else、比较运算符、逻辑运算符让程序会做选择缩进错误、条件顺序写反
循环for / while、range、break / continue让程序会重复干活死循环、循环变量作用域
函数def、参数、返回值、作用域让代码能复用可变默认参数、返回值丢失
数据结构列表、字典、元组、集合的增删改查让数据能组织起来浅拷贝、遍历时修改

这张表建议直接抄在笔记本第一页。第二次课的所有练习,本质上都是这四个模块的排列组合。比如"统计一段文字里每个词出现的次数",就是循环加字典;"判断一个数是不是素数",就是循环加条件判断加函数封装。看透这一点,练习就不再是零散的题目,而是同一套积木的不同搭法。

提示:不要试图一次记住所有语法细节。第二次课的目标是建立"遇到问题知道该用哪个模块"的条件反射,具体语法可以查、可以试,但选型思路必须刻在脑子里。

2. 条件判断:让程序学会做选择

2.1 if / elif / else 的执行逻辑

条件判断是程序有了"脑子"的第一步。很多同学写if语句时是凭感觉写的,觉得"差不多就是这个意思",结果程序跑出来的结果和预期对不上。问题出在没有真正理解执行顺序。

Python的if / elif / else是从上往下依次判断的,一旦某个条件为真,执行对应的代码块,然后整个判断结构就结束了,后面的elif和else连看都不看。这个特性带来一个非常重要的实操原则:条件要从特殊到一般排列。

举个例子,判断一个分数属于哪个等级:

score = 85 if score >= 60: print("及格") elif score >= 80: print("良好") elif score >= 90: print("优秀")

这段代码看起来没毛病,但实际输出永远是"及格"。因为85分满足第一个条件score >= 60,程序执行完就跳出了,根本不会去看后面的score >= 80。正确的写法是把最严格的条件放前面:

score = 85 if score >= 90: print("优秀") elif score >= 80: print("良好") elif score >= 60: print("及格") else: print("不及格")

这个坑我在实训里见过太多次,几乎每期都有同学踩。记住一句话:elif的顺序就是优先级的顺序,谁的条件更苛刻谁先上。

2.2 比较运算符与逻辑运算符的组合技巧

比较运算符(==、!=、>、<、>=、<=)单独用很简单,但和逻辑运算符(and、or、not)组合起来,初学者就容易写出逻辑混乱的表达式。

一个典型的场景是判断闰年。闰年的规则是:能被4整除但不能被100整除,或者能被400整除。翻译成代码:

year = 2024 if (year % 4 == 0 and year % 100 != 0) or year % 400 == 0: print("闰年") else: print("平年")

这里有个细节值得说:and的优先级高于or,所以括号其实可以省略,但强烈建议加上括号。原因不是程序需要,而是人需要。半年后你回头看这段代码,有括号能一眼看清逻辑分组,没括号就得在脑子里重新算一遍优先级。写代码是给人看的,顺便给机器执行。

还有一个初学者常犯的错误是用==比较浮点数。比如0.1 + 0.2 == 0.3在Python里返回的是False,因为浮点数在计算机里是近似存储的。正确做法是比较差值是否小于一个极小值:

a = 0.1 + 0.2 b = 0.3 if abs(a - b) < 1e-9: print("相等")

这个知识点第二次课不一定会讲,但做练习时一旦涉及小数运算就会遇到,提前知道能省不少调试时间。

2.3 条件判断的实操心得

我在实训里观察到一个现象:同学们写条件判断时,喜欢把所有逻辑塞进一个if里,写成又长又复杂的表达式。比如判断一个年份是否是21世纪的闰年,有人会写:

if year >= 2001 and year <= 2100 and ((year % 4 == 0 and year % 100 != 0) or year % 400 == 0): print("是21世纪的闰年")

这行代码没错,但可读性很差。更好的做法是拆成嵌套判断或者用多个if:

is_21st = 2001 <= year <= 2100 is_leap = (year % 4 == 0 and year % 100 != 0) or year % 400 == 0 if is_21st and is_leap: print("是21世纪的闰年")

Python支持链式比较(2001 <= year <= 2100),这是很多语言没有的特性,用起来很顺手。把复杂条件拆成有名字的布尔变量,代码立刻从"能跑"变成"好懂"。这个习惯越早养成越好,后面写大型程序时,可读性就是生命线。

注意:Python没有switch-case语句(3.10之前),多个固定值的判断用if / elif链或者字典映射。字典映射是个很实用的技巧,后面讲数据结构时会展开。

3. 循环:让程序重复干活的艺术

3.1 for循环与range的配合

for循环是第二次课的重头戏。Python的for循环和C系语言的for循环长得完全不一样,它不是"初始化、条件、递增"三段式,而是遍历一个可迭代对象。这个设计哲学上的差异,决定了学Python的for循环要换一种思维方式。

最常用的搭档是range()。range()有三种用法,必须记牢:

range(5) # 0, 1, 2, 3, 4 range(2, 8) # 2, 3, 4, 5, 6, 7 range(1, 10, 2) # 1, 3, 5, 7, 9

关键点是左闭右开:range(2, 8)包含2但不包含8。这个设计初看别扭,但用多了会发现很合理——range(len(lst))刚好生成列表的所有下标,不会越界。

一个经典练习是打印九九乘法表:

for i in range(1, 10): for j in range(1, i + 1): print(f"{j}x{i}={i*j}", end="\t") print()

这段代码里有两个细节:内层循环的range(1, i + 1)保证每行只打印到当前行号,end="\t"让print不换行而是用制表符分隔。外层循环每结束一轮,用一个空的print()换行。嵌套循环的执行顺序是"外层走一步,内层走一圈",理解这一点,所有嵌套循环的题目都能拆解。

3.2 while循环与死循环的避免

while循环适合"不知道要循环多少次,只知道循环条件"的场景。比如猜数字游戏:

import random target = random.randint(1, 100) guess = 0 while guess != target: guess = int(input("猜一个1到100的数字:")) if guess < target: print("小了") elif guess > target: print("大了") else: print("猜对了")

while循环最大的风险是死循环。上面这段代码如果用户一直猜不对,循环就一直跑,这是正常的。但如果循环条件永远为真,程序就卡死了。常见的死循环原因有三个:循环变量忘记更新、条件写反、break放错位置。

排查死循环有个笨办法但很有效:在循环体第一行加一句print(循环变量),跑一下看变量有没有变化。如果打印出来的值一直不变,那就是忘记更新了。这个技巧在实训里救过不少同学。

3.3 break、continue与循环的else子句

break和continue是循环的两个"遥控器"。break直接跳出整个循环,continue跳过本次循环剩下的代码,进入下一次。

for i in range(1, 11): if i == 5: continue if i == 8: break print(i)

这段代码输出1、2、3、4、6、7。5被continue跳过,到8时break跳出。

Python的循环还有一个别的语言没有的特性:循环的else子句。else在循环正常结束(没有被break打断)时执行:

for i in range(2, 10): if 10 % i == 0: print(f"10能被{i}整除") break else: print("10是素数")

这个特性用来判断素数特别优雅。但要注意,else子句容易被误读成"循环结束后执行",实际上它只在没有break时执行。这个细节第二次课老师可能会提,但很多同学当时没在意,后面做题时就会困惑"为什么else没执行"。

3.4 循环的实操心得

实训里我发现,同学们写循环时最大的问题不是语法,而是循环边界。比如"打印1到100的偶数",有人写range(1, 100, 2),结果打印的是奇数;有人写range(0, 100, 2),结果少了100。正确的写法是range(2, 101, 2)或者range(0, 101, 2)。

避免边界错误的方法是在写循环之前,先用自然语言把范围说清楚:"从2开始,到100结束,包含100,每次加2"。然后翻译成range:起点2,终点101(因为右开),步长2。这个"先说人话再写代码"的习惯,能消灭大部分边界bug。

提示:循环里尽量不要修改正在遍历的列表。比如for x in lst: lst.remove(x)会导致跳过元素。需要修改时,遍历列表的副本for x in lst[:],或者用列表推导式生成新列表。

4. 函数:把代码打包成可复用的工具

4.1 函数定义与参数传递

函数是第二次课从"写脚本"到"写程序"的关键跨越。没有函数,代码就是一堆流水账;有了函数,代码就有了结构。

定义函数的基本语法:

def greet(name): return f"你好,{name}" message = greet("同学") print(message)

这里要理解三个概念:参数(name)、返回值(return后面的东西)、调用(greet("同学"))。参数是函数的输入,返回值是函数的输出。一个设计良好的函数,应该是"给什么输入,出什么输出",中间的过程封装在函数体里,调用者不需要关心。

Python的参数传递有个容易混淆的点:可变对象和不可变对象的行为不同。不可变对象(数字、字符串、元组)传进函数后,函数内修改不影响外部;可变对象(列表、字典)传进去后,函数内修改会影响外部。

def add_item(lst): lst.append("新元素") my_list = [1, 2, 3] add_item(my_list) print(my_list) # [1, 2, 3, '新元素']

这个行为不是bug,是Python的设计。理解它的关键是记住:参数传递传的是对象的引用,不是对象的副本。如果不想让函数修改外部列表,就在函数内先复制一份。

4.2 返回值的多种形态

return语句有很多种写法,初学者容易迷糊。梳理一下:

  • 没有return:函数返回None
  • return:返回None
  • return a:返回单个值
  • return a, b:返回一个元组(a, b)
  • return [a, b]:返回一个列表
def divide(a, b): if b == 0: return None, "除数不能为0" return a / b, None result, error = divide(10, 2) if error: print(error) else: print(result)

这种"返回结果和错误信息"的模式在实际开发中很常见,叫"多返回值"。Python用元组解包的方式接收,写起来很自然。

一个常见的错误是忘记return。比如:

def add(a, b): a + b # 忘记写return result = add(1, 2) print(result) # None

函数体里计算了a + b,但没有return,结果就丢了。这个错误不报错,只是结果不对,排查起来很费时间。养成习惯:写完函数后,检查每个分支是否都有return。

4.3 作用域与可变默认参数的坑

作用域是第二次课的难点。简单说,函数内部定义的变量是局部变量,函数外部访问不到;函数外部定义的变量是全局变量,函数内部可以读取,但修改需要global声明。

count = 0 def increment(): global count count += 1 increment() print(count) # 1

不加global的话,count += 1会被当成在函数内新建一个局部变量,然后报UnboundLocalError。这个错误信息对初学者很不友好,但记住"函数内修改全局变量要加global"就能避免。

还有一个经典的坑是可变默认参数:

def add_item(item, lst=[]): lst.append(item) return lst print(add_item(1)) # [1] print(add_item(2)) # [1, 2] 而不是 [2]

默认参数lst=[]只在函数定义时创建一次,之后所有调用共享同一个列表。正确做法是用None作为默认值:

def add_item(item, lst=None): if lst is None: lst = [] lst.append(item) return lst

这个坑几乎每个Python初学者都会踩一次,第二次课如果老师没讲,自己也要记住。

4.4 函数的实操心得

我在实训里发现,同学们写函数时容易走两个极端:要么一个函数写几百行,什么都往里塞;要么把一行代码也封装成函数,过度设计。

合理的函数粒度是一个函数只做一件事,函数体控制在20到30行以内。判断标准很简单:如果你能用一句话说清楚这个函数干什么,且不需要用"然后"来连接,那粒度就合适。比如calculate_average只负责算平均值,print_report只负责打印报告,不要把计算和打印混在一起。

另一个心得是先写调用,再写实现。拿到题目后,先想"我希望怎么调用这个函数",把调用代码写出来,再去实现函数体。这种"自顶向下"的思路,能让函数的接口设计更合理,避免写着写着发现参数不够用。

5. 数据结构:把数据组织起来

5.1 列表的增删改查与切片

列表是Python里最常用的数据结构,第二次课会重点练。核心操作就四类:增、删、改、查。

fruits = ["苹果", "香蕉", "橙子"] fruits.append("葡萄") # 增:末尾添加 fruits.insert(1, "西瓜") # 增:指定位置插入 fruits.remove("香蕉") # 删:按值删除 last = fruits.pop() # 删:弹出末尾 fruits[0] = "红苹果" # 改:按下标修改 print(fruits[1]) # 查:按下标访问

切片是列表的杀手锏,必须掌握:

nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] nums[2:5] # [2, 3, 4] nums[:3] # [0, 1, 2] nums[7:] # [7, 8, 9] nums[::2] # [0, 2, 4, 6, 8] nums[::-1] # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

nums[::-1]是反转列表的常用写法,比reverse()方法更灵活,因为它返回新列表而不修改原列表。切片同样遵循左闭右开,且支持负数下标。

一个容易忽略的点是浅拷贝。b = a只是让b指向同一个列表,修改b会影响a。要真正复制,用b = a[:]或b = a.copy()。但如果列表里嵌套了列表,这两种方法都是浅拷贝,内层列表还是共享的,需要copy.deepcopy()。

5.2 字典的键值对操作

字典是"用名字找东西"的数据结构,适合存储有关联关系的数据。比如存储学生成绩:

scores = {"语文": 90, "数学": 95, "英语": 88} scores["物理"] = 92 # 增 scores["数学"] = 98 # 改 del scores["英语"] # 删 print(scores.get("化学", 0)) # 查,不存在返回默认值0

字典的遍历有三种方式:

for key in scores: # 遍历键 for value in scores.values(): # 遍历值 for key, value in scores.items(): # 遍历键值对

items()最常用,因为它一次拿到键和值。一个经典练习是统计词频:

text = "apple banana apple cherry banana apple" word_count = {} for word in text.split(): word_count[word] = word_count.get(word, 0) + 1 print(word_count) # {'apple': 3, 'banana': 2, 'cherry': 1}

word_count.get(word, 0) + 1这个写法很巧妙:如果word不在字典里,get返回0,加1后变成1;如果存在,返回当前值加1。这一行代码替代了if-else判断,是Python的惯用写法。

5.3 元组、集合与选型对比

元组和列表长得很像,区别是元组不可变。不可变带来两个好处:可以作为字典的键,可以保证数据不被意外修改。什么时候用元组?当一组数据在逻辑上是一个整体、不应该被单独修改时,比如坐标(x, y)、日期(年, 月, 日)。

集合是无序、不重复的元素集,主要用途是去重和集合运算:

a = {1, 2, 3, 4} b = {3, 4, 5, 6} print(a & b) # 交集 {3, 4} print(a | b) # 并集 {1, 2, 3, 4, 5, 6} print(a - b) # 差集 {1, 2}

四种数据结构的选型,可以对照这张表:

结构有序可变可重复典型场景
列表是是是有序数据集合,需要频繁增删改
元组是否是不可变数据,作为字典键
字典否是键不可重复键值映射,快速查找
集合否是否去重、集合运算

选型的核心问题是:数据需不需要顺序?需不需要用名字查找?会不会重复?把这三个问题回答清楚,结构就定了。

5.4 数据结构的实操心得

实训里最常见的错误是遍历时修改。比如想删除列表里所有的偶数:

nums = [1, 2, 3, 4, 5, 6] for n in nums: if n % 2 == 0: nums.remove(n) print(nums) # [1, 3, 5] 看起来对了,但换个数据就出错

问题在于remove会改变列表长度,导致遍历跳过元素。正确做法是遍历副本或者用列表推导式:

nums = [n for n in nums if n % 2 != 0]

列表推导式是Python的招牌特性,一行代码完成"遍历、筛选、生成新列表"。第二次课可能只讲基础用法,但值得提前掌握,因为它能让代码简洁很多。

另一个心得是字典的键必须是不可变类型。用列表当键会报TypeError: unhashable type。需要复合键时,用元组代替列表。

提示:判断元素在不在集合或字典里,比在列表里快得多。数据量大时,把列表转成集合再查找,性能提升非常明显。这是后面学算法时会反复用到的技巧。

6. 常见问题与排查技巧实录

6.1 缩进错误与IndentationError

Python用缩进表示代码块,这是它和其他语言最大的区别。缩进错误是初学者遇到最多的报错,没有之一。

常见的缩进问题有三种:混用空格和Tab、缩进层级不对、该缩进的地方没缩进。Python官方建议统一用4个空格,不要用Tab。很多编辑器可以设置"Tab转空格",建议开启。

# 错误示例 if True: print("hello") # IndentationError # 正确示例 if True: print("hello")

排查缩进错误有个技巧:把编辑器设置成"显示空白字符",空格显示成点,Tab显示成箭头,一眼就能看出混用。这个设置建议长期开着,能避免很多莫名其妙的报错。

6.2 类型错误与类型转换

TypeError是第二常见的错误,通常发生在类型不匹配的运算上。比如"1" + 1会报错,因为字符串和数字不能直接相加。

age = input("请输入年龄:") # input返回字符串 next_age = age + 1 # TypeError next_age = int(age) + 1 # 正确

input()永远返回字符串,这是初学者最容易忘的点。需要数字时,必须用int()或float()转换。转换时还要注意,如果用户输入的不是数字,int()会抛ValueError。健壮的写法是加try-except:

try: age = int(input("请输入年龄:")) except ValueError: print("请输入有效的数字")

try-except第二次课可能还没讲,但做练习时遇到输入转换就会需要,提前知道能少走弯路。

6.3 索引越界与KeyError

IndexError是列表下标超出范围,KeyError是字典键不存在。这两个错误的根源都是"访问了不存在的东西"。

nums = [1, 2, 3] print(nums[5]) # IndexError scores = {"语文": 90} print(scores["数学"]) # KeyError

避免IndexError的方法是在访问前检查长度,或者用切片(切片越界不报错,返回空列表)。避免KeyError的方法是用get()方法,或者用in检查:

if "数学" in scores: print(scores["数学"]) print(scores.get("数学", "无此科目"))

6.4 常见问题速查表

把实训里高频出现的问题整理成一张表,遇到报错先查表:

报错信息常见原因解决方法
IndentationError缩进不一致统一用4个空格
SyntaxError漏冒号、括号不匹配检查if/for/def行末冒号
TypeError类型不匹配用int()/str()转换
ValueError转换失败检查输入内容,加try-except
IndexError下标越界检查列表长度
KeyError字典键不存在用get()或in检查
NameError变量未定义检查变量名拼写
UnboundLocalError函数内修改全局变量加global声明

这张表建议打印出来贴在电脑旁边。报错不可怕,可怕的是看到报错就慌,不知道从哪查起。有了这张表,大部分错误都能自己定位。

6.5 调试的实操心得

最后分享几个调试技巧。第一,print大法永远有效。在关键位置打印变量值,看程序走到哪一步、变量是什么值。不要觉得print低级,能解决问题的就是好方法。

第二,二分法定位。如果程序很长,在中间加一个print,看前半段有没有问题,逐步缩小范围。这比从头到尾一行行看快得多。

第三,读懂报错信息。Python的报错信息其实很友好,它会告诉你错误类型、错误行号、错误原因。很多同学看到红色报错就跳过,直接问别人,其实报错信息里已经写了答案。养成先读报错、再查代码的习惯,进步会快很多。

第四,最小可复现。遇到复杂bug时,把相关代码复制到一个新文件里,删掉无关部分,只保留能触发bug的最少代码。这个过程本身就能帮你理清思路,很多时候删着删着就发现问题了。

我在实训里带过的一个同学,刚开始每次报错都举手问,后来我让他先自己读报错信息,读不懂再问。两周后他基本能独立解决80%的问题。调试能力不是天生的,是练出来的,而第二次课正是练这个的黄金时期。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询