Python基础语法:变量、类型、判断和循环

今天从一个空文件开始,逐次做出一份测试结果报告。我们只关心两个字段:状态status和耗时duration_ms。先处理一条记录,再处理四条;每增加一段代码,都先检查它解决了什么问题。

我是楠风。本文适合已经能运行Python文件,但看到几十行代码就不知道从哪里开始的同学。当前python沿用Day19准备的解释器。每个完整小例都可以单独保存、执行,不必先把后面的最终程序全部复制下来。

本课手写四条可信数据:passed/120、failed/850、skipped/0、passed/430。状态只有这三种,耗时是非负整数。先把输入限定清楚,注意力就能放在变量、判断和循环上;从外部接收未知数据时的字段检查留到后续学习。

第一次实现:两个名字保存一条结果

为什么需要变量?因为我们要先记住状态和耗时,随后再打印或计算。变量名让代码可以引用已经得到的值,而不必在每个位置重新写一个数字。

status = "passed"中,左边是名字,右边是值,单个等号表示赋值。引号说明passed是一段文字;120没有引号,是可以参加数值运算的整数。先运行这个完整小例:

python 复制代码
status = "passed"
duration_ms = 120
print("状态", status, "耗时", duration_ms)
duration_ms = duration_ms + 30
print("更新后", duration_ms)

输出:

text 复制代码
状态 passed 耗时 120
更新后 150

第一行让status表示passed,第二行让duration_ms表示120。print括号中的内容用逗号分开,会按顺序输出,默认在各项之间放一个空格。第四行先取右边的旧值120,算出120+30,再把150赋给左边的duration_ms。

这里的等号不是数学方程。如果把它理解成"两边永远相等",就会困惑为什么一个数可以等于自己加三十;程序做的是一次更新。更新完成后,后面的代码读到新值150。

立即改一处:把第二行120改成200,其他不动。答案是第一行输出耗时200,最后输出230。若你预测330,就把"先取旧值、算一次、再赋回"写在纸上,不能把同一行重复执行两次。

再用一段独立代码检查类型差异:

python 复制代码
print(120 + 30)
print("120" + "30")

输出分别是150和12030。相同的加号,数值输入做加法,文字输入做拼接。变量叫duration_ms也不能把字符串自动变成数字,值的类型才影响运算。本课统一使用不带引号的整数耗时。Python数值、文字与变量教程

第二次实现:只在通过时增加计数

现在的问题变成:这一条是否通过?判断需要比较,而不是重新赋值。写法是if 条件:,下一行缩进四个空格,表示条件成立时执行的动作。

python 复制代码
status = "passed"
passed_count = 0
if status == "passed":
    passed_count = passed_count + 1
print("通过", passed_count)

输出为通过 1。两个等号比较左右是否相等;冒号开启这一段判断。进入判断后,右边0+1算出1,再更新计数。最后一行没有缩进,因此无论条件是否成立,都要打印。

把第一行改成failed,输出变成通过 0。这解释了为什么初值要放在if前面:不符合条件的记录也应该有一个确定的计数零。如果只在分支内部创建计数变量,未进入该分支时,后面的打印就没有值可读。

做一次缩进练习:只把最后的print缩进四个空格,并保留failed输入。答案是没有输出,因为打印也成了"只有通过才执行"的动作。恢复原来的缩进后又会输出零。Python的缩进参与程序含义,不只是为了排版好看。

第三次实现:把同一条记录放进字典

两个分散变量还能处理一条结果,四条结果就难以配对了。我们需要明确"这个状态和这个耗时属于同一条"。字典用大括号保存键和值,冒号连接字段名与字段值,逗号分隔字段。

python 复制代码
result = {"status": "passed", "duration_ms": 120}
print(result["status"])
print(result["duration_ms"] + 30)

输出是passed和150。第一对status是键,passed是值;result["status"]根据键取出对应值。第二次取出的120仍然是整数,所以可以加三十。键上的引号属于取值语法,不能把整个表达式误认为字符串。

接着改一处:把字典中的duration_ms改成430。答案是第一行仍为passed,第二行变成460。状态没有跟着耗时变化,因为这是两个不同字段。Python字典教程

一条字典有了,接下来用列表装多条。列表外面使用方括号,各项之间用逗号隔开;这里每一项都是一条字典。

python 复制代码
records = [
    {"status": "passed", "duration_ms": 120},
    {"status": "failed", "duration_ms": 850},
    {"status": "skipped", "duration_ms": 0},
    {"status": "passed", "duration_ms": 430},
]
print(len(records))
print(records[0]["status"])
print(records[1]["duration_ms"])

输出依次为4、passed、850。len给出列表中有几项;下标从零开始,因此records0是第一条,records1是第二条。读最后一行时分两步:先按下标取第二条字典,再按键取850。

练习:不改数据,只让最后一行读取第四条的耗时。答案是print(records[3]["duration_ms"]),输出430。这是替换上一完整例子的最后一行,不是另开文件直接运行。

第四次实现:让for代替重复四次取值

如果手写四个下标相加,新增一条数据就得修改计算。for用于依次取得列表中的每一项:for record in records:把本轮那条字典交给record,缩进部分执行完,再取下一条。

python 复制代码
records = [
    {"status": "passed", "duration_ms": 120},
    {"status": "failed", "duration_ms": 850},
    {"status": "skipped", "duration_ms": 0},
    {"status": "passed", "duration_ms": 430},
]
total_ms = 0
for record in records:
    total_ms += record["duration_ms"]
    print(record["status"], total_ms)
print("最终累计", total_ms)

完整输出:

text 复制代码
passed 120
failed 970
skipped 970
passed 1400
最终累计 1400

for拿到的是一条字典,不是下标数字。这里的total_ms += record["duration_ms"]相当于先计算total_ms + record["duration_ms"],再赋回total_ms。第一轮零加120,第二轮用已经保存的120加850。

循环内的print展示每轮结果;循环外的print只在全部处理完成后执行一次。两种位置都有用途,调试时放在内部观察变化,最终报告放在外部。先看变化过程,再看最终答案,才能定位是哪一轮开始偏离预期。Python的if与for教程

填空练习不需要新语法:把更新累计的那一行改成长写法,右边应该填什么?答案是total_ms = total_ms + record["duration_ms"]。在刚才的完整代码中替换这一行,输出完全不变。能写出展开式,才说明理解了加等号。

第五次实现:同一轮既累计耗时,也统计状态

我们需要三个计数,所以在for前分别设成零。每一轮先累计耗时,再读取status,随后进入对应分支。if匹配passed,elif表示前一个没匹配时继续判断failed,else处理剩余情况。

先单独验证分支,使用第二条记录:

python 复制代码
result = {"status": "failed", "duration_ms": 850}
status = result["status"]
passed_count = 0
failed_count = 0
skipped_count = 0
if status == "passed":
    passed_count += 1
elif status == "failed":
    failed_count += 1
else:
    skipped_count += 1
print(passed_count, failed_count, skipped_count)

输出为0 1 0。第一个条件不成立,程序继续检查elif;它成立后增加失败计数,不再执行else。把输入状态改为skipped,两个比较都不成立,才会进入最后一支,输出0 0 1。改为passed则输出1 0 0。这三种输入各跑一次,比只背"多分支"更容易确认执行顺序。

本课输入只有passed、failed、skipped,因此else恰好对应skipped。不能把这个教学前提推广成"任何陌生状态都算跳过"。本阶段不再引入额外输入,只把这三个分支走清楚。

现在把分支放进for。下面是完整、可独立运行的阶段程序:四个初值都在for外,每轮的耗时更新和状态判断在for内,分支中的计数更新再缩进一级。

python 复制代码
records = [
    {"status": "passed", "duration_ms": 120},
    {"status": "failed", "duration_ms": 850},
    {"status": "skipped", "duration_ms": 0},
    {"status": "passed", "duration_ms": 430},
]

passed_count = 0
failed_count = 0
skipped_count = 0
total_ms = 0

for record in records:
    total_ms += record["duration_ms"]
    status = record["status"]
    if status == "passed":
        passed_count += 1
    elif status == "failed":
        failed_count += 1
    else:
        # 本课手写样例只含 passed、failed、skipped 三种状态。
        skipped_count += 1
    print(passed_count, failed_count, skipped_count, total_ms)

每行依次是通过、失败、跳过和耗时累计,完整输出如下:

text 复制代码
1 0 0 120
1 1 0 970
1 1 1 970
2 1 1 1400

先不用急着写最终报告,按照如下表追踪。每格是这一轮处理结束后的累计值,不是该条记录自己的值:

轮次 当前状态与耗时 passed_count failed_count skipped_count total_ms
开始 尚未处理 0 0 0 0
1 passed,120 1 0 0 120
2 failed,850 1 1 0 970
3 skipped,0 1 1 1 970
4 passed,430 2 1 1 1400

检查表格时可以固定顺序:先抄上一轮的四个累计值,再更新耗时,最后只更新对应状态的那个计数。这样不会把"当前耗时850"和"累计耗时970"混为一谈。调试输出也遵循这个顺序,如果第二轮已经错了,就不用先研究百分比的显示。

第三轮累计不变,跳过计数却增加一,说明"耗时为零"与"没有处理记录"不是同一件事。第四轮passed_count从一变二,另外两个计数保留旧值;不匹配某个分支,不代表把那个分支的计数清零。

第六次实现:先决定分母,再决定显示

通过率在本课定义为通过次数除以通过加失败次数。跳过仍是一条执行记录,所以执行次数是四;它没有参加通过或失败的判定,所以有效执行数是三。不能直接把列表长度拿来做分母。

先运行一个只有计算与显示的小例:

python 复制代码
passed_count = 2
failed_count = 1
decided_count = passed_count + failed_count
if decided_count == 0:
    rate_text = "N/A"
else:
    rate = passed_count / decided_count
    rate_text = f"{rate:.2%}"
print("有效执行", decided_count)
print("通过率", rate_text)

输出是有效执行 3和通过率 66.67%。斜杠执行除法;f放在字符串前,花括号内取rate的值,冒号后的.2%让它按百分数显示并保留两位小数。先算出比例,再得到用于输出的文字,是两步操作。Python格式化字符串教程

改输入练习:通过零、失败一,答案为0.00%;通过零、失败零,答案为N/A。前者有一条有效执行却没有通过,后者没有可计算的分母。必须先判断分母,不能先执行除法再想着补显示。

把已经理解的各段接起来

下面是完整主例。保存为report.py后执行python report.py。不要把它当成新知识:数据、初始化、循环、判断、分母、格式化都已经单独运行过。

python 复制代码
records = [
    {"status": "passed", "duration_ms": 120},
    {"status": "failed", "duration_ms": 850},
    {"status": "skipped", "duration_ms": 0},
    {"status": "passed", "duration_ms": 430},
]

passed_count = 0
failed_count = 0
skipped_count = 0
total_ms = 0

for record in records:
    total_ms += record["duration_ms"]
    status = record["status"]
    if status == "passed":
        passed_count += 1
    elif status == "failed":
        failed_count += 1
    else:
        # 本课手写样例只含 passed、failed、skipped 三种状态。
        skipped_count += 1

decided_count = passed_count + failed_count
if decided_count == 0:
    rate_text = "N/A"
else:
    rate = passed_count / decided_count
    rate_text = f"{rate:.2%}"

print("执行次数:", len(records))
print("通过:", passed_count, "失败:", failed_count, "跳过:", skipped_count)
print("累计耗时:", total_ms, "ms")
print("有效执行:", decided_count)
print("通过率:", rate_text)

完整输出:

text 复制代码
执行次数: 4
通过: 2 失败: 1 跳过: 1
累计耗时: 1400 ms
有效执行: 3
通过率: 66.67%

重新阅读时,先找所有缩进层次。total_ms更新缩进四个空格,属于每一轮;三个计数的更新再多缩进四个空格,属于对应状态分支。decided_count退回最左边,表示全部记录处理完才计算分母。

调试练习:为什么最后只剩430

下面是故意写错的独立例子,它能运行,但没有正确累计:

python 复制代码
records = [
    {"status": "passed", "duration_ms": 120},
    {"status": "failed", "duration_ms": 850},
    {"status": "skipped", "duration_ms": 0},
    {"status": "passed", "duration_ms": 430},
]
for record in records:
    total_ms = 0
    total_ms += record["duration_ms"]
    print(total_ms)

输出为120、850、0、430,每轮先把旧值抹成零。参考修复是把total_ms = 0移到for前,取消该行缩进;其余不变。修复后四行应为120、970、970、1400,正是前面的累计轨迹。

如果要让AI帮忙,只给这段错误代码和预期1400,要求"指出第一次丢失累计值的位置,不提供重写版本"。你仍要自己移动那一行,并解释第二轮为何从120继续,而不是从零重新开始。

最后从空文件写一次:筛出较慢的有效记录

新要求:统计非skipped且耗时至少500ms的记录数量与总耗时。先补两个符号:!=表示不相等,>=表示大于或等于;and连接两个条件,两边都成立才进入if。

python 复制代码
status = "failed"
duration_ms = 850
print(status != "skipped")
print(duration_ms >= 500)
print(status != "skipped" and duration_ms >= 500)

三行都是True,表示条件成立。把status改成skipped,三行变成False、True、False;耗时条件单独成立,也不足以通过组合条件。

这道题重新使用下面完整列出的四条原始数据,不沿用前题改过的状态或耗时。现在先遮住答案,用这四条数据手写。你需要列表、两个零初值、一个for和一个if,不需要新工具。参考答案如下:

python 复制代码
records = [
    {"status": "passed", "duration_ms": 120},
    {"status": "failed", "duration_ms": 850},
    {"status": "skipped", "duration_ms": 0},
    {"status": "passed", "duration_ms": 430},
]
threshold_ms = 500
slow_count = 0
slow_ms = 0
for record in records:
    if record["status"] != "skipped" and record["duration_ms"] >= threshold_ms:
        slow_count += 1
        slow_ms += record["duration_ms"]
print("符合数量", slow_count)
print("符合耗时", slow_ms)

输出为符合数量 1和符合耗时 850。只有第二条同时满足条件。把threshold_ms改成430后,第四条也满足,答案变为2和1280;等于430不能漏掉,因为比较符号带等号。

写完后倒查每个变量:哪一行给了初值,哪一行更新,哪一行只负责显示?如果某一项答不上来,就回到对应小例重做一次。今天真正需要交出的成果,是能独立写出最后这个变式,而不是收藏更多语法名称。

本文所有完整代码及替换练习在Python3.12.10实际运行,官方资料查阅于2026-10-19。下一篇:Python数据结构与函数:写出可复用的数据处理逻辑。

相关推荐
陈随易4 小时前
纯 Go 写的 JS 引擎,跑分超过了 C 语言的 QuickJS
前端·后端·程序员
陈随易7 小时前
专注飞机 FocusFlight,两个人把番茄钟做成了一趟航班
前端·后端·程序员
爱勇宝8 小时前
忙不是勋章,身体才是职场人唯一的长期资产
程序员
kyriewen10 小时前
我扫了 422 个仓库,近一半 CI 在裸装依赖
前端·javascript·程序员
致力于划水第一线11 小时前
现在矿石每天签到只送一个了吗
程序员
Thneonl12 小时前
桌宠穿透不是一行 SetWindowLong
windows·程序员
Captaincc13 小时前
永别了,程序员!(转)
程序员
深蓝AI13 小时前
GPT-6.1 Terra 在 Bedrock 全面可用:便宜一半快三倍,日常任务选型要重算
人工智能·程序员
陈随易14 小时前
Node.js 2026上半年都有哪些性能提升?
前端·后端·程序员
Hilaku15 小时前
Event Loop 这篇你看完就不用再看别的了!
前端·javascript·程序员