文章目录

环境配置和使用

环境搭建

Python 解释器详解

Python 是高级编程语言,无法直接被计算机执行,需通过解释器将代码转换为机器语言(01 二进制)。常见解释器类型:

  • CPython:官方默认版本,C 语言开发,应用最广泛
  • IPython:交互式解释器,增强了 CPython 的交互体验
  • PyPy:JIT 编译型解释器,执行效率远超 CPython
  • Jython/IronPython:分别运行在 Java/.NET 平台的解释器

环境安装(Anaconda)

(1)Anaconda/Miniconda/Conda 的区别

  • Conda:底层包管理和环境管理工具
  • Anaconda:“全家桶”,包含 Conda+Python+1500+数据科学库+图形化工具(体积约 500MB-1GB)
  • Miniconda:迷你版,仅包含 Conda+Python+核心依赖(体积小,适合定制化环境)

(2)安装步骤

1 下载对应系统版本的 Anaconda(官网:https://www.anaconda.com/)开始安装

2 选择为所有人安装

3 运行安装包,建议安装到非 C 盘目录

4 勾选 “Add Anaconda to my PATH environment variable”(可选,方便命令行调用)

5 等待安装完成,验证:

conda -V  # 查看conda版本
python -V # 查看Python版本

配置环境变量

为了可以在cmd中使用Python, 建议去系统环境变量中配置

PyCharm 使用指南

PyCharm 的优势

作为 Python 专用 IDE,PyCharm 提供:

  • 项目管理与代码组织
  • 智能代码提示与补全
  • 语法高亮与错误检测
  • 断点调试与性能分析
  • 内置版本控制(Git/SVN)
  • 丰富的插件生态

PyCharm 版本选择

现在收费版(专业版)和免费版统一下载,免费版可以供个人使用,如果想升级收费版,需要购买激活码

PyCharm 安装与配置

(1)下载安装

官网地址:https://www.jetbrains.com/pycharm/download/

安装步骤:

  1. 下载对应系统版本的社区版

双击exe安装

  1. 选择非 C 盘安装路径
  1. 勾选 “Create desktop shortcut” 和 “.py file association”

选择试用

(2)核心操作

① 创建项目

  1. 打开 PyCharm,点击 “New Project”
  2. 设置项目名称与路径(非 C 盘)
  3. 选择 Python 解释器(Anaconda 或系统 Python)
  1. 点击 “Create” 完成创建

ps:

如果找不到解释器,则打开目录输入解释器路径即可

② 新建文件与编写代码

  1. 右键项目根目录 → New → Python File
  2. 输入文件名(如 hello_world)
  1. 编写入门代码:
# 第一个Python程序
print("Hello Python!")

③ 运行代码

  • 方式 1:右键代码编辑区 → Run ‘文件名’
  • 方式 2:点击代码右上角的运行按钮

  • 方式 3:快捷键 Shift+F10

④ 个性化设置

  1. 主题设置:File → Settings → Appearance & Behavior → Appearance
  1. 字体设置:File → Settings → Editor → Font(建议字号 14-16,字体 Consolas / 等线)
  1. 解释器切换:File → Settings → Project: 项目名 → Python Interpreter

⑤ 常用快捷键

快捷键功能
Ctrl+S保存代码
Ctrl+/快速注释 / 取消注释
Ctrl+D复制当前行
Ctrl+Z撤销操作
Ctrl+Y恢复撤销
Ctrl+Alt+L格式化代码
Shift+F10运行代码
F9断点调试

变量和控制逻辑

注释规范

注释是代码的说明文档,解释器不会执行注释内容,核心作用是提高代码可读性。

(1)单行注释

以<font style="color:rgb(0, 0, 0);">#</font>开头,注释单行内容:

# 这是单行注释(推荐写在代码上方)
print("Hello World")  # 这是行尾注释(建议保留2个空格)

(2)多行注释

使用三引号("""或''')包裹多行内容:

"""
这是多行注释
适用于:
1. 函数/类的功能说明
2. 大段代码的逻辑解释
3. 临时注释多行代码
"""
'''
单引号三引号同样支持多行注释
'''
print("多行注释演示")

变量的定义与使用

(1)变量的本质

变量是存储数据的临时容器,程序运行过程中值可动态修改。

(2)定义语法

# 变量名 = 变量值(等号两侧保留1个空格)
age = 18  # 整数类型
name = "张三"  # 字符串类型
score = 98.5  # 浮点数类型

(3)命名规则

:::color4
强制规则

  1. 仅能包含字母、数字、下划线(_)
  2. 不能以数字开头
  3. 严格区分大小写(age 和 Age 是不同变量)
  4. 不能使用 Python 关键字(如 if、for、while)

:::

推荐规范

:::color2

  1. 见名知义(如 user_name 而非 a1)
  2. 多个单词命名:
    • 小驼峰:userName(第二个单词首字母大写)
    • 下划线:user_name(推荐,Python 官方风格)
    • 大驼峰:UserName(类名专用)

:::

(4)变量的数据类型

Python 是动态类型语言,变量类型由赋值的内容决定:

类型说明示例
整数(int)无小数的数字age = 20
浮点数(float)带小数的数字price = 19.9
字符串(str)文本内容(单 / 双引号包裹)name = “Python”
布尔值(bool)逻辑值(True/False)is_ok = True
列表(list)有序可变集合nums = [1,2,3]
元组(tuple)有序不可变集合info = (“张三”, 20)
字典(dict)键值对集合student = {“name”:“李四”, “age”:19}

(5)类型检测

# 方法1:type()函数
age = 18
print(type(age))  # <class 'int'>

# 方法2:isinstance()函数(推荐)
print(isinstance(age, int))  # True
print(isinstance(age, str))  # False

输入与输出

1️⃣:f-string(Python3.6+,推荐)

name = "李四"
age = 20
score = 95.5
# 基础用法
print(f"姓名:{name},年龄:{age},成绩:{score}")
# 格式控制(保留2位小数)
print(f"成绩:{score:.2f}")
# 数字补零(6位宽度)
print(f"学号:{1:06d}")  # 输出:000001

2️⃣:format() 方法

print("姓名:{},年龄:{}".format(name, age))
# 指定位置
print("年龄:{1},姓名:{0}".format(name, age))
# 格式控制
print("成绩:{:.2f}".format(score))

3️⃣**:% 占位符(兼容旧版本)**

print(“姓名:%s,年龄:%d,成绩:%.2f” % (name, age, score))

(2)转义字符

转义符功能
\n换行
\t制表符(4 个空格)
反斜杠本身
"双引号
'单引号

示例:

print("姓名:{}\t年龄:{}\n成绩:{:.2f}".format(name, age, score))
# 自定义print结束符(默认\n)
print("Hello", end=" ")
print("Python")  # 输出:Hello Python

(3)输入函数 input()

获取用户从键盘输入的内容,所有输入均为字符串类型:

# 基础用法
name = input("请输入您的姓名:")
age = input("请输入您的年龄:")
print(f"您输入的姓名:{name},年龄:{age}")

# 带提示的输入
phone = input("请输入您的手机号:")
print(f"手机号:{phone},类型:{type(phone)}")

数据类型转换

当需要对输入的字符串进行数值运算时,需进行类型转换:

函数功能示例
int(x)转换为整数int(“100”) → 100
float(x)转换为浮点数float(“9.9”) → 9.9
str(x)转换为字符串str(18) → “18”
eval(x)执行字符串表达式eval(“1+2”) → 3

示例:超市收银系统

# 输入商品信息
product_name = input("请输入商品名称:")
price_str = input("请输入商品单价:")
count_str = input("请输入购买数量:")

# 类型转换
price = float(price_str)
count = int(count_str)

# 计算总价
total = price * count

# 输出结果
print(f"您购买了{product_name},单价:{price:.2f}元,数量:{count}件,总价:{total:.2f}元")

运算符

(1)算术运算符

运算符描述示例
+加法10 + 20 → 30
-减法20 - 10 → 10
*乘法10 * 20 → 200
/除法(返回浮点数)20 / 10 → 2.0
//整除(向下取整)21 // 10 → 2
%取模(余数)21 % 10 → 1
**幂运算2 ** 3 → 8

示例:

a = 10
b = 3
print(f"{a} + {b} = {a + b}")    # 13
print(f"{a} - {b} = {a - b}")    # 7
print(f"{a} * {b} = {a * b}")    # 30
print(f"{a} / {b} = {a / b}")    # 3.3333333333333335
print(f"{a} // {b} = {a // b}")  # 3
print(f"{a} % {b} = {a % b}")    # 1
print(f"{a} ** {b} = {a ** b}")  # 1000

(2)赋值运算符

运算符描述示例
=基础赋值x = 10
+=加后赋值x += 5 → x = x + 5
-=减后赋值x -= 5 → x = x - 5
*=乘后赋值x *= 5 → x = x * 5
/=除后赋值x /= 5 → x = x / 5
//=整除后赋值x //= 5 → x = x // 5
%=取模后赋值x %= 5 → x = x % 5
**=幂后赋值x = 5 → x = x ** 5

(3)比较运算符(返回布尔值)

运算符描述示例
==等于10 == 10 → True
!=不等于10 != 20 → True
>大于20 > 10 → True
<小于10 < 20 → True
>=大于等于20 >= 10 → True
<=小于等于10 <= 20 → True

(4)逻辑运算符

运算符描述示例
and逻辑与(都真才真)(10>5) and (20>10) → True
or逻辑或(有真就真)(10>5) or (20<10) → True
not逻辑非(取反)not (10>5) → False

条件判断(if 语句)

(1)核心作用

根据条件表达式的布尔结果(True/False),执行不同的代码块,实现程序的分支逻辑,是程序实现 “判断” 能力的核心语法。

(2)基本语法规则

:::color4

  1. Python 中用 缩进(4 个空格 / 制表符) 划分代码块,而非大括号,缩进必须统一
  2. 条件表达式后必须加冒号 :
  3. 条件表达式可结合比较运算符、逻辑运算符组合使用

:::

(3)四种基本用法

① 单分支(if)

适用场景:满足条件则执行某段代码,不满足则跳过

# 语法
if 条件表达式:
    满足条件时执行的代码块(缩进)

# 示例:判断是否成年
age = 19
if age >= 18:
    print("你已成年,可独立出行")

② 双分支(if-else)

适用场景:二选一,满足条件执行 A 代码块,不满足执行 B 代码块

# 语法
if 条件表达式:
    满足条件执行的代码块
else:
    不满足条件执行的代码块

# 示例:判断是否成年
age = int(input("请输入你的年龄:"))
if age >= 18:
    print("你已成年,可观看此电影")
else:
    print("你未成年,需由监护人陪同")

③ 多分支(if-elif-else)

适用场景:多个条件依次判断,满足某个条件则执行对应代码块,后续条件不再判断;所有条件都不满足则执行 else

# 语法
if 条件表达式1:
    满足条件1执行的代码块
elif 条件表达式2:
    满足条件2执行的代码块
elif 条件表达式3:
    满足条件3执行的代码块
...
else:
    所有条件都不满足时执行的代码块

# 示例:成绩评级
score = float(input("请输入你的考试成绩:"))
if score >= 90:
    print("成绩评级:优秀")
elif score >= 80:
    print("成绩评级:良好")
elif score >= 70:
    print("成绩评级:中等")
elif score >= 60:
    print("成绩评级:及格")
else:
    print("成绩评级:不及格,需要补考")

④ 嵌套分支(if 内部嵌套 if/if-else)

适用场景:主条件满足后,需要再判断细分条件

# 示例:判断是否能参加驾照考试
age = int(input("请输入你的年龄:"))
if age >= 18:
    print("你满足年龄要求,可报名驾照考试")
    height = float(input("请输入你的身高(cm):"))
    if height >= 150:
        print("身高符合要求,可正常报名")
    else:
        print("身高未达到要求,无法报名")
else:
    print("年龄未达标,无法报名驾照考试")

(4)条件表达式的高级用法

  1. 结合逻辑运算符实现复杂条件
# 示例:判断是否为工作日的工作时间
hour = int(input("请输入当前小时数(0-23):"))
weekday = input("请输入是否为工作日(是/否):")
if weekday == "是" and 9 <= hour <= 18:
    print("当前是工作时间,请专注工作")
else:
    print("当前是非工作时间,可休息")
  1. 三元表达式(简化单 / 双分支)语法:结果1 if 条件表达式 else 结果2(满足条件返回结果 1,否则返回结果 2)

# 示例:简化判断成年与否
age = 20
result = "成年" if age >= 18 else "未成年"
print(f"你是:{result}")

# 等价于原双分支代码
if age >= 18:
    result = "成年"
else:
    result = "未成年"

(5)常见易错点

  1. 忘记写冒号 :条件表达式和 else/elif 后必须加冒号,否则语法报错
  2. 缩进不统一:同一代码块的缩进必须一致(要么全 4 个空格,要么全制表符)
  3. 把赋值号 =当成等于号 ==:条件判断中判断相等必须用==,=是赋值,会导致逻辑错误
# 错误写法
if age = 18:  # 赋值操作,非判断,直接报错
    print("成年")

# 正确写法
if age == 18:
    print("刚成年")
  1. 多分支中条件顺序错误:多分支是依次判断,范围大的条件不能写在前面
# 错误写法:score>=60写在前面,会导致80+的成绩也判断为"及格"
score = 85
if score >= 60:
    print("及格")
elif score >= 80:
    print("良好")

# 正确写法:从范围小的条件开始判断
if score >= 80:
    print("良好")
elif score >= 60:
    print("及格")

综合实战案例

案例 1:简易成绩管理系统(含条件判断)

"""
简易成绩管理系统
功能:
1. 输入学生姓名和三门科目成绩
2. 计算总分和平均分
3. 根据平均分进行成绩评级
4. 格式化输出所有信息
"""

# 输入学生信息
student_name = input("请输入学生姓名:")
chinese_str = input("请输入语文成绩:")
math_str = input("请输入数学成绩:")
english_str = input("请输入英语成绩:")

# 类型转换(处理输入的字符串,转为浮点数)
chinese = float(chinese_str)
math = float(math_str)
english = float(english_str)

# 计算总分和平均分
total_score = chinese + math + english
avg_score = total_score / 3

# 条件判断:根据平均分评级
if avg_score >= 90:
    grade = "优秀"
elif avg_score >= 80:
    grade = "良好"
elif avg_score >= 70:
    grade = "中等"
elif avg_score >= 60:
    grade = "及格"
else:
    grade = "不及格"

# 格式化输出结果
print("=" * 40)
print(f"学生姓名:{student_name}")
print(f"语文成绩:{chinese:.1f}分")
print(f"数学成绩:{math:.1f}分")
print(f"英语成绩:{english:.1f}分")
print(f"总成绩:{total_score:.1f}分")
print(f"平均成绩:{avg_score:.1f}分")
print(f"成绩评级:{grade}")
print("=" * 40)

案例 2:班级活动费用分摊系统(运算符 + 输入输出 + 条件判断)

"""
班级活动费用分摊系统
功能:
1. 输入参与人数和活动总花费
2. 额外收取10%组织管理费,计算最终总费用
3. 判断人数是否合法(至少1人)
4. 计算每人需要分摊的金额并输出
"""

# 输入并转换数据类型
total_people = int(input("请输入参与活动人数:"))
total_cost = float(input("请输入活动总花费(元):"))

# 条件判断:人数是否合法
if total_people < 1:
    print("人数输入错误,至少1人!")
else:
    # 计算含组织管理费的总费用和人均费用
    manage_fee = total_cost * 0.1  # 10%组织管理费
    final_total = total_cost + manage_fee
    per_person = final_total / total_people

    # 格式化输出
    print("=" * 30)
    print(f"原始活动花费:{total_cost:.2f}元")
    print(f"10%组织管理费:{manage_fee:.2f}元")
    print(f"最终总费用:{final_total:.2f}元")
    print(f"参与人数:{total_people}人")
    print(f"每人需分摊:{per_person:.2f}元")
    print("=" * 30)

循环控制

Python中循环的介绍

什么是循环

现实生活中,也有很多循环的应用场景:

(1)老师批改作业:拿起作业本→查看答案→批改打分→放回作业本,重复以上过程,直到所有学生的作业都批改完了

(2)收银员收款:接过顾客商品→扫码计价→收取钱款→打印小票,重复以上过程,直到所有排队顾客都完成付款。

(3)环卫工人扫地:拿起扫帚→清扫路面→收集垃圾→移动到下一段路面,重复以上过程,直到负责区域清扫完毕。

(4)学生晨读:打开课本→朗读段落→标记重点→继续朗读,重复以上过程,直到晨读时间结束。

以上场景都有一个共同的特点:有条件地重复地做一件事,每一次做的事情不同但类似。

程序是为了解决实际问题的,实际问题中存在着重复动作,那么程序中也应该有相应的描述,这就是循环。

循环的作用是什么?

答:让代码高效的重复执行,避免重复编写大量相同或相似的代码,提升编程效率。

## while循环语法及其应用 ### while循环的基本语法 ```python # Step1: 定义一个计数器(初始化一个计数器) i = 0或1 # Step2: 编写while循环结构 while 循环条件(判断 计数器 是否达到了目标位置): 循环体1 循环体2 ... # Step3: 在循环内部更新计数器 i = i + 1 或 i += 1 ```

普及小知识:在计算机程序中,计数器大部分都是从0开始的,少数场景会从1开始,根据需求灵活选择。

:::color2
总结:while循环三步走

① 初始化计数器

② 编写循环条件(判断计数器是否达到了目标位置)

③ 在循环内部更新计数器

:::

# 演示 while循环
"""
while 判断条件:
    # 当条件满足 需要执行的内容

说明:
    while: 实现循环操作的关键字
    条件判断: 是否进入循环的依据
        当条件成立, 就执行循环里面的内容, 条件一直成立 就一直循环执行内部的代码 直到条件不成立
"""

# 需求: 请循环 打印 5次  Python真有趣
# 1- 定义计数器
num = 1

# 2- 编写循环操作
while num <= 5:
    # 2.1 执行循环的主体代码
    print("Python真有趣")

    # 2.2 更新计数器 +1
    # num = num + 1
    num += 1

while循环流程图:

while循环案例

案例1:使用while循环求1…50的和

分析:定义一个while循环,让其可以帮助我们计算 1 + 2 + 3 + 4 + 5 … + 50,结果:1275

# 需求: 求 1~50的和
# 思路: 1- 需要对 1~50进行遍历(循环)  2- 每循环一个 就要对其进行累加计算(在while循环外部定义一个初试累加变量)

# 如何进行 1 ~ 50 循环呢
# 第一步: 定义计数器
i = 1

# 第二步: 编写循环操作
# 2.1 定义一个累加求和的变量
sum_total = 0

while i <= 50:
    # 2.2 循环的代码块
    sum_total = sum_total + i
    # 第三步: 更新计数器
    i += 1

print(sum_total)

案例2:求1~50之间,所有奇数的和

什么是奇数:所谓的奇数,就是不能被2整除的数字,数学中可以使用2n+1来表示奇数。(Python代码 => if 数值 % 2 != 0 代表它是一个奇数)

知识点:在while循环中,我们还可以结合if进行判断,筛选出符合条件的数据。

# 需求2: 求1~50之间,所有奇数的和  不能被2 整除的 就是奇数(余数不为0)
# 思路: 1- 需要对 1~50进行遍历(循环)  2- 每循环一个 先判断是否为奇数, 如果是对其进行累加计算(在while循环外部定义一个初试累加变量)

# 1- 定义计数器
i = 1

# 2- 编写循环操作
# 2.2.1 先定义一个累加的初始变量
sum_odd = 0
while i <= 50:
    # 循环的主体要执行的代码块
    # 2.1 判断是否为奇数
    if i % 2 != 0:
        # 2.2 如果是奇数, 进行累加
        # 2.2.2 执行累加
        sum_odd += i

    # 3- 更新计数器
    i += 1

print(sum_odd)

循环中的两大关键词

两大关键词

在Python循环中,经常会遇到两个常见的关键词:break 与 continue

:::danger
break:代表终止整个循环结构,一旦执行,直接退出循环,不再执行后续循环体内容。

continue:代表中止当前本次循环,跳过本次循环剩余内容,继续执行下一次循环。

:::

举例:老师检查6本作业,检查完第一本,检查第二本…,这里"检查作业"的动作是不是重复执行?

场景一:如果检查过程中,发现第三本作业空白未写,无需再检查后续作业,直接停止检查,这里就是break控制循环流程,即终止此循环。

场景二:如果检查过程中,发现第三本作业有少量错误,标注错误后跳过这本,继续检查第四本作业,这里就是continue控制循环流程,即退出当前一次循环继而执行下一次循环代码。
break关键字

场景一:如果检查过程中,发现第三本作业空白未写,无需再检查后续作业,直接停止检查,这里就是break控制循环流程,即终止此循环。

# 演示 break:  实现中断循环 直接退出循环操作
# 需求: 遍历从  1 ~ 50 , 要求 一旦值能够被15整除(除 15  余数为 0) 立即退出循环
# 1- 定义计数器
i = 1

# 2- 编写循环
while i <= 50:
    # 循环的主体内容
    # 如果值能够被15整除, 退出循环
    if i % 15 == 0:
        break

    print(i)

    # 3- 更新计数器
    i += 1

# 退出循环后, 执行后续的代码
print("循环已终止,退出程序")
continue关键字

场景二:如果检查过程中,发现第三本作业有少量错误,标注错误后跳过这本,继续检查第四本作业,这里就是continue控制循环流程,即退出当前一次循环继而执行下一次循环代码。

# 演示 continue:  跳过本次循环, 进入下一次循环

# 需求: 遍历从  1 ~ 50 , 要求 如果遍历的值能够被 6 整除  将该值直接跳过不打印
# 1- 定义计数器
i = 1

# 2- 编写循环
while i <= 50:
    # 循环的主体内容
    # 如果能够被6整除, 将该值跳过 不打印
    if i % 6 == 0:
        # 更新计数器: 必须在continue前更新,否则会出现死循环
        i += 1
        continue

    print(i)

    # 3- 更新计数器
    i += 1

# 退出循环后, 执行后续的代码
print("循环执行完毕")
如果在使用continue的时候,不手工更新计数器会有什么后果呢?

答:会出现死循环(计数器一直停留在满足continue条件的数值上,永远无法满足循环结束条件),建议使用Debug调试工具观看执行过程。

### 死循环概念 ** 在编程中一个靠自身控制无法终止的程序称为"死循环",死循环会一直占用计算机资源,开发中应尽量避免。**

在Python中,我们也可以使用while True来模拟死循环:

# 注意 如果条件永久成立, 那么这个循环也被成为 死循环
while True:
    print("这是一个死循环,按Ctrl+C可强制终止")

while循环案例:猜数字

案例需求

编写一个Python程序,系统提前设定一个1到20之间的固定整数(如12),让用户反复输入数字猜测,每次输入后提示"猜大了"“猜小了"或"猜对了”,直到用户猜对为止,猜对后提示游戏结束,不使用任何异常相关知识。

实现思路

:::color1
① 编写死循环,满足"猜对"条件后终止循环。

② 提前设定一个固定整数(如12)作为答案,无需随机生成(避开未学的random模块)。

③ 让用户输入数字,通过if判断输入数字与答案的大小关系,给出对应提示,猜对则终止循环。

:::

代码实现
# 演示 猜数字游戏(无random、无异常,适合未学相关知识的学生)
# 1- 提前设定固定答案(1~20之间),无需随机生成
answer = 12
# 2- 让用户反复猜测,写死循环方式
print("欢迎来到猜数字游戏!")
print("规则:系统已设定1~20之间的一个整数,猜对即可结束游戏,每次猜测会提示猜大或猜小~")
while True:
    # 2.1 让用户输入猜测的数字(直接转为整数,提示用户输入整数)
    user_guess = int(input("请输入你猜测的1~20之间的整数:"))
    # 2.2 判断猜测结果,给出对应提示
    if user_guess == answer:
        print("恭喜你,猜对了!游戏结束~")
        # 猜对后终止循环
        break
    elif user_guess > answer:
        print("猜大了,再试试小一点的数字!")
    else:
        print("猜小了,再试试大一点的数字!")

扩展: 要求增加限制次数(3次), 并且要提示用户还剩余几次猜测机会: 仅做了解即可, 可以尝试写一写(对理解 while 和 if 很有作用)

# 演示 猜数字游戏(限制次数版,无random、无异常)
# 1- 提前设定固定答案(1~20之间),无需随机生成
answer = 12
# 2- 定义猜测次数(限制3次)
guess_count = 1
# 3- 写死循环,控制猜测次数
print("欢迎来到猜数字游戏!")
print("规则:系统已设定1~20之间的一个整数,你有3次猜测机会,猜对即可获胜~")
while True:
    # 3.1 让用户输入猜测的数字,提示剩余次数
    user_guess = int(input(f"请输入你猜测的1~20之间的整数(剩余{3 - guess_count + 1}次机会):"))
    # 3.2 判断猜测结果,给出对应提示
    if user_guess == answer:
        print(f"恭喜你,猜对了!你在第{guess_count}次就猜对了,游戏结束~")
        break
    elif user_guess > answer:
        print("猜大了,再试试小一点的数字!")
    else:
        print("猜小了,再试试大一点的数字!")
    # 3.3 判断次数是否用完
    if guess_count >= 3:
        print(f"猜测次数已用完!正确答案是{answer},游戏结束~")
        break
    # 计数器+1
    guess_count += 1

for循环基本语法及其应用

for循环基本语法

:::info
for循环结构主要用于(序列 => 字符串、列表、元组、集合以及字典)类型数据的遍历(循环)操作,无需手动定义和更新计数器,使用起来更简洁。

for循环主要用于序列类型数据的循环操作(遍历操作)

另外当循环次数已知的情况,建议使用for循环(比while循环更简洁)。

:::

for 临时变量 in 序列:
    重复执行的代码1
    重复执行的代码2

案例:使用for循环遍历字符串"python"

# 演示: for基本使用
"""
    语法格式:  
        for 临时变量 in 序列:
            循环执行的代码块
        
        注意:
            临时变量的值 其实 就是 序列中每一个元素,循环次数等于序列的长度
"""
# 需求: 遍历一个字符串 :  python 
for char in "python":
    print(char)

使用Debug调试以上代码可知:for循环功能非常强大,可以自动判断序列的长度,长度为多少,则for循环就循环多少次。每次循环时,系统会自动将序列中的每个元素赋值给临时变量,赋值完成后,for循环内部会自动更新计数器,向后移动一位,继续循环,直至元素全部循环结束。

range方法(函数)

在Python3中 **<font style="color:#DF2A3F;">range() </font>**函数返回的是一个可迭代对象(类型是对象)

主要作用:用于生成一段连续的整数序列,方便配合for循环使用。

range(stop)
range(start, stop, step)

start: 计数从 start 开始。默认是从 0 开始。例如range(4)等价于range(0, 4);
stop: 计数到 stop 结束,但不包括 stop。例如:range(0,4) 是 [0, 1, 2, 3] 没有 4
step:步长,默认为1。例如:range(0,4) 等价于 range(0, 4, 1)
# 演示 range函数
# 作用: 帮我们生成一个连续的数字, 例如 0~3
# 语法:  range(起始值,结束值,步长)
#  起始值: 默认为  0   结束值: 必填的  在生成序列的时候, 不含结束值  步长: 默认为1 表示 数字与数字之间的间隔
# 需求: 生成 4以内的序列, 并且将每一个值打印出来
for num in range(0,4):
    print(num)

for循环案例

# 需求: 求 1~50 之间 所有的奇数之和
sum_odd = 0
for num in range(1,51):
    if num % 2 != 0:
        sum_odd += num
print(sum_odd)

综合案例:使用for循环实现用户名+密码认证-作业

案例:用for循环实现用户登录

:::info
① 输入用户名和密码

② 判断用户名和密码是否正确(username=‘student’,password=‘python123’)

③ 登录仅有2次机会,超过2次会提示"登录失败,次数已用完"

:::

分析:用户登陆情况有3种:

:::color1
① 用户名错误(此时便无需判断密码是否正确) – 登陆失败

② 用户名正确 密码错误 --登陆失败

③ 用户名正确 密码正确 --登陆成功

:::

# 用户登录案例(for循环版)

# 1- 构建循环, 并且循环只能有 2次
for i in range(0,2):
    # 2: 让用户输入 用户名 和 密码
    user_name = input("请输入用户名:")
    user_pwd = input("请输入密码:")

    # 3: 判断 用户名和密码是否正确
    if user_name == 'student':
        if user_pwd == 'python123':
            print("恭喜您, 登录成功!")
            # 登录成功后, 直接退出循环
            break
        else:
            print(f"您输入的密码不正确, 您还剩余{1-i}次机会")
    else:
        print(f"您输入的用户名不正确, 您还剩余{1-i}次机会")
else:
    # 循环正常结束(未break),说明次数用完
    print("登录失败,次数已用完!")

for循环中的else结构

为什么需要在for循环中添加else结构

循环可以和else配合使用,else下方缩进的代码指的是当循环正常结束之后要执行的代码(即没有被break终止,完整执行完所有循环次数)。

强调:‘正常结束’,非正常结束(如遇到break、程序异常),其else中的代码是不会执行的。

for循环结构中的else结构

基本语法:

for 临时变量 in 序列:
    循环体
else:
    当for循环正常结束后,执行的代码

break关键字对for…else结构的影响

str1 = 'python'
for i in str1:
    if i == 'h':
        print('遇h强制结束循环')
        break
    print(i)
else:
    print('循环正常结束之后执行的代码')

运行结果:只会打印p、y、t,然后打印"遇h强制结束循环",else中的代码不会执行(因为循环被break终止,属于非正常结束)。

continue关键字对for…else结构的影响

str1 = 'python'
for i in str1:
    if i == 'h':
        print('遇h不打印')
        continue
    print(i)
else:
    print('循环正常结束之后执行的代码')

运行结果:打印p、y、t、遇h不打印、o、n,最后打印"循环正常结束之后执行的代码"(continue只是跳过本次循环,循环整体正常结束)。

else结构整体演示

# 演示: for循环结合else情况
"""
    语法:
        for 临时变量 in  序列:
            循环体
        else:
            当循环正常结束后, 需要执行的代码

    如果for循环中,
        通过break退出的循环, 那么就不会执行else语句的内容
        通过continue跳过当前循环, 对于else 不会有影响的
        在循环过程中程序出现异常(程序异常结束), else也不会执行
"""
# 需求: 循环输出从 1~6, 当循环正常结束后, 打印 循环完成
for i in range(1,7):
    # 当循环到3的时候, 强制退出循环(注释后可测试正常情况)
    # if i == 3:
    #     break

    # 当循环到3的时候, 跳过当前循环 进入下一次循环
    # if i == 3:
    #     continue
    # 异常情况(注释后可测试正常情况)
    # 1/0
    print(i)
else:
    print("循环结束了.....")

双层循环

for循环里可以嵌套for循环以及while循环,while循环中也可以嵌套for循环和while循环

for循环嵌套for循环

for 临时变量1 in 序列1:
    for 临时变量2 in 序列2:
        内层循环体

for循环嵌套while循环

for 临时变量1 in 序列1:
    index = 初始值
    while index的判断条件:
        内层循环体

while循环嵌套while循环

index = 初始值
while index的判断条件:
    index2 = 初始值
    while index2的判断条件:
        内层循环体

while循环嵌套for循环

index = 初始值
while index的判断条件:
for 临时变量 in 序列:
    内层循环体

双层循环经常用来控制双层遍历的情况,比如打印九九乘法表

双层循环,如果内层循环使用了break,break只能跳出
它所在的循环,也就是内层循环
'''
演示双层循环实现九九乘法表
'''

'''
双层循环,如果内层循环使用了break,break只能跳出
它所在的循环,也就是内层循环
'''

'''
需求:
实现九九乘法表
思路:
1. 变量row控制行号,row从1到9结束,[1,9]
2. 变量col控制列号,col从1开始回到当前行号结束,[1,row]
3. 用外层循环控制行号的变化,内层循环控制列号的变化
for row in range(1,10):
    for col in range(1,row+1):
        pass
'''
for row in range(1,10):
    for col in range(1,row+1):
        # print(f'当前行号{row},当前列号{col}')
        print(f'{col} * {row} = {row * col}',end='\t')
    # print()默认输出换行符
    print()

print('#'*100)
'''
要求九九乘法表最多只输出到第3列
思路:
col为4的时候结束内层循环即可,外层循环继续
'''
for row in range(1,10):
    for col in range(1,row+1):
        # print(f'当前行号{row},当前列号{col}')
        print(f'{col} * {row} = {row * col}',end='\t')
        # 当col为4的时候结束内层循环,跳转到外层循环
        if col == 4:
            # break只能跳出它所在的循环(离他最近的循环)
            break
    # print()默认输出换行符
    print()
print('#'*100)

基础数据结构

列表及其应用场景

为什么需要列表

思考:有一个人的姓名(LiLei)怎么书写存储程序?

答:变量。

思考:如果一个班级50位学生,每个人的姓名都要存储,应该如何书写程序?声明50个变量吗?

答:No,我们使用列表就可以了, 列表一次可以存储多个数据。

在Python中,我们把这种数据类型称之为** 列表**。但是在其他的编程语言中,如Java、PHP、Go等等中其被称之为数组。

列表的定义

列表序列名称 = [列表中的元素1, 列表中的元素2, 列表中的元素3, ...]

注意: 列表是可变的

案例演示:定义一个列表,用于保存橙子、葡萄以及芒果

fruit_list = ['orange', 'grape', 'mango']
# list列表类型支持直接打印
print(fruit_list)
# 打印列表的数据类型
print(type(fruit_list))  # <class 'list'>

注意:列表可以一次存储多个数据且可以为不同的数据类型

相关操作:

# 演示: 如何定义列表
# 格式: 变量名称 = [元素1,元素2,元素3......]
# 需求: 构建一个列表, 保存 小明 小红 小刚 小丽 四个名字
name_list = ['小明','小红','小刚','小丽']
print(name_list)

# 如何获取列表中某一个元素呢? 与 字符串 一致
print(name_list[0])
print(name_list[1])
print(name_list[2])

# 如何遍历列表呢?
print("-----------------")
# for循环
for name in name_list:
    print(name)

# while循环
print("-----------------")
# 计数器
i = 0

while i < len(name_list):
    print(name_list[i])

    # 更新计数器
    i += 1

列表的相关操作

列表的作用是一次性存储多个数据,程序员可以对这些数据进行的操作有:增、删、改、查。

☆ 查操作

列表在计算机中的底层存储形式,列表和字符串一样,在计算机内存中都占用一段连续的内存地址,我们向访问列表中的每个元素,都可以通过"索引下标"的方式进行获取。

如果我们想获取列表中的某个元素,非常简单,直接使用索引下标:

fruit_list = ['orange', 'grape', 'mango']
# 获取列表中的grape
print(fruit_list[1])
函数作用
index()指定数据所在位置的下标
count()统计指定数据在当前列表中出现的次数
in判断指定数据在某个列表序列,如果在返回True,否则返回False
not in判断指定数据不在某个列表序列,如果不在返回True,否则返回False
☆ 增操作
函数作用
append()增加指定数据到列表中
extend()列表结尾追加数据,如果数据是一个序列,则将这个序列的数据逐一添加到列表
insert()指定位置新增数据

☆ 删操作
函数作用
del 列表[索引]删除列表中的某个元素
pop()删除指定下标的数据(默认为最后一个),并返回该数据
remove()移除列表中某个数据的第一个匹配项。
☆ 改操作
函数作用
列表[索引] = 修改后的值修改列表中的某个元素
reverse()将数据序列进行倒叙排列
sort()对列表序列进行排序

相关功能的演示:

# 演示列表的API
name_list = ['小明', '小红', '小刚', '小丽', '小强', '小芳', '小亮', '小红']
# 查询相关方法:
# 1- index(): 查询指定数据所在位置的下标  如果不存在 直接报错
index = name_list.index("小丽")
print(index)  # 3

# 2- count(): 统计数据在当前列表中出现的次数
count = name_list.count("小红")
print(count)  # 2

# 3- in 和 not in 判断是否存在或不存在
bool = "小刚" in name_list
print(bool)
bool = "小刚" not in name_list
print(bool)

# 增加函数
# 1- append()  增加指定数据到列表中
# 如果 小宇 不在列表中, 请增加到列表中
if "小宇" not in name_list:
    # 能进来, 说明 小宇 不在, 如果不在 添加到列表中
    name_list.append("小宇")

print(name_list)

# 2- extend(): 将一个序列 追加到另一个序列中
# 2.1 先定义一个新的序列
name2_list = ['小浩', '小琪']

# 2.2 将新列表中数据 增加 到 原有列表中
name_list.extend(name2_list)

print(name_list)

# 3- insert() 在指定位置新增数据
name_list.insert(4, '小诺')

print(name_list)

# 删除操作
# 1- del 列表[索引]
del name_list[-1]

print(name_list)

# 2- pop() 删除指定下标的数据, 默认是删除最后一个 并且会返回删除的数据
element = name_list.pop()
print(element)
print(name_list)

element = name_list.pop(2)
print(element)
print(name_list)

# 3- remove() 移除列表中某个指定数据  如果不存在, 也不会报错 无效果
name_list.remove("小诺")
print(name_list)

# 修改操作
# 1-  修改某个索引的值
name_list[0] = '大明'
print(name_list)

# 2- reverse: 反转操作
name_list.reverse()

print(name_list)

# 3- 排序操作: reverse 设置 是否反转   True 反转(倒序)  False 不反转(正序)
num_list = [3, 7, 10, 56, 2, 9, 4, 5]
num_list.sort(reverse=True)
print(num_list)

列表的循环遍历

循环遍历就是使用while或for循环对列表中的每个数据进行打印输出

while循环:

star_list = ['西施', '王昭君', '杨玉环']
i = 0
while i < len(star_list):
    print(star_list[i])
    i += 1

for循环(个人比较推荐):

star_list = ['西施', '王昭君', '杨玉环']
for star in star_list:
    print(star)

列表的嵌套

列表的嵌套:列表中又有一个列表,我们把这种情况就称之为列表嵌套

在其他编程语言中,称之为叫做二维数组或多维数组

应用场景:要存储班级一、二、三 => 三个班级学生姓名,且每个班级的学生姓名在一个列表。

classes = ['第一个班级','第二个班级','第三个班级']

一班:['小明', '小红']
二班:['小刚', '小丽']
三班:['小强', '小芳']

把班级和学员信息合并在一起,组成一个嵌套列表
students = [['小明', '小红'],['小刚', '小丽'],['小强', '小芳']]

students = [x,y,z]
students[0] == ['小明', '小红']
students[0][1]

问题:嵌套后的列表,我们应该如何访问呢?

# 访问小红
print(students[0][1])
# 嵌套列表进行遍历,获取每个班级的学员信息
for i in students:
    print(i)

相关演示:

# 演示 列表嵌套
# 说明: 容器内部嵌套容器方式
# 需求: 有 三个班级, 分别为 前端1班 前端2班  后端1班, 每个班级分别有5位学生
# 定义 班级列表
front1_class = ['小明','小红','小刚','小丽','小强']
front2_class = ['小芳','小亮','小宇','小浩','小琪']
back1_class = ['小诺','小泽','小琳','小航','小彤']

# 定义 学校列表, 学校列表中有三个班级
school_list = [front1_class,front2_class,back1_class]

# 以上内容 如果直接使用列表嵌套写法
school_list = [
    ['小明','小红','小刚','小丽','小强'],
    ['小芳','小亮','小宇','小浩','小琪'],
    ['小诺','小泽','小琳','小航','小彤']
]

# 如何 获取里面的数据呢?  比如 我想获取到 小浩
front2_class = school_list[1]
name = front2_class[3]
print(name)

# 可以简写
name = school_list[1][3]
print(name)

# 如何遍历打印每一个元素
for classes in school_list:
    for name in classes:
        print(name)
    print("-----------")

列表案例

:::color4
案例需求:

判断相邻元素的变化趋势

给定一个正整数数组,判断每一对相邻元素是"上升"“下降"还是"相等”,并输出结果列表

例如: 输入: [3, 5, 5, 2, 4] 输出: ['上升', '相等', '下降', '上升']

:::

实现思路

① 定义原始列表

② 创建一个空列表用于保存结果

③ 遍历相邻元素(当前值和下一个值)

④ 比较大小关系

  • 当前 < 下一个 → 上升
  • 当前 > 下一个 → 下降
  • 相等 → 相等

⑤ 将结果添加到列表中

⑥ 输出结果

![](https://i-blog.csdnimg.cn/img_convert/6ea3d9adaa383130a0b1e215c1681c19.svg)
# 判断相邻元素变化趋势

num_list = [3, 5, 5, 2, 4]

result_list = []

i = 0

while i < len(num_list) - 1:
    if num_list[i] < num_list[i + 1]:
        result_list.append("上升")
    elif num_list[i] > num_list[i + 1]:
        result_list.append("下降")
    else:
        result_list.append("相等")

    i += 1

print(result_list)

巩固练习

:::color4
找出列表中第一次出现的重复元素

给定一个列表,找到第一个重复出现的元素(只要出现第二次就算重复),并输出该元素

例如: 输入: [2, 3, 5, 3, 6, 5, 7] 输出: 3

:::

实现思路

① 创建一个空列表用于记录已经出现过的元素

② 遍历原始列表

③ 判断当前元素是否已经出现过

  • 如果出现过 → 直接输出并结束
  • 如果没有 → 添加到记录列表

④ 如果循环结束都没找到,说明没有重复

```python # 找第一个重复元素

num_list = [2, 3, 5, 3, 6, 5, 7]

seen_list = []

for e in num_list:
if e in seen_list:
print(e)
break
else:
seen_list.append(e)


## 元组的定义与使用
### 为什么需要元组
<details class="lake-collapse"><summary id="ue2571ad0"><strong><span class="ne-text" style="color: rgb(255, 111, 4); background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-image: linear-gradient(90deg, rgb(255, 111, 4) 0%, rgb(243, 48, 171) 100%);">思考:如果想要存储多个数据,但是这些数据是不能修改的数据,怎么做?</span></strong></summary><p id="u71ee2cf6" class="ne-p"><span class="ne-text">答:列表?列表可以一次性存储多个数据,但是列表中的数据允许更改。</span></p></details>
<details class="lake-collapse"><summary id="u725f44c0"><strong><span class="ne-text" style="color: rgb(255, 111, 4); background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-image: linear-gradient(90deg, rgb(255, 111, 4) 0%, rgb(243, 48, 171) 100%);">那这种情况下,我们想要存储多个数据且数据不允许更改,应该怎么办呢?</span></strong></summary><p id="u4da1de74" class="ne-p"><span class="ne-text">答:使用元组,元组可以存储多个数据且元组内的数据是不能修改的。</span></p></details>
![](https://cdn.nlark.com/yuque/__mermaid_v3/0a1adfba0a4325eb9ff281d22ce46e2c.svg)

### 元组的定义
元组特点:**<font style="color:#74B602;">定义元组使用小括号,且使用逗号隔开各个数据,数据可以是不同的数据类型</font>**。

基本语法:

```python
# 多个数据元组
tuple1 = (20, 30, 40)

# 单个数据元组
tuple2 = (20,)

注意:如果定义的元组只有一个数据,那么这个数据后面也要添加逗号,否则数据类型为唯一的这个数据的数据类型。

元组的相关操作方法

由于元组中的数据不允许直接修改,所以其操作方法大部分为查询方法。

函数作用
元组[索引]根据索引下标查找元素
index()查找某个数据,如果数据存在返回对应的下标,否则报错,语法和列表、字符串的index方法相同
count()统计某个数据在当前元组出现的次数
len()统计元组中数据的个数
# 1- 如何定义元组
# 格式:
#   变量名称 = (元素1,元素2,元素3......)
#   变量名称 = (元素1,) : 如果元组中只有一个元素, 必须在后面添加一个逗号 否则编译器会自动转换为 数据的本身类型, 而不是元组类型
user_tuple = ('小明','小红','小刚',20,30,40,True,False)
print(user_tuple)
print(type(user_tuple))

tuple1 = ('小明',)
print(tuple1)
print(type(tuple1))

# 2- 如何使用元组
# 2.1 获取元组中某个元素:  与 列表 和 字符串 一致 都是通过索引的方式来获取
print(user_tuple[0])
print(user_tuple[1])
print(user_tuple[2])

# 2.2 遍历: 与 列表 一致
for e in user_tuple:
    print(e)

# 2.3 获取某个元素在元组中索引值: index
print(user_tuple.index(True))

# 2.4 获取某个元素在元组中出现了几次
print(user_tuple.count('小刚'))

# 2.5 获取元组长度
print(len(user_tuple))

元组案例

:::color4
案例需求1

编写一个程序,计算嵌套元组中所有数字的总和。

例如:在嵌套元组 ((2,3,4),(3,5,7),(3,4,6)) 中,所有元素相加的结果为:

2+3+4+3+5+7+3+4+6 = 37

输出结果为:37

:::

实现思路

① 定义一个嵌套元组

② 定义一个变量,用来保存总和,初始值为 0

③ 使用外层循环遍历每个子元组

④ 使用内层循环遍历子元组中的每个元素

⑤ 将每个元素依次累加到总和变量中

⑥ 输出最终结果

```python # 编写一个程序,计算嵌套元组中所有数字的总和 # 例如: ((2,3,4),(3,5,7),(3,4,6)) 的总和为 37

1- 初始化嵌套元组

tuple1 = (
(2, 3, 4),
(3, 5, 7),
(3, 4, 6)
)

2- 定义变量,用于保存总和

total = 0

3- 遍历嵌套元组中的每个子元组

for tup_e in tuple1:
# 4- 遍历子元组中的每个元素
for e in tup_e:
# 5- 累加每个元素
total += e

6- 打印结果

print(total)


:::color4
**案例需求2**

给定一个元组 `my_tuple`,里面包含 `2, 3, 4, 5, 6, 7, 8, 9, 10` 元素,要求找出其中所有大于 5 的数字,并放入一个新列表中。

输出结果为:`[6, 7, 8, 9, 10]`

:::

<details class="lake-collapse"><summary id="u30d2b1db"><strong><span class="ne-text">实现思路</span></strong></summary><p id="u42830a81" class="ne-p"><span class="ne-text">① 定义一个元组</span></p><p id="ue9df396c" class="ne-p"><span class="ne-text">② 创建一个空列表,用于保存大于 5 的数字</span></p><p id="u605b8c62" class="ne-p"><span class="ne-text">③ 遍历元组中的每个元素</span></p><p id="ub207ca4c" class="ne-p"><span class="ne-text">④ 判断当前元素是否大于 5</span></p><p id="u61a5016f" class="ne-p"><span class="ne-text">⑤ 如果大于 5,就添加到结果列表中</span></p><p id="u14c195cc" class="ne-p"><span class="ne-text">⑥ 输出结果列表</span></p></details>
```python
# 给定一个元组my_tuple,找出其中所有大于5的数字

my_tuple = (2, 3, 4, 5, 6, 7, 8, 9, 10)

# 1- 创建空列表,用于保存结果
result_list = []

# 2- 遍历元组中的每个元素
for e in my_tuple:
    # 3- 判断是否大于5
    if e > 5:
        # 4- 如果大于5,则添加到列表中
        result_list.append(e)

# 5- 打印结果
print(result_list)

集合的定义与使用

什么是集合

集合(set)是一个**无序的不重复元素序列**。

① 天生去重

② 无序

集合的定义

在Python中,我们可以使用一对花括号{}或者set()方法来定义集合,但是如果你定义的集合是一个空集合,则只能使用set()方法。

# 1- 如何定义集合
# 格式:
#       格式一:  变量名称 = {元素1,元素2,元素3......}
#       格式二:  变量名称 = set()   空集合
# 特性: 无序 + 去重
# 定义一个有内容的集合
name_set = {'小明','小红','小刚','小明','小刚','小丽'}

print(name_set) #  去重 和 无序  发现每次都是不一样的 所以是无序的
print(type(name_set))
# 定义 空集合
# set2 = {}  # 此种定义方法 并不是定义空集合, 而是空的字典
empty_set = set()
print(type(empty_set))

# 获取数据: 遍历的方式
# for 循环
for name in name_set:
    print(name)

集合操作的相关方法(增删查)

☆ 集合的增操作

add()方法:向集合中增加一个元素(单一)

name_set = {'小明','小红','小刚'}
name_set.add('小强')
print(name_set)
☆ 集合的删操作

remove()方法:删除集合中的指定数据,如果数据不存在则报错。

name_set = {'小明','小红','小刚'}
name_set.remove('小红')
print(name_set)
☆ 集合中的查操作

① in :判断某个元素是否在集合中,如果在,则返回True,否则返回False

② not in :判断某个元素不在集合中,如果不在,则返回True,否则返回False

name_set = {'小明','小红','小刚'}
print('小明' in name_set)  # True
print('小丽' not in name_set)  # True

③ 集合的遍历操作

name_set = {'小明','小红','小刚'}
for name in name_set:
    print(name)
相关操作
# 演示 集合的API
num_set = {3,4, True,'小明',False}
# 1- 添加数据的API: add
num_set.add("小红")
print(num_set)

# 2- 删除数据的API: remove  在删除的时候 如果在集合中没有该值 会直接报错
num_set.remove(False)

print(num_set)

# 3- 判断是否存在和不存在操作: in 和 not in
# 如果 小刚在集合中不存在, 请添加该元素
if '小刚' not in num_set:
    # 能进来, 说明 小刚 不在集合中
    num_set.add('小刚')

print(num_set)

集合案例

"""
💡 题目:给定一个连续递增的整数列表,找出中断的位置。
例如:[1, 2, 3, 5, 6, 8] 中,3→5 断了,6→8 也断了。
"""
'''
定义集合方式
方式1 空集合: empty_set = set()
方式2 包含元素的集合 my_set = {元素1, 元素}
'''

# 定义空集合
empty_set = set()
# 打印集合
print(empty_set)
print(type(empty_set))

# 定义空字典
empty_dict = {}
print(empty_dict)
print(type(empty_dict))

# 定义一个包含元素的集合
name_set = {'小明','小红','小刚','小明','小刚','小刚'}
print(name_set)
print(type(name_set))

# 遍历
for name in name_set:
    print(name)

# 增加元素
name_set = {'小明','小红','小刚'}
name_set.add('小强')
print(name_set)

# 删除元素
name_set.remove('小强')
print(name_set)

# 判断元素是否在集合中, in和not in
if '小明' in name_set:
    print('小明在集合中')
else:
    print('小明不在集合中')

# 判断元素不在集合中
if '小强' not in name_set:
    print('小强不在集合中')
else:
    print('小强在集合中')

"""
💡 题目:一个数组中除了两个数字只出现一次外,其他数字都出现两次。
       找出这两个只出现一次的数字。
输入: [4, 1, 2, 1, 2, 3, 5, 5]
输出: 3 和 4

思路:
1. 准备集合A和列表B,集合A用来存储出现的数字,集合B用来记录只出现过1次的数字
2. 将出现的数字和集合A判断,如果集合A中有数字,说明这个数字出现过,将数字从集合B
中删除。
如果A中没有这个数字,说明数字没出现过,将数字放入集合A和集合B
3. 最后循环结束,打印集合B,就是只出现一次的数字了
"""
num_list = [4, 1, 2, 1, 2, 3, 5, 5]
# 存储所有出现的数字
set_a = set()
# 存储只出现一次的数据
set_b = set()
for num in num_list:
    # num没有出现过
    if num not in set_a:
        set_a.add(num)
        set_b.add(num)
        continue
    # num出现过,则从set_b中移除该数字
    set_b.remove(num)

print('出现过1次的数字: ')
for num in set_b:
    print(num,end='\t')
print()
"""
💡 题目:统计列表中每个数字出现的次数,并按数字大小排序输出。
输入: [4, 2, 2, 8, 3, 3, 1, 4, 3]
"""
"""
💡 题目:判断一个列表中的数字是否能组成连续序列。
例如:[4, 2, 3, 1] → True(可以重排成 [1,2,3,4])
      [5, 3, 2, 6] → False(范围是 2~6,缺了 4)
"""
"""
💡 题目:给你一个 1 到 n 的数组,但缺少了一些数字。
       找出所有 1~n 范围内消失的数字。
输入: [4, 3, 2, 7, 8, 2, 3, 1]  (n=8,但数组里缺了 5, 6)
"""

字典的定义与使用

为什么需要字典(dict)

思考1:比如我们要存储一个人的信息,姓名:ZhangSan,年龄:19周岁,性别:男,家庭住址:上海市浦东新区,如何快速存储。

person = ['ZhangSan', 19, '男', '上海市浦东新区']

思考2:在日常生活中,姓名、年龄以及性别同属于一个人的基本特征。但是如果使用列表对其进行存储,则分散为3个元素,这显然不合逻辑。我们有没有办法,将其保存在同一个元素中,姓名、年龄以及性别都作为这个元素的3个属性。

答:使用Python中的字典

![](https://i-blog.csdnimg.cn/img_convert/a7069e01d069d045e25c236792a23993.svg)

Python中字典(dict)的概念

:::info
特点:

① 符号为大括号(花括号) => {}

② 数据为键值对形式出现 => {key:value},key:键名,value:值,在同一个字典中,key必须是唯一(类似于索引下标)

③ 各个键值对之间用逗号隔开

:::

在字典中,键名除了可以使用字符串的形式,还可以使用数值的形式来进行表示

# 有数据字典
dict1 = {'name': 'ZhangSan', 'age': 19, 'gender': '男'}

# 空字典
dict2 = {}

dict3 = dict()
# 1- 演示: 如何定义字典
# 格式:
#   格式1:  变量名称 = {key1:value1,key2:value2,key3:value3.....}
#   格式2:  变量名称 = {}  或者 变量名称 = dict()   空字典

# 构建一个有内容的字典
person = {'name': '小明', 'age': 19, 'address': "上海市浦东新区张江镇", 'sex': '男', 'hobby': '篮球、足球、游泳'}
print(person)
print(type(person))

# 构建空字典
dict1 = {}
dict2 = dict()
print(dict1)
print(type(dict1))
print(dict2)
print(type(dict2))

# 遍历操作
for key in person:
    print(key)
    print(person[key])

字典的增操作

字典名称[key] = value
注:如果key存在则修改这个key对应的值;如果key不存在则新增此键值对。

字典的删操作

① del 字典名称[key]:删除指定元素

person = {'name': '小明', 'age': 19}
del person['age']
print(person)

② clear()方法:清空字典中的所有key

person = {'name': '小明', 'age': 19}
person.clear()
print(person)

字典的改操作

基本语法:

字典名称[key] = value
注:如果key存在则修改这个key对应的值;如果key不存在则新增此键值对。

案例:定义一个字典,里面有name、age以及address,修改address这个key的value值

person = {'name': '小明', 'age': 19, 'address': '上海市浦东新区'}
person['address'] = '上海市黄浦区'
print(person)

字典的查操作

① 查询方法:使用具体的某个key查询数据,如果未找到,则直接报错。

字典序列[key]

② 字典的相关查询方法

函数作用
keys()以列表返回一个字典所有的键
values()以列表返回字典中的所有值
items()以列表返回可遍历的(键, 值) 元组数据

案例1:提取person字典中的所有key

person = {'name': '小明', 'age': 19, 'address': '上海市浦东新区'}
print(person.keys())

案例2:提取person字典中的所有value值

person = {'name': '小明', 'age': 19, 'address': '上海市浦东新区'}
print(person.values())

案例3:使用items()方法提取数据

person = {'name': '小明', 'age': 19, 'address': '上海市浦东新区'}
print(person.items())

相关的API操作:

# 字典的相关API:
# 如何增加元素:
# 需求: 为person 字典增加一个 birthday 属性 值为 2005-08-20
person = {'name': '小明', 'age': 19, 'address': "上海市浦东新区张江镇", 'sex': '男', 'hobby': '篮球、足球、游泳'}
person["birthday"] = '2005-08-20'

print(person)

# 如何修改元素: 与添加一直, 如果key存在就是修改 如果不存在 就是添加
# 需求: 修改性别为 女
person['sex'] = "女"
print(person)

# 如何删除元素
# 删除某个元素:  根据key
del person['birthday']
print(person)
# 清空字典
# person.clear()
# print(person)

# 如何查询:
# 1- 根据 key 直接获取value: 如果key 不存在 直接报错
# 获取 address的值
print(person['address'])

# 2- 获取所有的keys
keys_list = person.keys()
print(keys_list)
# 3- 获取所有的value
values_list = person.values()
print(values_list)
# 4- 获取字典中每一个 kv对
# 快速返回变量: ctrl + alt + v
items = person.items()
print(items)

for kv in items:
    print(kv)
    print(f'{kv[0]},{kv[1]}')

字典案例

:::color4
案例需求

给定一个字符串my_string,现在要求统计每个字符出现的次数: 形成结果: {‘字符’:出现次数,‘字符2’:次数}例如: ‘abcdecf’ ==> {‘a’:1,‘b’:1,‘c’:2,‘d’:1,‘e’:1,‘f’:1}

:::

实现思路

①定义一个字符串

②初始化空字典,来存储对应字符和出现次数

③循环遍历字符串中每个字符

④如果字符串已经在字典中,计数加1,如果不在,初始化计数1: 字典['key'] += 1

⑤输出统计每个字符出现的次数

```python """ 给定一个字符串my_string,现在要求统计每个字符出现的次数: 形成结果: {'字符':出现次数,'字符2':次数}
例如: 'abcdecf' ==> {'a':1,'b':1,'c':2,'d':1,'e':1,'f':1}

“”"

1- 创建一个字符串

my_string = ‘abcdecf’

2- 遍历字符串, 获取每一个字符

2.1 初始化一个用于保存结果的空字典

my_dict = {}
for e in my_string:

# 2.2 判断 当前遍历的这个元素 是否在 字典中存在呢?
if e in my_dict.keys():
    # 说明 当前遍历的元素 在 字典中是存在的
    my_dict[e] += 1
else:
    # 说明 当前遍历的元素 在 字典中是不存在的
    my_dict[e] = 1

print(my_dict)


## 四种数据结构对比总结
![](https://cdn.nlark.com/yuque/__mermaid_v3/92844edde965c00af2dd7c120880e90b.svg)

| 数据结构 | 可变性 | 有序性 | 是否去重 | 定义符号 | 适用场景 |
| :--- | :--- | :--- | :--- | :--- | :--- |
| 列表 (list) | 可变 | 有序 | 不去重 | `[]` | 存储有序集合,需要增删改 |
| 元组 (tuple) | 不可变 | 有序 | 不去重 | `()` | 存储不应被修改的常量数据 |
| 集合 (set) | 可变 | 无序 | 去重 | `{}` 或 `set()` | 去重、成员判断、集合运算 |
| 字典 (dict) | 可变 | 有序(Python 3.7+) | 键去重 | `{k:v}` 或 `dict()` | 键值对映射、计数、配置 |


# 函数详解
## 容器拆包与推导式
![](https://cdn.nlark.com/yuque/__mermaid_v3/1946f724ed6904e7b16c96e9dc2b8370.svg)

### 容器拆包
#### 什么是拆包?
**<font style="color:#74B602;">简单来说就是把一个元组中的数据一个一个拆解出来的过程,就称之为叫做拆包操作。</font>**

![](https://cdn.nlark.com/yuque/__mermaid_v3/314101b7e26773cb715cff2a426935c3.svg)

#### 基本语法
```python
tuple1 = (10, 20)
# 拆包
num1, num2 = tuple1

以上代码可以简写为
num1, num2 = (10, 20)

还可以进一步简写
num1, num2 = 10, 20
拆包典型案例

定义一个容器(元组)

tuple1 = ("炸鸡", True, 25.5)

将元组中每个元素分别赋值给三个变量

name, is_flag, price = tuple1

print(f"{name},{is_flag},{price}")

输出:炸鸡,True,25.5

其他自动拆包方式

a, b = (10, 20)
print(f"{a},{b}")

a, b = 10, 20
print(f"{a},{b}")

两变量值交换

初始化两个变量

c1 = '游戏手柄'
c2 = 'Switch'

直接交换(利用自动拆包)

c1 = '游戏手柄'
c2 = 'Switch'

c2, c1 = c1, c2

print(c1)
print(c2)

输出:

Switch
游戏手柄

使用临时变量(不用自动拆包)

c1 = '游戏手柄'
c2 = 'Switch'

tmp = c1
c1 = c2
c2 = tmp

print(c1)
print(c2)

输出:

Switch
游戏手柄

总结:自动拆包本质上就是把容器的每个元素"打散"分别赋值给对应位置的变量,变量数量必须和元素数量一致。变量交换时直接 <font style="color:#74B602;">a, b = b, a</font> 即可,比传统临时变量法简洁得多

推导式

什么是推导式

推导式comprehensions(又称解析式),是Python的一种独有特性。推导式是可以从一个数据序列构建另一个新的数据序列(一个有规律的列表或控制一个有规律列表)的结构体。 共有三种推导:列表推导式、集合推导式、字典推导式。

为什么需要推导式

案例:创建一个0-9的列表

while循环:

# 初始化计数器
i = 0
list1 = []
# 编写循环条件
while i <= 9:
    list1.append(i)
    # 更新计数器
    i += 1
print(list1)

for循环:

list1 = []
# 编写for循环
for i in range(0, 10):
    list1.append(i)
print(list1)

列表推导式

基本语法:

变量名 = [表达式 for 变量 in 列表]
变量名 = [表达式 for 变量 in 列表 if 条件]
变量名 = [表达式 for 变量 in 列表 for 变量 in 列表]

案例:定义0-9之间的列表

list1 = []
for i in range(10):
    list1.append(i)
print(list1)

列表推导式

# 推导式演示:
"""
    格式:
        变量名 = [表达式 for 变量 in 列表]
        变量名 = [表达式 for 变量 in 列表 if 条件]
        变量名 = [表达式 for 变量 in 列表 for 变量 in 列表]

"""

# 案例1:定义0-9之间的列表
# 非推导式方式
my_list = []

for i in range(0, 10):
    my_list.append(i)

print(my_list)

# 推导式:
my_list = [i for i in range(0, 10)]
print(my_list)

# 案例2: 给定一个列表, 将该列表内容依次添加到另一个集合中, 实现去重效果 得到一个集合:  {7,9,5,4,6}
my_list2 = [7, 9, 5, 4, 5, 7, 6]

# 非推导方式:
my_set1 = set()

for e in my_list2:
    my_set1.add(e)

print(my_set1)

# 推导式方式:
my_set2 = {e for e in my_list2}
print(my_set2)

执行原理:[i for i in range(10)]

列表推导式先运行表达式右边的内容:

当第一次遍历时:i = 0,其得到变量i的结果后,会放入最左侧的变量i中,这个时候列表中就是[0]
当第二次遍历时:i = 1,其得到变量i的结果后,会追加最左侧的变量i中,这个时候列表中就是[0, 1]
...
当最后一次遍历时:i = 9,其得到变量i的结果后,会追加最左侧的变量i中,这个时候列表中就是[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

列表推导式 + if条件判断

在使用列表推导式时候,我们除了可以使用for循环,其实我们还可以在其遍历的过程中,引入if条件判断。

变量 = [表达式 for 临时变量 in 序列 if 条件判断]

等价于

for 临时变量 in 序列:
    if 条件判断

案例:生成0-9之间的偶数(i%2 0)序列

#  推导式 含  IF 操作
# 格式: 变量 = [表达式 for 临时变量 in 序列 if 条件判断]

# 需求: 案例:生成0-9之间的偶数(i%2  0)序列
# 非推导式方式
my_list3 = []
for e in range(0, 10):
    if e % 2 == 0:
        my_list3.append(e)


print(my_list3)

# 推导式:
my_list4 = [e for e in range(0, 10) if e % 2 == 0]

print(my_list4)

# 需求2: 生成 0~9的序列, 然后将 > 5的数据 存储到一个新的列表中
# 推导式
my_list5 = [ e for e in range(0,10) if e > 5 ]
print(my_list5)
for循环嵌套列表推导式
for 临时变量 in range(n):
    for 临时变量 in range(n):

基本语法:

变量 = [表达式 for 临时变量 in 序列 for 临时变量 in 序列]

🪑** 座位表生成案例**

需求:生成教室座位坐标列表

假设一个 3行 × 4列 的教室,生成所有座位的坐标:

座位布局:
第0列  第1列  第2列  第3列
行0: (0,0)  (0,1)  (0,2)  (0,3)
行1: (1,0)  (1,1)  (1,2)  (1,3)
行2: (2,0)  (2,1)  (2,2)  (2,3)

方法一:for循环嵌套

seats = []

# 外层循环:遍历每一行
for row in range(3):
    # 内层循环:遍历每一列
    for col in range(4):
        seat = (row, col)
        seats.append(seat)

print(seats)

输出:

[(0, 0), (0, 1), (0, 2), (0, 3),
 (1, 0), (1, 1), (1, 2), (1, 3),
 (2, 0), (2, 1), (2, 2), (2, 3)]

方法二:列表推导式

# 推导式方式(更简洁)
seats = [(row, col) for row in range(3) for col in range(4)]

print(seats)

输出:

[(0, 0), (0, 1), (0, 2), (0, 3),
 (1, 0), (1, 1), (1, 2), (1, 3),
 (2, 0), (2, 1), (2, 2), (2, 3)]

对比记忆

对比项for循环嵌套列表推导式
代码行数多行一行搞定
可读性逻辑清晰简洁但稍难读
执行效率稍慢更快
适用场景复杂逻辑简单转换

推导式本质就是:

# 外层 for 在前,内层 for 在后
[表达式 for 外层变量 in 外层范围 for 内层变量 in 内层范围]

更多练习 🎯

题目1: 生成 2×3 的棋盘坐标(从 (1,1) 开始)

# 推导式
chess = [(i, j) for i in range(1, 3) for j in range(1, 4)]
# 结果: [(1,1),(1,2),(1,3),(2,1),(2,2),(2,3)]

题目2: 生成所有 (0-9, 0-9) 的坐标对(排除对角线 i==j)

# 推导式
coords = [(i, j) for i in range(10) for j in range(10) if i != j]
推导式案例

案例一:使用列表推导式生成平方数集合

例如, 用户输入10, 表示要生成 1~10的每一个数字的平方的集合

# 推导式案例: 案例一:使用列表推导式生成平方数集合
# 例如, 用户输入10, 表示要生成 1~10的每一个数字的2平方的集合
# 1- 让用户输入一个正整数
n = int(input("请输入一个正整数:"))

# 2- 遍历 从 1 ~ N
# 2.1 初始化一个空列表
my_list7 = []
for j in range(1,n+1):
    # 2.2 计算 2的平方 将结果写入列表
    my_list7.append(j ** 2)

print(my_list7)

# 推导式 如何解决
my_list8 = [ j ** 2 for j in range(1,n+1)]
print(my_list8)


函数

Python中函数的作用与使用步骤

函数的定义

基本语法:

def 函数名称(参数1, 参数2, ...):
    函数体
    ...
    [return 返回值]


注意: 语法中的中括号表示的可选的意思

在Python中,函数和变量一样,都是先定义后使用。

# 定义函数
def 函数名称([参数1, 参数2, ...]):
    函数体
    ...
    [return 返回值]

# 调用函数
函数名称(参数1, 参数2, ...)

背包物品管理


1️⃣ 第一步:不用函数 — 重复代码写到手酸
# 场景:游戏角色背包里有 100 个空格
# 每次捡到道具,都要用掉一个空格

# 第一次捡到药水
inventory = 100
inventory -= 1
print(f"捡到1个道具,背包还剩 {inventory} 个空格")

# 第二次捡到武器
inventory -= 1
print(f"捡到1个道具,背包还剩 {inventory} 个空格")

# 第三次捡到宝石
inventory -= 1
print(f"捡到1个道具,背包还剩 {inventory} 个空格")

# 第四次捡到装备
inventory -= 1
print(f"捡到1个道具,背包还剩 {inventory} 个空格")

问题: 每次捡道具都要写一模一样的 3 行代码,太繁琐了!💀


2️⃣ 第二步:封装成函数 — 代码复用
# 定义一个函数:捡道具
def pick_item():
    """捡起一个道具,自动占用背包一个空格"""
    print("捡到1个道具!")

# 调用函数,不用重复写代码了
pick_item()      # 第1次捡道具
pick_item()      # 第2次捡道具
pick_item()      # 第3次捡道具
pick_item()      # 第4次捡道具

输出:

捡到1个道具!
捡到1个道具!
捡到1个道具!
捡到1个道具!

好处: 🎉 定义一次,随时调用!再也不用复制粘贴了!


3️⃣ 第三步:函数带参数 — 告诉函数具体捡什么
# 场景:捡到的东西不一样,打印的信息也要不一样
# 用参数来解决这个问题

def pick_item(item_name):
    """捡起一个道具

    参数:
        item_name: 道具名称
    """
    print(f"捡到了 【{item_name}】!")

# 调用时传入具体道具名
pick_item("生命药水")   # 捡到生命药水
pick_item("火焰剑")     # 捡到火焰剑
pick_item("蓝宝石")     # 捡到蓝宝石
pick_item("黄金头盔")   # 捡到黄金头盔

输出:

捡到了 【生命药水】!
捡到了 【火焰剑】!
捡到了 【蓝宝石】!
捡到了 【黄金头盔】!

好处: 🎯 一个函数,变出无数种用法!


4️⃣ 第四步:函数带返回值 — 告诉调用者结果
# 场景:希望函数告诉调用者,捡道具到底成不成功
# 比如背包满了就捡不了

# 设定背包容量
bag_capacity = 100
current_count = 0

def pick_item(item_name):
    """捡起一个道具
    返回 True 表示成功,False 表示失败(背包满了)
    """
    global current_count  # 声明使用全局变量

    # 检查背包是否满了
    if current_count >= bag_capacity:
        return False  # 捡不了,返回失败

    current_count += 1
    print(f"捡到了 【{item_name}】!背包进度:{current_count}/{bag_capacity}")
    return True  # 捡到了,返回成功

# 调用并判断结果
result = pick_item("生命药水")
if result:
    print("✅ 捡道具成功!")
else:
    print("❌ 背包已满,无法捡取!")

result = pick_item("火焰剑")
if result:
    print("✅ 捡道具成功!")
else:
    print("❌ 背包已满,无法捡取!")

输出:

捡到了 【生命药水】!背包进度:1/100
✅ 捡道具成功!
捡到了 【火焰剑】!背包进度:2/100
✅ 捡道具成功!

📊 总结对比
阶段代码特点能做什么
无函数重复代码多基本功能
无参函数定义一次,调用多次代码复用
带参函数传入不同数据一函多用
带返回值函数返回执行结果反馈给调用者
return返回值

思考1:如果一个函数如些两个return (如下所示),程序如何执行?

def return_num():
    return 1
    return 2


result = return_num()
print(result)  # 1

答:只执行了第一个return,原因是因为return可以退出当前函数,导致return下方的代码不执行。

思考2:如果一个函数要有多个返回值,该如何书写代码?

答:在Python中,理论上一个函数只能返回一个结果。但是如果我们向让一个函数可以同时返回多个结果,我们可以使用return 元组的形式。

def return_num():
    return 1, 2


result = return_num()
print(result)
print(type(result))  # <class 'tuple'>

思考3:封装一个函数,参数有两个num1,num2,求两个数的四则运算结果

四则运算:加、减、乘、除

def size(num1, num2):
    jia = num1 + num2
    jian = num1 - num2
    cheng = num1 * num2
    chu = num1 / num2
    return jia, jian, cheng, chu


# 调用size方法
print(size(20, 5))

相关演示:

# 需求: 请帮我定义一个函数: computer 计算器函数, 要求给这个函数传入 2个参数, 在函数的内部完成  + - 计算, 并且将 + -的二个结果返回

# 定义函数:
def computer(a, b):
    # 编写函数功能的核心位置
    jia = a + b
    jian = a - b

    # 将加和减结果一并返回
    return jia, jian
    # return [jia, jian]
    # return {jia, jian}
    # return {'jia':jia,'jian':jian}


# 调用函数
# res = computer(3,5)
i,j = computer(3,5)  # 利用自动拆包思想

print(f'加法结果:{i},减法的结果:{j}')

函数的嵌套及案例

什么是函数的嵌套

所谓函数嵌套调用指的是一个函数里面又调用了另外一个函数。

函数嵌套的基本语法
def funca():
    return 2
def funcb():
    return funca()

funcb()

扩展递归

# 1 利用递归实现阶乘运算
'''
递归: 一个函数自己调用自己,就是递归
必要条件: 要有结束条件
'''
def func_c(num:int):
    '''
    通过递归实现求阶乘操作
    :param num:参数
    :return: int
    '''
    if num <= 1:
        return 1
    return num * func_c(num-1)


print(func_c(3))
# 2 利用递归青蛙台阶
'''
分治思想,分而治之,简称分治
将一个大问题,拆解为相似的小问题,逐个击破
'''

'''
需求:有一个小青蛙,每次可以跳1个台阶,或者跳2个台阶,总共10个台阶,问有多少种走法
思路:
1. 当仅有一个台阶,那么就是1中走法,fn(1) = 1
2. 当有两个台阶,那么有两种走法,f(2) = 2
3. 当有三个台阶, 那么有这些走法 f(3) = f(2) + f(1)
4. 如果有四级台阶, 那么走法为f(4) = f(3) + f(2)
5. 如果有n级台阶,那么走法为f(n) = f(n-1) + f(n-2)
'''
def fn_steps(n):
    if n == 1:
        return 1
    if n == 2:
        return 2
    return fn_steps(n-1) + fn_steps(n-2)

print(fn_steps(10))

# 3 实现汉诺塔
'''
汉诺塔
移动n个盘子,从A--->C盘
思路:
将前n-1个盘子从A柱借助C柱移动到B柱
将第n个盘子从A柱移动到C柱
将前n-1个盘子从B柱借助A柱移动到C柱
'''

def hanuota(num, A,B,C):
    '''
    这是一个汉诺塔函数,表示将num个盘子,从A柱移动到C柱的流程
    :param num: 盘子个数
    :param A:  A柱子,起始柱子
    :param B:  B柱子,辅助柱子
    :param C:  C柱子,目的柱子
    :return: None
    '''
    if num == 1:
        print(f'将第{num}个盘子从{A}移动到{C}')
        return
    # 将num-1个盘子从A借助C移动到B
    hanuota(num-1, A,C,B)
    # 将第num个盘子,从A移动到C
    print(f'将第{num}个盘子从{A}移动到{C}')
    # 将num-1个盘子从B借助A移动到C
    hanuota(num-1,B,A,C)

hanuota(3,'A','B','C')

变量的作用域

什么是变量的作用域

变量作用域指的是变量的作用范围(变量在哪里可用,在哪里不可用),主要分为两类:全局作用域与局部作用域。

其实作用域的划分比较简单,在函数内部定义范围就称之为局部作用域,在函数外部(全局)定义范围就是全局作用域

# 全局作用域
def func():
    # 局部作用域
局部变量与全局变量

在Python中,定义在函数外部的变量就称之为全局变量;定义在函数内部变量就称之为局部变量。

# 定义在函数外部的变量(全局变量)
num = 10
# 定义一个函数
def func():
    # 函数体代码
    # 定义在函数内部的变量(局部变量)
    num = 100

演示全局和局部的使用场景

# 演示: 全局和局部变量:
#  全局变量: 指的是定义在函数外部的变量称为全局变量
#  局部变量: 指的是定义在函数内部的变量称为局部变量
#  全局变量的作用范围: 是整个全局的范围内, 一旦定义后, 在当前这个文件的任意位置均可以使用该变量
#  局部变量的作用范围: 仅在当前这个函数的内部, 一旦出了这个函数, 该变量就相当于不存在

# 定义一个全局变量:
a = 100

# 定义一个函数
def fun1():
    # 定义一个局部变量:
    b = 200
    # 在函数内部使用全局变量
    print(f"函数内--输出a的值为:{a}")
    print(f"函数内--输出b的值为:{b}")


# 调用函数
fun1()

# 在函数外部使用全局变量
print(f"函数外输出a的值:{a}")
# print(f"函数外输出a的值:{b}")
变量作用域的作用范围

全局变量:在整个程序范围内都可以直接使用

str1 = 'hello'
# 定义一个函数
def func():
    # 在函数内部调用全局变量str1
    print(f'在局部作用域中调用str1变量:{str1}')

# 直接调用全局变量str1
print(f'在全局作用域中调用str1变量:{str1}')
# 调用func函数
func()

局部变量:在函数的调用过程中,开始定义,函数运行过程中生效,函数执行完毕后,销毁

# 定义一个函数
def func():
    # 在函数内部定义一个局部变量
    num = 10
    print(f'在局部作用域中调用num局部变量:{num}')

# 调用func函数
func()
# 在全局作用域中调用num局部变量
print(f'在全局作用域中调用num局部变量:{num}')

global关键字的应用场景

思考一个问题:我们能不能在局部作用域中对全局变量进行修改呢?

'''
在Python中有两种类型的变量
1. 可变类型 list,dict,set,自定义的类型 ,尽管数据相同,但是占用不同的内存空间
2. 不可变类型 int, bool, str, double, tuple,相同的数据在内存中只存储1份数据
a = 1
b = 1
可以理解为a和b存储的都是1的地址
当a = 2 的时候,a指向的就是2的地址
'''
a = 1
b = 1
a = 2

list_a = [1,2,3]
list_b = [1,2,3]
list_a.append(4)
print(list_a)
print(list_b)

'''
因为Python中赋值和定义具有二义性
Python不同于C++, 
C++语法
int a = 100;  a = 200;
Python中
定义和赋值弄到一起
a = 100
a = 200
如果a = 200在函数内, a = 100在函数外
两个a 不是同一个a, 函数内的a = 200会被认为定义了一个新的变量a
'''

# 定义全局变量num = 10
num = 10
# 定义一个函数func
def func():
    # 尝试在函数内部修改全局变量,但是不会修改,只会重新定义一个num
    num = 20
    print(f"函数内num:{num}, id(num): {id(num)}")

# 调用函数func
func()
# 尝试访问全局变量num
print(f"函数外num:{num}, id(num): {id(num)}")
print('*'*20)

最终结果:弹出10,所以由运行结果可知,在函数体内部理论上是**没有办法对全局变量进行修改的,所以一定要进行修改,必须使用**<font style="color:#DF2A3F;">global</font>**关键字**。

# 定义全局变量num = 10
num = 10
# 定义一个函数func
def func():
    # 尝试在局部作用域中修改全局变量
    global num
    num = 20

# 调用函数func
func()
# 尝试访问全局变量num
print(num)

记住:global关键字只是针对不可变数据类型的变量进行修改操作(数值、字符串、布尔类型、元组类型),可变类型可以不加global关键字。

# 案例: 以卖票案例

# 1 定义一个票总量
ticket = 100


# 2- 定义一个卖票的程序(函数)
def sale_ticket():
    # 标记为全局变量: 主要是针对的是不可变的数据类型, 如果是可变的, 不需要添加global关键词
    # 不可变的数据类型: 数值 字符串 元组 布尔
    # 可变的数据类型:  列表 集合 字典
    global ticket

    # 执行一次, 需要让 票数 -1
    ticket -= 1  # 默认会认为这个变量是一个局部变量

    print(f"剩余票数为:{ticket}")


# 3- 调用卖票程序
sale_ticket()
sale_ticket()
sale_ticket()
sale_ticket()

可变数据类型, 不需要添加global

# 结论: 如果变量的数据类型是可变的, 不需要添加global 就可以 对 全局变量进行修改

# 1- 定义一个全局变量: 类型为 字典
person = {"name":"张三","age":18}

# 2- 定义一个函数
def fun1():
    # 向字典中添加一个属性:
    person['address'] = "南京市"

    print(f"函数内打印全局变量:{person}")
    #print("修改全局变量的内容")


# 3- 调用函数
fun1()

# 4- 在全局范围内 打印全局变量
print(person)
'''
如果函数内修改的是可变对象,不需要加global,(dict,set,list,自定义类型对象)
因为不可变对象有很多种修改方式,比如字典的插入,比如列表append, remove,都可以修改,
不会被当成变量定义,所以就不用写global了

如果修改的是不可变的变量,需要加global,(字符串,元组,数字,布尔类型)
因为不可变类型的变量做修改,只能通过赋值,而赋值会产生二义性
python中=(赋值操作),有两层意思,一个是定义变量,还有一个是修改变量
在函数内部,局部作用域使用=,优先理解为定义变量

内存原理:
不可变对象,数据在内存中保留1份
可变对象,数据在内存中可以相同,可以是不同地址
'''

作用域说明完整源码

'''
局部作用域和全局作用域
'''
'''
局部作用域: 函数内部作用域就是局部作用域
全局作用域: 在函数外部的作用域就是全局作用域
局部变量: 在函数内部定义的变量就是局部变量,可见范围只在自己定义的作用域内,
在哪个函数内定义,就在哪个函数内可见,不可被外部的全局作用域使用
全局变量: 在函数外部定义的变量就是全局变量,可以被程序的所有部分使用
'''
# 全局变量
num1 = 2000
def hello():
    # 局部变量
    num2 = 300
    print('hello world')
# 全局变量
num3 = 400
def sum(num1,num2):
    # num1和num2都是局部变量,num4
    num4 = num1 + num2
    return num4

# 全局变量
str1 = 'hello'
def func():
    # 局部变量
    str2 = 'heima'
    print(f'在局部作用域内使用全局变量str1: {str1}')
    print(f'在局部作用域内使用局部变量str2: {str2}')

func()
# 在全局作用域内无法使用局部变量
#print(f'在全局作用域内使用局部变量str2: {str2}')

# 如果局部变量和全局变量同名了,就会出现定义局部变量和全局变量冲突的问题
# 如果想要在函数内修改外部局部变量,用global声明
# 全局变量
name = 'Lisi'
def change_name():
    # 局部变量
    name = 'zs'
    print(f'局部变量name: {name}')
change_name()
print(f'全局变量name: {name}')

# 使用global关键字
def change_name2():
    # 声明全局变量
    global name
    # 修改全局变量,将lisi改为zs
    name = 'zs'
    print(f'函数内name: {name}')
change_name2()
print(f'全局变量name: {name}')

# 全局变量
name = 'Lisi'
# 使用形参name
def change_name3(name):
    # name是形参,局部变量
    # 修改全局变量,将lisi改为zs
    name = 'zs'
    print(f'函数内name: {name}')
# name 为实参,为全局变量
change_name3(name)
print(f'全局变量name: {name}')

'''
如果函数内修改的是可变对象,不需要加global,(dict,set,list,自定义类型对象)
因为可变对象有很多种修改方式,比如字典的插入,比如列表append, remove,都可以修改,
不会被当成变量定义,所以就不用写global了

如果修改的是不可变的变量,需要加global,(字符串,元组,数字,布尔类型)
因为不可变类型的变量做修改,只能通过赋值,而赋值会产生二义性
python中=(赋值操作),有两层意思,一个是定义变量,还有一个是修改变量
在函数内部,局部作用域使用=,优先理解为定义变量

内存原理:
不可变对象,数据在内存中保留1份
可变对象,数据在内存中可以相同,可以是不同地址
'''
# 这是可变类型的全局变量
person = {'name':'zs','age':18}
print(f'全局person地址: {id(person)}')
def func1():
    # = 被当作定义,所以内部person是新定义的局部变量
    person = {'name':'zs','age':18}
    print(f'函数内person地址: {id(person)}')

#func1()

def func2():
    person['address'] = 'beijing'
    print(f'函数内person地址: {id(person)}')
func2()

函数的参数进阶

函数的参数

在函数定义与调用时,我们可以根据自己的需求来实现参数的传递。在Python中,函数的参数一共有两种形式:

① 形参 ② 实参

形参:在函数定义时,所编写的参数就称之为形式参数

实参:在函数调用时,所传递的参数就称之为实际参数

def greet(name):  # name就是在函数greet定义时,所编写的参数(形参)
    return name + ',您好'

# 调用函数
name = '老王'
greet(name)  # 在函数调用时,所传递的参数就是实际参数

注意:虽然我们在函数传递时,喜欢使用相同的名称作为参数名称。但是两者的作用范围是不同的。name = ‘老王’,代表实参。其是一个全局变量,而greet(name)函数中的name实际是在函数定义时才声明的变量,所以其实一个局部变量。

函数的参数类型(调用)

☆ 位置参数

理论上,在函数定义时,我们可以为其定义多个参数。但是在函数调用时,我们也应该传递多个参数,正常情况,其要一一对应。

def user_info(name, age, address):
    print(f'我的名字{name},今年{age}岁了,家里住在{address}')

# 调用函数
user_info('Tom', 23, '美国纽约')

注意事项:位置参数强调的是参数传递的位置必须一一对应,不能颠倒

☆ 关键词参数(Python特有)

函数调用,通过"键=值"形式加以指定。可以让函数更加清晰、容易使用,同时也清除了参数的顺序需求。

def user_info(name, age, address):
    print(f'我的名字{name},今年{age}岁了,家里住在{address}')

# 调用函数(使用关键词参数)
user_info(name='Tom', age=23, address='美国纽约')

☆ 函数定义时缺省参数(参数默认值)

缺省参数也叫默认参数,用于定义函数,为参数提供默认值,调用函数时可不传该默认参数的值(注意:所有位置参数必须出现在默认参数前,包括函数定义和调用)。

def user_info(name, age, gender='男'):
    print(f'我的名字{name},今年{age}岁了,我的性别为{gender}')


user_info('李林', 25)
user_info('振华', 28)
user_info('婉儿', 18, '女')

谨记:我们在定义缺省参数时,一定要把其写在参数列表的最后侧

相关的演示
# 定义一个函数, 该函数需要传入三个参数
def fun1(name,age,address):
    print(f"姓名:{name},年龄:{age},地址:{address}")


# 如何调用呢?
# 位置传参方式: 要求调用函数的时候, 传递的参数顺序 要和 函数的形参的顺序保持一致
fun1("张三",20,"南京市")

# 关键词传参: 在传递参数的时候, 可以指定给谁传递  对顺序就没有要求
fun1(age=18,name="李四",address="北京市")

# 默认值传参: 指的是在定义这个函数的时候, 可以给某些参数赋一个默认值, 然后在调用该函数就可以不需要传递参数了
# 注意: 有默认值的形参 必须放置到整个参数列表的最后面
def fun2(name,address='南京市',age=18):
    print(f"姓名:{name},年龄:{age},地址:{address}")

# 调用操作: 如果不需要修改默认值, 那么在调用的时候也不需要传递, 如果需要修改 那么就正常传递即可
fun2(name="王五")
fun2(name="赵六",address="北京市")


注意: 如果函数中某个参数没有默认值, 那么在调用的时候, 必须传递 否则会直接报错
不定长参数

不定长参数也叫可变参数。用于不确定调用的时候会传递多少个参数(不传参也可以)的场景。此时,可用包裹(packing)位置参数,或者包裹关键字参数,来进行参数传递,会显得非常方便。

☆ 不定长元组(位置)参数
def user_info(*args):
    # print(args)  # 元组类型数据,对传递参数有顺序要求
    print(f'我的名字{args[0]},今年{args[1]}岁了,住在{args[2]}')

# 调用函数,传递参数
user_info('Tom', 23, '美国纽约')
☆ 不定长字典(关键字)参数
def user_info(**kwargs):
    # print(kwargs)  # 字典类型数据,对传递参数没有顺序要求,格式要求key = value值
    print(f'我的名字{kwargs["name"]},今年{kwargs["age"]}岁了,住在{kwargs["address"]}')

# 调用函数,传递参数
user_info(name='Tom', address='美国纽约', age=23)

kw = keyword + args

综上:无论是包裹位置传递还是包裹关键字传递,都是一个组包的过程。

Python组包:就是把多个数据组成元组或者字典的过程。

相关演示:

# 演示不定长参数:
# 不定长元组(位置)参数
# 定义函数:
def fun1(*args):  # 这种 一个 * 不定长参数, 背后 是一个容器(序列) --> 元组
    # 获取每一个参数的内容
    print(type(args))
    for e in args:
        print(f"参数:{e}")


# 调用函数
fun1("张三", "李四", "王五", "赵六", "田七")


# 不定长字典(关键词)参数
def fun2(**kwargs):  # 这种 一个 ** 不定长参数, 背后 是一个容器(序列) --> 字典
    print(type(kwargs))
    # 遍历参数
    for key in kwargs:
        print(f"参数的key:{key}, 对应的值为:{kwargs[key]}")

# 调用函数: 必须使用关键词传参
fun2(name="张三", age=18, address="南京市")  # 在调用的时候, 创建了一个字典, 将 kv数据放置到了字典中

案例:Python中数据的传递案例

def func(*args, **kwargs):
    print(args)
    print(kwargs)


# 定义一个元组(也可以是列表)
tuple1 = (10, 20, 30)
# 定义一个字典
dict1 = {'first': 40, 'second': 50, 'third': 60}
# 需求:把元组传递给*args参数,字典传递给**kwargs
# ① 如果想把元组传递给*args,必须在tuple1的前面加一个*号
# ② 如果想把字典传递给**kwargs,必须在dict1的前面加两个**号
func(*tuple1, **dict1)

lambda函数

普通函数与匿名函数

在Python中,函数是一个被命名的、独立的完成特定功能的一段代码,并可能给调用它的程序一个返回值。

所以在Python中,函数大多数是有名函数 => 普通函数。但是有些情况下,我们为了简化程序代码,也可以定义匿名函数 => lambda表达式

lambda表达式应用场景

如果一个函数有一个返回值,并且只有一句代码,可以使用 lambda简化。

lambda表达式基本语法
变量 = lambda 函数参数:表达式(函数代码 + return返回值)
# 调用变量
变量()

编写lambda表达式

定义一个lambda表达式

'''
演示lambda表达式用法
'''

'''
变量 = lambda 函数参数:表达式(函数代码 + return返回值)
# 调用变量
变量()
说明:
变量就是函数的地址,将来可以通过变量名调用函数
'''
def func(str):
    return 'hello '+ str

# fn是lambda表达式的地址,也就是函数地址
fn = lambda str: 'hello '+ str
# 打印函数调用结果
print(fn('zack'))
编写带参数的lambda表达式

编写一个函数求两个数的和

def fn1(num1, num2):
    return num1 + num2

print(fn1(10, 20))

lambda表达式进行简化:

'''
带参数的lambda表达式
计算num1和num2的加法结果
'''
# fn是lambda表达式的地址,也就是函数地址
fn = lambda num1,num2:num1+num2
# 函数调用
print(fn(1,2))
☆ 带默认参数的lambda表达式
# 有参数 参数带有默认值:
def fn2(num1, num2=30):
    return num1 + num2


print(fn2)
print(fn2(10))

# lambda 简化
'''
带默认值的参数
'''
fn2 = lambda num1, num2=20: num1-num2
# 打印函数的地址,也就是lambda表达式在堆区的地址
print(fn2)
# num2默认传递20
print(fn2(100))
☆ 带if判断(三目运算符)的lambda表达式
# 带 if判断(三目运算符)的lambda表达式
# 普通函数
def fn3(name: str):
    a = 0
    if name == "张三":
        a = 1
    else:
        a = 0
    return a

# 普通函数
def fn4(name: str):
    return 1 if name == "张三" else 0

print(fn4("李四"))

# 基于lambda方案实现
lambda name: 1 if name == '张三' else 0
☆ 列表数据+字典数据排序(重点)

lambda表达式经常配合泛型编程使用

不受限于数据结构

也不受限于实际参数

封装和调用解耦合

知识点:列表.sort(key=排序的key)

print("------------------------------------")
# 在一个列表中, 放置了三个元素, 每一个元素又是一个字典
students = [
    {'name': 'Tom', 'age': 20},
    {'name': 'Rose', 'age': 19},
    {'name': 'Jack', 'age': 22}
]
print(students)
# 按name值升序排列

# 按name值降序排列

本章知识总结

知识点核心语法一句话总结
拆包a, b = b, a一步交换,无需临时变量
推导式[x for x in seq if cond]一行代码替代for循环
函数def name(params): ... return封装复用,模块化编程
递归def f(n): return n * f(n-1)函数自调用,必须有出口
作用域global / local函数内修改全局不可变需global
不定长参数*args / **kwargs接收任意数量参数
lambdalambda x: x * 2单行函数的匿名写法

文件操作与异常处理

文件操作、异常处理与模块化编程

文件读写基础

为什么要学文件操作?

程序运行时的数据存储在内存中,程序结束后数据就消失了。文件操作让我们能够:

  • 数据持久化:保存数据到硬盘
  • 数据读取:读取之前保存的数据
  • 大数据处理:处理超过内存容量的数据
  • 日志记录:记录程序运行过程

文件操作的三个步骤

open() 函数详解
基本语法
open(filename, mode, encoding)
参数说明
参数说明示例
filename文件名或路径'example.txt', './data/file.txt'
mode打开模式'r', 'w', 'a', 'x'
encoding编码方式'utf-8', 'gbk'
打开模式详解

模式含义文件存在文件不存在
'r'读取(默认)打开报错
'w'写入覆盖创建
'a'追加追加创建
'x'创建报错创建
最简单的文件读取
# 方式1:一次性读取全部内容
with open('example.txt', 'r', encoding='utf-8') as f:
    content = f.read()
print(content)

为什么使用 with 语句?

  • 自动打开文件
  • 自动关闭文件
  • 即使发生错误也会关闭文件
  • 代码更简洁
四种读取方式

方式1:read() - 一次性读取全部
with open('example.txt', 'r', encoding='utf-8') as f:
    content = f.read()  # 返回字符串

适用场景:小文件,需要全部内容
风险:大文件会占用大量内存

方式2:readline() - 逐行读取
with open('example.txt', 'r', encoding='utf-8') as f:
    line = f.readline()  # 读取一行
    while line:
        print(line.rstrip('\n'))  # 移除换行符
        line = f.readline()

适用场景:需要逐行处理
优点:内存占用少

方式3:readlines() - 读取所有行到列表
with open('example.txt', 'r', encoding='utf-8') as f:
    lines = f.readlines()  # 返回列表
    for i, line in enumerate(lines):
        print(f"第{i+1}行: {line.rstrip()}")

适用场景:需要随机访问行
风险:大文件占用内存

方式4:迭代读取(最推荐)
with open('example.txt', 'r', encoding='utf-8') as f:
    for line in f:  # 最高效的方式
        print(line.rstrip('\n'))

优点:

  • 内存效率最高
  • 代码最简洁
  • 最Pythonic
处理大文件
分块读取
def read_large_file_by_chunks(filename, chunk_size=1024):
    """分块读取大文件"""
    with open(filename, 'r', encoding='utf-8') as f:
        while True:
            chunk = f.read(chunk_size)
            if not chunk:
                break
            process_data(chunk)
按行分块读取
def read_large_file_by_lines(filename, batch_size=1000):
    """按行分块读取大文件"""
    batch = []
    with open(filename, 'r', encoding='utf-8') as f:
        # 从文件中按照行读取
        for line in f:
            batch.append(line.rstrip('\n'))
            if len(batch) == batch_size:
                process_batch(batch)
                batch = []
        if batch:
            process_batch(batch)
文件写入
写入模式

# 模式1:写入(覆盖)
with open('output.txt', 'w', encoding='utf-8') as f:
    f.write('第一行\n')
    f.write('第二行\n')

# 模式2:追加
with open('output.txt', 'a', encoding='utf-8') as f:
    f.write('追加的内容\n')

# 模式3:使用 print() 函数
with open('output.txt', 'w', encoding='utf-8') as f:
    print('第一行', file=f)
    print('第二行', file=f)
写入列表
lines = ['第一行\n', '第二行\n', '第三行\n']
with open('output.txt', 'w', encoding='utf-8') as f:
    f.writelines(lines)
文件指针操作

with open('example.txt', 'r', encoding='utf-8') as f:
    # tell() - 获取当前位置
    print(f.tell())  # 0

    # 读取 10 个字符
    content = f.read(10)
    print(f.tell())  # 10

    # seek() - 移动指针
    f.seek(0)  # 回到开头
    f.seek(5, 0)  # 从开头移动 5 字节

    # 获取文件大小
    f.seek(0, 2)
    file_size = f.tell()
处理 CSV 文件
# 读取 CSV
def read_csv(filename):
    students = []
    with open(filename, 'r', encoding='utf-8') as f:
        f.readline()  # 跳过表头
        for line in f:
            name, age, score = line.rstrip('\n').split(',')
            students.append({
                'name': name,
                'age': int(age),
                'score': int(score)
            })
    return students

# 写入 CSV
def write_csv(filename, students):
    with open(filename, 'w', encoding='utf-8') as f:
        f.write('name,age,score\n')
        for student in students:
            line = f"{student['name']},{student['age']},{student['score']}\n"
            f.write(line)
处理 JSON 文件
import json

# 读取 JSON
def read_json(filename):
    with open(filename, 'r', encoding='utf-8') as f:
        data = json.load(f)
    return data

# 写入 JSON
def write_json(filename, data):
    with open(filename, 'w', encoding='utf-8') as f:
        json.dump(data, f, indent=2, ensure_ascii=False)

异常处理

什么是异常?

异常是程序执行过程中发生的错误事件。

常见异常类型:

  • FileNotFoundError - 文件不存在
  • PermissionError - 没有权限
  • ValueError - 值错误
  • TypeError - 类型错误
  • ZeroDivisionError - 除以零
  • IndexError - 索引超出范围
  • KeyError - 字典键不存在
为什么需要异常处理?
  1. 防止程序崩溃 - 程序可以继续运行
  2. 提供有意义的错误信息 - 帮助用户理解问题
  3. 采取恢复措施 - 尝试其他方法或保存数据
基础异常处理:try-except
基本结构
try:
    # 可能出错的代码
except 异常类型:
    # 处理异常的代码
示例1:捕获特定异常
def safe_divide(a, b):
    try:
        result = a / b
    except ZeroDivisionError:
        print("错误:不能除以零")
        return None
    return result

print(safe_divide(10, 2))   # 5.0
print(safe_divide(10, 0))   # 错误:不能除以零
示例2:文件操作中的异常处理
def safe_read_file(filename):
    try:
        with open(filename, 'r', encoding='utf-8') as f:
            content = f.read()
        return content
    except FileNotFoundError:
        print(f"错误:文件 '{filename}' 不存在")
        return None
捕获多个异常
方式1:多个 except 块
try:
    with open(filename, 'r', encoding='utf-8') as f:
        content = f.read()
except FileNotFoundError:
    print("错误:文件不存在")
except PermissionError:
    print("错误:没有权限")
except UnicodeDecodeError:
    print("错误:编码错误")
方式2:一个 except 块捕获多个异常
try:
    with open(filename, 'r', encoding='utf-8') as f:
        content = f.read()
except (FileNotFoundError, PermissionError):
    print("错误:文件访问失败")
获取异常信息
try:
    with open('nonexistent.txt', 'r') as f:
        content = f.read()
except FileNotFoundError as e:
    print(f"异常类型: {type(e)}")
    print(f"异常信息: {e}")
完整的异常处理结构

try:
    # 可能出错的代码
    with open('example.txt', 'r', encoding='utf-8') as f:
        content = f.read()
except FileNotFoundError:
    print("错误:文件不存在")
except Exception as e:
    print(f"发生错误: {e}")
else:
    # 只有在 try 块成功执行时才运行
    print(f"文件读取成功,内容长度: {len(content)} 字符")
finally:
    # 无论如何都会执行
    print("文件操作完成")

执行流程:

  1. 执行 try 块
  2. 如果发生异常,执行对应的 except 块
  3. 如果没有异常,执行 else 块
  4. 无论如何,最后执行 finally 块
finally 块的重要用途
f = None
try:
    f = open('example.txt', 'r', encoding='utf-8')
    content = f.read()
except FileNotFoundError:
    print("错误:文件不存在")
finally:
    # 即使发生异常,这里也会执行
    if f is not None:
        f.close()
        print("文件已关闭")

注意:这种方式不如 with 语句安全,推荐使用 with 语句。

异常的传播

def func2():
    x = 1 / 0  # 这里会抛出异常

def func1():
    func2()  # 异常会从这里传播出去

def main():
    try:
        func1()
    except ZeroDivisionError:
        print("捕获到异常:除以零")

main()

异常传播链:

main()
↓
func1()
↓
func2() (异常发生)
↓
func1() (没有处理,继续传播)
↓
main() (捕获异常)
实战:安全的文件读写
def safe_read_file(filename):
    """完整的安全文件读取函数"""
    try:
        with open(filename, 'r', encoding='utf-8') as f:
            content = f.read()
        return content
    except FileNotFoundError:
        print(f"错误:文件 '{filename}' 不存在")
        return None
    except PermissionError:
        print(f"错误:没有权限读取文件 '{filename}'")
        return None
    except UnicodeDecodeError:
        print(f"错误:文件编码错误,尝试使用 GBK 编码")
        try:
            with open(filename, 'r', encoding='gbk') as f:
                content = f.read()
            return content
        except:
            return None
    except IOError as e:
        print(f"错误:I/O 错误 - {e}")
        return None

def safe_write_file(filename, content):
    """完整的安全文件写入函数"""
    try:
        with open(filename, 'w', encoding='utf-8') as f:
            f.write(content)
        print(f"成功写入文件 '{filename}'")
        return True
    except PermissionError:
        print(f"错误:没有权限写入文件 '{filename}'")
        return False
    except IOError as e:
        print(f"错误:写入失败 - {e}")
        return False

包与模块管理

模块与包的概念

模块(Module)
  • 单个 .py 文件
  • 包含 Python 代码
  • 可以被导入使用
包(Package)
  • 包含 __init__.py 文件的目录
  • 可以包含多个模块和子包
  • 用于组织相关的模块
最小包结构
myproject/
├── mypackage/
│   ├── __init__.py
│   └── module.py
└── main.py
完整包结构
myproject/
├── README.md
├── setup.py
├── requirements.txt
├── mypackage/
│   ├── __init__.py
│   ├── core/
│   │   ├── __init__.py
│   │   ├── engine.py
│   │   └── utils.py
│   ├── io/
│   │   ├── __init__.py
│   │   ├── reader.py
│   │   └── writer.py
│   └── config.py
├── tests/
│   ├── __init__.py
│   └── test_core.py
└── examples/
    └── example.py

init.py 的作用
作用1:标记目录为包
# 有 __init__.py 的目录被识别为包
# 没有 __init__.py 的目录不是包
作用2:初始化包
# mypackage/__init__.py
print("mypackage 已加载")
作用3:定义公共接口
# mypackage/__init__.py
from .core import engine
from .io import reader

__all__ = ['engine', 'reader']
导入方式

方式1:导入整个模块
import mypackage.core.engine
engine_obj = mypackage.core.engine.Engine()
方式2:导入特定内容
from mypackage.core.engine import Engine
engine_obj = Engine()
方式3:导入子包
from mypackage.core import engine
engine_obj = engine.Engine()
方式4:相对导入(仅在包内使用)
# 在 mypackage/io/reader.py 中
from . import writer  # 导入同级模块
from ..core import engine  # 导入上级包的模块
创建自定义包
步骤1:创建目录结构
mkdir datatools
mkdir datatools/core
mkdir datatools/io
mkdir datatools/utils
步骤2:创建 init.py 文件
# datatools/__init__.py
__version__ = "1.0.0"

from .core import processor
from .io import reader, writer

__all__ = ['processor', 'reader', 'writer']
步骤3:创建模块文件
# datatools/core/processor.py
def process_data(data):
    """处理数据"""
    return data
# datatools/io/reader.py
def read_file(filename):
    """读取文件"""
    with open(filename, 'r', encoding='utf-8') as f:
        return f.read()
步骤4:使用包
import datatools
from datatools import processor, reader

data = reader.read_file('data.txt')
result = processor.process_data(data)
setup.py - 包的安装配置
from setuptools import setup, find_packages

setup(
    name='datatools',
    version='1.0.0',
    description='数据处理工具包',
    author='Your Name',
    author_email='your.email@example.com',
    url='https://github.com/yourusername/datatools',
    license='MIT',
    packages=find_packages(),
    python_requires='>=3.7',
    install_requires=[
        'numpy>=1.19.0',
    ],
    classifiers=[
        'Development Status :: 4 - Beta',
        'Intended Audience :: Developers',
        'License :: OSI Approved :: MIT License',
        'Programming Language :: Python :: 3',
    ],
)
安装包
# 本地开发安装(推荐)
pip install -e .

# 普通安装
pip install .

# 从 PyPI 安装
pip install datatools

# 指定版本
pip install datatools==1.0.0
requirements.txt
# requirements.txt
numpy>=1.19.0
pandas>=1.1.0
requests>=2.25.0

使用方式:

pip install -r requirements.txt

实战项目

项目1:学生成绩管理系统
项目结构
grade_system/
├── main.py
├── students.csv
└── utils.py
main.py
import csv
import json

def read_students_csv(filename):
    """读取学生信息"""
    students = []
    try:
        with open(filename, 'r', encoding='utf-8') as f:
            reader = csv.DictReader(f)
            for row in reader:
                students.append({
                    'name': row['name'],
                    'age': int(row['age']),
                    'score': float(row['score'])
                })
        return students
    except FileNotFoundError:
        print(f"错误:文件 '{filename}' 不存在")
        return []
    except ValueError:
        print("错误:数据格式错误")
        return []

def calculate_average(students):
    """计算平均成绩"""
    if not students:
        return 0
    total = sum(s['score'] for s in students)
    return total / len(students)

def find_top_student(students):
    """找出成绩最高的学生"""
    if not students:
        return None
    return max(students, key=lambda s: s['score'])

def save_report(filename, students):
    """保存成绩报告"""
    try:
        report = {
            'total_students': len(students),
            'average_score': calculate_average(students),
            'top_student': find_top_student(students),
            'students': students
        }
        with open(filename, 'w', encoding='utf-8') as f:
            json.dump(report, f, indent=2, ensure_ascii=False)
        print(f"报告已保存到 '{filename}'")
    except IOError as e:
        print(f"错误:保存报告失败 - {e}")

def main():
    """主函数"""
    # 读取学生信息
    students = read_students_csv('students.csv')

    if not students:
        print("没有学生数据")
        return

    # 显示统计信息
    print(f"总学生数:{len(students)}")
    print(f"平均成绩:{calculate_average(students):.2f}")

    top = find_top_student(students)
    if top:
        print(f"最高成绩:{top['name']} - {top['score']}")

    # 保存报告
    save_report('report.json', students)

if __name__ == '__main__':
    main()

项目2:日志记录系统
项目结构
log_system/
├── main.py
├── logger.py
└── logs/
logger.py
import os
from datetime import datetime

def create_log_file(log_dir='logs'):
    """创建日志目录"""
    if not os.path.exists(log_dir):
        os.makedirs(log_dir)
    return log_dir

def write_log(message, log_dir='logs', level='INFO'):
    """写入日志"""
    try:
        # 创建日志目录
        create_log_file(log_dir)
        
        # 生成日志文件名
        today = datetime.now().strftime('%Y-%m-%d')
        log_file = os.path.join(log_dir, f'{today}.log')
        
        # 生成日志内容
        timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        log_content = f"[{timestamp}] [{level}] {message}\n"
        
        # 写入日志
        with open(log_file, 'a', encoding='utf-8') as f:
            f.write(log_content)
        
        print(f"日志已记录:{log_content.strip()}")
    except IOError as e:
        print(f"错误:写入日志失败 - {e}")

def read_logs(log_dir='logs', date=None):
    """读取日志"""
    try:
        if date is None:
            date = datetime.now().strftime('%Y-%m-%d')
        
        log_file = os.path.join(log_dir, f'{date}.log')
        
        if not os.path.exists(log_file):
            print(f"日志文件不存在:{log_file}")
            return []
        
        with open(log_file, 'r', encoding='utf-8') as f:
            logs = f.readlines()
        
        return logs
    except IOError as e:
        print(f"错误:读取日志失败 - {e}")
        return []

def main():
    """主函数"""
    # 写入日志
    write_log("程序启动", level='INFO')
    write_log("开始处理数据", level='INFO')
    
    try:
        # 模拟处理
        result = 10 / 0
    except ZeroDivisionError:
        write_log("发生错误:除以零", level='ERROR')
    
    write_log("程序结束", level='INFO')
    
    # 读取日志
    logs = read_logs()
    print("\n今日日志:")
    for log in logs:
        print(log.strip())

if __name__ == '__main__':
    main()
项目3:配置文件管理
项目结构
config_system/
├── main.py
├── config.json
└── config_manager.py
config_manager.py
import json
import os

def load_config(filename):
    """加载配置文件"""
    try:
        with open(filename, 'r', encoding='utf-8') as f:
            config = json.load(f)
        print(f"配置已加载:{filename}")
        return config
    except FileNotFoundError:
        print(f"错误:配置文件不存在 - {filename}")
        return {}
    except json.JSONDecodeError:
        print(f"错误:配置文件格式错误 - {filename}")
        return {}

def save_config(filename, config):
    """保存配置文件"""
    try:
        with open(filename, 'w', encoding='utf-8') as f:
            json.dump(config, f, indent=2, ensure_ascii=False)
        print(f"配置已保存:{filename}")
        return True
    except IOError as e:
        print(f"错误:保存配置失败 - {e}")
        return False

def get_config_value(config, key, default=None):
    """获取配置值"""
    return config.get(key, default)

def set_config_value(config, key, value):
    """设置配置值"""
    config[key] = value
    return config

def main():
    """主函数"""
    config_file = 'config.json'
    
    # 加载配置
    config = load_config(config_file)
    
    if not config:
        # 创建默认配置
        config = {
            'app_name': 'MyApp',
            'version': '1.0.0',
            'debug': True,
            'port': 8000
        }
        save_config(config_file, config)
    
    # 获取配置值
    app_name = get_config_value(config, 'app_name')
    print(f"应用名称:{app_name}")
    
    # 修改配置
    config = set_config_value(config, 'debug', False)
    save_config(config_file, config)

if __name__ == '__main__':
    main()

本章知识总结

知识点核心语法一句话总结
文件读取with open(f,'r') as f: content=f.read()with 自动管理文件关闭
四种读取方式read() / readline() / readlines() / for line in f优先使用 for line in f
文件写入f.write() / f.writelines()w 覆盖, a 追加, x 创建
文件指针tell() / seek(offset, whence)可随机访问文件任意位置
CSV处理split(',') / csv.DictReader结构化数据存储
JSON处理json.load() / json.dump()支持嵌套结构
try-excepttry: ... except XxxError: ...防止程序崩溃
else-finallyelse: (无异常) / finally: (必执行)完整的异常处理
异常传播未捕获的异常沿调用栈向上传播在最外层统一处理
模块单 .py 文件import module
包含 __init__.py 的目录from pkg import mod
pip安装pip install -e . / pip install pkg开发和发布模式
Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐