第 2 周:循环、函数、随机数与对象
本周开始把“重复执行”和“重复使用”分开处理:循环负责重复步骤,函数负责封装一个可以反复调用的完整任务,对象负责把同一实体的数据和行为放在一起。
建议用时:8-10 小时。
本周目标#
学完本周,你应该能够:
- 根据执行次数选择
for或while。 - 正确写出
range()的起点、终点和步长。 - 用变量跟踪表推演循环每一轮的状态。
- 使用
break、continue和嵌套循环。 - 定义函数,传入参数并接收返回值。
- 区分
print()与return,解释函数返回None的情况。 - 使用
random生成可测试的随机结果。 - 用类和对象保存一个学生、一本书或一个游戏角色的数据。
2.1 为什么需要循环#
输出 1 到 5 可以直接写五行:
print(1)
print(2)
print(3)
print(4)
print(5)
但输出 1 到 1000 时,重复写代码显然不可取。循环可以把“变化的部分”统一成规律:
for number in range(1, 6):
print(number)
结果:
1
2
3
4
5
循环代码要回答三个问题:
- 循环变量从哪个值开始?
- 每轮循环变量怎样变化?
- 什么时候停止?
2.2 for 与 range()#
for 适合“数据或次数大致已知”的重复。
最常用的三种 range()#
for i in range(5):
print(i)
# 0 1 2 3 4
for i in range(2, 6):
print(i)
# 2 3 4 5
for i in range(1, 10, 3):
print(i)
# 1 4 7
规则:
range(stop):从 0 开始,不包含stop。range(start, stop):从start开始,不包含stop。range(start, stop, step):每次增加step,仍然不包含stop。step可以是负数,但起点要大于终点。
for i in range(5, 0, -1):
print(i)
# 5 4 3 2 1
循环变量只在下一轮重新赋值#
total = 0
for i in range(1, 5):
total += i
print("i =", i, "total =", total)
变量跟踪表:
| 轮次 | i |
本轮后 total |
输出 |
|---|---|---|---|
| 1 | 1 | 1 | i = 1 total = 1 |
| 2 | 2 | 3 | i = 2 total = 3 |
| 3 | 3 | 6 | i = 3 total = 6 |
| 4 | 4 | 10 | i = 4 total = 10 |
循环结束后,i 保留最后一轮的值 4。total 保留累积结果 10。
循环做多少次#
range |
值序列 | 次数 |
|---|---|---|
range(4) |
0, 1, 2, 3 | 4 |
range(1, 4) |
1, 2, 3 | 3 |
range(2, 11, 2) |
2, 4, 6, 8, 10 | 5 |
range(5, 0, -1) |
5, 4, 3, 2, 1 | 5 |
range(3, 3) |
无 | 0 |
手推时不要凭“感觉循环几次”,直接写下序列。
2.3 while#
while 适合“不知道要重复几次,只要条件成立就继续”的情况。
number = 1
total = 0
while number <= 5:
total += number
number += 1
print(total) # 15
循环条件在每一轮开始时检查。因为循环体最后让 number 增加,所以它最终会超过 5。
while 的三个必备部分#
number = 10 # 1. 初始化
while number > 0: # 2. 条件
print(number)
number -= 1 # 3. 更新
忘记更新可能造成无限循环:
number = 1
while number <= 3:
print(number)
# 缺少 number += 1
程序会一直输出 1,直到被手动停止。排错时先检查循环变量是否在循环体内部被修改。
先执行后判断#
有些交互适合先执行一次,再决定是否继续:
command = ""
while command != "退出":
command = input("请输入命令:")
print("收到:", command)
输入“退出”的那一轮仍会执行后面的 print,因为条件是在这一轮开始前检查的。如果需要输入后立即停止,可以使用 break。
2.4 break 与 continue#
break 立即结束整个循环:
for number in range(1, 10):
if number == 4:
break
print(number)
# 1 2 3
continue 立即结束本轮,进入下一轮:
total = 0
for number in range(1, 6):
if number % 2 == 0:
continue
total += number
print(total) # 9
这里只累加 1、3、5。continue 之后的语句在本轮不会执行。
用 while True 和 break 组织菜单#
while True:
choice = input("1 继续,2 退出:")
if choice == "2":
break
print("继续练习")
print("程序结束")
while True 不是无条件永远运行。只要每条路径最终都有机会执行 break,它就是一种清晰的循环写法。
2.5 嵌套循环#
嵌套循环就是循环体内还有循环。外层每执行一轮,内层会完整执行一遍。
for row in range(1, 4):
for column in range(1, 4):
print(row, column)
执行顺序:
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
外层共 3 轮,内层每轮 3 次,所以内层语句总共执行 3 * 3 = 9 次。
乘法表#
for row in range(1, 4):
for column in range(1, 4):
print(f"{row}x{column}={row * column}", end=" ")
print()
结果:
1x1=1 1x2=2 1x3=3
2x1=2 2x2=4 2x3=6
3x1=3 3x2=6 3x3=9
print() 在外层循环中结束一行,end=" " 在内层循环中让同一行的内容不换行。
星号三角形#
for row in range(1, 5):
print("*" * row)
结果:
*
**
***
****
嵌套循环常见错误是缩进。确定某个 print 属于内层还是外层,要通过它的缩进层级判断。
2.6 函数:给一段逻辑起名字#
函数把一段会重复使用的逻辑封装起来。
def show_welcome(name):
print("欢迎", name)
print("开始学习 Python")
show_welcome("小林")
show_welcome("李雨")
结构:
def 函数名(参数):
函数体
关键规则:
def表示定义函数,定义时函数体不会执行。- 调用
show_welcome(...)时才执行。 - 参数接收调用时传入的值。
- 函数体统一缩进 4 个空格。
- 函数名后需要圆括号和冒号。
参数与实参#
def add(a, b):
result = a + b
print(result)
add(3, 5)
add(10, 20)
定义中的 a、b 是形参,调用中的 3、5 是实参。默认按位置对应。
return 返回值#
仅仅打印结果的函数不能方便地被其他计算继续使用。返回值让函数把结果交还给调用者:
def add(a, b):
return a + b
result = add(3, 5)
print(result * 2) # 16
比较:
def print_sum(a, b):
print(a + b)
def return_sum(a, b):
return a + b
x = print_sum(2, 3) # 输出 5,同时 x 得到 None
y = return_sum(2, 3) # 不输出,y 得到 5
print() 是向屏幕显示内容,return 是把结果交给调用者。两者不是一回事。
return 会立即结束函数#
def check_score(score):
if score < 0 or score > 100:
return "成绩无效"
if score >= 60:
return "及格"
return "不及格"
当第一个 return 执行时,后面的代码不再执行。这种“提前返回”可以减少嵌套。
如果函数没有执行任何 return,调用结果就是 None:
def show_message():
print("你好")
value = show_message()
print(value) # None
2.7 参数规则与变量作用域#
参数数量必须匹配#
def rectangle_area(length, width):
return length * width
print(rectangle_area(4, 3)) # 12
只传一个参数会报 TypeError,因为它缺少 width;传三个参数也会报错。
默认参数#
def greet(name, greeting="你好"):
return f"{greeting},{name}"
print(greet("小林")) # 你好,小林
print(greet("小林", "早上好")) # 早上好,小林
带默认值的参数一般放在不带默认值的参数后面。不要在基础练习中使用可变对象作为默认参数,例如 [] 或 {}。
局部变量#
函数中创建的变量默认只属于这个函数:
def calculate():
local_value = 10
return local_value
print(calculate()) # 10
# print(local_value) # NameError
函数外部无法直接使用 local_value。需要把结果通过 return 带回外部。
同样是 result 这个名称,在不同函数中可以表示不同变量。这种隔离让函数更容易单独测试。
2.8 生成随机数#
import random
number = random.randint(1, 6)
print(number)
randint(a, b) 的结果包含 a 和 b,所以 randint(1, 6) 可能是 1、2、3、4、5、6。
常用函数:
| 写法 | 作用 | 示例结果范围 |
|---|---|---|
random.randint(1, 6) |
生成闭区间整数 | 1-6 |
random.randrange(1, 7) |
和 range 一样不含终点 |
1-6 |
random.choice(["A", "B", "C"]) |
随机选一个元素 | 三个元素之一 |
random.shuffle(items) |
原地打乱列表 | 原列表被修改 |
随机程序难以只靠肉眼复现时,可以固定随机种子:
import random
random.seed(2026)
print(random.randint(1, 100))
print(random.randint(1, 100))
相同 Python 环境和相同种子通常会得到相同序列,便于排查问题。正式比赛如果要求随机结果,不要为了“稳定输出”擅自固定种子,除非题目允许。
2.9 类与对象#
类描述一类事物“有哪些数据、能做什么”。对象是由类创建出来的具体实例。
以学生为例:
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def is_passed(self):
return self.score >= 60
student1 = Student("小林", 86)
student2 = Student("李雨", 52)
print(student1.name) # 小林
print(student1.score) # 86
print(student1.is_passed()) # True
print(student2.is_passed()) # False
逐行理解#
class Student:定义一个类。__init__是初始化方法,创建对象时自动执行。self表示当前这个对象。self.name = name把传入的姓名保存到对象自己的属性中。student1 = Student(...)创建对象并自动调用__init__。student1.name读取该对象的属性。student1.is_passed()调用该对象的方法。
两个对象各自保存自己的数据:
student1.score = 95
print(student1.score) # 95
print(student2.score) # 52
修改一个对象的属性,不会自动改变另一个对象。
常见错误#
class Book:
def __init__(self, title):
self.title = title
book = Book("Python")
print(book.title) # Python
- 定义
__init__时忘记self,创建对象时会报参数数量错误。 - 方法内部访问属性时把
self.title写成局部变量title。 - 把类名
Book当成对象属性,或误以为print(book)会自动输出书名。
2.10 本周作品一:猜数字#
需求#
- 答案在 1-100 之间。
- 每次输入一个整数。
- 猜小、猜大、猜对分别给出提示。
- 猜对后输出尝试次数并结束。
import random
answer = random.randint(1, 100)
attempts = 0
while True:
guess = int(input("请输入 1-100 的整数:"))
attempts += 1
if guess < answer:
print("猜小了")
elif guess > answer:
print("猜大了")
else:
print(f"猜对了,共尝试 {attempts} 次")
break
测试时可以将 answer = random.randint(1, 100) 临时改成固定值,例如 answer = 50,这样能稳定测试“猜小、猜大、猜对”三条路径。完成后恢复随机答案。
边界测试:
- 第一次就猜对。
- 连续猜小。
- 连续猜大。
- 猜测 1 和 100。
2.11 本周作品二:成绩统计函数#
先写小函数,再组合。下面的程序接受一个分数列表,统计人数、平均分、及格人数和最高分:
def calculate_average(scores):
if not scores:
return 0.0
return sum(scores) / len(scores)
def count_passed(scores):
count = 0
for score in scores:
if score >= 60:
count += 1
return count
def find_highest(scores):
highest = scores[0]
for score in scores:
if score > highest:
highest = score
return highest
scores = [88, 59, 92, 70, 100]
print("人数:", len(scores))
print(f"平均分:{calculate_average(scores):.2f}")
print("及格人数:", count_passed(scores))
print("最高分:", find_highest(scores))
本周只需要理解“函数接收一组数据并返回结果”。索引、切片和列表方法会在第 3 周系统讲解。
为什么空列表要先处理?因为 sum([]) / len([]) 会除以 0。先判断空数据,是边界测试意识。
2.12 本周作品三:学生对象#
class Student:
def __init__(self, student_id, name, score):
self.student_id = student_id
self.name = name
self.score = score
def level(self):
if self.score < 0 or self.score > 100:
return "成绩无效"
if self.score >= 90:
return "优秀"
if self.score >= 80:
return "良好"
if self.score >= 60:
return "及格"
return "不及格"
def add_bonus(self, points):
self.score += points
student = Student("S001", "张晨", 78)
student.add_bonus(5)
print(student.student_id, student.name, student.score)
print(student.level())
输出:
S001 张晨 83
良好
add_bonus() 修改的是对象自身的 score,所以调用后再次读取 student.score 会得到新值。
2.13 独立练习#
A. 快速判断题#
range(2, 8, 2)依次产生哪些数?下面的循环输出几行?
for i in range(3): for j in range(2): print(i, j)函数中没有
return,调用结果是什么?return和print有什么区别?random.randint(1, 6)能否得到 6?对象方法为什么通常需要第一个参数
self?下面的程序为什么不会停止?运行时会最先不断输出什么?
count = 0 while count < 3: print(count)
B. 编程题#
- 用循环计算 1 到 100 的整数和。
- 输出 1 到 20 中的所有偶数。
- 输入一个正整数,判断它是不是质数。
- 输出九九乘法表。
- 编写函数
findMinMax(values),返回列表的最小值和最大值。 - 编写函数
count_above(scores, target),统计高于目标分数的数量。 - 定义
Book类,包含书名、作者、价格和显示信息的方法。 - 让猜数字程序在输入非整数时给出提示并继续,而不是直接崩溃。提示:本周的完整异常处理还没学,可以先只在输入
"q"时退出,其他非法输入留到第 7 周。
2.14 练习答案与解释#
A. 快速判断题答案#
2, 4, 6。终点 8 不包含。- 6 行。外层 3 轮,内层每轮 2 次。
None。print显示内容但不把值交给调用者;return结束函数并把值交给调用者。- 可以。
randint包含区间两端。 self指向当前对象,用来区分和访问该对象自己的属性。count从未增加,条件count < 3永远为真,循环会无限执行。程序最先不断输出0,直到被手动中断。
B. 编程题参考答案#
1 到 100 求和:
total = 0 for number in range(1, 101): total += number print(total)1 到 20 的偶数:
for number in range(2, 21, 2): print(number)质数判断:
number = int(input()) is_prime = True if number < 2: is_prime = False else: for divisor in range(2, number): if number % divisor == 0: is_prime = False break if is_prime: print("质数") else: print("不是质数")九九乘法表:
for row in range(1, 10): for column in range(1, row + 1): print(f"{column}x{row}={column * row}", end="\t") print()最小值和最大值:
def findMinMax(values): minimum = values[0] maximum = values[0] for value in values: if value < minimum: minimum = value if value > maximum: maximum = value return minimum, maximum print(findMinMax([4, 9, 1, 7]))返回结果是元组
(1, 9)。空列表不能直接取values[0],调用者应保证列表非空或先处理空列表。统计高于目标分数的数量:
def count_above(scores, target): count = 0 for score in scores: if score > target: count += 1 return count print(count_above([58, 76, 90, 60], 60)) # 2Book类:class Book: def __init__(self, title, author, price): self.title = title self.author = author self.price = price def show_info(self): print(f"《{self.title}》 作者:{self.author} 价格:{self.price:.2f} 元") book = Book("Python 入门", "张老师", 45.5) book.show_info()猜数字增加退出选项:
import random answer = random.randint(1, 100) while True: guess_text = input("请输入整数,或输入 q 退出:") if guess_text == "q": print("已退出") break guess = int(guess_text) if guess < answer: print("猜小了") elif guess > answer: print("猜大了") else: print("猜对了") break输入
abc仍会触发ValueError。第 7 周会学习完整处理方式。
2.15 常见错误清单#
- 把
range(1, 6)误认为包含 6。 - 循环变量不在循环体中更新,造成无限循环。
- 把
continue当成结束整个循环。 - 忘记函数体的缩进。
- 定义了有返回值的函数,却把调用结果丢掉,只期待屏幕上出现结果。
- 把
return写在循环第一轮中,导致循环提前结束。 - 认为函数中的局部变量可以在函数外用。
self.attribute与局部变量混用。- 随机程序只测试一次就认为正确。
2.16 过关检查#
- 能不运行代码,写出任意
range()产生的序列和次数。 - 能用表格推演一个
while循环每轮变量值。 - 能说清
break和continue的差异。 - 能预测两层嵌套循环的总执行次数。
- 能定义带参数和返回值的函数,并至少调用两次。
- 能解释“函数返回
None”的原因。 - 能使用
randint,并通过固定答案完成完整路径测试。 - 能定义类、创建两个对象,并证明它们的属性互相独立。
- 独立完成猜数字程序,覆盖猜小、猜大、猜对和退出。
- 第 1 周的三个作品可以关掉资料重写。