0%

Python 入门


环境搭建

一、安装 Python

两种主流方式,选一个即可。

方式一:官网安装包

到 https://www.python.org/downloads/ 下载 Windows 安装包,双击安装。

关键一步:安装第一屏记得勾选 Add Python to PATH——不勾选的话,之后在命令行里敲 python 会提示”不是内部或外部命令”。

方式二:Anaconda

Anaconda 是”全家桶”发行版,内置了 Python 解释器 + conda 环境管理 + numpy / pandas / matplotlib 等常用库

1
2
3
4
5
Anaconda 自带:
├── Python 解释器
├── conda(环境与包管理工具)
├── numpy / pandas / matplotlib 等数据科学库
└── Jupyter Notebook

到 https://www.anaconda.com/download 下载安装即可。

无论哪种方式,装完都是标准的 Python,不影响学习。

二、验证安装

安装完成后,打开命令提示符(Win+R 输入 cmd)执行:

1
2
python --version     # 输出 Python 3.x.x 说明安装成功
pip --version # 输出 pip 版本说明包管理工具可用

pip 是 Python 的包管理工具,以后装第三方库全靠它(比如 pip install requests)。

三、编辑器:VS Code

选一个顺手的编辑器写代码,我使用的是 VS Code:

  1. 官网 https://code.visualstudio.com 下载安装
  2. 打开 VS Code,安装 Python 扩展(微软官方那个)
  3. Ctrl+Shift+P → 输入 Python: Select Interpreter → 选择刚装的 Python 解释器

装好后新建 test.py,写一行:

1
print("Hello, Python!")

点右上角 ▶ 运行,输出 Hello, Python! 就大功告成。

四、运行 Python 的三种方式

1. 交互式解释器(随手试验)

命令行直接输入 python 回车,进入 >>> 提示符模式,适合随手验证:

1
2
3
4
5
>>> print("Hello")
Hello
>>> 2 + 3
5
>>> exit() # 退出

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
2
3
4
5
$ python --version
(没有输出)
$ where python
C:\Users\tongshixuan\AppData\Local\Microsoft\WindowsApps\python.exe ← 商店占位符,排在第一位
D:\anaconda\python.exe ← 真正的 Python,被挡住

根治方法:开始菜单搜”应用执行别名”→ 关掉 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
2
3
4
5
6
7
8
age = 25                # ✅ 普通变量
user_name = "Alice" # ✅ 下划线连接(Python 推荐这种命名)
MAX_SIZE = 1024 # ✅ 全大写通常表示常量
_total = 100 # ✅ 下划线开头表示"内部使用"

2nd_place = 1 # ❌ 以数字开头
user-name = "Bob" # ❌ 不能有连字符
class = "Math" # ❌ 关键字

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
2
import keyword
print(keyword.kwlist)

三、注释

单行注释用 #,多行注释用三个引号 ''' 或 """:

1
2
3
4
5
6
7
# 这是单行注释
print("Hello, Python!") # 行尾也可以写注释

'''
多行注释
用三引号括起来
'''

四、行与缩进

C 用 {} 表示代码块,Python 用缩进表示代码块:

1
2
3
4
// C 语言
if (a > 0) {
printf("正数");
}
1
2
3
# Python
if a > 0:
print("正数")

注意两个细节:

  1. if 语句末尾要写冒号 :
  2. 缩进推荐统一用 4 个空格(VS Code 装了 Python 扩展后会自动处理,按 Tab 会自动转成 4 空格)

同一个代码块内的缩进必须一致,否则直接报错:

1
2
3
4
5
6
if True:
print("Answer")
print("True")
else:
print("Answer")
print("False") # ❌ 缩进不一致,IndentationError 运行错误
1
2
3
4
File "test.py", line 6
print ("False")
^
IndentationError: unindent does not match any outer indentation level

多个语句组成一个代码块(代码组),首行以关键字开始、以冒号结束,这就是 Python 版的”大括号”。


五、多行语句

一行写不下时,用反斜杠 \ 换行:

1
2
3
total = item_one + \
item_two + \
item_three

在 ()、[]、{} 内部可以直接换行,不需要反斜杠:

1
2
total = ['item_one', 'item_two',
'item_three', 'item_four']

六、数字类型

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
2
3
4
5
6
7
8
9
10
str = '123456789'

print(str) # 123456789
print(str[0:-1]) # 12345678 (第一个到倒数第二个,含头不含尾)
print(str[0]) # 1 (第一个字符)
print(str[2:5]) # 345 (下标2到5,不包含5)
print(str[2:]) # 3456789 (从2开始到最后)
print(str[1:5:2]) # 24 (下标1到5,步长为2)
print(str * 2) # 123456789123456789
print(str + '你好') # 123456789你好

转义符对比:

1
2
print('hello\nrunoob')   # 输出两行:hello 换行 runoob
print(r'hello\nrunoob') # r 开头:原样输出 hello\nrunoob

7.1 索引:取单个字符

用下标取字符,和 C 数组一样从 0 开始;但 Python 多了一个负索引,从右边数:

1
2
3
字符串:  1  2  3  4  5  6  7  8  9
正索引: 0 1 2 3 4 5 6 7 8 ← 从左边数,0 开始
负索引: -9 -8 -7 -6 -5 -4 -3 -2 -1 ← 从右边数,-1 开始
1
2
3
4
5
6
str = '123456789'

print(str[0]) # 1 正索引0 = 第一个字符
print(str[4]) # 5 正索引4 = 第五个字符
print(str[-1]) # 9 负索引-1 = 最后一个字符
print(str[-3]) # 7 负索引-3 = 倒数第三个字符

规律:str[-n] 等价于 str[len(str) - n]。比如 str[-1] 就是 str[8]。所以取最后一个字符永远写 str[-1],不用先数字符串长度。

7.2 切片:取一段字符

切片语法 str[start:end:step],三个参数都可省略,核心规则只有一条:含头不含尾。

  • start:从哪个下标开始(含)
  • end:到哪个下标结束(不含)
  • step:步长,隔几个取一个,默认 1
1
2
3
4
5
6
7
8
9
str = '123456789'

print(str[0:5]) # 12345 下标0到5(不含5)
print(str[2:5]) # 345 下标2到5(不含5)
print(str[:5]) # 12345 start省略 = 从0开始
print(str[2:]) # 3456789 end省略 = 一直到末尾
print(str[:]) # 123456789 全都要(拷贝)
print(str[1:5:2]) # 24 下标1和3,步长2
print(str[0:-1]) # 12345678 负索引同样适用:0到倒数第一个(不含)

“含头不含尾”是最容易搞混的点,图解 str[2:5] 到底取哪几个:

1
2
3
4
字符串:  1  2  3  4  5  6  7  8  9
下标: 0 1 2 3 4 5 6 7 8
[ 2 .. 5 ) ← 起点含下标2,终点不含下标5
取出的字符: 3 4 5

补充一个切片技巧——倒序切片(step 为负数时从右往左取):

1
print(str[::-1])   # 987654321  一行反转整个字符串

切片是 Python 的技能,C 里要写循环才能干的事,Python 不需要


八、等待用户输入 input()

input() 让程序停下来等你输入,回车后继续执行:

1
input("\n\n按下 enter 键后退出。")
1
2
name = input("请输入你的名字:")
print("你好,", name) # 输入的内容存进变量 name

九、同一行写多条语句(不推荐)

Python 允许用分号 ; 分隔多条语句,但强烈不推荐,可读性差:

1
import sys; x = 'runoob'; sys.stdout.write(x + '\n')

记住:Python 里换行就是语句结束,分号是可选的——这和 C 恰好相反,C 必须分号结尾。


十、print 输出

print 默认换行输出,不想换行就加 end="":

1
2
3
4
5
6
7
8
9
10
11
x = "a"
y = "b"

print(x) # 换行输出
print(y)

print('---------')

print(x, end=" ") # 不换行,末尾加一个空格
print(y, end=" ")
print() # 输出一个换行

输出结果:

1
2
3
4
a
b
---------
a b

十一、import 导入模块

三种导入方式:

1
2
3
import math              # 导入整个模块,用 math.sqrt(4) 调用
from math import sqrt # 只导入 sqrt 函数,直接用 sqrt(4)
from math import * # 导入全部函数(不推荐,容易命名冲突)

十二、和 C 的区别速查表

C Python
代码块 {} 大括号 缩进
语句结束 分号 ; 换行
变量声明 int a; 先声明 直接 a = 1,动态类型
注释 // /* */ # 和三引号
字符串 字符数组,操作靠函数 内置 str,直接切片
数组 固定大小 列表自动扩容(后面讲)

小结:这一部分是 Python 的”世界观”——缩进代替大括号、换行代替分号、注释更简单、字符串更强大。基本的程序骨架已经齐全,接下来把数据类型一一展开。


基本数据类型

Python 的核心特点:变量没有类型,类型属于值。不需要像 C 那样先声明 int a,直接赋值就创建了变量,而且同一个变量可以随时指向不同类型的值。

一、变量与赋值

1
2
3
counter = 100        # 整型
miles = 1000.0 # 浮点型
name = "runoob" # 字符串

多变量赋值有两种写法:

1
2
a = b = c = 1                  # 三个变量指向同一个值 1
a, b, c = 1, 2, "runoob" # 同时给多个变量赋不同值

用 type() 查看变量的类型:

1
2
x = 10
print(type(x)) # <class 'int'>

变量必须先赋值再使用,直接访问未定义的变量会报错:

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
2
3
4
5
var1 = 1        # int
var2 = 10.0 # float
var3 = 1 + 2j # complex,实部 1、虚部 2

a = 2.5e2 # 科学计数法 = 2.5 × 10² = 250.0
  • int 只有一种,没有 C 的 long/short 之分
  • float 对应 C 的 double,科学计数法写作 2.5e2
  • complex 复数由实部和虚部构成,虚部用 j 表示(数学里是 i,Python 用 j),也可以用 complex(1, 2) 创建

数字的更多特性(不可变机制、整数无上限、math/random 模块)见后面”数字”部分。

数值运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> 5 + 4    # 加法
9
>>> 4.3 - 2 # 减法
2.3
>>> 3 * 7 # 乘法
21
>>> 2 / 4 # 除法,结果是浮点数
0.5
>>> 2 // 4 # 整除,只取整数部分(向下取整)
0
>>> 17 % 3 # 取余
2
>>> 2 ** 5 # 乘方
32

注意两个 C 里没有的运算符:// 整除和 ** 乘方(C 里要写 pow(2, 5))。另外 C 里 2 / 4 整型除法直接得 0,Python 的 / 永远返回浮点数,想截断必须用 //。

混合运算时,Python 会自动把整数提升为浮点数,不用担心类型问题。

进制写法

1
2
3
0o17    # 八进制(C 的写法是 017,Python 3 必须用 0o 前缀)
0x69 # 十六进制
0b1010 # 二进制

删除变量

1
2
del var          # 删除变量的引用
del var_a, var_b # 一次删多个

和 C 对比:C 里没有”删除变量”这个操作——

  • 栈上的局部变量:函数结束时自动销毁,不用管
  • 堆上的变量(malloc 出来的):要手动 free() 释放内存

Python 的 del 和 free() 不一样:它只是切断变量名和对象之间的联系(相当于把”标签”从对象上撕下来),对象本身由 Python 的垃圾回收机制自动处理——没人引用它时自动回收内存,不需要手动释放。

del 之后再访问这个变量会直接报错:

1
2
del var
print(var) # ❌ NameError: name 'var' is not defined

这个操作日常写代码用得不多,了解机制即可。

四、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
2
3
4
print(bool(0))          # False
print(bool(42)) # True
print(bool('')) # False
print(bool('Python')) # True

逻辑运算用 and、or、not(对应 C 的 &&、||、!)。

五、String 字符串

“基本语法”部分已经详细讲过索引和切片,这里补充两个重要区别:

  1. 字符串不可变:word[0] = 'm' 会直接报错(C 的字符数组可以改,Python 不行)
  2. 没有单独的字符类型:一个字符就是长度为 1 的字符串
1
2
3
word = 'Python'
print(word[0], word[-1]) # P n
# word[0] = 'm' ← 报错 TypeError,字符串不能修改

六、List 列表

列表是 Python 使用最频繁的类型,对应 C 里的数组,但强得多:

  • 写在方括号 [] 里,元素用逗号隔开
  • 元素类型可以混搭:数字、字符串、甚至嵌套列表
  • 自动扩容,不需要 C 的 realloc
  • 索引和切片规则与字符串完全一样
1
2
3
4
5
6
7
8
my_list = ['abcd', 786, 2.23, 'runoob', 70.2]

print(my_list) # ['abcd', 786, 2.23, 'runoob', 70.2]
print(my_list[0]) # abcd
print(my_list[1:3]) # [786, 2.23] 切片返回新列表
print(my_list[2:]) # [2.23, 'runoob', 70.2]
print(my_list * 2) # ['abcd', 786, 2.23, 'runoob', 70.2, 'abcd', 786, 2.23, 'runoob', 70.2] 重复
print(my_list + [1, 2]) # 拼接

列表可变

1
2
3
4
5
6
7
a = [1, 2, 3, 4, 5, 6]
a[0] = 9 # 修改单个元素
print(a) # [9, 2, 3, 4, 5, 6]
a[2:5] = [13, 14, 15] # 切片赋值:整段替换
print(a) # [9, 2, 13, 14, 15, 6]
a[2:5] = [] # 赋空列表 = 删除这一段
print(a) # [9, 2, 6]

注意:不要用 list、str 这类内置类型名当变量名,会覆盖掉内置功能。

七、Tuple 元组

元组和列表几乎一样,唯一区别:不能修改。写在小括号 () 里:

1
2
3
4
5
6
7
my_tuple = ('abcd', 786, 2.23, 'runoob', 70.2)

print(my_tuple[0]) # abcd
print(my_tuple[1:3]) # (786, 2.23)
print(my_tuple + (123, 'runoob')) # 也能拼接

tup[0] = 11 # ❌ TypeError: 'tuple' object does not support item assignment

单元素元组的陷阱:

1
2
3
tup1 = ()       # 空元组
tup2 = (20,) # ✅ 一个元素的元组,必须加逗号
not_tuple = (42) # ❌ 这是整数 42,不是元组!

因为不带逗号时,Python 会把括号当成数学运算的括号。

八、Set 集合

集合是无序、不重复的元素集,写在大括号 {} 里。去重是它的招牌能力:

1
2
sites = {'Google', 'Taobao', 'Runoob'}
print(sites) # 输出顺序不固定(无序)

**创建空集合必须用 set()**,因为 {} 创建的是空字典:

1
2
s = set()      # ✅ 空集合
d = {} # ❌ 这是空字典

什么是”去重”

“去重”就是把一组数据中重复出现的元素只保留一个。集合能去重的根本原因:元素不允许重复——往集合里添加一个已存在的元素,会被静默忽略:

1
2
3
s = {1, 2}
s.add(2) # 2 已经存在,什么都不发生
print(s) # {1, 2}

最常见的用法是——把列表丢进 set 再转回列表:

1
2
nums = [1, 3, 2, 1, 3, 1]
unique = list(set(nums)) # [1, 2, 3] 自动去重

list(set(nums)) 是嵌套调用,从里往外执行,等价于两步:

1
2
temp = set(nums)      # ① set(nums):列表 → 集合,自动去重 → {1, 2, 3}
unique = list(temp) # ② list(temp):集合 → 列表 → [1, 2, 3]

为什么要转回列表? 因为集合无序、不能按下标取(s[0] 会报错),日常按顺序处理数据还是列表顺手,所以去重完再转回列表。

注意:去重后元素的顺序不保证(例子输出 [1, 2, 3] 恰好有序是小整数哈希的巧合)。要”去重且保序”得用循环,后面”循环”部分会讲。

同样的事在 C 里要写两层循环,每个元素和前面所有元素比较一遍、重复的跳过,代码一大堆:

1
2
3
// C:两层循环 + 逐个比较,去重要自己实现
int a[] = {1, 3, 2, 1, 3, 1};
// 去重后 {1, 2, 3},需要自己写循环

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
sites = {'Google', 'Taobao', 'Runoob', 'Facebook', 'Zhihu', 'Baidu'}

print(sites) # 输出集合(无序,重复元素会被自动去掉)

# 成员测试
if 'Runoob' in sites:
print('Runoob 在集合中')
else:
print('Runoob 不在集合中')

# set 可以进行集合运算
a = set('abracadabra')
b = set('alacazam')

print(a) # a 中的唯一字符

print(a - b) # a 和 b 的差集(在 a 中但不在 b 中)
print(a | b) # a 和 b 的并集(在 a 或 b 中)
print(a & b) # a 和 b 的交集(同时在 a 和 b 中)
print(a ^ b) # a 和 b 的对称差集(在 a 或 b 中,但不同时存在)

输出:

1
2
3
4
5
6
7
{'Zhihu', 'Baidu', 'Taobao', 'Runoob', 'Google', 'Facebook'}
Runoob 在集合中
{'b', 'c', 'a', 'r', 'd'}
{'r', 'b', 'd'}
{'b', 'c', 'a', 'z', 'm', 'r', 'l', 'd'}
{'c', 'a'}
{'z', 'b', 'm', 'r', 'l', 'd'}

九、Dictionary 字典

字典是键值对集合,对应其他语言里的”映射/哈希表”。C 里没有直接对应的东西,可以理解成”用字符串当下标的数组“:

1
2
3
4
5
6
7
8
tinydict = {'name': 'runoob', 'code': 1, 'site': 'www.runoob.com'}

print(tinydict['name']) # runoob 按键取值
print(tinydict.keys()) # 所有键
print(tinydict.values()) # 所有值

tinydict['code'] = 2 # 修改已有键的值
tinydict['new'] = 'hello' # 新增键值对

键的要求:必须是不可变类型(数字、字符串、元组),且同一个字典里键不能重复。

1
2
d = {}                                    # 空字典
d = dict([('Runoob', 1), ('Google', 2)]) # 从键值对序列构造

Python 3.7 起字典保持插入顺序,不用担心”无序”问题。

四种容器怎么选

C 里只有”数组”这一种容器,Python 拆成了四种,各管一摊:

类型 什么时候用 和 C 数组对比
List 列表 默认选择:存一组数据、要增删改 C 数组的升级版:自动扩容、元素类型不限、支持切片
Tuple 元组 数据固定不变(坐标、日期、常量配置) 类似 C 的 const 数组,不可修改
Set 集合 去重、判断”在不在”、交并差运算 C 没有对应物,要自己写代码实现
Dictionary 字典 按名字找值(姓名→成绩、编号→记录) C 没有直接对应,相当于自己手写哈希表

选型一句话:默认列表;不可改用元组;去重用集合;按名查找用字典。

十、bytes 二进制类型(了解)

bytes 表示二进制数据(图片、网络传输等),在字符串前加 b:

1
2
x = b"hello"
print(x[0]) # 104,bytes 的元素是整数(ASCII 值),不是字符

入门阶段知道有这回事即可,学到网络编程再深入。

小结:这一部分把 Python 的”容器”过了一遍——列表可变可切片、元组只读、集合去重、字典按键查值,加上假值清单。


数据类型转换

一、隐式类型转换(自动完成)

不同类型的数据做运算时,Python 自动把精度低的类型转成精度高的类型,防止数据丢失:

1
2
3
4
5
6
num_int = 123
num_flo = 1.23

num_new = num_int + num_flo
print(num_new) # 124.23
print(type(num_new)) # <class 'float'> 整数自动提升为浮点数

类型从低到高:bool < int < float < complex。这和 C 的规则一样——C 里 int 和 double 相加,整数也会自动提升为 double。

但不是所有类型都能自动转,整数加字符串直接报错:

1
2
3
num_int = 123
num_str = "456"
print(num_int + num_str) # ❌ TypeError: unsupported operand type(s) for +: 'int' and 'str'

这种情况就需要手动转换——显式类型转换。

二、显式类型转换(手动转换)

把类型名当函数用:int()、float()、str()。

1
2
3
4
5
6
7
8
9
10
x = int(1)     # 1
y = int(2.8) # 2 浮点转整数直接截断,不是四舍五入
z = int("3") # 3 数字字符串可以转

x = float(1) # 1.0
y = float("3") # 3.0
w = float("4.2") # 4.2

x = str(2) # '2'
y = str(3.0) # '3.0'

刚才报错的例子,转一下就能算了:

1
2
3
4
5
num_int = 123
num_str = "456"

num_str = int(num_str) # 先把字符串转成整数
print(num_int + num_str) # 579

三、不是想转就能转

转换是否可行,取决于源数据本身有没有足够的信息来表示目标类型:

  • "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
2
3
9 // 2    # 4     正数看不出区别
-9 // 2 # -5 Python 向下取整
# 而 C 里 -9 / 2 = -4(向零取整),结果不同!

二、比较运算符

== != > < >= <=,和 C 完全一样,结果是 True / False:

1
2
3
10 == 10    # True
10 != 20 # True
10 > 20 # False

三、赋值运算符

= += -= *= /= %= **= //=,含义和 C 一样(c += a 等价于 c = c + a):

1
2
3
c = 10
c += 5 # c = c + 5 = 15
c *= 2 # c = c * 2 = 30

四、位运算符

位运算符把数字当作二进制来运算。Python 的位运算符和 C 完全一样,学过 STM32 寄存器操作的会非常熟悉。

以下用 a = 60、b = 13 举例,先写出二进制:

1
2
a = 60   →   0011 1100
b = 13 → 0000 1101

按位与 &

两个位都是 1 结果才是 1:

1
2
3
  0011 1100
& 0000 1101
= 0000 1100 → 12

常用:取位(掩码),比如 x & 1 判断奇偶、x & 0xFF 取低 8 位。

按位或 |

两个位有一个是 1 结果就是 1:

1
2
3
  0011 1100
| 0000 1101
= 0011 1101 → 61

常用:置位(把某一位设为 1),如 x | 0x10 把第 5 位置 1。

按位异或 ^

两个位不相同结果才是 1:

1
2
3
  0011 1100
^ 0000 1101
= 0011 0001 → 49

特性:x ^ x 得 0,x ^ 0 得 x。常用:翻转指定某一位。

按位取反 ~

每个位 0 变 1、1 变 0。注意 Python 里 ~x 等于 -x - 1(补码表示):

1
~60     # -61   相当于 -(60+1)

左移 << 和右移 >>

把二进制位整体移动,空出的位补 0:

1
2
0011 1100 << 2   =  1111 0000   →  240
0011 1100 >> 2 = 0000 1111 → 15

常用:左移一位相当于乘 2,右移一位相当于整除 2(嵌入式里比乘除法快)。

综合示例:

1
2
3
4
5
6
7
8
9
a = 60    # 0011 1100
b = 13 # 0000 1101

print(a & b) # 12 按位与
print(a | b) # 61 按位或
print(a ^ b) # 49 按位异或
print(~a) # -61 按位取反
print(a << 2) # 240 左移2位
print(a >> 2) # 15 右移2位

用 bin() 函数可以随时查看一个数的二进制形式:

1
2
print(bin(60))     # 0b111100
print(bin(a & b)) # 0b1100

嵌入式开发的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,否则返回 y
  • x or y:x 为真返回 x,否则返回 y
1
2
3
4
10 and 20    # 20   10 为真,返回后面的 20
0 and 20 # 0 0 为假,直接返回 0
10 or 20 # 10 10 为真,直接返回 10
0 or 20 # 20 0 为假,返回 20

这个特性最常见的用途:给变量一个默认值:

1
name = input_name or "匿名用户"   # input_name 为空(假值)就用默认值

not 和 C 的 ! 一样,返回 True / False。

六、成员运算符 in(Python 独有)

判断一个元素在不在序列里,C 里要写循环,Python 一个关键字搞定:

1
2
3
4
'Runoob' in {'Google', 'Taobao', 'Runoob'}   # True   在集合里
'a' in 'hello' # False 字符串里没有 a
2 in [1, 2, 3] # True 在列表里
'name' in {'name': 'runoob'} # True 在字典里(判断的是键)

not in 取反:5 not in [1, 2, 3] → True。

七、身份运算符 is(判断是不是同一个对象)

  • == 比较值是否相等
  • is 比较对象身份(是不是同一个内存对象),类似 C 里比较指针地址
1
2
3
4
5
6
7
8
a = [1, 2, 3]
b = a # b 和 a 指向同一个列表
b is a # True 同一个对象
b == a # True 值也相等

b = a[:] # 切片拷贝,b 变成新对象
b is a # False 不是同一个对象了
b == a # True 但值仍然相等

日常判断值相等用 == 就够了;is 最常用的场景是和 None 比较(x is None)。注意 1 is True 是 False——虽然 1 == True 是 True(bool 是 int 的子类),但两者不是同一个对象。

八、运算符优先级

完整的优先级表很长,记住几条常用的(从高到低):

1
2
3
4
5
6
7
**        幂运算
* / // % 乘除取余
+ - 加减
比较(== != > < ...)
not
and
or

和 C 的规则基本一致:算术 > 比较 > 逻辑。记不住的时候加括号最稳妥,可读性也更好:

1
2
3
# x or y and z 是 x or (y and z),因为 and 优先级高于 or
if x or y and z:
...

数字

“基本数据类型”部分已经讲过数字的类型(int / float / complex)和基本运算,本篇补充数字的不可变机制、和 C 不一样的两个特性,以及最常用的两个模块:math 和 random。

一、数字是不可变的(改值 = 重新分配内存)

数字是不可变类型:你无法”修改”一个数字对象本身,改变变量的值,实际上是让变量改指向一个新的对象。

C 里 a = 6 是修改同一块内存里的值:

1
2
int a = 5;
a = 6; // 还是同一块内存,内容从 5 改成 6

Python 里 a = 6 是让 a 改指向一个新的对象,旧的 5 对象还留在内存里(直到被垃圾回收):

1
2
a = 5
a = 6 # a 改指向新的整数对象 6,原来的 5 被丢弃

用 id() 函数可以验证——它返回对象的内存地址,改值后地址变了,说明确实是新对象:

1
2
3
4
a = 5
print(id(a)) # 140734982556400(每次运行的数字不同)
a = 6
print(id(a)) # 140734982556432 地址变了 → 是新的对象

顺带了解:整数有个小整数缓存机制(-5 ~ 256 常驻内存),所以 a = 5; b = 5 时 a is b 为 True;大数字每次都是新建对象。写代码时不用管,知道有这回事即可。

二、整数没有大小限制

C 的 int 有范围(32 位下约 ±21 亿),溢出会产生各种诡异问题;Python3 的整数没有大小限制,想多大就多大:

1
2
10 ** 30    # 1000000000000000000000000000000   随便算,不会溢出
2 ** 100 # 1267650600228229401496703205376

刷算法题时完全不用担心溢出,这是 Python 的福利。

三、// 的结果类型跟着操作数走

// 的结果不一定是整数——和操作数的类型一致:

1
2
3
7 // 2      # 3     两个整数 → 整数
7.0 // 2 # 3.0 有浮点数 → 浮点数
7 // 2.0 # 3.0

同理 / 永远返回浮点数。判断结果类型时不要想当然,先看操作数。

四、math 模块(数学函数)

数学运算大多要导入 math 模块(对应 C 的 <math.h>):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import math

abs(-10) # 10 绝对值(内置函数,不用导入)
round(3.14159) # 3 四舍五入(内置函数)
round(3.14159, 2) # 3.14 保留两位小数
pow(2, 10) # 1024 幂运算(内置函数,等于 2**10)
max(3, 7, 1) # 7 最大值(内置函数)
min([3, 7, 1]) # 1 最小值,参数可以是序列

math.sqrt(16) # 4.0 平方根
math.floor(4.9) # 4 向下取整
math.ceil(4.1) # 5 向上取整
math.exp(1) # 2.718281828459045 e 的 x 次方
math.fabs(-10) # 10.0 浮点绝对值(和 abs 一样,但结果总是 float)
math.log(math.e) # 1.0 自然对数
math.log(100, 10) # 2.0 以 10 为底的对数
math.log10(100) # 2.0 以 10 为底的对数(等价于 log(x, 10))
math.modf(3.7) # (0.7, 3.0) 拆分小数部分和整数部分

math.pi # 3.141592653589793 圆周率
math.e # 2.718281828459045 自然常数

五、三角函数

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
2
3
4
5
6
import math

math.sin(math.pi / 2) # 1.0
math.degrees(math.pi) # 180.0
math.radians(180) # 3.141592653589793
math.hypot(3, 4) # 5.0

六、random 模块(随机数)

随机数在游戏、算法、抽样里都很常用:

1
2
3
4
5
6
7
8
import random

random.random() # 0.0 ~ 1.0 之间的随机小数
random.randint(1, 100) # 1 ~ 100 之间的随机整数(含两端)
random.randrange(0, 10, 2) # 0~9 之间按步长 2 抽取(0, 2, 4, 6, 8)
random.uniform(1.0, 5.0) # 1.0 ~ 5.0 之间的随机小数
random.choice(['石头', '剪刀', '布']) # 从序列里随机抽一个
random.shuffle([1, 2, 3, 4, 5]) # 原地打乱列表顺序

random.seed(x) 可以固定随机种子,让随机结果可复现——调试需要确定性时常用。


字符串

字符串是 Python 中最常用的数据类型。”基本语法”部分已经见过它的概貌,本篇完整展开:创建、索引切片、转义、运算符、格式化(重点)、常用方法。

一、创建字符串

用单引号 ' 或双引号 " 括起来即可,两者完全等价:

1
2
var1 = 'Hello World!'
var2 = "Runoob"

注意:Python 没有单独的字符类型,一个字符就是长度为 1 的字符串。

二、索引与切片

字符串可以用 [] 按下标访问,索引从 0 开始,负索引从右往左:

1
2
3
字符串:  H  e  l  l  o     W  o  r  l  d  !
正索引: 0 1 2 3 4 5 6 7 8 9 10 11
负索引: -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1
1
2
3
4
5
6
var1 = 'Hello World!'
var2 = "Runoob"

print(var1[0]) # H 正索引 0 = 第一个字符
print(var2[1:5]) # unoo 切片:下标1到5(不含5)
print(var1[-1]) # ! 负索引 -1 = 最后一个字符

切片语法 str[start:end:step],含头不含尾(start 含、end 不含),step 是步长默认 1:

1
2
3
4
5
6
7
s = 'Hello World!'

print(s[0:5]) # Hello 下标0到5(不含5)
print(s[:5]) # Hello start省略 = 从0开始
print(s[6:]) # World! end省略 = 到末尾
print(s[::2]) # HloWrd 隔一个取一个
print(s[::-1]) # !dlroW olleH 反向(一行反转字符串)

三、字符串更新(拼接)

字符串是不可变的,不能修改某个字符;”更新”其实是切片 + 拼接生成新字符串:

1
2
var1 = 'Hello World!'
print(var1[:6] + 'Runoob!') # Hello Runoob! 截取前半段再拼新的

四、转义字符

在字符串里表示特殊字符,用反斜杠 \ 转义:

转义字符 含义 示例
\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
2
3
4
5
print('Hello\nWorld')        # 换行输出两行
print('Hello\tWorld') # Hello 和 World 之间一个制表符
print('It\'s ok') # It's ok 转义单引号
print('\\n') # \n 输出反斜杠本身
print("\x48\x65\x6c\x6c\x6f") # Hello 十六进制拼出 Hello

\r 的一个经典应用:终端进度条(\r 回到行首重画):

1
2
3
4
5
6
import time

for i in range(101):
bar = '[' + '=' * (i // 2) + ' ' * (50 - i // 2) + ']'
print(f"\r{bar} {i}%", end='', flush=True)
time.sleep(0.05)

原始字符串:字符串前加 r(大小写都可以),反斜杠不转义,按字面输出:

1
2
3
4
print(r'\n')      # \n   原样输出,不换行
print(R'\n') # \n
print('C:\new') # 换行了,因为 \n 被转义
print(r'C:\new') # C:\new 路径场景必须用 r

五、字符串运算符

假设 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
2
3
4
5
6
7
8
a = "Hello"
b = "Python"

print(a + b) # HelloPython
print(a * 2) # HelloHello
print(a[1:4]) # ell
print('H' in a) # True
print('M' not in a) # True

六、字符串格式化(重点)

什么是格式化? 就是把变量嵌入到一段模板文本中,并按指定的格式输出。

比如要把名字、年龄拼成一句话。手工拼接很痛苦——要不停用 + 连接,数字还得先 str() 转换:

1
2
3
name = '小明'
age = 10
print("我叫" + name + "今年" + str(age) + "岁") # 容易写错、难看

格式化就是在模板里留好占位符,把变量填进去:

1
print("我叫 %s 今年 %d 岁" % (name, age))   # 我叫 小明 今年 10 岁

它还能顺便控制输出格式:保留几位小数、占几个宽度、补零(如 %.2f 把 96.5 输出成 96.50)。这和 C 里 printf / sprintf 的格式化是同一回事。

Python 有 3 种格式化方式,按推荐程度从低到高。

6.1 % 格式化(和 C 的 sprintf 同款)

1
2
print("我叫 %s 今年 %d 岁!" % ('小明', 10))
# 我叫 小明 今年 10 岁!

常用格式符:

符号 含义
%s 字符串
%d 整数
%f 浮点数(%.2f 保留两位小数)
%x / %o 十六进制 / 八进制
%e 科学计数法

宽度和精度:%5d 占 5 格、%.2f 两位小数:

1
2
print('%5d' % 42)      # '   42'
print('%.2f' % 3.14159) # '3.14'

6.2 str.format()(Python 2.6+)

用 {} 占位,不用记 %s 还是 %d:

1
2
print("我叫 {} 今年 {} 岁!".format('小明', 10))
# 我叫 小明 今年 10 岁!

6.3 f-string(Python 3.6+,推荐)

字符串前加 f,大括号里直接写变量或表达式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
name = 'Runoob'

f'Hello {name}' # 'Hello Runoob' 替换变量
f'{1 + 2}' # '3' 直接算表达式
f'{name.upper()}' # 'RUNOOB' 甚至调用方法

w = {'name': 'Runoob', 'url': 'www.runoob.com'}
f'{w["name"]}: {w["url"]}' # 'Runoob: www.runoob.com' 字典取值

x = 1
f'{x + 1=}' # 'x+1=2' 3.8+ 自动打印表达式和结果

# 保留小数:冒号是分隔符,.2f 表示保留 2 位小数
pi = 3.1415926
f'{pi:.2f}' # '3.14' 保留 2 位小数
f'{pi:.3f}' # '3.142' 保留 3 位小数(四舍五入)
f'{0.5:.2f}' # '0.50' 不足位补 0

七、三引号(多行字符串)

用 ''' 或 """ 包起来,字符串可以跨多行,所见即所得:

1
2
3
4
5
6
para_str = """这是一个多行字符串的实例
多行字符串可以使用制表符
TAB ( \t )。
也可以使用换行符 [ \n ]。
"""
print(para_str)

典型场景:写 HTML 片段、SQL 语句(不用手工拼接和转义):

1
2
3
4
5
6
sql = '''
CREATE TABLE users (
login VARCHAR(8),
uid INTEGER
)
'''

八、Unicode 字符串

Python3 中所有字符串都是 Unicode 字符串,中英文混写没有任何问题(Python2 里要写 u'...' 前缀,Python3 不需要):

1
print('你好,世界')    # 直接输出中文

九、常用字符串方法

9.1 最常用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
s = '  Hello, Python World  '

s.upper() # ' HELLO, PYTHON WORLD ' 全大写
s.lower() # ' hello, python world ' 全小写
s.title() # ' Hello, Python World ' 每个单词首字母大写
s.strip() # 'Hello, Python World' 去掉两端空白
s.replace('Python', 'C') # ' Hello, C World ' 替换

'123'.isdigit() # True 是不是全是数字
'abc'.isalpha() # True 是不是全是字母
'abc123'.isalnum() # True 是不是字母或数字组成

s.find('Python') # 9 找子串位置,找不到返回 -1
s.count('o') # 3 统计子串出现次数
s.startswith(' He') # True 以什么开头
s.endswith(' ') # True 以什么结尾

'www.runoob.com'.split('.') # ['www', 'runoob', 'com'] 按分隔符切成列表
'-'.join(['a', 'b', 'c']) # 'a-b-c' 用分隔符把列表连成字符串

len(s) # 24 字符串长度
'42'.zfill(5) # '00042' 补零到指定位数

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
2
3
4
list1 = ['Google', 'Runoob', 1997, 2000]   # 元素类型可以混搭
list2 = [1, 2, 3, 4, 5]
list3 = ["a", "b", "c", "d"]
list4 = ['red', 'green', 'blue', 'yellow', 'white', 'black']

二、访问列表元素

索引从 0 开始,负索引从尾部数(-1 是最后一个):

1
2
3
4
5
6
list = ['red', 'green', 'blue', 'yellow', 'white', 'black']

print(list[0]) # red
print(list[2]) # blue
print(list[-1]) # black 负索引:最后一个
print(list[-3]) # yellow

三、切片

和字符串的切片规则完全一样:list[start:end:step],含头不含尾:

1
2
3
4
5
6
nums = [10, 20, 30, 40, 50, 60, 70, 80, 90]

print(nums[0:4]) # [10, 20, 30, 40] 下标0到4(不含4)
print(nums[1:-2]) # [20, 30, 40, 50, 60, 70] 负数同样适用
print(nums[::2]) # [10, 30, 50, 70, 90] 隔一个取一个
print(nums[::-1]) # [90, 80, 70, 60, 50, 40, 30, 20, 10] 反转

注意:切片返回的是新列表,原列表不变。

四、更新列表

列表是可变的,可以修改单个元素、追加新元素:

1
2
3
4
5
6
7
8
9
list = ['Google', 'Runoob', 1997, 2000]

print(list[2]) # 1997
list[2] = 2001 # 修改元素
print(list[2]) # 2001

list1 = ['Google', 'Runoob', 'Taobao']
list1.append('Baidu') # 末尾追加
print(list1) # ['Google', 'Runoob', 'Taobao', 'Baidu']

还能切片赋值(整段替换),C 里做不到:

1
2
3
a = [1, 2, 3, 4, 5, 6]
a[2:5] = [13, 14, 15] # 把下标2~4整段替换
print(a) # [1, 2, 13, 14, 15, 6]

五、删除列表元素

用 del 按下标删除:

1
2
3
4
5
list = ['Google', 'Runoob', 1997, 2000]

print("原始列表 : ", list) # ['Google', 'Runoob', 1997, 2000]
del list[2]
print("删除第三个元素 : ", list) # ['Google', 'Runoob', 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
2
3
squares = [1, 4, 9, 16, 25]
squares += [36, 49, 64]
print(squares) # [1, 4, 9, 16, 25, 36, 49, 64]

七、嵌套列表

列表里可以再装列表(相当于 C 的二维数组):

1
2
3
4
5
6
a = ['a', 'b', 'c']
n = [1, 2, 3]
x = [a, n] # x 里装两个列表
print(x) # [['a', 'b', 'c'], [1, 2, 3]]
print(x[0]) # ['a', 'b', 'c']
print(x[0][1]) # 'b' 先取第0个,再取它的第1个

八、列表方法(重点)

添加元素

1
2
3
4
5
list = [1, 2, 3]

list.append(4) # [1, 2, 3, 4] 末尾追加一个
list.extend([5, 6]) # [1, 2, 3, 4, 5, 6] 末尾追加多个(扩展)
list.insert(0, 0) # [0, 1, 2, 3, 4, 5, 6] 指定位置插入

append 和 extend 的区别:append 把参数当一个元素放进去,extend 把参数展开逐个放:

1
2
3
4
a = [1, 2]
a.append([3, 4]) # [1, 2, [3, 4]] 整个列表作为一个元素
b = [1, 2]
b.extend([3, 4]) # [1, 2, 3, 4] 展开后逐个加入

删除元素(四种方式对比)

1
2
3
4
5
6
7
list = [10, 20, 30, 20]

list.remove(20) # 按值删第一个匹配:list → [10, 30, 20]
x = list.pop() # 弹出最后一个并返回值:x = 20,list → [10, 30]
x = list.pop(0) # 弹出指定下标:x = 10,list → [30]
del list[0] # 按下标删,不返回值
list.clear() # 清空所有元素 → []
方式 用法 区别
remove(值) list.remove(20) 按值删,只删第一个匹配
pop([下标]) list.pop() 按下标删,并返回被删元素,默认删最后一个
del list[下标] del list[0] 按下标删,不返回
clear() list.clear() 清空全部

查找与统计

1
2
3
4
list = ['Google', 'Runoob', 'Taobao', 'Runoob']

list.index('Runoob') # 1 第一次出现的位置,找不到会报错
list.count('Runoob') # 2 出现次数

排序与反转

1
2
3
4
5
nums = [3, 1, 4, 1, 5, 9, 2]

nums.sort() # 原地升序排序 → [1, 1, 2, 3, 4, 5, 9]
nums.sort(reverse=True) # 降序 → [9, 5, 4, 3, 2, 1, 1]
nums.reverse() # 原地反转

sort() 是原地排序(直接改原列表),和内置函数 sorted()(返回新列表)不同。

复制:一个隐藏的坑

b = a 不是复制——两个变量指向同一个列表,改一个另一个也变:

1
2
3
4
a = [1, 2, 3]
b = a # 只是让 b 也指向 a 的列表
b.append(4)
print(a) # [1, 2, 3, 4] a 也被改了!

要真正复制用 copy():

1
2
3
4
5
a = [1, 2, 3]
b = a.copy() # 复制出一个新列表
b.append(4)
print(a) # [1, 2, 3] 不受影响
print(b) # [1, 2, 3, 4]

这和前面”身份运算符 is”讲的是同一件事:b = a 时 b is a 为 True(同一对象),b = a.copy() 后 b is a 为 False。

九、列表内置函数

1
2
3
4
5
6
list = [1, 5, 3, 9, 2]

len(list) # 5 元素个数
max(list) # 9 最大值
min(list) # 1 最小值
list((1, 2)) # [1, 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
2
tup1 = ('Google', 'Runoob', 1997, 2000)
tup2 = (1, 2, 3, 4, 5)

括号不是必须的——元组的本质是逗号:

1
2
tup3 = "a", "b", "c", "d"    # 不写括号也是元组
print(type(tup3)) # <class 'tuple'>

空元组:

1
tup1 = ()

单元素元组的坑:只有一个元素时,必须加逗号,否则括号被当成运算符,(50) 只是整数 50:

1
2
3
4
5
tup1 = (50)
print(type(tup1)) # <class 'int'> ← 这是整数!

tup2 = (50,)
print(type(tup2)) # <class 'tuple'> ← 加逗号才是元组

这和 C 的道理一样:(a + b) * c 里括号就是运算符,Python 的括号默认也是运算符,想表示元组必须用逗号表态。

二、访问元组

下标从 0 开始,支持索引和切片,和字符串、列表完全一样:

1
2
3
4
5
tup1 = ('Google', 'Runoob', 1997, 2000)
tup2 = (1, 2, 3, 4, 5, 6, 7)

print(tup1[0]) # Google
print(tup2[1:5]) # (2, 3, 4, 5) 切片结果还是元组

三、元组不能修改(和列表最本质的区别)

给元素赋值直接报错:

1
2
3
tup = ('r', 'u', 'n', 'o', 'o', 'b')
tup[0] = 'g'
# TypeError: 'tuple' object does not support item assignment

不能改,但可以连接:用 + 把两个元组合成一个新元组,原元组不变:

1
2
3
4
5
tup1 = (12, 34.56)
tup2 = ('abc', 'xyz')

tup3 = tup1 + tup2
print(tup3) # (12, 34.56, 'abc', 'xyz')

不能删单个元素,但能删整个元组:

1
2
3
4
tup = ('Google', 'Runoob', 1997, 2000)
del tup # 整个元组被删除
print(tup)
# NameError: name 'tup' is not defined ← 元组已不存在,访问报错

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
2
3
4
5
6
7
8
9
10
11
12
13
14
a = (1, 2, 3)
b = (4, 5, 6)
c = a + b
print(c) # (1, 2, 3, 4, 5, 6)

a += b # 相当于 a = a + b,a 指向新元组
print(a) # (1, 2, 3, 4, 5, 6)

print(('Hi!',) * 4) # ('Hi!', 'Hi!', 'Hi!', 'Hi!')

print(3 in (1, 2, 3)) # True

for x in (1, 2, 3):
print(x, end=" ") # 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
2
3
4
5
tup = ('Google', 'Runoob', 'Taobao', 'Wiki', 'Weibo', 'Weixin')
print(tup[1]) # Runoob
print(tup[-2]) # Weibo
print(tup[1:]) # ('Runoob', 'Taobao', 'Wiki', 'Weibo', 'Weixin')
print(tup[1:4]) # ('Runoob', 'Taobao', 'Wiki')

六、元组内置函数

1
2
3
4
5
6
tuple1 = ('Google', 'Runoob', 'Taobao')
print(len(tuple1)) # 3 元素个数

tuple2 = ('5', '4', '8')
print(max(tuple2)) # 8 最大值(字符串按字典序比较)
print(min(tuple2)) # 4 最小值

tuple(iterable):把列表、字符串等可迭代对象转成元组:

1
2
3
list1 = ['Google', 'Taobao', 'Runoob', 'Baidu']
tuple1 = tuple(list1)
print(tuple1) # ('Google', 'Taobao', 'Runoob', 'Baidu')

列表部分讲过 list((1, 2)) 把元组转成列表,这里正好相反:tuple() 把列表转成元组。

七、不可变性的真相

“不可变”指的是:元组指向的那块内存内容不能变。重新赋值不是修改,而是让变量绑定一个新对象:

1
2
3
4
5
tup = ('r', 'u', 'n', 'o', 'o', 'b')
print(id(tup)) # 4440687904 原对象的地址

tup = (1, 2, 3) # 重新赋值:绑定到新元组
print(id(tup)) # 4441088800 地址变了,是另一个对象
1
2
3
4
5
6
7
8
第一次赋值:
tup ──────► 元组A ('r','u','n','o','o','b')
地址 4440687904

重新赋值:
tup ──────► 元组B (1,2,3) ← 全新对象
地址 4441088800
元组A 还在内存里,只是没人引用它了

对比列表:list[0] = 'g' 是原地改内容,地址不变;元组做不到原地改,只能整个换对象。

八、元组解包(实用技巧)

元组可以直接拆开赋给多个变量:

1
2
tup = (1, 2, 3)
a, b, c = tup # a=1, b=2, c=3

最经典的用途:交换两个变量。C 里需要临时变量:

1
2
3
4
// C 写法
int temp = a;
a = b;
b = temp;
1
2
# Python 写法
a, b = b, a # 一行搞定,没有临时变量

原理:右边先组合成一个临时元组 (b, a),再解包赋给 a, b。

函数返回多个值也靠元组:

1
2
3
4
def get_point():
return 3, 4 # 返回元组 (3, 4)

x, y = get_point() # x=3, y=4

九、元组 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
2
tinydict1 = { 'abc': 456 }
tinydict2 = { 'abc': 123, 98.6: 37 } # 键还可以是数字

注意:dict 是 Python 的关键字和内置函数名,变量名不要命名为 dict——和列表章节说过的”不要用 list、str 当变量名”是同一个道理,会覆盖掉内置功能。

二、创建空字典

两种方式,效果一样:

1
2
3
4
5
6
7
8
emptyDict = {}         # 方式一:大括号
print(emptyDict) # {}
print("Length:", len(emptyDict)) # Length: 0
print(type(emptyDict)) # <class 'dict'>

emptyDict2 = dict() # 方式二:内置函数 dict()
print(emptyDict2) # {}
print(type(emptyDict2)) # <class 'dict'>

注意:{} 创建的是空字典,不是空集合——集合章节讲过,空集合必须用 set(),因为 {} 被字典占用了。

三、访问字典里的值

把键放进方括号 []:

1
2
3
4
tinydict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'}

print(tinydict['Name']) # Runoob
print(tinydict['Age']) # 7

和列表的区别:列表按下标(0、1、2…)访问,字典按键访问,键可以是字符串、数字、元组。

访问不存在的键会直接报错:

1
2
3
tinydict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'}
print(tinydict['Alice'])
# KeyError: 'Alice'

C 里数组越界不提示(未定义行为),Python 直接抛 KeyError,反而更好排查。不想报错就用 get()(见第八节)。

四、修改字典

向字典添加新内容 = 给新键赋值;修改已有内容 = 给已有键赋新值。写法完全相同,Python 自己判断:

1
2
3
4
5
6
7
tinydict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'}

tinydict['Age'] = 8 # 键 Age 已存在 → 修改值
tinydict['School'] = "菜鸟教程" # 键 School 不存在 → 添加新键值对

print(tinydict['Age']) # 8
print(tinydict['School']) # 菜鸟教程

五、删除字典元素

三种删除,范围从小到大:

1
2
3
4
5
6
7
8
tinydict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'}

del tinydict['Name'] # 1. 删除单个键值对
tinydict.clear() # 2. 清空字典(变成空字典 {})
del tinydict # 3. 删除整个字典

print(tinydict['Age'])
# NameError: name 'tinydict' is not defined ← 字典已不存在,访问报错

clear() 和 del 的区别:清空后字典还在(空的),del 之后字典整个没了。

六、字典键的特性(重点)

1. 键不允许重复

同一个键出现两次,后写的值覆盖先写的:

1
2
3
tinydict = {'Name': 'Runoob', 'Age': 7, 'Name': '小菜鸟'}

print(tinydict['Name']) # 小菜鸟 后写的覆盖了先写的

2. 键必须不可变

值可以是任意类型,键必须是不可变的——数字、字符串、元组都可以,列表不行:

1
2
tinydict = {['Name']: 'Runoob', 'Age': 7}
# TypeError: unhashable type: 'list'

原因:字典靠哈希(把键算成一个数字)实现快速查找,可变对象内容一变哈希就乱了,所以不能用。这正是元组章节讲的”不可变”的用武之地——需要多个字段当键时用元组:

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
tinydict = {'Name': 'Runoob', 'Age': 7}

# get:键不存在不报错,返回默认值
print(tinydict.get('Name')) # Runoob
print(tinydict.get('Alice')) # None 键不存在,默认返回 None
print(tinydict.get('Alice', '查无此人')) # 查无此人 自己指定默认值

# in:判断键是否存在
print('Name' in tinydict) # True
print('Alice' in tinydict) # False

# keys / values / items:三个"视图"
print(tinydict.keys()) # dict_keys(['Name', 'Age'])
print(tinydict.values()) # dict_values(['Runoob', 7])
print(tinydict.items()) # dict_items([('Name', 'Runoob'), ('Age', 7)])

get() 和 tinydict['key'] 的区别:直接访问不存在的键抛 KeyError 中断程序,get() 返回默认值让程序继续。**不确定键是否存在时用 get()**。

keys / values / items 返回的是视图对象,不是列表。它跟着字典实时变化,能用 for 遍历,想转成列表用 list() 包一下。

改:update / setdefault

1
2
3
4
5
6
7
8
9
10
tinydict = {'Name': 'Runoob', 'Age': 7}
extra = {'Age': 8, 'School': '菜鸟教程'}

tinydict.update(extra) # 合并:Age 已存在被改成 8,School 不存在被添加
print(tinydict) # {'Name': 'Runoob', 'Age': 8, 'School': '菜鸟教程'}

# setdefault:键存在返回原值;键不存在则添加
print(tinydict.setdefault('Age', 0)) # 8 键存在,不动
print(tinydict.setdefault('City', '杭州')) # 杭州 键不存在,添加进去
print(tinydict) # 现在多了 'City': '杭州'

删:pop / popitem / clear

1
2
3
4
5
6
7
8
9
10
11
12
tinydict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'}

x = tinydict.pop('Age') # 删除键 Age,返回它的值
print(x) # 7
print(tinydict) # {'Name': 'Runoob', 'Class': 'First'}

pair = tinydict.popitem() # 删除并返回最后一对(Python 3.7 起按插入顺序)
print(pair) # ('Class', 'First')
print(tinydict) # {'Name': 'Runoob'}

tinydict.clear() # 清空
print(tinydict) # {}

pop 和 del 的区别:pop 返回被删的值,del 不返回。pop('Age') 删一个并拿到值;del d['Age'] 删一个、拿不到值。

其他:copy / fromkeys

1
2
3
4
5
6
7
8
9
10
# copy:复制一份(和列表章节的 copy() 同一个道理:b = a 只是共享,copy 才是真复制)
a = {'Name': 'Runoob'}
b = a.copy()
b['Name'] = '小菜鸟'
print(a) # {'Name': 'Runoob'} a 不受影响

# fromkeys:批量创建——序列的元素当键,值统一
keys = ('Name', 'Age')
d = dict.fromkeys(keys, 0)
print(d) # {'Name': 0, 'Age': 0}

九、和 C 的对照

C 语言没有字典。要”按键查值”,笨办法是结构体数组 + 循环比对:

1
2
3
4
5
6
7
8
9
struct student { char name[20]; int age; };
struct student list[] = {{"张三", 19}, {"李四", 20}};

int find_age(const char *name) {
for (int i = 0; i < 2; i++)
if (strcmp(list[i].name, name) == 0)
return list[i].age;
return -1; // 找不到返回 -1
}

数据少还行,数据一多每次都要从头遍历。Python 字典底层是哈希表,dict[key] 一步定位:

1
2
C 结构体数组:  按键查找 = 遍历比对,O(n),n 大就慢
Python 字典: 哈希表定位,O(1),数据多少无所谓

十、实际应用场景

  • 查表:学号 → 姓名、星期几 → 中文名、状态码 → 描述
  • 配置项:config = {'baud': 115200, 'parity': 'N'},按键取参数
  • 统计词频:遍历一段文字,统计每个字符出现次数
  • JSON:前后端传数据的 JSON 对象,解析成 Python 就是字典
1
2
3
4
5
6
7
# 统计每个字符出现次数(用上了 get 的默认值技巧)
text = "hello world"
count = {}
for ch in text:
count[ch] = count.get(ch, 0) + 1
print(count)
# {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1}

核心套路:count.get(ch, 0) 取当前次数,第一次遇到返回 0,加 1 存回去;后面再遇到就加 1。


集合

集合(set)是无序、不重复的元素序列。两个招牌能力:去重+ 快速判断在不在(in)。还能做交集、并集、差集等集合运算。C 里没有对应物,去重要自己写循环。

一、创建集合

大括号 {} 或 set() 函数:

1
2
3
4
set1 = {1, 2, 3, 4}            # 直接使用大括号创建集合
set2 = set([4, 5, 6, 7]) # 使用 set() 函数从列表创建集合
print(set1) # {1, 2, 3, 4}
print(set2) # {4, 5, 6, 7}

**空集合必须用 set()**,{} 创建的是空字典(字典章节讲过):

1
2
s = set()      # ✅ 空集合
d = {} # ❌ 这是空字典,不是空集合

去重演示

创建时重复元素自动只留一个:

1
2
basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
print(basket) # 重复的 apple、orange 只留一个

注意:集合无序,输出顺序不固定,不要指望它按插入顺序输出(Python 3.7 起字典保持插入顺序,集合不保证)。

二、判断元素在不在

x in s 快速判断成员:

1
2
3
4
basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}

print('orange' in basket) # True
print('crabgrass' in basket) # False

C 对照:C 里判断数组有没有某元素,要写 for 循环挨个比;集合直接 in 一步,底层也是哈希表。

三、集合运算

1
2
3
4
a = set('abracadabra')    # 从字符串创建:拆成单个字符并去重
b = set('alacazam')
print(a) # 五个字符('abracadabra' 里的不同字符)
print(b) # 五个字符('alacazam' 里的不同字符)

四种运算:

1
2
3
4
print(a - b)    # 差集:a 里有、b 里没有
print(a | b) # 并集:a 或 b 的全部元素
print(a & b) # 交集:a 和 b 都有的元素
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
2
a = {x for x in 'abracadabra' if x not in 'abc'}
print(a) # {'r', 'd'}

解读:遍历字符串 'abracadabra' 的每个字符 x,筛掉在 'abc' 里的,剩下的放进集合(自动去重)。

五、添加元素

add:加单个

1
2
3
thisset = set(("Google", "Runoob", "Taobao"))
thisset.add("Facebook")
print(thisset) # {'Taobao', 'Facebook', 'Google', 'Runoob'}

元素已存在时 add 不进行任何操作(去重特性)。

update:批量加

参数可以是列表、元组、字典、集合,还可以传多个:

1
2
3
4
5
6
thisset = set(("Google", "Runoob", "Taobao"))
thisset.update({1, 3})
print(thisset) # {1, 3, 'Google', 'Taobao', 'Runoob'}

thisset.update([1, 4], [5, 6]) # 传多个参数
print(thisset) # {1, 3, 4, 5, 6, 'Google', 'Taobao', 'Runoob'}

add 和 update 的区别:add 加一个元素,update 把参数展开逐个加(和列表的 append / extend 关系类似)。

六、移除元素

remove:不存在会报错

1
2
3
4
5
6
thisset = set(("Google", "Runoob", "Taobao"))
thisset.remove("Taobao")
print(thisset) # {'Google', 'Runoob'}

thisset.remove("Facebook") # 不存在 → 报错
# KeyError: 'Facebook'

discard:不存在也不报错

1
2
3
thisset = set(("Google", "Runoob", "Taobao"))
thisset.discard("Facebook") # 不存在,忽略
print(thisset) # {'Taobao', 'Google', 'Runoob'}

pop:随机删一个

1
2
3
thisset = set(("Google", "Runoob", "Taobao", "Facebook"))
x = thisset.pop() # 删哪个不确定
print(x) # 每次运行结果可能不同

集合无序,pop 删的是”第一个”,但哪个是第一个不确定。要删指定元素用 remove / discard。

方式 不存在时 返回
remove(x) 报错 KeyError 无
discard(x) 忽略,不报错 无
pop() 集合为空报错 返回被删的元素(随机)

七、其他基本操作

1
2
3
4
5
6
7
8
thisset = set(("Google", "Runoob", "Taobao"))

print(len(thisset)) # 3 元素个数
print("Runoob" in thisset) # True 判断存在
print("Facebook" in thisset) # False

thisset.clear() # 清空
print(thisset) # set()

八、内置方法完整列表

方法名和运算符一一对应:| → 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
2
3
4
5
6
7
8
a = {1, 2, 3}
b = {2, 3, 4}

print(a.union(b)) # {1, 2, 3, 4} 并集
print(a.intersection(b)) # {2, 3} 交集
print(a.difference(b)) # {1} 差集
print(a.symmetric_difference(b)) # {1, 4} 对称差
print(a) # {1, 2, 3} 原集合没变

带 _update 的方法:直接改原集合,不返回新集合

不带 _update 的方法(union 等)算出结果后返回一个新集合,原集合不动;带 _update 的方法把原集合直接改掉,不返回任何东西:

1
2
3
4
5
a = {1, 2, 3}
b = {2, 3, 4}

a.intersection_update(b) # a 被直接改成交集,没生成新集合
print(a) # {2, 3}

关系判断:子集 / 超集 / 不相交

1
2
3
4
5
6
a = {1, 2}
b = {1, 2, 3}

print(a.issubset(b)) # True a 是 b 的子集
print(b.issuperset(a)) # True b 是 a 的超集
print(a.isdisjoint({9})) # True a 和 {9} 没有共同元素

九、和 C 的对照

C 没有集合类型。最常用的”去重”功能,C 要两层循环:

1
2
3
4
5
6
7
8
9
10
11
// C:数组去重,O(n²)
int uniq(int *src, int n, int *dst) {
int m = 0;
for (int i = 0; i < n; i++) {
int dup = 0;
for (int j = 0; j < m; j++)
if (dst[j] == src[i]) { dup = 1; break; }
if (!dup) dst[m++] = src[i];
}
return m; // 返回去重后长度
}
1
2
# Python:一行
dst = list(set(src))

十、实际应用场景

  • 去重:统计文章里有哪些不同的词、列表去重
  • 快速判断在不在:黑名单、已访问记录(配合 in,一步到位)
  • 集合运算:求共同好友(交集)、推荐用户(差集:A 关注了但 B 没关注)
  • 配合字典:字典的 keys() 可以直接和集合做运算
1
2
3
4
5
6
# 共同好友:两个好友列表的交集
my_friends = {'张三', '李四', '王五'}
her_friends = {'李四', '赵六'}

print(my_friends & her_friends) # {'李四'} 共同好友
print(my_friends - her_friends) # {'张三', '王五'} 我独有

条件控制

条件控制就是根据条件(True / False)决定执行哪段代码,和 C 的 if / switch 是一回事,只是写法不同。

一、if 语句的基本形式

1
2
3
4
5
6
if condition_1:
语句块1
elif condition_2:
语句块2
else:
语句块3

执行逻辑:

  1. condition_1 为 True → 执行语句块 1
  2. 否则判断 condition_2,为 True → 执行语句块 2
  3. 都不满足 → 执行 else 的语句块 3

和 C 的对照:

1
2
3
4
5
6
7
8
// C 语言
if (a > 0) {
...
} else if (a == 0) {
...
} else {
...
}
1
2
3
4
5
6
7
# Python
if a > 0:
...
elif a == 0: # 注意:是 elif,不是 else if
...
else:
...

三个必须记住的点:

  1. 每个条件后面要写冒号 :
  2. 用缩进划分语句块(相同缩进 = 同一代码块),没有大括号
  3. 没有 else if,用 elif

看一个实际例子——if 后面直接放变量当条件:

1
2
3
4
5
6
7
8
9
var1 = 100
if var1: # var1 非零,为真
print("1 - if 表达式条件为 true")
print(var1) # 100

var2 = 0
if var2: # 0 是假值,不执行
print("2 - if 表达式条件为 true") # 不会打印
print("Good bye!")

输出:

1
2
3
1 - if 表达式条件为 true
100
Good bye!

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
2
3
age = 25
if age >= 18 and age < 60:
print("成年")

三、if 嵌套

if 里面可以再套 if(和 C 一样),注意缩进层级表示嵌套深度:

1
2
3
4
5
6
7
8
9
10
11
12
num = int(input("输入一个数字:"))

if num % 2 == 0:
if num % 3 == 0:
print("你输入的数字可以整除 2 和 3")
else:
print("你输入的数字可以整除 2,但不能整除 3")
else:
if num % 3 == 0:
print("你输入的数字可以整除 3,但不能整除 2")
else:
print("你输入的数字不能整除 2 和 3")

四、综合实例:数字猜谜游戏

用 while + if-elif-else 写一个小游戏(while 循环下一部分详讲):

1
2
3
4
5
6
7
8
9
10
11
12
13
number = 7
guess = -1
print("数字猜谜游戏!")

while guess != number:
guess = int(input("请输入你猜的数字:"))

if guess == number:
print("恭喜,你猜对了!")
elif guess < number:
print("猜的数字小了...")
elif guess > number:
print("猜的数字大了...")

运行示例:

1
2
3
4
5
6
7
数字猜谜游戏!
请输入你猜的数字:1
猜的数字小了...
请输入你猜的数字:9
猜的数字大了...
请输入你猜的数字:7
恭喜,你猜对了!

五、match…case

match...case 是 Python 3.10 新增的结构化模式匹配,类似 C 的 switch:

1
2
3
4
5
6
7
8
9
10
def http_error(status):
match status:
case 400:
return "Bad request"
case 404:
return "Not found"
case 418:
return "I'm a teapot"
case _:
return "Something's wrong with the internet"

(上面用到了函数 def 和 return,还没学到也没关系,函数部分会详讲,先看 match 的语法。)

规则:

  • match 后面的值从上到下依次和每个 case 匹配,匹配成功就执行对应的语句
  • case _: 匹配一切(通配符),相当于 C 的 default:,放在最后兜底
  • 一个 case 可以用 | 匹配多个值:
1
2
3
4
5
6
7
8
9
match status:
case 200:
return "OK"
case 301 | 302: # 301 或 302 都走这里
return "Redirect"
case 401 | 403 | 404:
return "Not allowed"
case _:
return "Unknown"

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
2
while 判断条件:
执行语句……

计算 1 到 100 的总和:

1
2
3
4
5
6
7
8
9
n = 100

sum = 0
counter = 1
while counter <= n:
sum = sum + counter
counter += 1

print("1 到 100 之和为: ", sum) # 1 到 100 之和为: 5050

这段和 C 的写法几乎一模一样,只有格式区别(冒号 + 缩进代替大括号)。

C 对照:Python 没有 do…while。C 的 do { } while(条件);(先执行一次再判断)在 Python 里要改写:

1
2
3
4
while True:
do_something()
if 条件:
break # 手动判断退出

无限循环

条件永远为 True 就无限循环:

1
2
3
4
5
6
var = 1
while var == 1: # 表达式永远为 true
num = int(input("输入一个数字: "))
print("你输入的数字是: ", num)

print("Good bye!")

按 CTRL+C 退出。无限循环在服务器上处理客户端实时请求很有用(一直监听)。

简单语句组

循环体只有一条语句时,可以和 while 写在同一行:

1
2
flag = 1
while (flag): print('欢迎访问菜鸟教程!') # 无限循环,CTRL+C 中断

三、while…else

while 条件变为 False(正常退出)时,执行 else 的语句块:

1
2
3
4
5
6
count = 0
while count < 5:
print(count, " 小于 5")
count = count + 1
else:
print(count, " 大于或等于 5")

输出:

1
2
3
4
5
6
0  小于 5
1 小于 5
2 小于 5
3 小于 5
4 小于 5
5 大于或等于 5

注意:被 break 中断退出时不执行 else(见第七节)。

四、for 循环

Python 的 for 是遍历循环:把可迭代对象(列表、字符串、range 等)里的元素挨个取出来处理,不需要下标。

一般格式:

1
2
for <variable> in <sequence>:
<statements>

遍历列表:

1
2
3
sites = ["Baidu", "Google", "Runoob", "Taobao"]
for site in sites:
print(site)

输出:

1
2
3
4
Baidu
Google
Runoob
Taobao

遍历字符串(每个字符):

1
2
3
word = 'runoob'
for letter in word:
print(letter)

输出:

1
2
3
4
5
6
r
u
n
o
o
b

和 C 最大的不同:C 的 for 是计数循环(for (i = 0; i < n; i++)),要自己管下标;Python 的 for 是遍历循环,直接拿元素本身,不用下标(类似 C++ 的范围 for)。

五、range() 函数(重点)

range() 生成整数序列,常和 for 配合:

1
2
for i in range(5):
print(i)

输出 0 1 2 3 4——从 0 开始,到 5 结束,不含 5(含头不含尾,和切片规则一样)。

指定区间:

1
2
for i in range(5, 9):
print(i) # 5 6 7 8

指定步长(第三个参数):

1
2
for i in range(0, 10, 3):
print(i) # 0 3 6 9

步长可以是负数(倒着数):

1
2
for i in range(-10, -100, -30):
print(i) # -10 -40 -70

和 C 对照:range(起始, 结束, 步长) 等价于:

1
for (i = 起始; i < 结束; i += 步长)

注意含头不含尾(结束值不包含)。

range(len(列表)) 配合下标遍历:

1
2
3
a = ['Google', 'Baidu', 'Runoob', 'Taobao', 'QQ']
for i in range(len(a)):
print(i, a[i])
1
2
3
4
5
0 Google
1 Baidu
2 Runoob
3 Taobao
4 QQ

range() 也可以直接转成列表:

1
print(list(range(5)))    # [0, 1, 2, 3, 4]

六、for…else

循环正常遍历完后执行 else 子句;被 break 中断则不执行:

1
2
3
4
for x in range(6):
print(x)
else:
print("Finally finished!")
1
2
3
4
5
6
7
0
1
2
3
4
5
Finally finished!

被 break 中断时 else 不执行:

1
2
3
4
5
6
7
8
9
sites = ["Baidu", "Google", "Runoob", "Taobao"]
for site in sites:
if site == "Runoob":
print("菜鸟教程!")
break
print("循环数据 " + site)
else:
print("没有循环数据!")
print("完成循环!")
1
2
3
4
循环数据 Baidu
循环数据 Google
菜鸟教程!
完成循环!

七、break 和 continue

和 C 的语义一样:break 终止整个循环,continue 跳过本次循环剩余代码、进入下一轮。

while 中使用 break:

1
2
3
4
5
6
7
n = 5
while n > 0:
n -= 1
if n == 2:
break # n 变到 2 时直接跳出循环
print(n)
print('循环结束。')
1
2
3
4
3
循环结束。

while 中使用 continue:

1
2
3
4
5
6
7
n = 5
while n > 0:
n -= 1
if n == 2:
continue # n == 2 时跳过 print,进入下一轮
print(n)
print('循环结束。')
1
2
3
4
5
4
3
1
0
循环结束。

对比两个输出:break 版循环直接结束了(没有 1、0);continue 版只是少了 2 那一次,其余照常。

for 中使用 break:

1
2
3
4
for letter in 'Runoob':
if letter == 'b':
break # 碰到 b 终止循环
print('当前字母 :', letter)

for 中使用 continue:

1
2
3
4
for letter in 'Runoob':
if letter == 'o':
continue # 字母为 o 时跳过输出
print('当前字母 :', letter)

八、循环 else 综合实例:判断质数

循环 else 的实际用途:循环里没找到(没 break)时执行 else。经典例子——判断 2~9 哪些是质数:

1
2
3
4
5
6
7
8
for n in range(2, 10):
for x in range(2, n):
if n % x == 0:
print(n, '等于', x, '*', n // x)
break
else:
# 内层循环一个因子都没找到 → n 是质数
print(n, ' 是质数')
1
2
3
4
5
6
7
8
2  是质数
3 是质数
4 等于 2 * 2
5 是质数
6 等于 2 * 3
7 是质数
8 等于 2 * 4
9 等于 3 * 3

注意 else 挂在内层 for 上:内层循环找因子,找到了就 break(不是质数),一个因子都没找到就正常遍历完 → 执行 else 打印质数。C 里这个逻辑要用标志变量,Python 的 else 更直接。

九、pass 语句

pass 是空语句,什么都不做,用来占位,保持程序结构完整:

1
2
while True:
pass # 等待键盘中断 (Ctrl+C)

写代码时类先空着,用 pass 占位(面向对象章节会看到):

1
2
class MyEmptyClass:
pass # 暂时什么都不做

for 循环里演示 pass 的执行时机:

1
2
3
4
5
6
7
for letter in 'Runoob':
if letter == 'o':
pass # pass 什么都不做,继续往下执行
print('执行 pass 块')
print('当前字母 :', letter)

print("Good bye!")
1
2
3
4
5
6
7
8
9
当前字母 : R
当前字母 : u
当前字母 : n
执行 pass 块
当前字母 : o
执行 pass 块
当前字母 : o
当前字母 : b
Good bye!

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
2
3
sites = ['Google', 'Runoob', 'Taobao']
for i, site in enumerate(sites):
print(i, site)
1
2
3
0 Google
1 Runoob
2 Taobao

enumerate 同时返回下标和元素,等价于 C 里”用下标访问数组元素”,但更省事。


函数

函数是组织好的、可重复使用的代码段,用来实现单一或相关联的功能。Python 提供了很多内置函数(print()、len() 等),自己定义的就叫用户自定义函数。C 里有函数,Python 里也有,区别在格式和灵活性。

一、定义函数

规则:

  • 以 def 关键字开头,后接函数名 + 圆括号 ()
  • 参数放在圆括号里(可空)
  • 函数体以冒号 : 起始,用缩进表示(和 if / while 一样)
  • return [表达式] 结束函数,选择性地返回一个值;不带表达式的 return 相当于返回 None

语法:

1
2
def 函数名(参数列表):
函数体

最简单的例子:

1
2
3
4
def hello():
print("Hello World!")

hello() # Hello World!

带参数:比较两个数,返回较大的:

1
2
3
4
5
6
7
8
9
def max(a, b):
if a > b:
return a
else:
return b

a = 4
b = 5
print(max(a, b)) # 5

计算面积:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 计算面积函数
def area(width, height):
return width * height

# 打印欢迎信息函数(没有返回值)
def print_welcome(name):
print("Welcome", name)

print_welcome("Runoob") # Welcome Runoob
w = 4
h = 5
print("width =", w, " height =", h, " area =", area(w, h))
# width = 4 height = 5 area = 20

C 对照:C 定义函数要写返回类型(int max(int a, int b)),Python 不用——返回什么类型运行时决定。

二、函数调用

函数定义好之后,通过函数名加括号调用:

1
2
3
4
5
6
7
8
9
# 定义函数
def printme(str):
# 打印任何传入的字符串
print(str)
return

# 调用函数
printme("我要调用用户自定义函数!") # 我要调用用户自定义函数!
printme("再次调用同一函数") # 再次调用同一函数

同一函数可以反复调用,这就是”复用”。return 单独出现(不带值)表示结束函数,返回 None。

三、参数传递(重点)

先分清:变量是”引用”,对象才有类型

1
2
a = [1, 2, 3]
a = "Runoob"

[1, 2, 3] 是列表对象,”Runoob” 是字符串对象,而变量 a 没有类型——它只是对象的引用(类似 C 的指针),先指向列表对象,再指向字符串对象。

可变 vs 不可变对象

  • 不可变:数字、字符串、元组——改值 = 生成新对象整体替换
  • 可变:列表、字典——可以原地改内容(内存地址不变)
1
2
3
4
5
a = 5
a = 10 # 新生成 int 对象 10,让 a 指向它,5 被丢弃

la = [1, 2, 3, 4]
la[2] = 5 # 改的是列表内部的值,列表本身还是那个列表

传不可变对象:函数内改了,不影响外面

1
2
3
4
5
6
7
8
9
def change(a):
print(id(a)) # 4379369136 刚进来,和外面是同一个对象
a = 10
print(id(a)) # 4379369424 函数内重新赋值,生成了新对象

a = 1
print(id(a)) # 4379369136
change(a)
print(a) # 1 外面的 a 没变

形参 a 和实参 a 一开始指向同一个对象(id 相同);函数里 a = 10 是让形参指向新对象,外面的变量不受影响。

为什么”指向同一个对象,外面却不变”:和 C 的区别

先理解为什么形参实参会指向同一块内存:Python 传参的本质是拷贝”引用”(引用 ≈ C 的指针)。调用 change(a) 时,Python 把实参 a 指向的对象的地址复制一份给形参,所以两者一开始指向同一个对象(id 相同)——但形参是新变量,只是恰好拿着同一个地址。

C 对照:

1
2
3
4
5
6
7
C 传值:
实参 a ──拷贝数据──► 形参(栈上新开一块内存,存值的副本)
两者地址不同,改形参不影响实参

Python(任何参数):
实参 a ──────────► 对象(1) @0x1000
形参 a ──────────► 对象(1) @0x1000 ← 拷贝的是"引用",都指向同一块内存

C 里只有传指针时才出现”两个变量指向同一块内存”;Python 里任何参数都是这个行为——拷贝引用,形参实参永远先指向同一个对象。

那为什么外面没变?关键不在”传的什么”,而在对象本身不可变:

1
2
3
4
5
6
def change(a):
a = 10 # 只是让形参 a 改指到 对象(10),对象(1) 原封不动

a = 1
change(a)
print(a) # 1
1
2
3
4
调用前:  实参 a ──► 对象(1) @0x1000
形参 a ──► 对象(1) @0x1000
执行后: 实参 a ──► 对象(1) @0x1000 ← 还在
形参 a ──► 对象(10) @0x2000 ← 形参换了指向,实参不受影响

数字不可变,没法”原地改内容”,a = 10 只能让形参换个对象指,所以外面不变——表现上和 C 传值一样,但机制不同(C 是副本,Python 是换指向)。

传列表时,列表可变,函数内 mylist.append(...) 是原地改内容,形参实参指向同一个列表,所以外面也变——表现上和 C 传指针一样。

一句话总结:Python 传参永远传引用(地址相同),外面变不变取决于对象可不可变,而不是传参方式。

传可变对象:函数内改了,外面也变

1
2
3
4
5
6
7
8
def changeme(mylist):
"修改传入的列表"
mylist.append([1, 2, 3, 4]) # 改的是列表内部
print("函数内取值: ", mylist)

mylist = [10, 20, 30]
changeme(mylist)
print("函数外取值: ", mylist)
1
2
函数内取值:  [10, 20, 30, [1, 2, 3, 4]]
函数外取值: [10, 20, 30, [1, 2, 3, 4]]

和 C 对照:

1
2
3
4
C:传值    —— 函数内改形参,外部变量不变
C:传指针 —— 函数内改 *p,外部变量跟着变
Python:传不可变对象 —— 相当于 C 传值(数字、字符串、元组)
Python:传可变对象 —— 相当于 C 传指针(列表、字典)

四、必需参数

必需参数必须按顺序传、数量必须一致,少传会报错:

1
2
3
4
5
def printme(str):
print(str)

printme() # 不传参数
# TypeError: printme() missing 1 required positional argument: 'str'

五、关键字参数

调用时用 参数名=值 传参,Python 按名字匹配,顺序可以打乱:

1
2
3
4
5
def printinfo(name, age):
print("名字: ", name)
print("年龄: ", age)

printinfo(age=50, name="runoob") # 顺序反了也没关系
1
2
名字:  runoob
年龄: 50

和必需参数的区别:一个说的是定义,一个说的是调用

必需参数和关键字参数不是两类不同的参数,而是同一批参数的两个视角:

  • 必需参数说的是定义:函数声明了没默认值的参数,调用时必须传、必须按顺序,少传报错
  • 关键字参数说的是调用:传参时写上 参数名=值,顺序可以乱
1
2
3
def printinfo(name, age):    # name、age 都是必需参数(定义时的说法)
print("名字: ", name)
print("年龄: ", 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
2
3
4
5
6
7
def printinfo(name, age=35):    # age 有默认值 35
print("名字: ", name)
print("年龄: ", age)

printinfo(age=50, name="runoob")
print("------------------------")
printinfo(name="runoob") # 不传 age,用默认值 35
1
2
3
4
5
名字:  runoob
年龄: 50
------------------------
名字: runoob
年龄: 35

注意:带默认值的参数必须放在不带默认值的参数后面(name 在前,age=35 在后),反过来写会语法报错。

七、不定长参数

函数可能处理比声明时更多的参数:

*args:多余的参数打包成元组

1
2
3
4
5
6
def printinfo(arg1, *vartuple):
print("输出: ")
print(arg1)
print(vartuple)

printinfo(70, 60, 50) # 多出的 60、50 打包成元组
1
2
3
输出: 
70
(60, 50)

不传多余参数时,它是一个空元组:

1
2
3
4
5
6
7
def printinfo(arg1, *vartuple):
print(arg1)
for var in vartuple: # 用 for 遍历不定长参数
print(var)

printinfo(10) # 10
printinfo(70, 60, 50) # 70 60 50

**kwargs:多余的”名=值”参数打包成字典

1
2
3
4
5
def printinfo(arg1, **vardict):
print(arg1)
print(vardict)

printinfo(1, a=2, b=3)
1
2
3
输出: 
1
{'a': 2, 'b': 3}

单独的 *:后面的参数必须用关键字传

1
2
3
4
5
6
7
def f(a, b, *, c):
return a + b + c

f(1, 2, 3) # ❌ 报错:c 必须用关键字传入
# TypeError: f() takes 2 positional arguments but 3 were given

f(1, 2, c=3) # ✅ 6

C 对照:C 的变参函数(printf 那种)要用 va_list 一堆宏,Python 一个 *args 搞定。

八、匿名函数 lambda

lambda 创建匿名函数——不用 def 定义、没有函数名的函数:

  • lambda 是表达式,不是代码块,函数体比 def 简单得多,只能封装有限逻辑(只能写一个表达式,不能写 if、赋值等语句)
  • 虽然只有一行,但不等于 C/C++ 的内联函数——内联函数是为了减少函数调用开销,lambda 没有这个目的

语法:

1
lambda [arg1, arg2, ...]: 表达式

单个参数:

1
2
x = lambda a: a + 10
print(x(5)) # 15

多个参数:

1
2
3
4
sum = lambda arg1, arg2: arg1 + arg2

print("相加后的值为 : ", sum(10, 20)) # 相加后的值为 : 30
print("相加后的值为 : ", sum(20, 20)) # 相加后的值为 : 40

lambda 可以封装在函数里,同一个函数创建多个不同的匿名函数:

1
2
3
4
5
6
7
8
def myfunc(n):
return lambda a: a * n # 返回一个"乘 n"的函数

mydoubler = myfunc(2) # 得到一个"乘2"的函数
mytripler = myfunc(3) # 得到一个"乘3"的函数

print(mydoubler(11)) # 22
print(mytripler(11)) # 33

C 对照:C 没有匿名函数,只能写函数指针;C++ 11 起才有 lambda,Python 的写法更简洁。

九、return 语句

return [表达式] 退出函数并返回结果;不带表达式的 return(或没有 return)返回 None:

1
2
3
4
5
6
7
def sum(arg1, arg2):
total = arg1 + arg2
print("函数内 : ", total)
return total # 把结果返回给调用方

total = sum(10, 20)
print("函数外 : ", total)
1
2
函数内 :  30
函数外 : 30

函数里没有 return 时,调用结果就是 None:

1
2
3
4
def hello():
print("hi")

print(hello()) # 先输出 hi,再输出 None

十、强制位置参数(Python 3.8+)

/ 和 * 可以限定参数的传入方式:

1
2
def f(a, b, /, c, d, *, e, f):
print(a, b, c, d, e, f)
  • / 之前的参数(a、b):只能按位置传,不能用 b=20
  • / 和 * 之间的参数(c、d):位置、关键字都行
  • * 之后的参数(e、f):只能用关键字传
1
2
3
f(10, 20, 30, d=40, e=50, f=60)           # ✅ 正确
f(10, b=20, c=30, d=40, e=50, f=60) # ❌ b 不能用关键字传
f(10, 20, 30, 40, 50, f=60) # ❌ e 必须用关键字传

十一、参数类型小结

参数类型 写法 特点
必需参数 def f(a, b) 按顺序传,数量必须一致
关键字参数 f(a=1, b=2) 按名字匹配,顺序可乱
默认参数 def f(a, b=5) 不传就用默认值,必须放后面
不定长参数 *args / **kwargs 多余参数打包成元组/字典
强制位置 / 之前的参数只能用位置传
强制关键字 * 之后的参数只能用关键字传

​