本文所有代码均基于 Python 3.10 + 版本运行,兼容 Python3.6 +
参考资料:比特就业课 Python 基础语法体系、Python 官网文档等
前言
Python凭借其简洁的语法、极强的可读性和丰富的生态库,常年霸占 TIOBE 编程语言排行榜榜首位置。截至 2026 年 3 月,Python 在 TIOBE 榜单中以超过23%市场份额持续领跑,广泛应用于人工智能、数据分析、Web 开发、自动化运维、网络爬虫等诸多领域,成为零基础入门编程的首选语言。
而基础语法是 Python 学习的基石,无论是后续进阶框架使用,还是复杂项目开发,都离不开扎实的语法功底。本文将基于 Python3 标准,系统梳理 Python 核心基础语法,从最基础的常量表达式,到流程控制、函数、复合数据类型,再到文件操作,全覆盖讲解核心知识点,同时配套可运行代码示例、原理图表和行业最佳实践,帮读者一文吃透 Python 基础语法。
一、常量、变量与数据类型
1.1 常量与表达式
在 Python 中,我们可以直接将解释器作为计算器使用,通过常量 和运算符 组成表达式完成算术运算。
-
字面值常量 :代码中直接写出的固定值,如
1、2.5、'hello',分为数值常量、字符串常量、布尔常量等 -
表达式:由常量、变量和运算符组成的算式,每个表达式都会有一个明确的返回值
-
运算符优先级 :遵循数学规则,
先算乘除,后算加减,可通过括号()改变运算顺序
代码示例:基础算术运算
python
# 基础算术运算
print(1 + 2 - 3) # 加减运算,输出:0
print(1 + 2 * 3) # 先乘后加,输出:7
print(1 + 2 / 3) # 除法运算,输出:1.6666666666666665
print((67.5 + 89.0 + 12.9 + 32.2) / 4) # 求平均数,输出:50.4

注意:
-
print 是一个 Python 内置的函数,这个稍后详细介绍。
-
可以使用 +、-、*、/ 以及括号 ( ) 等运算符进行算术运算。计算时先算乘除,后算加减。
-
运算符和数字之间可以没有空格,也可以有多个空格,但是一般习惯上写一个空格,这样比较美观。
形如 1 + 2 - 3 这样的算式,在编程语言中称为表达式 。算式的运算结果,称为表达式的返回值 。其中 1、2、3 这种称为字面值常量 ,+++、-、*、/++ 这种称为运算符 或操作符。
注意 :和 C/Java 等语言不同,Python 中
/除法不会截断小数部分,会直接返回浮点数,更符合日常使用直觉。
示例:
给定四个分数 67.5、89.0、12.9、32.2,编写代码,求这四个分数的平均数。
python
print( (67.5 + 89.0 + 12.9 + 32.2) / 4 )
1.2 变量与命名规范
变量是程序中用于存储数据的容器,对应计算机内存中的一块存储空间,我们可以通过变量名读取 或修改其中的数据,是实现复杂计算的核心载体。
1.2.1 变量的定义与使用
Python 中变量 无需提前声明类型,赋值时自动确定类型,核心语法为**变量名 = 数值**
创建变量的语句非常简单,其中
-
a 为变量名。当我们创建很多个变量的时候,就可以用名字来进行区分。
-
= 为赋值运算符,表示把 = 右侧的数据放到 = 左侧的空间中。
示例:
给定四个分数 67.5、89.0、12.9、32.2,编写代码,求这四个分数的方差。
PS:方差的计算过程:取每一项,减去平均值,计算平方,再求和,最后除以(项数 - 1)。
在以下代码中,就需要先计算这四个数字的平均值,然后再计算方差。这就需要把计算的平均值使用变量保存起来。
python
# 定义变量,计算4个数字的方差
avg = (67.5 + 89.0 + 12.9 + 32.2) / 4 # 存储平均值
total = (67.5 - avg) ** 2 + (89.0 - avg) ** 2 + (12.9 - avg) ** 2 + (32.2 - avg) ** 2
result = total / 3
print(result) # 输出方差结果:1278.0499999999997
# 变量的读取与修改
a = 10
print(a) # 读取变量,输出:10
a = 20 # 修改变量值
print(a) # 输出:20
注意:
-
avg、total、result 均为变量。
-
** 在 Python 中表示乘方运算,** 2 即为求平方。
就像计算器中的 M 键功能类似,通过变量就可以保存计算过程中的中间结果。

只不过,计算器一般只能保存一个数据,而在 Python 代码中,可以创建任意多的变量,来随心所欲地保存很多很多的数据。
变量可以视为是一块能够容纳数据的空间,这个空间往往对应到"内存"这样的硬件设备上。

PS:我们可以把内存想像成是一个宿舍楼,这个宿舍楼上有很多的房间。每个房间都可以存放数据。衡量内存的一个重要指标就是内存空间的大小,比如我的电脑内存是 16GB。这个数字越大,意味着内存的存储空间就越大,能够存放的数据(变量)就越多。
1.2.2 变量命名规则
变量名不是随意定义的,需要遵守 Python 的语法规则,分为 硬性规则(务必遵守)和 软性规则(建议遵守) 两类:
| 规则类型 | 具体要求 | 反例 | 正确示例 |
|---|---|---|---|
| 硬性规则 | 只能由数字、字母、下划线组成 | a*b、123num |
num123、user_name |
| 硬性规则 | 数字不能作为开头 | 123abc |
abc123 |
| 硬性规则 | 不能与 Python 关键字重名 | if、for、def |
if_flag、for_num |
| 硬性规则 | 大小写敏感 | Num和num是两个完全不同的变量 |
- |
| 软性规范 | 使用有描述性的单词,见名知意 | x、y、a1 |
user_age、total_count |
| 软性规范 | 多单词推荐使用蛇形命名法(PEP8 规范) | userName(驼峰) |
user_name |
| 软性规范 | 不使用拼音 / 拼音英文混合 | yong_hu_ming |
user_name |
补充:Python3 支持中文作为变量名(如
姓名 = "张三"),但工业级开发中强烈不建议使用,会存在兼容性和可读性问题。
1.3 变量的类型
Python 中变量的类型在赋值时自动确定,无需显式声明,通过内置函数**type()** 可以查看变量的类型。核心基础数据类型分为 4 类,同时支持++列表、元组、字典++等复合类型,核心基础类型如下:
1.3.1 四类基础数据类型
| 数据类型 | 关键字 | 特点说明 | 代码示例 |
|---|---|---|---|
| 整数 | int | 无大小上限,只要内存足够,可存储无限大的整数,支持正负数、0 | a = 10、b = -9999999999999 |
| 浮点数 | float | 双精度浮点数(等价于 C/Java 的 double),占用 8 字节内存,精度约小数点后 15 位 | a = 0.5、b = 3.1415926 |
| 字符串 | str | 单引号' '或双引号" "包裹的文本内容,两者完全等价,支持拼接、长度获取等操作 |
a = 'hello'、b = "Python" |
| 布尔值 | bool | 仅两个取值:True(真)和False(假),用于逻辑判断 |
a = True、b = 10 > 20 |
除了上述类型之外,Python 中还有 list、tuple、dict、自定义类型等等。
(1) 整数
代码示例:数据类型操作
python
a = 10
print(type(a))

PS:type 和 print 类似,也是 Python 内置的函数。可以使用 type 来查看一个变量的类型。
注意:和 C++ / Java 等语言不同,Python 的 int 类型变量表示的数据范围是没有上限的。只要内存足够大,理论上就可以表示无限大小的数据。
(2) 浮点数
python
a = 0.5
print(type(a))

注意:和 C++ / Java 等语言不同,Python 的小数只有 float 一种类型,没有 double 类型。但实际上 Python 的 float 就相当于 C++ / Java 的 double,表示双精度浮点数。
PS:关于单精度浮点数和双精度浮点数的问题,此处不做过多讨论。大家只要知道,相比于单精度浮点数,双精度浮点数占用的内存空间更多,同时表示的数据精度更高即可(大概精确到小数点后 15 位)。
(3) 字符串
python
a = 'hello'
print(type(a))

使用 ' ' 或者 " " 引起来的,称为字符串,可以用来表示文本。
注意:在 Python 中,单引号构成的字符串和双引号构成的字符串没有区别,'hello' 和 "hello" 是完全等价的。
可以使用 len 函数来获取字符串的长度。
python
a = 'hello'
print(len(a))
可以使用 + 针对两个字符串进行拼接
python
a = 'hello'
b = 'world'
print(a + b)
PS:此处是两个字符串相加,不能拿字符串和整数/浮点数相加。
字符串作为开发中最常用到的数据类型,支持的操作方式也是非常丰富的。
(4) 布尔
布尔类型是一个特殊的类型,取值只有两种:True(真)和 False(假)。
PS:布尔类型也是数学上的一个概念。我们初中就学过一个概念叫做"命题",进一步的就可以判定命题的真假。

布尔类型在咱们后续进行逻辑判断的时候,是非常有用的。
1.3.2 为什么要有这么多类型?
(1) 类型决定了数据在内存中占据多大空间。
例如 float 类型在内存中占据 8 个字节。
PS:计算机里面使用二进制来表示数据,也就是每个位只能表示 0 或者 1。
1 个二进制位就称为是一个"比特",8 个二进制位就称为一个"字节"(Byte)。
一个 float 变量在内存中占据 8 个字节空间,也就是 64 个二进制位。
我的电脑有 16GB 的内存空间,也就是一共有 1024 × 1024 × 1024 × 8 这么多的二进制位。
示例代码:
python
a = 'hello'
b = 'world'
print(a + b)
a = True
print(type(a))
b = False
print(type(b))
(2) 类型约定了能对这个变量做什么样的操作。
例如 int / float 类型的变量,可以进行 +、-、、/ 等操作。而 str 类型的变量,只能进行 +(并且行为是字符串拼接),不能进行 -、、/,但是还能使用 len 等其他操作。
总结: 类型系统其实是在对变量进行"归类"。相同类型的变量(数据)往往具有类似的特性和使用规则。
1.3.3 动态类型特性
Python 是典型的动态类型语言,变量的类型可以在程序运行过程中发生改变;而 C++/Java 等静态类型语言,变量定义后类型就固定不可修改。
python
# 动态类型演示
a = 10
print(type(a)) # 初始为int类型,输出:<class 'int'>
a = 'hello'
print(type(a)) # 运行中变为str类型,输出:<class 'str'>

优缺点对比:
| 优势 | 劣势 |
|---|---|
| 代码更简洁,开发效率高,一段代码可支持多种类型 | 大型项目中,提高了模块间的交互成本,不利于类型校验 |
| 无需提前声明类型,学习门槛低 | 容易出现类型相关的隐藏 bug,需依赖类型注解和单元测试规避 |
动态类型特性是把双刃剑!!!!
二、注释
2.1注释是什么
注释是不会被程序执行的特殊代码,核心作用是解释代码逻辑,提升代码可读性,是团队协作和后期维护的核心保障。Python 中主要分为行注释和文档字符串两类。
PS: 写代码是一件比较烧脑的事情,读代码同样也非常烧脑。相比于一板一眼的代码,一些口语化的描述能更好地帮助程序员理解程序。
python
# 计算 4 个数字 67.5, 89.0, 12.9, 32.2 的方差
avg = (67.5 + 89.0 + 12.9 + 32.2) / 4
total = (67.5 - avg) ** 2 + (89.0 - avg) ** 2 + (12.9 - avg) ** 2 + (32.2 - avg)
** 2
result = total / 3
print(result)
形如上述代码,如果没有注释,直接阅读,是不容易 get 到代码的含义是计算方差的。但是通过加了一行注释解释一下,就让人一目了然了。
PS: 代码的第一目标是容易理解,第二目标才是执行正确。写注释不光是为了方便别人来理解,也是方便三个月之后的自己理解。
一个反例:早些年医生的手写处方:


2.2 注释的两种语法
2.2.1 行注释
以**# 开头,#** 后的所有内容都会被解释器忽略,通常用于对++单行代码++做解释。
python
# 计算4个数字的方差(行注释:说明代码整体功能)
avg = (67.5 + 89.0 + 12.9 + 32.2) / 4 # 计算平均值(行注释:单行代码说明)
total = (67.5 - avg) ** 2 + (89.0 - avg) ** 2 + (12.9 - avg) ** 2 + (32.2 - avg) ** 2
result = total / 3 # 方差公式:平方和/(项数-1)
print(result)
2.2.2 文档字符串
使用三引号 """ """或 ''' '''包裹,支持++多行内容++,通常放在文件、函数、类的开头,用于说明整体功能,是 Python 官方推荐的文档注释方式。
python
"""
这是文档字符串示例
功能:计算一组数据的方差
作者:Python教程
日期:2026-04
"""
def calc_variance(data_list):
"""
计算列表数据的方差
:param data_list: 输入的数值列表
:return: 方差计算结果
"""
avg = sum(data_list) / len(data_list)
total = sum((x - avg) ** 2 for x in data_list)
return total / (len(data_list) - 1)
2.3 注释的规范
-
内容准确:注释必须和代码逻辑一致,代码修改时同步更新注释,禁止出现 "注释和代码不符" 的情况
-
篇幅合理 :注释应简洁明了,核心代码必须加注释,无需对显而易见的代码做冗余注释(如
a = 10 # 给a赋值10) -
语言规范:国内企业统一使用中文写注释,外企可根据要求使用英文,禁止中英文混合、拼音注释
-
格式规范 :行注释
#后必须加一个空格;文档字符串需遵循 Google 风格或 NumPy 风格,统一团队标准 -
积极正向:注释中禁止包含负能量、吐槽、人身攻击等无关内容
三、控制台输入与输出
程序与用户的交互核心分为两步:
-
用户把信息传递给程序的过程,称为"输入"。
-
程序把结果展示给用户的过程,称为"输出"。
输入输出的最基本方法就是控制台。用户通过控制台输入一些字符串,程序再通过控制台打印出一些字符串。
PyCharm 运行程序,下方弹出的窗口就可以视为是控制台。


**输入输出的最常见方法是图形化界面。**如我们平时用到的 QQ, 浏览器, steam 等, 都不需要用户输入命令,而只是通过鼠标点击窗口点击按钮的方式来操作.
PS: Python 当然也可以用来开发图形化界面的程序,但是图形化程序开发本身是一个大话题,咱们暂时不做介绍。
3.1 控制台输出
**print()**是 Python 内置的输出函数,用于将内容打印到控制台,支持多种数据类型和格式化输出。
3.1.1 基础用法
不仅能输出一个字符串,还可以输出一个其他类型的变量。更多的时候,我们希望能够输出的内容是混合了字符串和变量的。
python
# 打印单个常量/变量
print('hello Python') # 输出字符串:hello Python
a = 10
print(a) # 输出变量:10
print(True) # 输出布尔值:True
# 打印多个内容,用逗号分隔,默认用空格拼接
name = '张三'
age = 20
print('姓名:', name, '年龄:', age) # 输出:姓名: 张三 年龄: 20


3.1.2 格式化输出:f-string
Python3.6 + 支持的 f-string 格式化语法,是目前工业级开发的首选,
语法为f'内容{变量/表达式}',直接在字符串中内嵌变量或表达式,可读性极强。
python
# f-string基础用法
num = 10
print(f'num = {num}') # 输出:num = 10
# 内嵌表达式
a = 20
b = 30
print(f'a + b = {a + b}') # 输出:a + b = 50
# 格式化浮点数精度
pi = 3.1415926
print(f'圆周率保留2位小数:{pi:.2f}') # 输出:圆周率保留2位小数:3.14
扩展:Python 还支持**
%格式化、format()**格式化,三者对比如下:
格式化方式 优点 缺点 适用场景 f-string 语法简洁、可读性强、性能最高 仅支持 Python3.6+ 绝大多数开发场景,首选 format() 兼容性好,支持复杂格式化 语法繁琐,长字符串可读性差 需要兼容 Python3.5 及以下版本 % 格式化 最传统,兼容所有版本 类型限制严格,容易出现类型错误 老项目维护
3.2 控制台输入
**input()**是 Python 内置的输入函数,用于接收用户在控制台输入的内容,核心特点:
-
**
input()**的参数是提示信息,可省略 -
无论用户输入什么内容,返回值永远是字符串类型
-
程序执行到 **
input()**时会阻塞,等待用户输入完成后继续执行
代码示例:
python
# 基础输入
num = input('请输入一个整数:')
print(f'你输入的内容是:{num}')
print(f'输入内容的类型是:{type(num)}') # 永远是<class 'str'>
# 重点:输入数值时,必须进行类型转换
a = input('请输入第一个整数:')
b = input('请输入第二个整数:')
# 错误用法:直接相加,会变成字符串拼接
print(f'错误的相加结果:{a + b}') # 输入10和20,输出:1020
# 正确用法:先转换为int/float类型,再进行算术运算
a = int(a)
b = int(b)
print(f'正确的相加结果:{a + b}') # 输入10和20,输出:30



**实战案例:**输入 4 个小数,计算平均值
python
# 接收用户输入的4个小数,计算平均值
a = float(input('请输入第一个数字:'))
b = float(input('请输入第二个数字:'))
c = float(input('请输入第三个数字:'))
d = float(input('请输入第四个数字:'))
avg = (a + b + c + d) / 4
print(f'4个数字的平均值是:{avg:.2f}')

四、运算符
运算符是用于执行各种运算的符号,Python 中核心运算符分为 5 大类,下面逐一讲解核心用法和注意事项。
4.1 算术运算符
用于执行基础的数学运算,核心运算符如下表:
| 运算符 | 功能 | 示例 | 结果 | 注意事项 |
|---|---|---|---|---|
| + | 加法 | 10 + 20 | 30 | 字符串之间可用于拼接 |
| - | 减法 | 20 - 10 | 10 | 可用于负数表示 |
| * | 乘法 | 2 * 3 | 6 | 字符串与整数可用于重复('a'*3得到'aaa') |
| / | 除法 | 10 / 2 | 5.0 | 永远返回浮点数,除数不能为 0,否则抛出异常 |
| // | 地板除(取整除法) | 7 // 2-7 // 2 | 3-4 | 舍弃小数部分,向下取整,而非四舍五入 |
| % | 取余(取模) | 7 % 2 | 1 | 返回除法运算的余数 |
| ** | 乘方 | 4 ** 24 ** 0.5 | 162.0 | 支持整数次方、小数次方,可用于开方 |
坑点:
-
除零异常:
/、//、%运算符中,除数不能为 0,否则会抛出ZeroDivisionError -
地板除的向下取整:负数场景需注意,
-7//2结果是 - 4,而非 - 3 -
浮点数精度问题:算术运算中浮点数可能出现微小误差,如
0.1 + 0.2 = 0.30000000000000004

4.2 关系运算符
用于比较两个值的大小 或 相等关系,运算结果永远是布尔值True或False,核心运算符如下:
| 运算符 | 含义 | 示例 | 结果 |
|---|---|---|---|
| < | 小于 | 10 < 20 | True |
| <= | 小于等于 | 10 <= 10 | True |
| > | 大于 | 10 > 20 | False |
| >= | 大于等于 | 10 >= 20 | False |
| == | 等于 | 10 == 10 | True |
| != | 不等于 | 10 != 20 | True |
注意事项
-
字符串比较 :支持关系运算符,按照字典序比较
pythona = 'hello' b = 'world' print(a < b) # 输出:True,因为'h'的Unicode编码小于'w' print(a == 'hello') # 输出:True,直接对比字符串内容 -
浮点数相等判断的巨坑 :禁止直接用
==判断两个浮点数相等,因为浮点数的存储存在精度误差python# 错误用法 print(0.1 + 0.2 == 0.3) # 输出:False,因为0.1+0.2=0.30000000000000004 # 正确用法:判断差值在允许的误差范围内 a = 0.1 + 0.2 b = 0.3 print(-1e-6 < (a - b) < 1e-6) # 输出:True,误差小于百万分之一即认为相等 -
Python 特有语法 :支持连续比较,++
a < b < c等价于a < b and b < c++,更符合数学书写习惯
PS: 关于字典序:想象一个英文词典,上面的单词都是按照字母顺序排列。如果首个字母相同,就比较第二个字母(比如著名单词 abandon)咱们可别放弃。我们认为一个单词在词典上越靠前,就越小;越靠后,就越大。
4.3 逻辑运算符
用于多个布尔值之间的逻辑运算,核心运算符为 and、or、not,运算结果为布尔值,核则如下表:
| 运算符 | 含义 | 运算规则 | 示例 | 结果 |
|---|---|---|---|---|
| and | 逻辑与(并且) | 两侧都为 True,结果才为 True;一假则假 | True and False | False |
| or | 逻辑或(或者) | 两侧有一个为 True,结果就为 True;一真则真 | True or False | True |
| not | 逻辑非(取反) | 操作数为 True,结果为 False;反之亦然 | not True | False |
此处说的"并且"和"或者",就是我们日常生活中使用的"并且"和"或者"。想象一下未来丈母娘问你要彩礼,什么叫做"有房并且有车",什么叫做"有房或者有车"。
python
a = 10
b = 20
c = 30
print(a < b and b < c)
print(a < b and b > c)
print(a > b or b > c)
print(a < b or b > c)
print(not a < b)
print(not a > b)

(1) 扩展:短路求值
逻辑运算符支持短路求值,即当表达式的结果可以提前确定时,不再执行后续的代码,是 Python 中重要的性能优化机制,也是面试高频考点。
-
对于
and:左侧表达式为False时,整体结果一定为False,右侧表达式不再执行 -
对于
or:左侧表达式为True时,整体结果一定为True,右侧表达式不再执行
python
# 短路求值演示
# 1. and短路:左侧为False,右侧的除零运算不会执行,无异常
print(10 > 20 and 10 / 0 == 1) # 输出:False
# 2. or短路:左侧为True,右侧的除零运算不会执行,无异常
print(10 < 20 or 10 / 0 == 1) # 输出:True

上述代码没有抛出异常,说明右侧的除以 0 操作没有真正执行。
4.4 赋值运算符
用于给变量赋值,核心分为基础赋值、复合赋值、多元赋值三类。
4.4.1 基础赋值与复合赋值
(1) = 的使用
= 表示赋值。注意和 **==**区分。
**=**除了基本的用法之外,还可以同时针对多个变量进行赋值:
-
链式赋值
pythona = b = 10 -
多元赋值
pythona, b = 10, 20
代码实例:交换两个变量
基础写法
python
a = 10
b = 20
tmp = a
a = b
b = tmp
基于多元赋值
python
a = 10
b = 20
a, b = b, a
(2) 复合赋值运算符
Python 还有一些复合赋值运算符,例如 +=、-=、*=、/=、%=。
其中 a += 1 等价于 a = a + 1,其他复合赋值运算符也是同理。
python
a = 10
a = a + 1
print(a)
b = 10
b += 1
print(b)
| 运算符 | 含义 | 等价写法 |
|---|---|---|
| = | 基础赋值 | a = 10,将 10 赋值给变量 a |
| += | 加法赋值 | a += 1 等价于 a = a + 1 |
| -= | 减法赋值 | a -= 1 等价于 a = a - 1 |
| *= | 乘法赋值 | a *= 2 等价于 a = a * 2 |
| /= | 除法赋值 | a /= 2 等价于 a = a / 2 |
| //= | 地板除赋值 | a //= 2 等价于 a = a // 2 |
| %= | 取余赋值 | a %= 2 等价于 a = a % 2 |
| **= | 乘方赋值 | a **= 2 等价于 a = a ** 2 |
注意:Python 不支持 C/Java 中的
++、--自增自减运算符,统一使用+= 1、-= 1实现,避免了前置 / 后置的混淆问题。
4.5 其他运算符
除了上述核心运算符,Python 还有两类常用运算符,这里做基础讲解:
-
成员运算符 :
in、not in,用于判断元素是否存在于序列 / 字典中,返回布尔值pythonprint(2 in [1,2,3]) # 输出:True print('hello' in 'hello world') # 输出:True -
身份运算符 :
is、is not,用于判断两个变量是否指向同一个内存对象(即内存地址是否相同),和 ==有本质区别:==判断值是否相等,is判断内存地址是否相同pythona = [1,2,3] b = [1,2,3] print(a == b) # 输出:True,值相等 print(a is b) # 输出:False,内存地址不同,是两个独立的列表
五、顺序、条件分支与循环语句
流程控制是程序的核心骨架,Python 中程序的执行流程分为 3 类:顺序结构 、分支结构 、循环结构,通过这三类结构可以实现所有复杂的业务逻辑。
5.1 顺序结构
顺序结构是 Python 默认的执行流程,代码按照从上到下的顺序依次执行,不会跳过任何一行代码,是最基础的流程结构。
python
print("1")
print("2")
print("3")
执行结果一定为 "123",而不会出现 "321" 或者 "132" 等。这种按照顺序执行的代码,我们称为顺序语句。
这个顺序是很关键的。编程是一件明确无歧义的事情。安排好任务的顺序,计算机才能够正确地执行。就好像人生的顺序,是上学、工作、结婚、生子。一旦这里的顺序乱了,就很麻烦。
5.2 分支结构:if-elif-else 条件语句
什么是条件语句
条件语句能够表达"如果......否则......"这样的语义,这构成了计算机中基础的逻辑判定。条件语句也叫做分支语句,表示了接下来的逻辑可能有几种走向。
一个典型的例子:如果丘处机没有路过牛家村,那么:
金兵不会死在郭、杨两家手上
郭、杨两家就不会流亡北方
郭夫人就不会去到大漠,完颜洪烈就不会遇到包惜弱
郭靖就不会和江南七怪救了铁木真
蒙古就不会统一
蒙古铁骑就不会西征
欧洲就不会出现火药,也就不会出现文艺复兴、大航海
大炮就不会从欧洲传到日本,日本得不到统一
完颜洪烈就不会全力战,金国内乱
宋朝不会灭亡,并诞生出资本主义,中国成为最发达的国家
python
如果 我认真敲代码
我就很容易找到工作
否则
我就容易毕业就失业
其中"我认真敲代码"称为条件。如果条件成立(条件为真),则会出现"我就很容易找到工作"这个情况;如果条件不成立(条件为假),则会出现"我就容易毕业就失业"。
当然,同样的逻辑还可以反着表达。
python
如果 我选择躺平摆烂
我就容易毕业就失业
否则
我就很容易找到工作
虽然结构变了,但是整体表达的语义是等价的。
PS:亲爱的读者们,你们是选择认真敲代码,还是躺平摆烂呢?
5.2.1 三种语法格式
1. 单分支:if
仅当条件为True 时,执行 if内的代码块,否则跳过。
python
# 单分支:输入一个数字,如果大于0,打印正数
num = int(input('请输入一个整数:'))
if num > 0:
print('这是一个正数') # 仅num>0时执行
print('程序执行结束') # 无论条件是否成立,都会执行
2. 双分支:if-else
条件为True 执行 if 代码块,条件为False 执行 else 代码块,两个分支二选一。
python
# 双分支:判断奇数偶数
num = int(input('请输入一个整数:'))
if num % 2 == 0:
print('这是一个偶数')
else:
print('这是一个奇数')
3. 多分支:if-elif-else
用于多个条件的判断,从上到下依次匹配条件,匹配到第一个为 True 的条件后,执行对应的代码块,后续分支不再执行;所有条件都不成立,执行 else 代码块。
python
# 多分支:判断成绩等级
score = float(input('请输入你的考试成绩:'))
if score >= 90:
print('成绩等级:A')
elif score >= 80:
print('成绩等级:B')
elif score >= 60:
print('成绩等级:C')
else:
print('成绩等级:D,考试不及格')
5.2.2 注意事项
注意: Python 中的条件语句写法,和很多编程语言不太一样。
-
if 后面的条件表达式,没有
( ),使用:作为结尾。 -
if/else命中条件后要执行的"语句块",使用缩进(通常是 4 个空格或者 1 个 tab)来表示,而不是{ }。 -
对于多条件分支,不是写作
else if,而是elif(合体了)。
示例: 输入 1 表示愿意认真学习,输入 2 表示躺平摆烂。
python
choice = input("输入 1 表示认真学习, 输入 2 表示躺平摆烂: ")
if choice == "1":
print("你会找到好工作!")
elif choice == "2":
print("你可能毕业就失业了!")
else:
print("你的输入有误!")
-
缩进规则:Python 通过缩进表示代码块,PEP8 规范推荐使用4 个空格作为一级缩进,禁止使用 Tab 和空格混合缩进,否则会报语法错误。
-
代码块嵌套:if 语句内部可以嵌套 if 语句,实现更复杂的逻辑判断,通过缩进区分层级。
-
pass 空语句:Python 语法要求代码块不能为空,如果暂时不需要执行任何逻辑,使用
pass关键字占位,保证语法合法。
python
# pass空语句用法
num = int(input('请输入一个整数:'))
if num != 1:
pass # 占位,无任何执行效果,保证语法不报错
else:
print('num等于1')
基于缩进的方式表示代码块,带来的好处就是强制要求程序员要写明确的缩进,来明确代码之间的相对关系。如果缩进书写的不对,则直接报错。
像 C++ / Java 这些语言,即使完全不写缩进,语法也不会报错,代码可读性就比较差。
同时,带来的坏处就是,如果缩进层次比较多,就容易分不清楚某个语句属于哪个层级。
python
if a == 1:
if b == 2:
if c == 3:
if d == 4:
if e == 5:
if f == 6:
if g == 7:
print("hello")
print("1")
print("2")
上述代码中的 print("1") 和 print("2") 属于 **if 语句内部** 的缩进层级(通常缩进 4 个空格或 1 个 tab)。
因此,就有了"写 Python 需要自备游标卡尺"这个梗。

例题:闰年判断
闰年的判断规则:
-
世纪闰年:能被 400 整除的年份
-
普通闰年:能被 4 整除,但不能被 100 整除的年份
python
# 闰年判断
year = int(input('请输入要判断的年份:'))
if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
print(f'{year}年是闰年')
else:
print(f'{year}年是平年')
5.3 循环结构:while 与 for 循环
循环语句用于重复执行某一段代码,避免重复的 CV 操作,提升代码复用率,Python 中循环分为**while循环** 和 for循环 两类。
5.3.1 while 循环
(1)基础语法
python
while 循环条件:
循环体代码
-
条件为真,则执行循环体代码
-
条件为假,则结束循环
(2)代码示例
python
# 示例1:打印1-10的整数
num = 1
while num <= 10:
print(num)
num += 1 # 循环变量更新,必须有,否则会死循环
# 示例2:计算1-100的和
sum_result = 0
num = 1
while num <= 100:
sum_result += num
num += 1
print(f'1-100的和是:{sum_result}') # 输出:5050
代码示例:求 1! + 2! + 3! + 4! + 5!
python
num = 1
sum = 0
while num <= 5:
factorResult = 1
i = 1
while i <= num:
factorResult *= i
i += 1
sum += factorResult
num += 1
print(sum)
这个程序用到了两重循环,也就是在循环语句中也可以套循环。
5.3.2 for 循环
for 循环是 Python 中更常用的循环方式,主要用于遍历可迭代对象(如列表、字符串、range 序列等),语法更简洁,可读性更强。
(1)基本语法
python
for 循环变量 in 可迭代对象:
循环体
注意:
-
Python 的 for 和其他语言不同,没有"初始化语句"、"循环条件判定语句"、"循环变量更新语句",而是更加简单。
-
所谓的"可迭代对象",指的是"内部包含多个元素,能一个一个把元素取出来的特殊变量"。
(2) range 函数
range(start, end, step) 函数用于生成一个整数序列,是 for 循环的最佳搭档:
-
start:起始值(包含),默认为 0 -
end:结束值(不包含),必填 -
step:步长,默认为 1,可设为负数实现倒序
python
# 示例1:打印1-10的整数
for i in range(1, 11):
print(i)
# 示例2:打印2、4、6、8、10(步长为2)
for i in range(2, 12, 2):
print(i)
# 示例3:倒序打印10-1
for i in range(10, 0, -1):
print(i)
# 示例4:计算1-100的和
sum_result = 0
for i in range(1, 101):
sum_result += i
print(f'1-100的和是:{sum_result}')
5.3.3 循环控制:break 与 continue
**break **和 continue 用于在循环内部控制执行流程,两者的核心区别:
-
continue:结束本次循环,直接进入下一次循环 -
break:结束整个循环,直接跳出循环体
python
# continue示例:吃包子,第3个包子吃出虫子,跳过第3个,继续吃后面的
for i in range(1, 6):
if i == 3:
continue # 跳过本次循环,不执行后续的print
print(f'吃完第{i}个包子')
# break示例:吃包子,第3个包子吃出半只虫,直接不吃了,结束整个循环
for i in range(1, 6):
if i == 3:
break # 直接结束整个循环
print(f'吃完第{i}个包子')
5.3.4 循环嵌套
循环内部可以嵌套循环,外层循环执行一次,内层循环执行完整的一轮,经典场景如九九乘法表。
python
# 九九乘法表
for i in range(1, 10):
for j in range(1, i+1):
print(f'{j}*{i}={i*j}', end='\t')
print() # 换行
5.3.5 综合案例
实现"人生重开模拟器"
这是一款之前很火的文字类小游戏。玩家输入角色的初始属性之后,就可以开启不同的人生经历。大家可以在网上搜索"人生重开模拟器",就可以玩到这个游戏的各种版本。完整的程序代码较多,此处我们只实现其中的一部分逻辑。
python
import random
import sys
import time
print("+---------------------------------------------------------------------+")
print("| |")
print("| 花有重开日, 人无再少年 |")
print("| |")
print("| 欢迎来到, 人生重开模拟器 |")
print("| |")
print("+---------------------------------------------------------------------+")
# 设置初始属性
while True:
print("请设定初始属性(可用总点数 20)")
face = int(input("设定 颜值(1-10):"))
strong = int(input("设定 体质(1-10):"))
iq = int(input("设定 智力(1-10):"))
home = int(input("设定 家境(1-10):"))
if face < 1 or face > 10:
print("颜值设置有误!")
continue
if strong < 1 or strong > 10:
print("体质设置有误!")
continue
if iq < 1 or iq > 10:
print("智力设置有误!")
continue
if home < 1 or home > 10:
print("家境设置有误!")
continue
if face + strong + iq + home > 20:
print("总点数超过了 20!")
continue
print("初始属性设定完成!")
break
point = random.randint(1, 6) # 掷色子
if point % 2 == 1:
gender = 'boy'
print("你是个男孩")
else:
gender = 'girl'
print("你是个女孩")
# 出生地点和家庭背景
if 1 <= point <= 2:
print('你出生在村里, 你的父母是辛苦劳作的农民')
strong += 1
face -= 2
elif 3 <= point <= 4:
print('你出生在穷乡僻壤, 你的父母是无业游民')
home -= 1
else:
print('你出生在镇上, 你的父母是医生')
strong += 1
# 年龄循环
for age in range(1, 11):
info = f'你今年 {age} 岁, '
point = random.randint(1, 3)
# 性别触发事件
if gender == 'girl' and home <= 3 and point == 1:
info += '你家里人重男轻女思想非常严重, 你被遗弃了!'
print(info)
print("游戏结束!")
sys.exit(0)
# 体质触发的事件
elif strong < 6 and point != 3:
info += '你生了一场病, '
if home >= 5:
info += '在父母的精心照料下恢复了健康'
strong += 1
home -= 1
else:
info += '你的父母没精力管你, 你的身体状况更糟糕了'
strong -= 1
# 颜值触发的事件
elif face < 4 and age >= 7:
info += '你因为长的太丑, 别的小朋友不喜欢你, '
if iq > 5:
info += '你决定用学习填充自己'
iq += 1
else:
if gender == 'boy':
info += '你和别的小朋友经常打架'
iq -= 1
strong += 1
else:
info += '你经常被别的小朋友欺负'
strong -= 1
# 智商触发的事件
elif iq < 5:
info += '你看起来傻傻的, '
if home >= 8 and age >= 6:
info += '你的父母给你送到更好的学校学习'
elif 4 <= home <= 7:
if gender == 'boy':
info += '你的父母鼓励你多运动, 加强身体素质'
strong += 1
else:
info += '你的父母鼓励你多打扮自己'
face += 1
else:
info += '你的父母为此经常吵架'
if point == 1:
strong -= 1
elif point == 2:
iq -= 1
# 健康成长
else:
info += '你健康成长, '
if point == 1:
info += '看起来更聪明了'
iq += 1
elif point == 2:
info += '看起来更好看了'
face += 1
else:
info += '看起来更结实了'
strong += 1
print('-------------------------------------------')
print(info)
print(f'strong={strong}, face={face}, iq={iq}, home={home}')
time.sleep(1)
更多的逻辑,此处就不再实现了。大家可以按照类似的方式,设计更多的事件,完成青年、壮年、老年的相关逻辑。
六、函数
6.1函数是什么
编程中的函数和数学中的函数有一定的相似之处。
数学上的函数,比如 y = sin x,x 取不同的值,y 就会得到不同的结果。
编程中的函数,是一段可以被重复使用的代码片段。
代码示例:求束流的和,不使用函数
python
# 1. 求 1 - 100 的和
sum = 0
for i in range(1, 101):
sum += i
print(sum)
# 2. 求 300 - 400 的和
sum = 0
for i in range(300, 401):
sum += i
print(sum)
# 3. 求 1 - 1000 的和
sum = 0
for i in range(1, 1001):
sum += i
print(sum)
可以发现,这几组代码基本是相似的,只有一点点差异。可以把重复代码提取出来,做成一个函数。
实际开发中,复制粘贴是一种不太好的策略。实际开发的重复代码可能存在几十份甚至上百份。一旦这个重复代码需要被修改,那就得改几十次,非常不便于维护。
代码示例:求数列的和,使用函数
python
# 定义函数
def calcSum(beg, end):
sum = 0
for i in range(beg, end + 1):
sum += i
print(sum)
# 调用函数
calcSum(1, 100)
calcSum(300, 400)
calcSum(1, 1000)
可以明显看到,重复的代码已经被消除了。
6.2 函数的定义
(1)基础语法
python
# 函数定义
def 函数名(形参列表):
函数体代码
return 返回值
# 函数调用
函数名(实参列表)
-
def:定义函数的关键字,固定写法 -
函数名:和变量名遵循相同的命名规则,见名知意,推荐动词 + 名词格式(如
calc_sum、get_user_info) -
形参列表:函数的输入参数,可无,可多个
-
return:函数的返回值,执行到 return 时,函数立即结束,将结果返回给调用者;无 return 时,默认返回None -
函数必须先定义,后调用,仅定义不调用,函数体代码不会执行
python
# 还没有执行到定义,就先执行调用了,此时就会报错
test3()
def test3():
print('hello')

动漫里释放技能之前,需要大喊招式的名字,就是"先定义,再使用"!!!!

(2)代码示例
数列求和函数
python
# 定义函数:计算[beg, end]区间内所有整数的和
def calc_sum(beg, end):
"""
计算区间内整数的和
:param beg: 起始值(包含)
:param end: 结束值(包含)
:return: 区间内整数的和
"""
sum_result = 0
for i in range(beg, end + 1):
sum_result += i
return sum_result
# 调用函数,重复使用逻辑
print(calc_sum(1, 100)) # 计算1-100的和,输出:5050
print(calc_sum(300, 400)) # 计算300-400的和,输出:35350
print(calc_sum(1, 1000)) # 计算1-1000的和,输出:500500
6.2 函数的特性
6.2.1 函数参数
-
形参与实参:形参是函数定义时括号内的参数,相当于 "占位符";实参是函数调用时传递的具体值,会赋值给对应的形参,两者数量必须匹配。
-
动态类型参数:Python 函数的形参无需指定类型,一个函数可支持多种类型的实参,符合动态类型特性。
-
参数默认值:可以给形参指定默认值,调用时如果不传该参数,自动使用默认值;带有默认值的参数必须放在无默认值参数的后面。
python# 参数默认值示例 def add(x, y, debug=False): if debug: print(f'调试信息:x={x}, y={y}') return x + y print(add(10, 20)) # 不传递debug参数,使用默认值False,输出:30 print(add(10, 20, True)) # 传递debug=True,打印调试信息,输出:30 -
关键字参数:调用函数时,可通过形参名=实参的方式显式指定参数传递给哪个形参,无需严格按照形参顺序传参,提升代码可读性。
python# 关键字参数示例 def test(x, y): print(f'x={x}, y={y}') test(10, 20) # 位置传参,输出:x=10, y=20 test(y=100, x=200) # 关键字传参,调整顺序,输出:x=200, y=100
在函数定义的时候,可以在 ( ) 中指定"形式参数"(简称 形参),然后在调用的时候,由调用者把"实际参数"(简称 实参)传递进去。
注意:
-
一个函数可以有一个形参,也可以有多个形参,也可以没有形参。
-
一个函数的形参有几个,那么传递实参的时候也得传几个。保证个数要匹配。
python
def test(a, b, c):
print(a, b, c)
test(10) # 这里会报错,因为只传了1个实参,但函数需要3个

- 和 C++ / Java 不同,Python 是动态类型的编程语言,函数的形参不必指定参数类型。换句话说,一个函数可以支持多种不同类型的参数。
python
def test(a):
print(a)
test(10)
test('hello')
test(True)

6.2.2 函数返回值
函数的参数可以视为是函数的**"输入**",则函数的返回值,就可以视为是函数的"输出"。
此处的"输入"、"输出"是更广义的输入输出,不是单纯指通过控制台输入输出。
我们可以把函数想象成一个"工厂"。工厂需要买入原材料,进行加工,并生产出产品。函数的参数就是原材料,函数的返回值就是生产出的产品。
下列代码:
python
def calcSum(beg, end):
sum = 0
for i in range(beg, end + 1):
sum += i
print(sum)
calcSum(1, 100)
可以转换成:
python
def calcSum(beg, end):
sum = 0
for i in range(beg, end + 1):
sum += i
return sum
result = calcSum(1, 100)
print(result)
这两个代码的区别就在于,前者直接在函数内部进行了打印,后者则使用 return 语句把结果返回给函数调用者,再由调用者负责打印。
我们一般倾向于第二种写法。
实际开发中我们的一个通常的编程原则,是"逻辑和用户交互分离"。而第一种写法的函数中,既包含了计算逻辑,又包含了和用户交互(打印到控制台上)。这种写法是不太好的,如果后续我们需要的是把计算结果保存到文件中,或者通过网络发送,或者展示到图形化界面里,那么第一种写法的函数,就难以胜任了。
而第二种写法则专注于做计算逻辑,不负责和用户交互。那么就很容易把这个逻辑搭配不同的用户交互代码,来实现不同的效果。
-
一个函数可以有多个 return 语句,执行到任意一个 return 都会立即结束函数
-
一个函数可以一次返回多个值,用逗号分隔,调用时用对应数量的变量接收
-
不需要返回值时,可省略 return,函数默认返回 None
python
# 多个返回值示例
def get_point():
x = 10
y = 20
return x, y # 返回两个值
# 接收多个返回值
a, b = get_point()
print(a, b) # 输出:10 20
# 用_忽略不需要的返回值
_, c = get_point()
print(c) # 输出:20
6.3 变量作用域
变量作用域指的是变量的生效范围,Python 中分为局部变量和全局变量两类:
-
局部变量:在函数内部定义的变量,仅在函数内部生效,函数执行结束后,变量就会被销毁,外部无法访问
-
全局变量:在函数外部定义的变量,在整个 py 文件中都生效,函数内部可以读取全局变量
变量只能在所在的函数内部生效。
在函数 getPoint() 内部定义的 x, y 只是在函数内部生效。一旦出了函数的范围,这两个变量就不再生效了。
python
def getPoint():
x = 10
y = 20
return x, y
getPoint()
print(x, y) # 此处会报错,因为 x 和 y 在函数外部不存在

在不同的作用域中,允许存在同名的变量。
虽然名字相同,实际上是不同的变量。
python
# 局部变量与全局变量
x = 20 # 全局变量
def test():
x = 10 # 局部变量,和全局变量x只是同名,是两个不同的变量
print(f'函数内部x={x}') # 优先使用局部变量,输出:函数内部x=10
test()
print(f'函数外部x={x}') # 全局变量不受影响,输出:函数外部x=20

注意:
-
在函数内部的变量,也称为"局部变量"
-
不在任何函数内部的变量,也称为"全局变量"
(1)扩展:global 关键字
如果需要在函数内部修改全局变量的值 ,必须使用 global关键字声明,否则会被视为创建一个新的局部变量,而非修改全局变量。
如果函数内部尝试访问的变量在局部不存在,就会尝试去全局作用域中查找。
python
x = 20
def test():
print(f'x = {x}')
test() # 可以访问到全局变量 x

如果是想在函数内部修改全局变量的值,需要使用 global 关键字声明。
python
x = 20
def test():
global x
x = 30
test()
print(x) # 输出 30

如果此处没有 global,则函数内部的 x = 10 就会被视为是创建一个局部变量 x,这样就和全局变量 x 不相关了。
if / while / for 等语句块不会影响到变量作用域。
换而言之,在 if / while / for 中定义的变量,在语句外面也可以正常使用。
python
if True:
a = 10
print(a) # 输出 10,可以正常访问
for i in range(5):
b = i
print(b) # 输出 4,可以正常访问
6.4 函数执行过程
-
调用函数才会执行函数体代码,不调用则不会执行。
-
函数体执行结束(或者遇到 return 语句),则回到函数调用位置,继续往下执行。
python
def test():
print("执行函数内部代码")
print("执行函数内部代码")
print("执行函数内部代码")
print("1111")
test()
print("2222")
test()
print("3333")

这个过程还可以使用 PyCharm 自带的调试器来观察。
点击行号右侧的空白,可以在代码中插入断点
右键,Debug,可以按照调试模式执行代码。每次执行到断点,程序都会暂停下来
使用 Step Into (F7) 功能可以逐行执行代码
6.5 函数的调用
6.5.1 链式调用
前面的代码很多都是写作:
python
# 判定是否是奇数
def isOdd(num):
if num % 2 == 0:
return False
else:
return True
result = isOdd(10)
print(result)
实际上也可以简化写作:
python
print(isOdd(10))
把一个函数的返回值,作为另一个函数的参数,这种操作称为链式调用。
这是一种比较常见的写法。
6.5.2 嵌套调用
函数内部还可以调用其他的函数,这个动作称为"嵌套调用"。
python
def test():
print("执行函数内部代码")
print("执行函数内部代码")
print("执行函数内部代码")
test 函数内部调用了 print 函数,这里就属于嵌套调用。
一个函数里面可以嵌套调用任意多个函数。
函数嵌套的过程是非常灵活的。
python
def a():
print("函数 a")
def b():
print("函数 b")
a()
def c():
print("函数 c")
b()
def d():
print("函数 d")
c()
d()

如果把代码稍微调整,打印结果则可能发生很大变化。
python
def a():
print("函数 a")
def b():
a()
print("函数 b")
def c():
b()
print("函数 c")
def d():
c()
print("函数 d")
d()

注意体会上述代码的执行顺序,可以通过画图的方式来理解。
函数之间的调用关系,在 Python 中会使用一个特定的数据结构来表示,称为函数调用栈。每次函数调用,都会在调用栈里新增一个元素,称为栈帧。
可以通过 PyCharm 调试器看到函数调用栈和栈帧。
在调试状态下,PyCharm 左下角一般就会显示出函数调用栈。
每个函数的局部变量,都包含在自己的栈帧中。
python
def a():
num1 = 10
print("函数 a")
def b():
num2 = 20
a()
print("函数 b")
def c():
num3 = 30
b()
print("函数 c")
def d():
num4 = 40
c()
print("函数 d")
d()

选择不同的栈帧,就可以看到各自栈帧中的局部变量。
思考:上述代码,a、b、c、d 函数中的局部变量名各不相同。如果变量名是相同的,比如都是 num,那么这四个函数中的 num 是属于同一个变量,还是不同变量呢?
6.5.3 递归调用
递归是嵌套调用的特殊情况,即函数内部调用自身,递归代码必须满足两个核心条件:
-
存在明确的递归结束条件,否则会出现无限递归,导致栈溢出
-
每次递归调用,实参必须逐渐逼近结束条件
python
# 递归实现:计算n的阶乘
def factor(n):
# 递归结束条件:1的阶乘是1
if n == 1:
return 1
# 递推公式:n! = n * (n-1)!
return n * factor(n - 1)
print(factor(5)) # 计算5!,输出:120
上述代码中,就属于典型的递归操作。在 factor 函数内部,又调用了 factor 自身。
**注意:**递归代码务必要保证
-
存在递归结束条件。比如 if n == 1 就是结束条件。当 n 为 1 的时候,递归就结束了。
-
每次递归的时候,要保证函数的实参是逐渐逼近结束条件的。
如果上述条件不能满足,就会出现"++无限递归++"。这是一种典型的代码错误。
python
def factor(n):
return n * factor(n - 1)
result = factor(5)
print(result)

如前面所描述,函数调用时会在函数调用栈中记录每一层函数调用的信息。但是函数调用栈的空间不是无限大的。如果调用层数太多,就会超出栈的最大范围,导致出现问题。
递归的优缺点:
-
优点:代码简洁,符合数学归纳法逻辑,能轻松解决树、分治等复杂问题
-
缺点:理解门槛高,容易出现栈溢出,执行效率低于等价的循环代码,大型项目需谨慎使用
七、列表、元组与字典
基础数据类型只能存储单个值,当我们需要批量存储、处理大量数据时,就需要用到复合数据类型,Python 中最常用的就是++列表(list) 、元组(tuple) 、字典(dict)++三类。
7.1 列表是什么,元组是什么
编程中,经常需要使用变量来保存或表示数据。
如果代码中需要表示的数据个数比较少,可以直接创建多个变量:
python
num1 = 10
num2 = 20
num3 = 30
......
但是,当代码中需要表示的数据特别多,甚至也不知道要表示多少个数据时,就需要用到列表。
列表是一种让程序员在代码中批量表示或保存数据的方式。
就像我们去超市买辣条,如果只买一两根,直接拿着就行;但如果一次买十根八根,用手拿不方便,超市老板就会给一个袋子。这个袋子,就相当于列表。
元组和列表非常相似,区别在于:列表中的元素可以修改调整,而元组中的元素在创建时就设定好了,不能修改调整。
"列表"就像买散装辣条,装好袋子后,随时可以打开往里加辣条或拿出去一些。
"元组"就像买包装辣条,厂家生产好后一包就是固定的量,不能变动。
7.2 列表(list)
7.2.1 列表的创建与访问下标
(1) 创建列表
-
创建列表主要有两种方式。[ ] 表示一个空的列表。
-
如果需要往里面设置初始值,可以直接写在 [ ] 当中。
可以直接使用 print 来打印 list 中的元素内容。
- 列表中存放的元素允许是不同的类型(这一点和 C++/Java 差别较大)。
因为 list 本身是 Python 中的内建函数,不宜再使用 list 作为变量名,因此命名为 alist。
python
# 创建列表
# 创建空列表
alist = []
alist = list()
# 创建带初始值的列表,支持任意类型元素
alist = [1, 2, 3, 4]
alist = [1, 'hello', True, 3.14]
print(alist) # 输出:[1, 'hello', True, 3.14]
(2) 访问下标
- 可以通过下标访问操作符 [ ] 来获取到列表中的任意元素。
我们把 [ ] 中填写的数字,称为下标或者索引。
python
alist = [1, 2, 3, 4]
print(alist[2])

**注意:**下标是从 0 开始计数的,因此下标为 2,则对应着 3 这个元素。
-
通过下标不光能读取元素内容,还能修改元素的值。
pythonalist = [1, 2, 3, 4] alist[2] = 100 print(alist) -
如果下标超出列表的有效范围,会抛出异常。
pythonalist = [1, 2, 3, 4] print(alist[100])
-
因为下标是从 0 开始的,因此下标的有效范围是 [0, 列表长度 - 1] 。使用 len 函数可以获取到列表的元素个数。
pythonalist = [1, 2, 3, 4] print(len(alist)) -
下标可以取负数,表示"++倒数第几个元素++"。
pythonalist = [1, 2, 3, 4] print(alist[3]) print(alist[-1])
alist[-1] 相当于 alist[len(alist) - 1] 。
7.2.2 切片操作
切片用于一次获取列表中的一组连续元素,生成一个子列表,语法为 列表[起始下标:结束下标:步长] ,核心规则:
-
区间是前闭后开:包含起始下标元素,不包含结束下标元素
-
起始下标、结束下标可省略:省略起始下标从 0 开始,省略结束下标到列表末尾
-
步长:每次访问元素后,下标增加的数值,默认为 1,负数表示从后往前取
python
# 切片操作示例
alist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 基础切片:[1:3],取下标1、2的元素
print(alist[1:3]) # 输出:[2, 3]
# 省略边界
print(alist[1:]) # 省略结束下标,取到末尾,输出:[2, 3, 4, 5, 6, 7, 8, 9, 10]
print(alist[:-1]) # 省略起始下标,从开头取到倒数第二个元素,输出:[1, 2, 3, 4, 5, 6, 7, 8, 9]
print(alist[:]) # 省略两个边界,复制整个列表,输出:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 指定步长
print(alist[::2]) # 步长2,取奇数位元素,输出:[1, 3, 5, 7, 9]
print(alist[::-1]) # 步长-1,列表倒序,输出:[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
7.2.3 常用操作
| 操作类型 | 方法 / 语法 | 功能说明 |
|---|---|---|
| 新增元素 | append(value) |
在列表末尾添加一个元素 |
| 新增元素 | insert(index, value) |
在指定下标位置插入元素 |
| 删除元素 | pop() |
删除列表末尾的元素,返回被删除的元素 |
| 删除元素 | pop(index) |
删除指定下标的元素,返回被删除的元素 |
| 删除元素 | remove(value) |
删除第一个匹配 value 的元素 |
| 查找元素 | value in alist |
判断 value 是否在列表中,返回布尔值 |
| 查找元素 | index(value) |
查找 value 第一次出现的下标,不存在则抛异常 |
| 遍历元素 | for elem in alist |
直接遍历列表中的每个元素 |
| 遍历元素 | for i in range(len(alist)) |
通过下标遍历列表 |
| 列表拼接 | alist + blist |
拼接两个列表,生成新列表,不修改原列表 |
| 列表拼接 | extend(blist) |
将 blist 的元素拼接到 alist 末尾,修改 alist |
7.3 元组(tuple)
元组和列表功能几乎一致,核心区别是:元组是不可变类型,创建完成后,无法修改里面的元素、新增 / 删除元素,相当于 "只读列表"。
(1) 基础用法
元组使用 ( ) 来表示。
python
# 1. 创建元组
atuple = ()
atuple = tuple()
atuple = (1, 2, 3, 4)
atuple = (1, 'hello', True)
print(atuple) # 输出:(1, 'hello', True)
# 2. 支持的操作(只读类)
print(atuple[0]) # 下标访问,输出:1
print(atuple[1:3]) # 切片,输出:('hello', True)
print(len(atuple)) # 获取长度,输出:3
print(1 in atuple) # 判断元素是否存在,输出:True
# 3. 不支持的操作(修改类)
# atuple[0] = 100 # 报错,元组不支持修改元素
# atuple.append(200) # 报错,元组不支持新增元素
(2) 使用场景
-
数据不希望被修改时,使用元组保证数据安全,比如函数返回多个值时,默认返回的就是元组
-
元组是可哈希类型,可以作为字典的 key,而列表不行
-
元组的内存占用更小,访问效率更高,只读场景下优先使用元组
(3) 疑问
既然已经有了列表,为啥还需要有元组?
先对比一下:
| 特性 | 列表(list) | 元组(tuple) |
|---|---|---|
| 可变性 | 可变,支持增删改查 | 不可变,创建后无法修改 |
| 表示符号 | [] |
() |
| 支持的操作 | 下标、切片、遍历、增删改查、拼接 | 下标、切片、遍历、拼接(生成新元组) |
| 可哈希性 | 不可哈希,不能作为字典的 key | 可哈希,能作为字典的 key |
| 性能 | 内存占用略高,修改效率高 | 内存占用小,访问效率更高 |
| 适用场景 | 数据需要动态变化的场景 | 数据固定、不希望被修改的场景 |
元组相比于列表来说,优势有两方面:
-
你有一个列表,现在需要调用一个函数进行一些处理。但是你又不太确认这个函数是否会把你的列表数据弄乱。那么这时候传一个元组就安全很多。
-
我们马上要讲的字典,是一个键值对结构。要求字典的键必须是"可 hash 对象"(字典本质上也是一个 hash 表)。而一个可 hash 对象的前提就是不可变。因此元组可以作为字典的键,但是列表不行。
7.4 字典(dict)
7.4.1 字典是什么
字典是一种存储键值对的结构。
++啥是键值对?++这是计算机/生活中一个非常广泛使用的概念。把键(key)和值(value)进行一个一对一的映射,然后就可以根据键,快速找到值。
**举个栗子~~**学校的每个同学,都会有一个唯一的学号。知道了学号,就能确定这个同学。此处"学号"就是"键",这个"同学"就是"值"。
7.4.2 字典的创建与常用操作
(1) 创建字典
- 创建一个空的字典,使用 { } 表示字典。
python
a = { }
b = dict()
print(type(a))
print(type(b))
-
也可以在创建的同时指定初始值。
-
键值对之间使用 `,` 分割,键和值之间使用 `:` 分割(冒号后面推荐加一个空格)。
-
使用 print 来打印字典内容。
pythonstudent = { 'id': 1, 'name': 'zhangsan' } print(student)
-
为了代码更规范美观,在创建字典的时候往往会把多个键值对分成多行来书写。
pythonstudent = { 'id': 1, 'name': 'zhangsan' } -
最后一个键值对,后面可以写 `,` 也可以不写。
pythonstudent = { 'id': 1, 'name': 'zhangsan', }
(2) 常用操作
| 操作类型 | 方法 / 语法 | 功能说明 |
|---|---|---|
| 查找 key | key in dict |
判断 key 是否在字典中,返回布尔值,时间复杂度 O (1) |
| 遍历字典 | for key in dict |
遍历字典的所有 key,通过 key 获取 value |
| 获取所有 key | keys() |
返回字典中所有的 key,类型为 dict_keys |
| 获取所有 value | values() |
返回字典中所有的 value,类型为 dict_values |
| 获取所有键值对 | items() |
返回字典中所有的 (key, value) 元组,类型为 dict_items |
python
# 字典遍历与常用操作
student = {
'id': 1,
'name': '张三',
'age': 20,
'score': 90.5
}
# 1. 遍历字典
for key in student:
print(f'{key}: {student[key]}')
# 2. 遍历键值对
for key, value in student.items():
print(f'{key}: {value}')
# 3. 获取所有key和value
print(student.keys()) # 输出:dict_keys(['id', 'name', 'age', 'score'])
print(student.values()) # 输出:dict_values([1, '张三', 20, 90.5])
7.4.3 合法的 key 类型
字典的 key 必须是可哈希类型(不可变类型),value 可以是任意类型。
-
合法的 key 类型:int、float、str、bool、元组(元素均为不可变类型)
-
非法的 key 类型:列表、字典(可变类型,不可哈希,无法计算哈希值)
python
# 合法的key
dict1 = {
1: '整数key',
'name': '字符串key',
(1,2,3): '元组key'
}
# 非法的key,会报错
# dict2 = {
# [1,2,3]: '列表key' # TypeError: unhashable type: 'list'
# }
八、文件
变量中的数据存储在内存中,程序重启或电脑关机后,数据就会丢失。如果需要长期保存数据,就需要将数据写入到硬盘的文件中,实现数据持久化。
在 Windows"此电脑"中,看到的内容都是文件。

通过文件的后缀名,可以看到文件的类型。常见的文件类型如下:
- 文本文件(txt)
- 可执行文件(exe、dll)
- 图片文件(jpg、gif)
- 视频文件(mp4、mov)
- Office 文件(.ppt、.docx)
- ......
8.1 文件路径
一个机器上会存在很多文件,为了让这些文件更方便地被组织,往往会使用很多的"文件夹"(也叫做目录)来整理文件。
实际一个文件往往是放在一系列的目录结构之中的。
为了方便确定一个文件所在的位置,使用文件路径来进行描述。
例如,QQ.exe 这个文件,描述它的位置就可以使用路径 D:\program\qq\Bin\QQ.exe 来表示:
-
D : 表示盘符,不区分大小写
-
每一个 \ 表示一级目录。当前 QQ.exe 就是放在"++D 盘下的 program 目录下的 qq 目录下的 Bin 目录中++"
-
目录之间的分隔符,可以使用 \ 也可以使用 / 。一般在编写代码的时候使用 / 更方便。
上述以盘符开头的路径,我们也称为绝对路径。
除了绝对路径之外,还有一种常见的表示方式是相对路径。相对路径需要先指定一个基准目录,然后以基准目录为参照点,间接地找到目标文件。咱们课堂上暂时不详细介绍。
描述一个文件的位置,使用绝对路径和相对路径都是可以的。对于新手来说,使用绝对路径更简单、更好理解,也不容易出错。
8.2 文件操作
文件操作的核心分为 3 步:打开文件 → 读写文件 → 关闭文件 ,Python 通过内置函数 open() 完成文件操作。
8.2.1 open 函数
python
open(file, mode, encoding=None)
-
file:必填,文件的路径(绝对路径 / 相对路径) -
mode:必填,文件的打开模式,常用模式如下表 -
encoding:可选,指定文件的编码格式,读取中文文件时,通常指定为utf-8,避免乱码
| 打开模式 | 含义 | 核心说明 |
|---|---|---|
r |
只读模式 | 默认模式,文件必须存在,不存在则抛异常,只能读取,不能写入 |
w |
只写模式 | 文件不存在则创建,文件存在则清空原有内容,只能写入,不能读取 |
a |
追加写模式 | 文件不存在则创建,文件存在则在末尾追加内容,不会清空原有内容 |
rb |
二进制只读模式 | 用于读取图片、视频、音频等二进制文件 |
wb |
二进制只写模式 | 用于写入二进制文件 |
8.2.2 打开文件
使用内建函数 open 打开一个文件。
python
open(file, mode, encoding=None)
-
第一个参数是一个字符串,表示要打开的文件路径
-
第二个参数是一个字符串,表示打开方式。其中 `r` 表示按照读方式打开,w 表示按照写方式打开,a 表示追加写方式打开
-
如果打开文件成功,返回一个文件对象。后续的读写文件操作都是围绕这个文件对象展开。
-
如果打开文件失败(比如路径指定的文件不存在),就会抛出异常。

8.2.3 关闭文件
文件操作完成后必须调用close() 关闭文件,否则会出现文件句柄泄漏,导致系统能打开的文件数量达到上限。Python 提供了 with 上下文管理器,会在代码块执行完毕后自动关闭文件,无需手动调用 close(),是工业级开发的首选写法。
一个程序能同时打开的文件个数,是存在上限的。
python
flist = []
count = 0
while True:
f = open('d:/test.txt', 'r')
flist.append(f)
count += 1
print(f'count = {count}')

如上面代码所示,如果一直循环地打开文件,而不去关闭的话,就会出现上述报错。当一个程序打开的文件个数超过上限,就会抛出异常。
**注意:**上述代码中,使用一个列表来保存了所有的文件对象。如果不进行保存,那么 Python 内置的垃圾回收机制,会在文件对象销毁的时候自动关闭文件。
但是由于垃圾回收操作不一定及时,所以我们写代码仍然要考虑手动关闭,尽量避免依赖自动关闭。
8.2.4 写文件
文件打开之后,就可以写文件了。
写文件要使用写方式打开,open 第二个参数设为 'w' 。
- 使用 write 方法写入文件
python
f = open('d:/test.txt', 'w')
f.write('hello')
f.close()

用记事本打开文件,即可看到文件修改后的内容。
-
如果是使用 'r' 方式打开文件,则写入时会抛出异常。
pythonf = open('d:/test.txt', 'r') f.write('hello') f.close()
-
使用 'w' 一旦打开文件成功,就会清空文件原有的数据。
-
使用 'a' 实现"追加写",此时原有内容不变,写入的内容会存在于之前文件内容的末尾。
pythonf = open('d:/test.txt', 'w') f.write('hello') f.close() f = open('d:/test.txt', 'a') f.write('world') f.close()
-
针对已经关闭的文件对象进行写操作,也会抛出异常。
python
f = open('d:/test.txt', 'w')
f.write('hello')
f.close()
f.write('world')

8.2.5 读文件
-
读文件内容需要使用 'r' 的方式打开文件
-
使用 read 方法完成读操作。参数表示"读取几个字符"。
pythonf = open('d:/test.txt', 'r') result = f.read(2) print(result) f.close()
-
如果文件是多行文本,可以使用 for 循环一次读取一行。

先构造一个多行文件。
python
f = open('d:/test.txt', 'r')
for line in f:
print(f'line = {line}')
f.close()

注意:由于文件里每一行末尾都自带换行符,print 打印一行的时候又会默认加上一个换行符,因此打印结果看起来之间存在空行。
使用 print(f'line = {line}', end='') 手动把 print 自带的换行符去掉。
- 使用 readlines 直接把文件整个内容读取出来,返回一个列表,每个元素即为一行。
python
f = open('d:/test.txt', 'r')
lines = f.readlines()
print(lines)
f.close()

此处的 \n 即为换行符。
8.3 中文乱码解决方法
(1) 中文处理
Python 读取中文文件出现乱码,核心原因是文件本身的编码格式和代码中指定的编码格式不匹配。
当文件内容存在中文的时候,读取文件内容不一定顺利。同样上述代码,有的同学执行时可能会出现异常

也有的同学可能出现乱码。

计算机表示中文的时候,会采取一定的编码方式,我们称为"字符集"。
**注意:**所谓"编码方式",本质上就是使用数字表示汉字。
我们知道,计算机只能表示二进制数据。要想表示英文字母、汉字或其他文字符号,都要通过编码。
最简单的字符编码就是 ASCII,使用一个简单的整数就可以表示英文字母和阿拉伯数字。
但是要想表示汉字,就需要一个更大的码表。
一般常用的汉字编码方式,主要是 GBK 和 UTF-8。
必须要保证文件本身的编码方式,和 Python 代码中读取文件使用的编码方式匹配,才能避免出现上述问题。
Python 3 中默认打开文件的字符集跟随系统,而 Windows 简体中文版的字符集采用了 GBK,所以如果文件本身是 GBK 的编码,直接就能正确处理。如果文件本身是其他编码(比如 UTF-8),那么直接打开就可能出现问题。
使用记事本打开文本文件,在++"菜单栏" -> "文件" -> "另存为"窗口++中,可以看到当前文件的编码方式。

-
如果此处的编码为 ANSI,则表示 GBK 编码
-
如果此处为 UTF-8,则表示 UTF-8 编码
此时修改打开文件的代码,给 open 方法加上 encoding 参数,显式地指定为和文本相同的字符集,问题即可解决。
python
f = open('d:/test.txt', 'r', encoding='utf8')
**PS:**字符编码问题是编程中一类比较常见又比较棘手的问题,需要对字符编码有一定的理解才能从容应对。同学们可以参考腾讯官方账号发表的帖子,详细介绍了里面的细节:程序员必备:彻底弄懂常见的7种中文字符编码 - 知乎
(2) 使用上下文管理器
打开文件之后,是容易忘记关闭的。Python 提供了上下文管理器,来帮助程序员自动关闭文件。
-
使用 with 语句打开文件
-
当 with 内部的代码块执行完毕后,就会自动调用关闭方法
python
with open('d:/test.txt', 'r', encoding='utf8') as f:
lines = f.readlines()
print(lines)
九、总结常见错误与解决方案
9.1 语法类错误
- 缩进错误:Tab 和空格混合缩进、缩进层级不对,会报 IndentationError
解决方案:统一使用 4 个空格作为一级缩进,开启 IDE 的 "将 Tab 转换为空格" 功能
- 语法格式错误:if/for/while/def 语句末尾忘记加冒号
:,括号不匹配
解决方案:写完一行代码后检查冒号,IDE 会自动标红语法错误,及时修正
- 关键字作为变量名:使用 if、for、def、list、dict 等关键字 / 内置函数作为变量名,导致后续调用报错
解决方案:禁止使用 Python 关键字和内置函数作为变量名
9.2 数据类型类错误
- input 输入的类型转换错误:input 返回的是字符串,直接进行算术运算,导致类型错误
解决方案:输入数值时,先通过 int()/float() 转换类型,再进行运算
- 字符串与数字直接拼接:print('年龄:' + 20),会报 TypeError
解决方案:使用 f-string 格式化,或先将数字转为字符串 str(20)
- 浮点数直接用 == 判断相等:0.1 + 0.2 == 0.3
结果为 False
解决方案:判断两个浮点数的差值在允许的误差范围内,而非直接相等
9.3 逻辑类错误
- while 循环忘记更新循环变量:导致死循环
解决方案: while 循环必须在循环体内更新循环变量,确保循环能结束
- if 条件判断用 = 代替 ==:if num = 10:,会报语法错误
解决方案:相等判断必须用双等号 ==,赋值用单等号 =,区分两者用途
- 列表 / 字典遍历过程中修改元素:导致遍历异常、元素遗漏
解决方案:遍历过程中不要删除元素,可先复制一份列表,再遍历原列表、修改复制后的列表
9.4 文件操作类错误
- 文件路径错误:文件不存在,报 FileNotFoundError
解决方案:使用绝对路径,确保路径书写正确,斜杠使用 / 而非 \ (避免转义)
- 中文乱码:读取中文文件出现乱码
解决方案:打开文件时显式指定 encoding='utf-8',匹配文件的实际编码
- 文件忘记关闭:导致文件句柄泄漏
解决方案:统一使用 with 上下文管理器打开文件,自动关闭,无需手动处理
十、总结
本文系统梳理了Python基础语法的核心内容:
-
基础语法:常量、变量、数据类型、输入输出
-
运算符:算术、关系、逻辑、赋值运算符的使用与注意事项
-
条件语句 :
if-elif-else结构及Python特有的缩进语法 -
循环语句 :
while、for循环及continue/break的使用 -
函数:定义、调用、参数传递、返回值、作用域、递归等核心概念
-
列表与元组:序列数据的创建、访问、修改与遍历
-
字典:键值对结构的使用及哈希表的底层原理
-
文件操作:文件的打开、读写、关闭及编码处理
Python是一门"易学难精"的语言。掌握这些基础语法只是第一步,后续还需要不断练习,在实战中深入理解。建议读者在学习过程中多动手写代码,用实践检验知识,才能真正将Python内化为自己的技能。
参考资料
1\] Python 3.10+ 官方文档:[https://docs.python.org/3/](https://docs.python.org/3/ "https://docs.python.org/3/") \[2\] PEP 8 -- Style Guide for Python Code:[https://peps.python.org/pep-0008/](https://peps.python.org/pep-0008/ "https://peps.python.org/pep-0008/") \[3\] 比特就业课 Python 基础语法体系 \[4\] TIOBE 编程语言排行榜 2026 年 4 月榜单:[https://www.tiobe.com/tiobe-index/](https://www.tiobe.com/tiobe-index/ "https://www.tiobe.com/tiobe-index/") \[5\][\*.py:以二进制模式打开文件,必要时显式解码为UTF-8(a5349f5d)·提交次数·基里尔·斯梅尔科夫 / pygolang ·GitLabhttps://lab.nexedi.com/kirr/pygolang/-/commit/a5349f5d7e53b877db3ab4f2da7c5787e5d460d1](https://lab.nexedi.com/kirr/pygolang/-/commit/a5349f5d7e53b877db3ab4f2da7c5787e5d460d1 "*.py:以二进制模式打开文件,必要时显式解码为UTF-8(a5349f5d)·提交次数·基里尔·斯梅尔科夫 / pygolang ·GitLab") \[6\][Python中元组和列表差异:底层结构分析-CSDN博客](https://blog.csdn.net/2502_92691558/article/details/159931135 "Python中元组和列表差异:底层结构分析-CSDN博客") \[7
Python中列表List和元组Tuple的区别_聚合数据 - 天聚地合
https://www.juhe.cn/news/index/id/10101
8
Python函数注解怎么写_类型提示实战解析【教程】-Python教程-PHP中文网
https://www.php.cn/faq/1884574.html
9\][Python数据结构的性能比较 -- Multiable 万达宝](https://blog.multiable.com.cn/wz_95104.htm "Python数据结构的性能比较 – Multiable 万达宝") \[10\][解包 Python 的泛泛函数签名参数参数(ParamSpec.kwargs)https://runebook.dev/en/docs/python/library/typing/typing.ParamSpec.kwargs](https://runebook.dev/en/docs/python/library/typing/typing.ParamSpec.kwargs "解包 Python 的泛泛函数签名参数参数(ParamSpec.kwargs)") \[11
快速学会使用Python3.12的新特性
https://www.e-com-net.com/article/1727604706739564544.htm
结尾留言
本篇文章3w+字数,耗时近一周,每天都在写作,免不了排版失误,缺失注解和理论错误,欢迎指正!最近sbti很火,俺也去测了测,以下是作者滴,仅供娱乐,欢迎读者们分享(●'◡'●)。


如果本文对你有帮助,欢迎点赞、收藏、评论交流~如果你在 Python 学习过程中有任何问题,都可以在评论区留言,我会一一解答。

