环境搭建
一、安装 Python
两种主流方式,选一个即可。
方式一:官网安装包
到 https://www.python.org/downloads/ 下载 Windows 安装包,双击安装。
关键一步:安装第一屏记得勾选 Add Python to PATH——不勾选的话,之后在命令行里敲 python 会提示”不是内部或外部命令”。
方式二:Anaconda
Anaconda 是”全家桶”发行版,内置了 Python 解释器 + conda 环境管理 + numpy / pandas / matplotlib 等常用库
1 | Anaconda 自带: |
到 https://www.anaconda.com/download 下载安装即可。
无论哪种方式,装完都是标准的 Python,不影响学习。
二、验证安装
安装完成后,打开命令提示符(Win+R 输入 cmd)执行:
1 | python --version # 输出 Python 3.x.x 说明安装成功 |
pip 是 Python 的包管理工具,以后装第三方库全靠它(比如 pip install requests)。
三、编辑器:VS Code
选一个顺手的编辑器写代码,我使用的是 VS Code:
- 官网 https://code.visualstudio.com 下载安装
- 打开 VS Code,安装 Python 扩展(微软官方那个)
Ctrl+Shift+P→ 输入Python: Select Interpreter→ 选择刚装的 Python 解释器
装好后新建 test.py,写一行:
1 | print("Hello, Python!") |
点右上角 ▶ 运行,输出 Hello, Python! 就大功告成。
四、运行 Python 的三种方式
1. 交互式解释器(随手试验)
命令行直接输入 python 回车,进入 >>> 提示符模式,适合随手验证:
1 | >>> print("Hello") |
2. 命令行运行脚本
把代码写进 .py 文件,用 python 文件名.py 运行:
1 | python test.py |
3. IDE 运行
VS Code / PyCharm 里点”运行”按钮,等价于执行上面那条命令。
五、常见坑
坑 1:python 不是内部或外部命令
原因:安装时没勾选 Add to PATH,环境变量没配好。
解决:重装时勾选;或者手动把 Python 安装路径和 Scripts 文件夹加进系统环境变量的 Path。
坑 2:Windows 商店版是”假 python”
Windows 应用商店里的”Python”是一个占位符——命令行敲 python 时它不会运行真正的 Python,而是静默失败(什么都不输出):
1 | $ python --version |
根治方法:开始菜单搜”应用执行别名”→ 关掉 python.exe 和 python3.exe 两个开关 → 重新打开 cmd,python 命令就会命中真正的解释器。
备选:在 VS Code 里用 Python: Select Interpreter 选 D:\anaconda\python.exe,编辑器里运行代码不受影响。
坑 3:pip 下载第三方库很慢
pip 默认从国外源下载,换成国内镜像(清华源):
1 | pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple |
基本语法
一、编码
Python3 源码默认 UTF-8 编码,所有字符串都是 Unicode 字符串——中文随便写,不用像 C 那样折腾编码问题。
1 | print("你好,世界") # 直接输出中文,没问题 |
极少数情况下需要指定编码,写在文件第一行或第二行:
1 | # -*- coding: cp-1252 -*- |
平时完全用不到,知道有这回事即可。
二、标识符与关键字
标识符(变量名、函数名、类名)的规则:
- 只能由字母、数字、下划线组成,不能以数字开头
- 大小写敏感:
count和Count是两个不同的变量 - 不能是关键字(
if、for、class等)
1 | age = 25 # ✅ 普通变量 |
Python 的常用关键字(不能拿来当变量名):
| 类别 | 关键字 |
|---|---|
| 逻辑值 | True False None |
| 逻辑运算 | and or not |
| 条件控制 | if elif else |
| 循环控制 | for while break continue |
| 函数 | def return lambda |
| 类 | class |
| 模块 | import from as |
| 异常处理 | try except finally raise |
| 其他 | pass global nonlocal with yield |
如果想查看全部关键字,运行下面两行代码
1 | import keyword |
三、注释
单行注释用 #,多行注释用三个引号 ''' 或 """:
1 | # 这是单行注释 |
四、行与缩进
C 用 {} 表示代码块,Python 用缩进表示代码块:
1 | // C 语言 |
1 | # Python |
注意两个细节:
if语句末尾要写冒号:- 缩进推荐统一用 4 个空格(VS Code 装了 Python 扩展后会自动处理,按 Tab 会自动转成 4 空格)
同一个代码块内的缩进必须一致,否则直接报错:
1 | if True: |
1 | File "test.py", line 6 |
多个语句组成一个代码块(代码组),首行以关键字开始、以冒号结束,这就是 Python 版的”大括号”。
五、多行语句
一行写不下时,用反斜杠 \ 换行:
1 | total = item_one + \ |
在 ()、[]、{} 内部可以直接换行,不需要反斜杠:
1 | total = ['item_one', 'item_two', |
六、数字类型
Python 的数字有四种类型:
| 类型 | 说明 | 示例 |
|---|---|---|
int |
整数,只有一种(没有 C 的 long/short 之分) | 1 |
bool |
布尔值 | True / False |
float |
浮点数 | 1.23、3E-2 |
complex |
复数 | 1 + 2j |
无需声明类型,直接赋值即可(动态类型),数值运算和 C 基本一致,详见下一部分的”数字”小节。
七、字符串
几个要点:
- 单引号
'和双引号"完全一样,随意用 - 三引号
'''或"""表示多行字符串 \是转义符;字符串前加r表示原始字符串,\n不转义+拼接、*重复- 索引从
0开始,负数从右往左数;切片str[start:end:step]含头不含尾
1 | str = '123456789' |
转义符对比:
1 | print('hello\nrunoob') # 输出两行:hello 换行 runoob |
7.1 索引:取单个字符
用下标取字符,和 C 数组一样从 0 开始;但 Python 多了一个负索引,从右边数:
1 | 字符串: 1 2 3 4 5 6 7 8 9 |
1 | str = '123456789' |
规律:
str[-n]等价于str[len(str) - n]。比如str[-1]就是str[8]。所以取最后一个字符永远写str[-1],不用先数字符串长度。
7.2 切片:取一段字符
切片语法 str[start:end:step],三个参数都可省略,核心规则只有一条:含头不含尾。
start:从哪个下标开始(含)end:到哪个下标结束(不含)step:步长,隔几个取一个,默认 1
1 | str = '123456789' |
“含头不含尾”是最容易搞混的点,图解 str[2:5] 到底取哪几个:
1 | 字符串: 1 2 3 4 5 6 7 8 9 |
补充一个切片技巧——倒序切片(step 为负数时从右往左取):
1 | print(str[::-1]) # 987654321 一行反转整个字符串 |
切片是 Python 的技能,C 里要写循环才能干的事,Python 不需要
八、等待用户输入 input()
input() 让程序停下来等你输入,回车后继续执行:
1 | input("\n\n按下 enter 键后退出。") |
1 | name = input("请输入你的名字:") |
九、同一行写多条语句(不推荐)
Python 允许用分号 ; 分隔多条语句,但强烈不推荐,可读性差:
1 | import sys; x = 'runoob'; sys.stdout.write(x + '\n') |
记住:Python 里换行就是语句结束,分号是可选的——这和 C 恰好相反,C 必须分号结尾。
十、print 输出
print 默认换行输出,不想换行就加 end="":
1 | x = "a" |
输出结果:
1 | a |
十一、import 导入模块
三种导入方式:
1 | import math # 导入整个模块,用 math.sqrt(4) 调用 |
十二、和 C 的区别速查表
| C | Python | |
|---|---|---|
| 代码块 | {} 大括号 |
缩进 |
| 语句结束 | 分号 ; |
换行 |
| 变量声明 | int a; 先声明 |
直接 a = 1,动态类型 |
| 注释 | // /* */ |
# 和三引号 |
| 字符串 | 字符数组,操作靠函数 | 内置 str,直接切片 |
| 数组 | 固定大小 | 列表自动扩容(后面讲) |
小结:这一部分是 Python 的”世界观”——缩进代替大括号、换行代替分号、注释更简单、字符串更强大。基本的程序骨架已经齐全,接下来把数据类型一一展开。
基本数据类型
Python 的核心特点:变量没有类型,类型属于值。不需要像 C 那样先声明 int a,直接赋值就创建了变量,而且同一个变量可以随时指向不同类型的值。
一、变量与赋值
1 | counter = 100 # 整型 |
多变量赋值有两种写法:
1 | a = b = c = 1 # 三个变量指向同一个值 1 |
用 type() 查看变量的类型:
1 | x = 10 |
变量必须先赋值再使用,直接访问未定义的变量会报错:
1 | print(n) # ❌ NameError: name 'n' is not defined |
二、六大标准类型总览
| 类型 | 中文 | 示例 | 是否可变 |
|---|---|---|---|
Number |
数字 | 1、1.5、3+2j |
不可变 |
String |
字符串 | "hello" |
不可变 |
bool |
布尔 | True / False |
不可变 |
List |
列表 | [1, 'a', 2.5] |
可变 |
Tuple |
元组 | (1, 'a') |
不可变 |
Set |
集合 | {1, 2, 3} |
可变 |
Dictionary |
字典 | {'name': 'runoob'} |
可变 |
可变 vs 不可变是 Python 最重要的概念之一:不可变类型一旦创建就不能修改(只能整体替换),可变类型可以原地增删改。C 里普通数组可变、const 数组不可变,大致可以这样类比。
三、Number 数字
三种数值类型
| 类型 | 说明 | 示例 |
|---|---|---|
int |
整数,没有大小限制(C 的 int 会溢出,Python 不会) | 1、-786、0x69 |
float |
浮点数,支持科学计数法 | 1.23、2.5e2 |
complex |
复数,实部虚部都是浮点数 | 1 + 2j、complex(1, 2) |
1 | var1 = 1 # int |
int只有一种,没有 C 的 long/short 之分float对应 C 的 double,科学计数法写作2.5e2complex复数由实部和虚部构成,虚部用j表示(数学里是 i,Python 用 j),也可以用complex(1, 2)创建
数字的更多特性(不可变机制、整数无上限、math/random 模块)见后面”数字”部分。
数值运算
1 | 5 + 4 # 加法 |
注意两个 C 里没有的运算符:// 整除和 ** 乘方(C 里要写 pow(2, 5))。另外 C 里 2 / 4 整型除法直接得 0,Python 的 / 永远返回浮点数,想截断必须用 //。
混合运算时,Python 会自动把整数提升为浮点数,不用担心类型问题。
进制写法
1 | 0o17 # 八进制(C 的写法是 017,Python 3 必须用 0o 前缀) |
删除变量
1 | del var # 删除变量的引用 |
和 C 对比:C 里没有”删除变量”这个操作——
- 栈上的局部变量:函数结束时自动销毁,不用管
- 堆上的变量(
malloc出来的):要手动free()释放内存
Python 的 del 和 free() 不一样:它只是切断变量名和对象之间的联系(相当于把”标签”从对象上撕下来),对象本身由 Python 的垃圾回收机制自动处理——没人引用它时自动回收内存,不需要手动释放。
del 之后再访问这个变量会直接报错:
1 | del var |
这个操作日常写代码用得不多,了解机制即可。
四、bool 布尔类型
只有 True 和 False 两个值(注意首字母大写,C 是 true/false 全小写)。
bool 是 int 的子类:True == 1、False == 0,甚至能直接参与运算:
1 | print(True + 1) # 2 |
用 bool() 可以把任意值转换为布尔,记住假值清单(其余所有值都是 True):
| 假值 | 说明 |
|---|---|
None |
空值 |
False |
布尔本身 |
0、0.0、0j |
各种零 |
'' |
空字符串 |
[]、()、{} |
空列表、空元组、空字典 |
1 | print(bool(0)) # False |
逻辑运算用 and、or、not(对应 C 的 &&、||、!)。
五、String 字符串
“基本语法”部分已经详细讲过索引和切片,这里补充两个重要区别:
- 字符串不可变:
word[0] = 'm'会直接报错(C 的字符数组可以改,Python 不行) - 没有单独的字符类型:一个字符就是长度为 1 的字符串
1 | word = 'Python' |
六、List 列表
列表是 Python 使用最频繁的类型,对应 C 里的数组,但强得多:
- 写在方括号
[]里,元素用逗号隔开 - 元素类型可以混搭:数字、字符串、甚至嵌套列表
- 自动扩容,不需要 C 的
realloc - 索引和切片规则与字符串完全一样
1 | my_list = ['abcd', 786, 2.23, 'runoob', 70.2] |
列表可变
1 | a = [1, 2, 3, 4, 5, 6] |
注意:不要用
list、str这类内置类型名当变量名,会覆盖掉内置功能。
七、Tuple 元组
元组和列表几乎一样,唯一区别:不能修改。写在小括号 () 里:
1 | my_tuple = ('abcd', 786, 2.23, 'runoob', 70.2) |
单元素元组的陷阱:
1 | tup1 = () # 空元组 |
因为不带逗号时,Python 会把括号当成数学运算的括号。
八、Set 集合
集合是无序、不重复的元素集,写在大括号 {} 里。去重是它的招牌能力:
1 | sites = {'Google', 'Taobao', 'Runoob'} |
**创建空集合必须用 set()**,因为 {} 创建的是空字典:
1 | s = set() # ✅ 空集合 |
什么是”去重”
“去重”就是把一组数据中重复出现的元素只保留一个。集合能去重的根本原因:元素不允许重复——往集合里添加一个已存在的元素,会被静默忽略:
1 | s = {1, 2} |
最常见的用法是——把列表丢进 set 再转回列表:
1 | nums = [1, 3, 2, 1, 3, 1] |
list(set(nums)) 是嵌套调用,从里往外执行,等价于两步:
1 | temp = set(nums) # ① set(nums):列表 → 集合,自动去重 → {1, 2, 3} |
为什么要转回列表? 因为集合无序、不能按下标取(s[0] 会报错),日常按顺序处理数据还是列表顺手,所以去重完再转回列表。
注意:去重后元素的顺序不保证(例子输出
[1, 2, 3]恰好有序是小整数哈希的巧合)。要”去重且保序”得用循环,后面”循环”部分会讲。
同样的事在 C 里要写两层循环,每个元素和前面所有元素比较一遍、重复的跳过,代码一大堆:
1 | // C:两层循环 + 逐个比较,去重要自己实现 |
实例:
1 | sites = {'Google', 'Taobao', 'Runoob', 'Facebook', 'Zhihu', 'Baidu'} |
输出:
1 | {'Zhihu', 'Baidu', 'Taobao', 'Runoob', 'Google', 'Facebook'} |
九、Dictionary 字典
字典是键值对集合,对应其他语言里的”映射/哈希表”。C 里没有直接对应的东西,可以理解成”用字符串当下标的数组“:
1 | tinydict = {'name': 'runoob', 'code': 1, 'site': 'www.runoob.com'} |
键的要求:必须是不可变类型(数字、字符串、元组),且同一个字典里键不能重复。
1 | d = {} # 空字典 |
Python 3.7 起字典保持插入顺序,不用担心”无序”问题。
四种容器怎么选
C 里只有”数组”这一种容器,Python 拆成了四种,各管一摊:
| 类型 | 什么时候用 | 和 C 数组对比 |
|---|---|---|
List 列表 |
默认选择:存一组数据、要增删改 | C 数组的升级版:自动扩容、元素类型不限、支持切片 |
Tuple 元组 |
数据固定不变(坐标、日期、常量配置) | 类似 C 的 const 数组,不可修改 |
Set 集合 |
去重、判断”在不在”、交并差运算 | C 没有对应物,要自己写代码实现 |
Dictionary 字典 |
按名字找值(姓名→成绩、编号→记录) | C 没有直接对应,相当于自己手写哈希表 |
选型一句话:默认列表;不可改用元组;去重用集合;按名查找用字典。
十、bytes 二进制类型(了解)
bytes 表示二进制数据(图片、网络传输等),在字符串前加 b:
1 | x = b"hello" |
入门阶段知道有这回事即可,学到网络编程再深入。
小结:这一部分把 Python 的”容器”过了一遍——列表可变可切片、元组只读、集合去重、字典按键查值,加上假值清单。
数据类型转换
一、隐式类型转换(自动完成)
不同类型的数据做运算时,Python 自动把精度低的类型转成精度高的类型,防止数据丢失:
1 | num_int = 123 |
类型从低到高:bool < int < float < complex。这和 C 的规则一样——C 里 int 和 double 相加,整数也会自动提升为 double。
但不是所有类型都能自动转,整数加字符串直接报错:
1 | num_int = 123 |
这种情况就需要手动转换——显式类型转换。
二、显式类型转换(手动转换)
把类型名当函数用:int()、float()、str()。
1 | x = int(1) # 1 |
刚才报错的例子,转一下就能算了:
1 | num_int = 123 |
三、不是想转就能转
转换是否可行,取决于源数据本身有没有足够的信息来表示目标类型:
"123"是数字字符串 →int("123")得到 123 ✅"Hello"不是数字 →int("Hello")报错 ❌
1 | int("Hello") # ❌ ValueError: invalid literal for int() with base 10: 'Hello' |
- 列表可以转集合(
set([1, 1, 2])→{1, 2})、转元组(tuple([1, 2])→(1, 2)),但不能转成整数 ❌
四、常用转换函数表
| 函数 | 作用 |
|---|---|
int(x) / float(x) / str(x) |
转整数 / 浮点数 / 字符串(最常用) |
complex(x[, y]) |
创建复数,实部 x、虚部 y(y 省略时虚部为 0) |
list(x) / tuple(x) / set(x) |
转列表 / 元组 / 集合(去重常用) |
bool(x) |
转布尔(假值清单见上一部分) |
dict(...) |
转字典 |
ord('a') / chr(97) |
字符 ↔ 整数 |
hex(x) / oct(x) / bin(x) |
转十六进制 / 八进制 / 二进制字符串 |
五、和 C 的对照
| C | Python |
|---|---|
(int)x 强制类型转换 |
int(x) 函数调用 |
int + double 自动提升为 double |
同样自动提升(bool < int < float < complex) |
字符转数字要写 '5' - '0' |
int("5") 一行搞定 |
数字转字符串用 sprintf(buf, "%d", n) |
str(n) |
运算符
运算符的构成和 C 一样:操作数 运算符 操作数(4 + 5 中 4、5 是操作数,+ 是运算符)。大部分运算符和 C 完全相同,本篇重点讲不一样的。
一、算术运算符
| 运算符 | 说明 | 示例 |
|---|---|---|
+ - * |
加、减、乘 | 10 + 21 = 31 |
/ |
除,结果是浮点数 | 21 / 10 = 2.1 |
// |
整除,向下取整 | 9 // 2 = 4 |
% |
取余 | 21 % 10 = 1 |
** |
幂运算 | 2 ** 5 = 32 |
/、//、** 在”数字”小节已详细讲过。这里强调一个容易踩的坑——// 是向下取整(往负无穷方向),和 C 的整数除法(向零取整)不一样:
1 | 9 // 2 # 4 正数看不出区别 |
二、比较运算符
== != > < >= <=,和 C 完全一样,结果是 True / False:
1 | 10 == 10 # True |
三、赋值运算符
= += -= *= /= %= **= //=,含义和 C 一样(c += a 等价于 c = c + a):
1 | c = 10 |
四、位运算符
位运算符把数字当作二进制来运算。Python 的位运算符和 C 完全一样,学过 STM32 寄存器操作的会非常熟悉。
以下用 a = 60、b = 13 举例,先写出二进制:
1 | a = 60 → 0011 1100 |
按位与 &
两个位都是 1 结果才是 1:
1 | 0011 1100 |
常用:取位(掩码),比如 x & 1 判断奇偶、x & 0xFF 取低 8 位。
按位或 |
两个位有一个是 1 结果就是 1:
1 | 0011 1100 |
常用:置位(把某一位设为 1),如 x | 0x10 把第 5 位置 1。
按位异或 ^
两个位不相同结果才是 1:
1 | 0011 1100 |
特性:x ^ x 得 0,x ^ 0 得 x。常用:翻转指定某一位。
按位取反 ~
每个位 0 变 1、1 变 0。注意 Python 里 ~x 等于 -x - 1(补码表示):
1 | ~60 # -61 相当于 -(60+1) |
左移 << 和右移 >>
把二进制位整体移动,空出的位补 0:
1 | 0011 1100 << 2 = 1111 0000 → 240 |
常用:左移一位相当于乘 2,右移一位相当于整除 2(嵌入式里比乘除法快)。
综合示例:
1 | a = 60 # 0011 1100 |
用 bin() 函数可以随时查看一个数的二进制形式:
1 | print(bin(60)) # 0b111100 |
嵌入式开发的STM32 寄存器操作:
x |= (1 << n)置位、x &= ~(1 << n)清零、x ^= (1 << n)翻转——Python 里同样适用。
五、逻辑运算符(和 C 最大的不同)
and、or、not 对应 C 的 &&、||、!,但有一个关键区别:
C 的
&&/||返回 0 或 1;Python 的and/or返回的是操作数本身。
记住两条规则:
x and y:x 为假返回 x,否则返回 yx or y:x 为真返回 x,否则返回 y
1 | 10 and 20 # 20 10 为真,返回后面的 20 |
这个特性最常见的用途:给变量一个默认值:
1 | name = input_name or "匿名用户" # input_name 为空(假值)就用默认值 |
not 和 C 的 ! 一样,返回 True / False。
六、成员运算符 in(Python 独有)
判断一个元素在不在序列里,C 里要写循环,Python 一个关键字搞定:
1 | 'Runoob' in {'Google', 'Taobao', 'Runoob'} # True 在集合里 |
not in 取反:5 not in [1, 2, 3] → True。
七、身份运算符 is(判断是不是同一个对象)
==比较值是否相等is比较对象身份(是不是同一个内存对象),类似 C 里比较指针地址
1 | a = [1, 2, 3] |
日常判断值相等用
==就够了;is最常用的场景是和None比较(x is None)。注意1 is True是 False——虽然1 == True是 True(bool 是 int 的子类),但两者不是同一个对象。
八、运算符优先级
完整的优先级表很长,记住几条常用的(从高到低):
1 | ** 幂运算 |
和 C 的规则基本一致:算术 > 比较 > 逻辑。记不住的时候加括号最稳妥,可读性也更好:
1 | # x or y and z 是 x or (y and z),因为 and 优先级高于 or |
数字
“基本数据类型”部分已经讲过数字的类型(int / float / complex)和基本运算,本篇补充数字的不可变机制、和 C 不一样的两个特性,以及最常用的两个模块:math 和 random。
一、数字是不可变的(改值 = 重新分配内存)
数字是不可变类型:你无法”修改”一个数字对象本身,改变变量的值,实际上是让变量改指向一个新的对象。
C 里 a = 6 是修改同一块内存里的值:
1 | int a = 5; |
Python 里 a = 6 是让 a 改指向一个新的对象,旧的 5 对象还留在内存里(直到被垃圾回收):
1 | a = 5 |
用 id() 函数可以验证——它返回对象的内存地址,改值后地址变了,说明确实是新对象:
1 | a = 5 |
顺带了解:整数有个小整数缓存机制(-5 ~ 256 常驻内存),所以
a = 5; b = 5时a is b为 True;大数字每次都是新建对象。写代码时不用管,知道有这回事即可。
二、整数没有大小限制
C 的 int 有范围(32 位下约 ±21 亿),溢出会产生各种诡异问题;Python3 的整数没有大小限制,想多大就多大:
1 | 10 ** 30 # 1000000000000000000000000000000 随便算,不会溢出 |
刷算法题时完全不用担心溢出,这是 Python 的福利。
三、// 的结果类型跟着操作数走
// 的结果不一定是整数——和操作数的类型一致:
1 | 7 // 2 # 3 两个整数 → 整数 |
同理 / 永远返回浮点数。判断结果类型时不要想当然,先看操作数。
四、math 模块(数学函数)
数学运算大多要导入 math 模块(对应 C 的 <math.h>):
1 | import math |
五、三角函数
math 模块还提供三角函数,注意参数是弧度(不是角度):
| 函数 | 说明 | 示例 |
|---|---|---|
math.sin(x) / math.cos(x) / math.tan(x) |
正弦 / 余弦 / 正切(弧度) | math.sin(math.pi / 2) = 1.0 |
math.asin(x) / math.acos(x) / math.atan(x) |
反正弦 / 反余弦 / 反正切 | math.asin(1) ≈ 1.5708(π/2) |
math.degrees(x) |
弧度 → 角度 | math.degrees(math.pi / 2) = 90.0 |
math.radians(x) |
角度 → 弧度 | math.radians(180) ≈ 3.14159(π) |
math.hypot(x, y) |
斜边长度 √(x² + y²) | math.hypot(3, 4) = 5.0 |
1 | import math |
六、random 模块(随机数)
随机数在游戏、算法、抽样里都很常用:
1 | import random |
random.seed(x) 可以固定随机种子,让随机结果可复现——调试需要确定性时常用。
字符串
字符串是 Python 中最常用的数据类型。”基本语法”部分已经见过它的概貌,本篇完整展开:创建、索引切片、转义、运算符、格式化(重点)、常用方法。
一、创建字符串
用单引号 ' 或双引号 " 括起来即可,两者完全等价:
1 | var1 = 'Hello World!' |
注意:Python 没有单独的字符类型,一个字符就是长度为 1 的字符串。
二、索引与切片
字符串可以用 [] 按下标访问,索引从 0 开始,负索引从右往左:
1 | 字符串: H e l l o W o r l d ! |
1 | var1 = 'Hello World!' |
切片语法 str[start:end:step],含头不含尾(start 含、end 不含),step 是步长默认 1:
1 | s = 'Hello World!' |
三、字符串更新(拼接)
字符串是不可变的,不能修改某个字符;”更新”其实是切片 + 拼接生成新字符串:
1 | var1 = 'Hello World!' |
四、转义字符
在字符串里表示特殊字符,用反斜杠 \ 转义:
| 转义字符 | 含义 | 示例 |
|---|---|---|
\n |
换行(最常用) | "a\nb" → a 换行 b |
\t |
制表符(Tab) | "a\tb" → a 和 b 之间空 4 格 |
\\ |
反斜杠本身 | "\\" → \ |
\' |
单引号 | '\'' → ' |
\" |
双引号 | "\"" → " |
\r |
回车,回到行首覆盖 | "123\rAB" → AB3 |
\b |
退格 | "ab\bc" → ac |
\a |
响铃(喇叭响一声) | "\a" |
\v / \f |
纵向制表符 / 换页 | 很少用 |
\ooo |
八进制 ASCII 码 | "\110" → H |
\xhh |
十六进制 ASCII 码 | "\x48" → H |
1 | print('Hello\nWorld') # 换行输出两行 |
\r 的一个经典应用:终端进度条(\r 回到行首重画):
1 | import time |
原始字符串:字符串前加 r(大小写都可以),反斜杠不转义,按字面输出:
1 | print(r'\n') # \n 原样输出,不换行 |
五、字符串运算符
假设 a = "Hello"、b = "Python":
| 运算符 | 说明 | 示例 |
|---|---|---|
+ |
连接 | a + b → HelloPython |
* |
重复 | a * 2 → HelloHello |
[] |
取字符 | a[1] → e |
[ : ] |
切片(含头不含尾) | a[1:4] → ell |
in |
包含子串 | 'H' in a → True |
not in |
不包含子串 | 'M' not in a → True |
r/R |
原始字符串 | r'\n' → 原样 |
% |
格式化 | 见下一节 |
1 | a = "Hello" |
六、字符串格式化(重点)
什么是格式化? 就是把变量嵌入到一段模板文本中,并按指定的格式输出。
比如要把名字、年龄拼成一句话。手工拼接很痛苦——要不停用 + 连接,数字还得先 str() 转换:
1 | name = '小明' |
格式化就是在模板里留好占位符,把变量填进去:
1 | print("我叫 %s 今年 %d 岁" % (name, age)) # 我叫 小明 今年 10 岁 |
它还能顺便控制输出格式:保留几位小数、占几个宽度、补零(如 %.2f 把 96.5 输出成 96.50)。这和 C 里 printf / sprintf 的格式化是同一回事。
Python 有 3 种格式化方式,按推荐程度从低到高。
6.1 % 格式化(和 C 的 sprintf 同款)
1 | print("我叫 %s 今年 %d 岁!" % ('小明', 10)) |
常用格式符:
| 符号 | 含义 |
|---|---|
%s |
字符串 |
%d |
整数 |
%f |
浮点数(%.2f 保留两位小数) |
%x / %o |
十六进制 / 八进制 |
%e |
科学计数法 |
宽度和精度:%5d 占 5 格、%.2f 两位小数:
1 | print('%5d' % 42) # ' 42' |
6.2 str.format()(Python 2.6+)
用 {} 占位,不用记 %s 还是 %d:
1 | print("我叫 {} 今年 {} 岁!".format('小明', 10)) |
6.3 f-string(Python 3.6+,推荐)
字符串前加 f,大括号里直接写变量或表达式:
1 | name = 'Runoob' |
七、三引号(多行字符串)
用 ''' 或 """ 包起来,字符串可以跨多行,所见即所得:
1 | para_str = """这是一个多行字符串的实例 |
典型场景:写 HTML 片段、SQL 语句(不用手工拼接和转义):
1 | sql = ''' |
八、Unicode 字符串
Python3 中所有字符串都是 Unicode 字符串,中英文混写没有任何问题(Python2 里要写 u'...' 前缀,Python3 不需要):
1 | print('你好,世界') # 直接输出中文 |
九、常用字符串方法
9.1 最常用
1 | s = ' Hello, Python World ' |
9.2 其他方法(认识即可)
| 方法 | 作用 |
|---|---|
lstrip() / rstrip() |
只去掉左 / 右空白 |
islower() / isupper() |
是否全小写 / 全大写 |
isspace() |
是否全是空白 |
isnumeric() / isdecimal() |
是否全是数字字符(比 isdigit 范围更广) |
capitalize() |
首字母大写,其余小写 |
swapcase() |
大小写互换 |
index() |
同 find,但找不到抛异常 |
rfind() / rindex() |
从右边开始找 |
center(w) / ljust(w) / rjust(w) |
居中对齐 / 左对齐 / 右对齐填充到宽度 w |
splitlines() |
按换行符切成多行列表 |
encode('utf-8') |
字符串 → bytes 字节串 |
expandtabs() |
tab 转成空格 |
maketrans() / translate() |
字符映射替换 |
istitle() |
是否标题化(每个单词首字母大写) |
列表
列表(List)是 Python 最常用的数据类型。它属于序列——序列里的每个元素都有位置(索引),第一个索引是 0。和 C 数组一样按下标访问,但强得多:元素类型可以不统一、自动扩容、还有一整套现成方法。
一、创建列表
用方括号 [] 把元素括起来,逗号分隔:
1 | list1 = ['Google', 'Runoob', 1997, 2000] # 元素类型可以混搭 |
二、访问列表元素
索引从 0 开始,负索引从尾部数(-1 是最后一个):
1 | list = ['red', 'green', 'blue', 'yellow', 'white', 'black'] |
三、切片
和字符串的切片规则完全一样:list[start:end:step],含头不含尾:
1 | nums = [10, 20, 30, 40, 50, 60, 70, 80, 90] |
注意:切片返回的是新列表,原列表不变。
四、更新列表
列表是可变的,可以修改单个元素、追加新元素:
1 | list = ['Google', 'Runoob', 1997, 2000] |
还能切片赋值(整段替换),C 里做不到:
1 | a = [1, 2, 3, 4, 5, 6] |
五、删除列表元素
用 del 按下标删除:
1 | list = ['Google', 'Runoob', 1997, 2000] |
(按值删除用 remove()、取出并删除用 pop(),见下文”列表方法”)
六、列表操作符
| 表达式 | 结果 | 说明 |
|---|---|---|
len([1, 2, 3]) |
3 |
长度 |
[1, 2, 3] + [4, 5, 6] |
[1, 2, 3, 4, 5, 6] |
拼接(生成新列表) |
['Hi!'] * 4 |
['Hi!', 'Hi!', 'Hi!', 'Hi!'] |
重复 |
3 in [1, 2, 3] |
True |
成员判断 |
for x in [1, 2, 3]: ... |
逐个迭代 | 循环遍历(循环部分详讲) |
拼接也可以用 +=(原地扩展):
1 | squares = [1, 4, 9, 16, 25] |
七、嵌套列表
列表里可以再装列表(相当于 C 的二维数组):
1 | a = ['a', 'b', 'c'] |
八、列表方法(重点)
添加元素
1 | list = [1, 2, 3] |
append 和 extend 的区别:append 把参数当一个元素放进去,extend 把参数展开逐个放:
1 | a = [1, 2] |
删除元素(四种方式对比)
1 | list = [10, 20, 30, 20] |
| 方式 | 用法 | 区别 |
|---|---|---|
remove(值) |
list.remove(20) |
按值删,只删第一个匹配 |
pop([下标]) |
list.pop() |
按下标删,并返回被删元素,默认删最后一个 |
del list[下标] |
del list[0] |
按下标删,不返回 |
clear() |
list.clear() |
清空全部 |
查找与统计
1 | list = ['Google', 'Runoob', 'Taobao', 'Runoob'] |
排序与反转
1 | nums = [3, 1, 4, 1, 5, 9, 2] |
sort()是原地排序(直接改原列表),和内置函数sorted()(返回新列表)不同。
复制:一个隐藏的坑
b = a 不是复制——两个变量指向同一个列表,改一个另一个也变:
1 | a = [1, 2, 3] |
要真正复制用 copy():
1 | a = [1, 2, 3] |
这和前面”身份运算符 is”讲的是同一件事:b = a 时 b is a 为 True(同一对象),b = a.copy() 后 b is a 为 False。
九、列表内置函数
1 | list = [1, 5, 3, 9, 2] |
十、和 C 数组的对比
| C 数组 | Python 列表 | |
|---|---|---|
| 创建 | int a[5]; 固定大小 |
a = [] 随意,自动扩容 |
| 类型 | 必须同一类型 | 元素类型随便混搭 |
| 长度 | sizeof(a) / sizeof(a[0]) |
len(a) |
| 追加元素 | 不支持,要 realloc | a.append(x) 一行 |
| 删除元素 | 不支持,要手动搬移 | a.remove(x) / a.pop() |
| 排序 | 自己写冒泡/快排 | a.sort() 一行 |
| 切片 | 不支持 | a[1:3] 直接取一段 |
| 多维 | int a[2][3] |
嵌套列表 |
元组
元组(tuple)和列表几乎一样:有序、按下标访问、可切片、可遍历。唯一区别:创建后元素不能修改。可以理解为”只读的列表”,相当于 C 里加 const 的数组。
一、创建元组
用小括号 () 包起来,元素用逗号隔开:
1 | tup1 = ('Google', 'Runoob', 1997, 2000) |
括号不是必须的——元组的本质是逗号:
1 | tup3 = "a", "b", "c", "d" # 不写括号也是元组 |
空元组:
1 | tup1 = () |
单元素元组的坑:只有一个元素时,必须加逗号,否则括号被当成运算符,(50) 只是整数 50:
1 | tup1 = (50) |
这和 C 的道理一样:(a + b) * c 里括号就是运算符,Python 的括号默认也是运算符,想表示元组必须用逗号表态。
二、访问元组
下标从 0 开始,支持索引和切片,和字符串、列表完全一样:
1 | tup1 = ('Google', 'Runoob', 1997, 2000) |
三、元组不能修改(和列表最本质的区别)
给元素赋值直接报错:
1 | tup = ('r', 'u', 'n', 'o', 'o', 'b') |
不能改,但可以连接:用 + 把两个元组合成一个新元组,原元组不变:
1 | tup1 = (12, 34.56) |
不能删单个元素,但能删整个元组:
1 | tup = ('Google', 'Runoob', 1997, 2000) |
C 里释放变量靠 free() 或等作用域结束,Python 用 del 直接删除变量,但平时很少主动用。
四、元组运算符
和字符串一样,+、+=、* 都能用于元组,运算后生成新元组:
| 表达式 | 结果 | 说明 |
|---|---|---|
len((1, 2, 3)) |
3 |
元素个数 |
(1, 2, 3) + (4, 5, 6) |
(1, 2, 3, 4, 5, 6) |
连接 |
a += b |
a 变成 (1, 2, 3, 4, 5, 6) |
连接后赋回 a |
('Hi!',) * 4 |
('Hi!', 'Hi!', 'Hi!', 'Hi!') |
复制 |
3 in (1, 2, 3) |
True |
元素是否存在 |
for x in (1, 2, 3) |
依次输出 1 2 3 |
迭代 |
运行示例:
1 | a = (1, 2, 3) |
五、元组索引与截取
反向索引和切片与字符串、列表规则完全相同:
| 表达式 | 结果 | 描述 |
|---|---|---|
tup[1] |
'Runoob' |
第二个元素 |
tup[-2] |
'Weibo' |
倒数第二个元素 |
tup[1:] |
('Runoob', 'Taobao', 'Wiki', 'Weibo', 'Weixin') |
从第二个到最后 |
tup[1:4] |
('Runoob', 'Taobao', 'Wiki') |
下标 1 到 3(不含 4) |
1 | tup = ('Google', 'Runoob', 'Taobao', 'Wiki', 'Weibo', 'Weixin') |
六、元组内置函数
1 | tuple1 = ('Google', 'Runoob', 'Taobao') |
tuple(iterable):把列表、字符串等可迭代对象转成元组:
1 | list1 = ['Google', 'Taobao', 'Runoob', 'Baidu'] |
列表部分讲过 list((1, 2)) 把元组转成列表,这里正好相反:tuple() 把列表转成元组。
七、不可变性的真相
“不可变”指的是:元组指向的那块内存内容不能变。重新赋值不是修改,而是让变量绑定一个新对象:
1 | tup = ('r', 'u', 'n', 'o', 'o', 'b') |
1 | 第一次赋值: |
对比列表:list[0] = 'g' 是原地改内容,地址不变;元组做不到原地改,只能整个换对象。
八、元组解包(实用技巧)
元组可以直接拆开赋给多个变量:
1 | tup = (1, 2, 3) |
最经典的用途:交换两个变量。C 里需要临时变量:
1 | // C 写法 |
1 | # Python 写法 |
原理:右边先组合成一个临时元组 (b, a),再解包赋给 a, b。
函数返回多个值也靠元组:
1 | def get_point(): |
九、元组 vs 列表:怎么选
| 元组 tuple | 列表 list | |
|---|---|---|
| 符号 | () |
[] |
| 内容 | 创建后不能修改 | 可以增删改 |
| 内存 | 更小 | 更大 |
| 速度 | 更快 | 稍慢 |
| 典型用途 | 坐标、常量、多返回值 | 动态数据集合 |
选择原则:
- 数据不允许被改(坐标、颜色、固定配置)→ 用元组
- 数据需要增删改(收集动态内容)→ 用列表
字典
字典(dict)是键值对(key-value)的集合:每个元素由”键”和”值”组成,通过键直接找到对应的值,不用像列表那样按下标挨个数。C 里没有直接对应的类型,可以理解成”用任意不可变值当下标的数组“;Python 字典底层是哈希表实现的,查找一步到位。
一、创建字典
格式:花括号 {} 包起来,键和值用冒号 : 分割,键值对之间用逗号 , 分割:
1 | d = {key1: value1, key2: value2, key3: value3} |
1 | tinydict = {'name': 'runoob', 'likes': 123, 'url': 'www.runoob.com'} |
键和值的类型很自由:
1 | tinydict1 = { 'abc': 456 } |
注意:dict 是 Python 的关键字和内置函数名,变量名不要命名为 dict——和列表章节说过的”不要用 list、str 当变量名”是同一个道理,会覆盖掉内置功能。
二、创建空字典
两种方式,效果一样:
1 | emptyDict = {} # 方式一:大括号 |
注意:{} 创建的是空字典,不是空集合——集合章节讲过,空集合必须用 set(),因为 {} 被字典占用了。
三、访问字典里的值
把键放进方括号 []:
1 | tinydict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'} |
和列表的区别:列表按下标(0、1、2…)访问,字典按键访问,键可以是字符串、数字、元组。
访问不存在的键会直接报错:
1 | tinydict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'} |
C 里数组越界不提示(未定义行为),Python 直接抛 KeyError,反而更好排查。不想报错就用 get()(见第八节)。
四、修改字典
向字典添加新内容 = 给新键赋值;修改已有内容 = 给已有键赋新值。写法完全相同,Python 自己判断:
1 | tinydict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'} |
五、删除字典元素
三种删除,范围从小到大:
1 | tinydict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'} |
clear() 和 del 的区别:清空后字典还在(空的),del 之后字典整个没了。
六、字典键的特性(重点)
1. 键不允许重复
同一个键出现两次,后写的值覆盖先写的:
1 | tinydict = {'Name': 'Runoob', 'Age': 7, 'Name': '小菜鸟'} |
2. 键必须不可变
值可以是任意类型,键必须是不可变的——数字、字符串、元组都可以,列表不行:
1 | tinydict = {['Name']: 'Runoob', 'Age': 7} |
原因:字典靠哈希(把键算成一个数字)实现快速查找,可变对象内容一变哈希就乱了,所以不能用。这正是元组章节讲的”不可变”的用武之地——需要多个字段当键时用元组:
1 | d = {('张三', '2024级'): 95} # 键是元组,合法 |
七、字典内置函数
| 函数 | 作用 | 示例 |
|---|---|---|
len(dict) |
键值对个数 | len({'Name': 'Runoob', 'Age': 7}) → 2 |
str(dict) |
转成字符串 | str({'a': 1}) → "{'a': 1}" |
type(variable) |
变量类型 | type({}) → <class 'dict'> |
八、字典内置方法
12 个方法全表:
| 方法 | 作用 |
|---|---|
clear() |
清空所有键值对 |
copy() |
复制一份字典(浅复制) |
fromkeys(seq, val) |
用序列的元素作键创建新字典,值统一为 val |
get(key, default) |
取键的值,键不存在返回 default(不报错) |
key in dict |
判断键是否存在 |
items() |
返回所有 (键, 值) 对的视图 |
keys() |
返回所有键的视图 |
setdefault(key, default) |
类似 get,但键不存在时会把键添加进去 |
update(dict2) |
把另一个字典的键值对合并进来 |
values() |
返回所有值的视图 |
pop(key, default) |
删除指定键,返回被删的值 |
popitem() |
删除并返回最后一对 (键, 值) |
分四组讲:
查:get / in / keys / values / items
1 | tinydict = {'Name': 'Runoob', 'Age': 7} |
get() 和 tinydict['key'] 的区别:直接访问不存在的键抛 KeyError 中断程序,get() 返回默认值让程序继续。**不确定键是否存在时用 get()**。
keys / values / items 返回的是视图对象,不是列表。它跟着字典实时变化,能用 for 遍历,想转成列表用 list() 包一下。
改:update / setdefault
1 | tinydict = {'Name': 'Runoob', 'Age': 7} |
删:pop / popitem / clear
1 | tinydict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'} |
pop 和 del 的区别:pop 返回被删的值,del 不返回。pop('Age') 删一个并拿到值;del d['Age'] 删一个、拿不到值。
其他:copy / fromkeys
1 | # copy:复制一份(和列表章节的 copy() 同一个道理:b = a 只是共享,copy 才是真复制) |
九、和 C 的对照
C 语言没有字典。要”按键查值”,笨办法是结构体数组 + 循环比对:
1 | struct student { char name[20]; int age; }; |
数据少还行,数据一多每次都要从头遍历。Python 字典底层是哈希表,dict[key] 一步定位:
1 | C 结构体数组: 按键查找 = 遍历比对,O(n),n 大就慢 |
十、实际应用场景
- 查表:学号 → 姓名、星期几 → 中文名、状态码 → 描述
- 配置项:
config = {'baud': 115200, 'parity': 'N'},按键取参数 - 统计词频:遍历一段文字,统计每个字符出现次数
- JSON:前后端传数据的 JSON 对象,解析成 Python 就是字典
1 | # 统计每个字符出现次数(用上了 get 的默认值技巧) |
核心套路:count.get(ch, 0) 取当前次数,第一次遇到返回 0,加 1 存回去;后面再遇到就加 1。
集合
集合(set)是无序、不重复的元素序列。两个招牌能力:去重+ 快速判断在不在(in)。还能做交集、并集、差集等集合运算。C 里没有对应物,去重要自己写循环。
一、创建集合
大括号 {} 或 set() 函数:
1 | set1 = {1, 2, 3, 4} # 直接使用大括号创建集合 |
**空集合必须用 set()**,{} 创建的是空字典(字典章节讲过):
1 | s = set() # ✅ 空集合 |
去重演示
创建时重复元素自动只留一个:
1 | basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'} |
注意:集合无序,输出顺序不固定,不要指望它按插入顺序输出(Python 3.7 起字典保持插入顺序,集合不保证)。
二、判断元素在不在
x in s 快速判断成员:
1 | basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'} |
C 对照:C 里判断数组有没有某元素,要写 for 循环挨个比;集合直接 in 一步,底层也是哈希表。
三、集合运算
1 | a = set('abracadabra') # 从字符串创建:拆成单个字符并去重 |
四种运算:
1 | print(a - b) # 差集:a 里有、b 里没有 |
| 运算 | 符号 | 含义 | 示例(a={a,r,b,c,d},b={a,l,c,z,m}) |
|---|---|---|---|
| 差集 | a - b |
a 里有、b 里没有 | {r, d, b} |
| 并集 | a | b |
两个集合合并去重 | {a, c, r, d, b, m, z, l} |
| 交集 | a & b |
两个集合都有 | {a, c} |
| 对称差 | a ^ b |
两边不同时有的 | {r, d, b, m, z, l} |
每个运算都有对应的方法版(union / intersection / difference / symmetric_difference),效果一样,第八节展开。
四、集合推导式
类似列表推导式的写法,一行生成集合:
1 | a = {x for x in 'abracadabra' if x not in 'abc'} |
解读:遍历字符串 'abracadabra' 的每个字符 x,筛掉在 'abc' 里的,剩下的放进集合(自动去重)。
五、添加元素
add:加单个
1 | thisset = set(("Google", "Runoob", "Taobao")) |
元素已存在时 add 不进行任何操作(去重特性)。
update:批量加
参数可以是列表、元组、字典、集合,还可以传多个:
1 | thisset = set(("Google", "Runoob", "Taobao")) |
add 和 update 的区别:add 加一个元素,update 把参数展开逐个加(和列表的 append / extend 关系类似)。
六、移除元素
remove:不存在会报错
1 | thisset = set(("Google", "Runoob", "Taobao")) |
discard:不存在也不报错
1 | thisset = set(("Google", "Runoob", "Taobao")) |
pop:随机删一个
1 | thisset = set(("Google", "Runoob", "Taobao", "Facebook")) |
集合无序,pop 删的是”第一个”,但哪个是第一个不确定。要删指定元素用 remove / discard。
| 方式 | 不存在时 | 返回 |
|---|---|---|
remove(x) |
报错 KeyError | 无 |
discard(x) |
忽略,不报错 | 无 |
pop() |
集合为空报错 | 返回被删的元素(随机) |
七、其他基本操作
1 | thisset = set(("Google", "Runoob", "Taobao")) |
八、内置方法完整列表
方法名和运算符一一对应:| → union、& → intersection、- → difference、^ → symmetric_difference。
| 方法 | 描述 |
|---|---|
add() |
添加元素 |
clear() |
移除所有元素 |
copy() |
拷贝集合 |
difference() |
返回差集(-) |
difference_update() |
把”在另一个集合里也存在”的元素删掉(直接改原集合) |
discard() |
删除指定元素(不存在不报错) |
intersection() |
返回交集(&) |
intersection_update() |
只留下交集部分(直接改原集合) |
isdisjoint() |
两个集合有没有共同元素,没有返回 True |
issubset() |
是否为另一个集合的子集 |
issuperset() |
是否包含另一个集合 |
pop() |
随机移除元素 |
remove() |
移除指定元素(不存在报错) |
symmetric_difference() |
返回对称差(^) |
symmetric_difference_update() |
只留下对称差部分(直接改原集合) |
union() |
返回并集(|) |
update() |
添加元素(可多个参数) |
len() |
元素个数 |
运算的方法版(有返回值,原集合不变)
1 | a = {1, 2, 3} |
带 _update 的方法:直接改原集合,不返回新集合
不带 _update 的方法(union 等)算出结果后返回一个新集合,原集合不动;带 _update 的方法把原集合直接改掉,不返回任何东西:
1 | a = {1, 2, 3} |
关系判断:子集 / 超集 / 不相交
1 | a = {1, 2} |
九、和 C 的对照
C 没有集合类型。最常用的”去重”功能,C 要两层循环:
1 | // C:数组去重,O(n²) |
1 | # Python:一行 |
十、实际应用场景
- 去重:统计文章里有哪些不同的词、列表去重
- 快速判断在不在:黑名单、已访问记录(配合
in,一步到位) - 集合运算:求共同好友(交集)、推荐用户(差集:A 关注了但 B 没关注)
- 配合字典:字典的
keys()可以直接和集合做运算
1 | # 共同好友:两个好友列表的交集 |
条件控制
条件控制就是根据条件(True / False)决定执行哪段代码,和 C 的 if / switch 是一回事,只是写法不同。
一、if 语句的基本形式
1 | if condition_1: |
执行逻辑:
condition_1为 True → 执行语句块 1- 否则判断
condition_2,为 True → 执行语句块 2 - 都不满足 → 执行 else 的语句块 3
和 C 的对照:
1 | // C 语言 |
1 | # Python |
三个必须记住的点:
- 每个条件后面要写冒号
: - 用缩进划分语句块(相同缩进 = 同一代码块),没有大括号
- 没有
else if,用elif
看一个实际例子——if 后面直接放变量当条件:
1 | var1 = 100 |
输出:
1 | 1 - if 表达式条件为 true |
if var1: 这种”直接拿变量当条件”的写法,判断依据是假值清单(前面 bool 部分讲过):0、''、[]、None 等为假,其余为真——和 C 里 if (a) 的规则一样。
二、条件里常用的比较运算符
| 运算符 | 说明 | 示例 |
|---|---|---|
< <= |
小于 / 小于等于 | 5 < 6 → True |
> >= |
大于 / 大于等于 | 5 > 6 → False |
== |
等于(注意是两个等号) | 5 == 6 → False |
!= |
不等于 | 5 != 6 → True |
in |
在序列里 | 3 in [1, 2, 3] → True |
and / or / not |
逻辑与 / 或 / 非 | x > 0 and x < 10 |
组合条件示例:
1 | age = 25 |
三、if 嵌套
if 里面可以再套 if(和 C 一样),注意缩进层级表示嵌套深度:
1 | num = int(input("输入一个数字:")) |
四、综合实例:数字猜谜游戏
用 while + if-elif-else 写一个小游戏(while 循环下一部分详讲):
1 | number = 7 |
运行示例:
1 | 数字猜谜游戏! |
五、match…case
match...case 是 Python 3.10 新增的结构化模式匹配,类似 C 的 switch:
1 | def http_error(status): |
(上面用到了函数 def 和 return,还没学到也没关系,函数部分会详讲,先看 match 的语法。)
规则:
match后面的值从上到下依次和每个case匹配,匹配成功就执行对应的语句case _:匹配一切(通配符),相当于 C 的default:,放在最后兜底- 一个 case 可以用
|匹配多个值:
1 | match status: |
match...case适合”一个值对应多种结果”的场景,比一串if-elif-else清晰。注意要 Python 3.10+ 才能用。
循环语句
Python 只有两种循环:while(条件循环,条件为 True 就一直执行)和 for(遍历循环,把可迭代对象里的元素挨个取一遍)。没有 C 的 do…while。break / continue 和 C 语义一样,但 Python 多了一个 C 没有的循环 else 子句。
一、循环控制关键字总览
| 关键字/函数 | 说明 | 示例 |
|---|---|---|
for |
遍历循环 | for i in list: |
while |
条件循环 | while x > 0: |
break |
立即终止当前循环 | break |
continue |
跳过本次循环剩余代码,进入下一次 | continue |
else(循环) |
循环正常结束(未被 break)时执行 | for i in range(3): ... else: ... |
pass |
占位语句(空操作) | for i in range(5): pass |
range() |
生成整数序列,常配合 for | range(0, 5) |
enumerate() |
遍历时同时拿索引和值 | for i, v in enumerate(list): |
二、while 循环
一般形式,注意冒号和缩进:
1 | while 判断条件: |
计算 1 到 100 的总和:
1 | n = 100 |
这段和 C 的写法几乎一模一样,只有格式区别(冒号 + 缩进代替大括号)。
C 对照:Python 没有 do…while。C 的 do { } while(条件);(先执行一次再判断)在 Python 里要改写:
1 | while True: |
无限循环
条件永远为 True 就无限循环:
1 | var = 1 |
按 CTRL+C 退出。无限循环在服务器上处理客户端实时请求很有用(一直监听)。
简单语句组
循环体只有一条语句时,可以和 while 写在同一行:
1 | flag = 1 |
三、while…else
while 条件变为 False(正常退出)时,执行 else 的语句块:
1 | count = 0 |
输出:
1 | 0 小于 5 |
注意:被 break 中断退出时不执行 else(见第七节)。
四、for 循环
Python 的 for 是遍历循环:把可迭代对象(列表、字符串、range 等)里的元素挨个取出来处理,不需要下标。
一般格式:
1 | for <variable> in <sequence>: |
遍历列表:
1 | sites = ["Baidu", "Google", "Runoob", "Taobao"] |
输出:
1 | Baidu |
遍历字符串(每个字符):
1 | word = 'runoob' |
输出:
1 | r |
和 C 最大的不同:C 的 for 是计数循环(for (i = 0; i < n; i++)),要自己管下标;Python 的 for 是遍历循环,直接拿元素本身,不用下标(类似 C++ 的范围 for)。
五、range() 函数(重点)
range() 生成整数序列,常和 for 配合:
1 | for i in range(5): |
输出 0 1 2 3 4——从 0 开始,到 5 结束,不含 5(含头不含尾,和切片规则一样)。
指定区间:
1 | for i in range(5, 9): |
指定步长(第三个参数):
1 | for i in range(0, 10, 3): |
步长可以是负数(倒着数):
1 | for i in range(-10, -100, -30): |
和 C 对照:range(起始, 结束, 步长) 等价于:
1 | for (i = 起始; i < 结束; i += 步长) |
注意含头不含尾(结束值不包含)。
range(len(列表)) 配合下标遍历:
1 | a = ['Google', 'Baidu', 'Runoob', 'Taobao', 'QQ'] |
1 | 0 Google |
range() 也可以直接转成列表:
1 | print(list(range(5))) # [0, 1, 2, 3, 4] |
六、for…else
循环正常遍历完后执行 else 子句;被 break 中断则不执行:
1 | for x in range(6): |
1 | 0 |
被 break 中断时 else 不执行:
1 | sites = ["Baidu", "Google", "Runoob", "Taobao"] |
1 | 循环数据 Baidu |
七、break 和 continue
和 C 的语义一样:break 终止整个循环,continue 跳过本次循环剩余代码、进入下一轮。
while 中使用 break:
1 | n = 5 |
1 | 4 |
while 中使用 continue:
1 | n = 5 |
1 | 4 |
对比两个输出:break 版循环直接结束了(没有 1、0);continue 版只是少了 2 那一次,其余照常。
for 中使用 break:
1 | for letter in 'Runoob': |
for 中使用 continue:
1 | for letter in 'Runoob': |
八、循环 else 综合实例:判断质数
循环 else 的实际用途:循环里没找到(没 break)时执行 else。经典例子——判断 2~9 哪些是质数:
1 | for n in range(2, 10): |
1 | 2 是质数 |
注意 else 挂在内层 for 上:内层循环找因子,找到了就 break(不是质数),一个因子都没找到就正常遍历完 → 执行 else 打印质数。C 里这个逻辑要用标志变量,Python 的 else 更直接。
九、pass 语句
pass 是空语句,什么都不做,用来占位,保持程序结构完整:
1 | while True: |
写代码时类先空着,用 pass 占位(面向对象章节会看到):
1 | class MyEmptyClass: |
for 循环里演示 pass 的执行时机:
1 | for letter in 'Runoob': |
1 | 当前字母 : R |
pass 和 continue 的区别:pass 什么都不做,继续往下执行本行后面的代码;continue 是跳过本次循环剩余代码、直接进下一轮。
十、和 C 的对照总结
| C | Python | |
|---|---|---|
| for | 计数循环,自己管下标 | 遍历循环,直接拿元素 |
| while | 有 | 有,写法几乎一样 |
| do…while | 有 | 没有 |
| break / continue | 有 | 有,语义一样 |
| 循环 else | 没有 | for / while 都能挂 else |
| 数字循环 | for (i = 0; i < n; i++) |
for i in range(n): |
| 同时拿下标 | 下标就是循环变量 | enumerate() |
enumerate() 补一个示例(总览表提过,这里跑通):
1 | sites = ['Google', 'Runoob', 'Taobao'] |
1 | 0 Google |
enumerate 同时返回下标和元素,等价于 C 里”用下标访问数组元素”,但更省事。
函数
函数是组织好的、可重复使用的代码段,用来实现单一或相关联的功能。Python 提供了很多内置函数(print()、len() 等),自己定义的就叫用户自定义函数。C 里有函数,Python 里也有,区别在格式和灵活性。
一、定义函数
规则:
- 以
def关键字开头,后接函数名 + 圆括号 () - 参数放在圆括号里(可空)
- 函数体以冒号
:起始,用缩进表示(和 if / while 一样) return [表达式]结束函数,选择性地返回一个值;不带表达式的 return 相当于返回 None
语法:
1 | def 函数名(参数列表): |
最简单的例子:
1 | def hello(): |
带参数:比较两个数,返回较大的:
1 | def max(a, b): |
计算面积:
1 | # 计算面积函数 |
C 对照:C 定义函数要写返回类型(int max(int a, int b)),Python 不用——返回什么类型运行时决定。
二、函数调用
函数定义好之后,通过函数名加括号调用:
1 | # 定义函数 |
同一函数可以反复调用,这就是”复用”。return 单独出现(不带值)表示结束函数,返回 None。
三、参数传递(重点)
先分清:变量是”引用”,对象才有类型
1 | a = [1, 2, 3] |
[1, 2, 3] 是列表对象,”Runoob” 是字符串对象,而变量 a 没有类型——它只是对象的引用(类似 C 的指针),先指向列表对象,再指向字符串对象。
可变 vs 不可变对象
- 不可变:数字、字符串、元组——改值 = 生成新对象整体替换
- 可变:列表、字典——可以原地改内容(内存地址不变)
1 | a = 5 |
传不可变对象:函数内改了,不影响外面
1 | def change(a): |
形参 a 和实参 a 一开始指向同一个对象(id 相同);函数里 a = 10 是让形参指向新对象,外面的变量不受影响。
为什么”指向同一个对象,外面却不变”:和 C 的区别
先理解为什么形参实参会指向同一块内存:Python 传参的本质是拷贝”引用”(引用 ≈ C 的指针)。调用 change(a) 时,Python 把实参 a 指向的对象的地址复制一份给形参,所以两者一开始指向同一个对象(id 相同)——但形参是新变量,只是恰好拿着同一个地址。
C 对照:
1 | C 传值: |
C 里只有传指针时才出现”两个变量指向同一块内存”;Python 里任何参数都是这个行为——拷贝引用,形参实参永远先指向同一个对象。
那为什么外面没变?关键不在”传的什么”,而在对象本身不可变:
1 | def change(a): |
1 | 调用前: 实参 a ──► 对象(1) @0x1000 |
数字不可变,没法”原地改内容”,a = 10 只能让形参换个对象指,所以外面不变——表现上和 C 传值一样,但机制不同(C 是副本,Python 是换指向)。
传列表时,列表可变,函数内 mylist.append(...) 是原地改内容,形参实参指向同一个列表,所以外面也变——表现上和 C 传指针一样。
一句话总结:Python 传参永远传引用(地址相同),外面变不变取决于对象可不可变,而不是传参方式。
传可变对象:函数内改了,外面也变
1 | def changeme(mylist): |
1 | 函数内取值: [10, 20, 30, [1, 2, 3, 4]] |
和 C 对照:
1 | C:传值 —— 函数内改形参,外部变量不变 |
四、必需参数
必需参数必须按顺序传、数量必须一致,少传会报错:
1 | def printme(str): |
五、关键字参数
调用时用 参数名=值 传参,Python 按名字匹配,顺序可以打乱:
1 | def printinfo(name, age): |
1 | 名字: runoob |
和必需参数的区别:一个说的是定义,一个说的是调用
必需参数和关键字参数不是两类不同的参数,而是同一批参数的两个视角:
- 必需参数说的是定义:函数声明了没默认值的参数,调用时必须传、必须按顺序,少传报错
- 关键字参数说的是调用:传参时写上
参数名=值,顺序可以乱
1 | def printinfo(name, age): # name、age 都是必需参数(定义时的说法) |
调用时用位置方式传(必需参数的常规用法):
1 | printinfo("runoob", 50) # 必须按声明顺序、数量一致 |
调用时用关键字方式传(传的还是那两个必需参数):
1 | printinfo(age=50, name="runoob") # 乱序也没事,按名字匹配 |
为什么位置传参容易出问题:顺序搞错不报错,只是值对位错,静默出错:
1 | printinfo(50, "runoob") # 不报错,但打印 名字: 50 年龄: runoob ← 错位 |
关键字传参写清楚谁是谁,不会错位。
| 本质 | 出错方式 | |
|---|---|---|
| 必需参数 | 定义的参数没默认值,必须传够 | 少传 → 报错 TypeError |
| 关键字参数 | 调用时用 名字=值 传参 |
不会错位,名字写错 → 报错 |
必需参数小节的报错信息 missing 1 required positional argument: 'str' 里的 positional(位置的)就点明了:必需参数 = 位置参数,强调的是”按位置、必须传”。
六、默认参数
调用时不传该参数,就使用默认值:
1 | def printinfo(name, age=35): # age 有默认值 35 |
1 | 名字: runoob |
注意:带默认值的参数必须放在不带默认值的参数后面(name 在前,age=35 在后),反过来写会语法报错。
七、不定长参数
函数可能处理比声明时更多的参数:
*args:多余的参数打包成元组
1 | def printinfo(arg1, *vartuple): |
1 | 输出: |
不传多余参数时,它是一个空元组:
1 | def printinfo(arg1, *vartuple): |
**kwargs:多余的”名=值”参数打包成字典
1 | def printinfo(arg1, **vardict): |
1 | 输出: |
单独的 *:后面的参数必须用关键字传
1 | def f(a, b, *, c): |
C 对照:C 的变参函数(printf 那种)要用 va_list 一堆宏,Python 一个 *args 搞定。
八、匿名函数 lambda
lambda 创建匿名函数——不用 def 定义、没有函数名的函数:
- lambda 是表达式,不是代码块,函数体比 def 简单得多,只能封装有限逻辑(只能写一个表达式,不能写 if、赋值等语句)
- 虽然只有一行,但不等于 C/C++ 的内联函数——内联函数是为了减少函数调用开销,lambda 没有这个目的
语法:
1 | lambda [arg1, arg2, ...]: 表达式 |
单个参数:
1 | x = lambda a: a + 10 |
多个参数:
1 | sum = lambda arg1, arg2: arg1 + arg2 |
lambda 可以封装在函数里,同一个函数创建多个不同的匿名函数:
1 | def myfunc(n): |
C 对照:C 没有匿名函数,只能写函数指针;C++ 11 起才有 lambda,Python 的写法更简洁。
九、return 语句
return [表达式] 退出函数并返回结果;不带表达式的 return(或没有 return)返回 None:
1 | def sum(arg1, arg2): |
1 | 函数内 : 30 |
函数里没有 return 时,调用结果就是 None:
1 | def hello(): |
十、强制位置参数(Python 3.8+)
/ 和 * 可以限定参数的传入方式:
1 | def f(a, b, /, c, d, *, e, f): |
/之前的参数(a、b):只能按位置传,不能用b=20/和*之间的参数(c、d):位置、关键字都行*之后的参数(e、f):只能用关键字传
1 | f(10, 20, 30, d=40, e=50, f=60) # ✅ 正确 |
十一、参数类型小结
| 参数类型 | 写法 | 特点 |
|---|---|---|
| 必需参数 | def f(a, b) |
按顺序传,数量必须一致 |
| 关键字参数 | f(a=1, b=2) |
按名字匹配,顺序可乱 |
| 默认参数 | def f(a, b=5) |
不传就用默认值,必须放后面 |
| 不定长参数 | *args / **kwargs |
多余参数打包成元组/字典 |
| 强制位置 | / |
之前的参数只能用位置传 |
| 强制关键字 | * |
之后的参数只能用关键字传 |