一、先明确:Python 数据类型的核心分类
Python 基本数据类型主要分两类:不可变类型 (创建后不能改内容)、可变类型(创建后能改内容),先记这个大框架,后续好理解:
| 类型分类 | 具体类型 | 特点 |
|---|---|---|
| 不可变类型 | 数字、字符串、元组 | 内容改不了,改就是新建 |
| 可变类型 | 列表、字典、集合 | 内容能直接增删改 |
如果从数据复杂度上来区分,又可以分为基本数据类型和复合数据类型
| 类型分类 | 具体类型 | 特点 |
| 基本数据类型 | 数字、字符串 | 结构简单,数据类型单一 |
| 复合数据类型 | 列表、字典、集合、元组 | 结构复杂,数据类型存在多样性 |
|---|
与C/C++的区别和联系,从上面可以看出来,python对基本数据类型做了很大的精简,不再对数字做出严格划分,如short long long long 以及有符号,无符号。
但是对复合数据类型却做了,很大的扩充,添加了很多属性,至于class,我们认为是一种特殊的用户自定义复杂类型(注意,我说的不是复杂数据类型,因为class还兼有方法,我认为不能单纯的认为是一种数据结构)。
二、逐个拆解:核心基本数据类型(用法 + 例子)
2.1、数字(Number)------ 用来做计算
包含整数(int)、浮点数(float)、布尔值(bool)(布尔值是特殊的整数:True=1,False=0)。
用法:
- 定义:直接赋值,不用声明类型(Python 自动识别);
- 操作:加减乘除、比较(> < ==)、取余(%)、幂运算(**)(这里需要区分c/c++中的^符号)等。
例子:
python
# 整数
age = 18 # int类型
# 浮点数(带小数点)
height = 175.5 # float类型
# 布尔值(只有True/False)
is_adult = age >= 18 # bool类型,结果是True
# 常用计算
print(age + 2) # 20(整数相加)
print(height * 2) # 351.0(浮点数相乘)
print(10 % 3) # 1(取余:10除以3余1)
print(2 **3) # 8(幂运算:2的3次方)
2.2、字符串(String,str)------ 用来存文本
就是 "一串文字",用单引号' '、双引号" "或三引号''' '''包裹。
用法:
- 定义:直接赋值文本;
- 核心操作:拼接(+)、切片(取部分)、格式化(f-string)、查找 / 替换等。
- 以及常见的join方法,这里需要非常注意,很多人误以为join也是一个关键字,其实本质上
join是 Python 字符串(str类型)专属的「内置方法」,不是关键字!下文代码中也可以看出,还需要关注join前还需要指定插入符。 - 此处,可能还有有同学会问,不是说join是一种内置方法吗?为什么不需要实例对象,就可以调用?
例子:
python
# 基础定义
name = "小明" # str类型
intro = '我叫{},今年{}岁'.format(name, age) # 老版格式化,使用format的内置方法
intro_new = f"我叫{name},今年{age}岁" # f-string(最常用)
str1 = "".join(["Py", "thon", "教程"]) #此处join前用用""空字符代表,后面参数无间隙拼接
str2 = "-"join("2025","12","1") #此处得到的字符串为 2025-12-1
# 常用操作
print(name + "你好") # 拼接:小明你好
print(name[0]) # 切片:取第一个字符"小"
print(len(name)) # 长度:2(统计字符数)len也是python内置的库函数,直接返回str的长度
print(name.replace("小", "大")) # 替换:大明 #replace,是str内置的一种方法,存在两个参数,第一个参数是被替换值,第二个参数是,替换值
python中字符串高级用法
| 字符串类型 | Python 2 中的定义 | Python 3 中的定义 | 版本兼容范围 |
|---|---|---|---|
str |
字节串(对应 Python3 的 bytes) | Unicode 文本字符串(默认) | Python 2.x / Python 3.0+ |
bytes |
无此类型 | 字节串(二进制数据) | Python 3.0+ |
unicode (现在基本不用) |
Unicode 文本字符串 | 无此类型(合并到 str) | Python 2.0+;Python3.3 + 允许 u 前缀 |
basestring (现在基本不用了) |
str/unicode 的抽象基类 | 已移除 | 仅 Python 2.x |
于是对于str类型的字符串,我们都已经很熟悉了,来看看byte类型的字符串是什么意思?
str:Unicode 文本字符串(Python 3 默认字符串类型)
-
定义 :表示Unicode 字符序列 (支持全球所有语言的字符,如中文、英文、符号等),是 Python 3 的默认字符串类型。
-
表示方式 :用单引号
'xxx'、双引号"xxx"、三引号'''xxx'''/"""xxx"""表示,不需要任何前缀。 -
版本支持 :从 Python 3.0 开始作为默认字符串类型,所有 Python 3 版本(3.0+)均兼容。
-
示例 :
python
pythons1 = "Python" # str类型 s2 = "你好,世界" # str类型(支持Unicode字符) s3 = '''多行 字符串''' # str类型
bytes:字节串(二进制数据序列)
-
定义 :表示原始二进制数据 (不是文本,是字节的序列),常用于文件读写、网络传输等场景。
-
表示方式 :在字符串前加
b前缀(如b'xxx'),只能包含 ASCII 范围内的字符(超出则需用转义符表示二进制值)。 -
版本支持 :从 Python 3.0 开始支持,所有 Python 3 版本(3.0+)均兼容。
-
示例 :
python
pythonb1 = b"abc" # bytes类型(ASCII字符) b2 = b"\xe4\xbd\xa0" # bytes类型(转义表示非ASCII字符的二进制值)
这时又会有人会问,这个byte类型的字符,到底能起到什么作用。大家可能还记得C/C++中的char类型,通过格式控制符%d,是可以直接打印为数字的,赋值时char类型的变量,也可以直接赋值为数字常量。
python中的byte类型的字符串,和char比较类似。
Python 3 补充说明
-
str和bytes不能直接拼接,需通过encode()(str→bytes)或decode()(bytes→str)转换(使用内置方法的编码和解码方法):python
s = "你好" b = s.encode("utf-8") # str转bytes s2 = b.decode("utf-8") # bytes转str
2.3、 列表(List)------ 有序的 "杂货筐"
用**[]**包裹,能存任意类型的数据(数字、字符串、列表等),可修改。
用法:
- 定义:
列表名 = [元素1, 元素2, ...]; - 核心操作:增(append默认加在末尾处)、删(remove)、改(直接赋值)、查(索引)、切片。(增删改查切)
例子:
python
# 基础定义
my_list = [18, "小明", 175.5, True] # list类型,混合数据
# 常用操作
my_list.append("男") # 新增元素:[18, "小明", 175.5, True, "男"]
my_list[1] = "小红" # 修改元素:[18, "小红", 175.5, True, "男"]
my_list.remove(18) # 删除元素:["小红", 175.5, True, "男"]
print(my_list[0]) # 查元素:小红
print(my_list[1:3]) # 切片:取第2-3个元素 [175.5, True]
2.4、 元组(Tuple)------ 有序的 "只读杂货筐"
用**()包裹,和列表类似,但不可修改**(创建后不能增删改),更安全。类似于只读
用法:
- 定义:
元组名 = (元素1, 元素2, ...)(小括号可省略,但建议写); - 核心操作:只能查(索引 / 切片),不能改。
例子:
python
# 基础定义
my_tuple = (18, "小明", 175.5) # tuple类型
# 常用操作(只能查)
print(my_tuple[1]) # 查元素:小明
print(my_tuple[:2]) # 切片:(18, "小明")
# 尝试修改会报错(不可变的核心特点)
# my_tuple[1] = "小红" # 报错:TypeError: 'tuple' object does not support item assignment
小结:关于切片语法的小结,语法中存在两个参数,被【】包括,两个参数中间使用:分隔开,其中第一个参数可以不写,不写就代表从第一个参数开始切片。写了第一个参数就是从该参数+1处开始切片
2.5、 字典(Dictionary,dict)------ 键值对 "对照表"
用{键: 值}包裹,按 "键(key)" 找 "值(value)",可修改,像查字典一样(拼音→汉字)。
用法:
- 定义:
字典名 = {key1: value1, key2: value2, ...}(key 通常是字符串 / 数字,value 任意类型); - 核心操作:增(新增键值对)、删(del)、改(直接赋值)、查(按 key 找)。
例子:
python
# 基础定义
person = {"name": "小明", "age": 18, "height": 175.5} # dict类型
# 常用操作
person["gender"] = "男" # 新增键值对:{"name":"小明", "age":18, "height":175.5, "gender":"男"}
person["age"] = 19 # 修改值:age变成19
del person["height"] # 删除键值对:{"name":"小明", "age":19, "gender":"男"}
print(person["name"]) # 查值:小明
print(person.keys()) # 查所有键:dict_keys(['name', 'age', 'gender'])
这里del不是方法,而是一个关键字
2.6、 集合(Set)------ 无序的 "无重复集合"
用{}包裹(注意和字典区分:集合只有元素,没有键值对),自动去重 ,支持交集 / 并集等集合操作。
用法:
- 定义:
集合名 = {元素1, 元素2, ...}或set(列表); - 核心操作:增(add) 、删(discard)、去重、找交集(&)/ 并集(|)。
例子:
python
# 基础定义(自动去重)
my_set = {1, 2, 2, 3} # set类型,结果是{1,2,3}
# 常用操作
my_set.add(4) # 新增:{1,2,3,4}
my_set.discard(2) # 删除:{1,3,4}
# 集合运算(交集/并集)
set1 = {1,2,3}
set2 = {3,4,5}
print(set1 & set2) # 交集:{3}
print(set1 | set2) # 并集:{1,2,3,4,5}
2.7、python这些特有特殊语法,"+"拼接字符串 和"+="
对熟悉C/C++的人看这段代码:
cpp
int NumArray1[2] = {1,2};
int NumArray2[2] = {3,4};
int NumArray3[4] = NumArray1+NumArray2;
编译器会直接报错,不支持数组直接相加,其他类型如结构体也不支持,甚至数组都不支持整体赋值,但是结构体可以。class实例对象也不支持直接相加,但是同一个模板的实例对象可以直接赋值。
但是我们一直说python是一种易学语言。它提供了很多更偏向于日常生活中常用的语法,比如,你们公司再赶一个项目,大老板说,明天项目必须上线,人手不够,软件组先上,软件搞不定+硬件组,再搞不定运维+测试也一起上。一个组内有搞设计的,有管理的领导,有专门测试的,有协调项目进度的,映射到数据结构中,更像一些复杂数据结构。
python就很好的支持了,这种类似于口语化的语法。看下面一段代码
python
# ✅ 1. 字符串拼接(最常用)
s1 = "Py" + "thon"
print(s1) # 输出:Python
# ✅ 2. 列表拼接
lst1 = [1,2] + [3,4]
print(lst1) # 输出:[1, 2, 3, 4]
#这里需要注意与内置方法append的区别
# ✅ 3. 元组拼接
t1 = (10,20) + (30,40)
print(t1) # 输出:(10, 20, 30, 40)
# ✅ 4. 字节串拼接
b1 = b"abc" + b"def"
print(b1) # 输出:b'abcdef'#注意这种拼接方法,也是非常独特,不被引号包围的不参与拼接
以下几点,需要特别注意:
(1)只有同类型的可以使用拼接运输符(不同类型的需要使用转换函数,强制转换类型)
(2)元组可以拼接,但是必须在定义时刻初始化(这段话,看起来很具有矛盾性,)
二、各数据类型「专属高效拼接技巧」(比运算符更实用)
+运算符适合少量数据拼接 ,如果需要拼接多个元素 / 大批量数据,推荐用各类型的专属方法,效率和可读性更优。✅ 1. 字符串:3 种高效拼接(远超
+的实用性)字符串拼接是开发中最频繁的操作,这 3 种方法比
+更常用:①
str.join(可迭代对象)→ 批量拼接(推荐)分为3个部分,str必须被双引号包含,
接收可迭代对象 (列表、元组、生成器等),将所有元素用指定字符串(也就是str) 连接,批量拼接超高效。
python
python# 用空字符串拼接(无缝连接) res1 = "".join(["Py", "thon", "666"]) print(res1) # Python666 # 用指定分隔符拼接 res2 = "-".join(["2025", "12", "31"]) print(res2) # 2025-12-31② f-string 格式化拼接 → 混合类型拼接(Python3.6+,最推荐)
完美解决「字符串 + 数字 / 变量」的拼接需求,语法简洁、可读性最强,支持任意数据类型混合拼接。
python
运行
pythonname = "Python" version = 3.12 res = f"编程语言:{name},版本:{version}" print(res) # 编程语言:Python,版本:3.12③ 格式化符
%/str.format()→ 兼容式拼接适合 Python 低版本,或批量格式化场景,也是混合类型拼接的常用方案。
python
运行
python# % 格式化 res1 = "数字:%d,浮点数:%.2f" % (10, 3.1415) print(res1) # 数字:10,浮点数:3.14 # format 格式化 res2 = "姓名:{},年龄:{}".format("小明", 18) print(res2) # 姓名:小明,年龄:18✅ 2. 列表:2 个专属拼接方法
除了
+/+=,列表还有 2 个更灵活的拼接方法,适配不同场景:①
list.extend(可迭代对象)→ 批量追加(等价于+=,更语义化)直接将可迭代对象的所有元素追加到列表末尾,原地修改,效率极高。
python
运行
pythonlst = [1,2] lst.extend([3,4]) # 等价于 lst += [3,4] lst.extend((5,6)) # 支持传入任意可迭代对象 print(lst) # [1,2,3,4,5,6]②
list.append(元素)→ 单个元素追加(注意和拼接区分)⚠️ 易错点:
append是追加单个元素,不是拼接!如果传入列表,会把整个列表当成 1 个元素存入。python
运行
lst = [1,2] lst.append(3) # 追加单个元素 ✔ lst.append([4,5]) # 把列表当成1个元素存入 ❌ print(lst) # [1,2,3,[4,5]]✅ 3. 元组:特殊拼接补充
元组是不可变对象 ,所有拼接操作都会生成新元组,无原地修改方法(这两个方法需要特别注意);如果需要拼接单个元素,必须给元素加
,(否则会被识别为普通数据类型)。python
python# 正确:单个元素拼接(加逗号,标识为元组) t2 = (1,2) t3 = (3,) t1 = t2+t3 print(t1) # (1,2,3) print(t2) # (1,2)不会改变t2原本的值 # 错误:不加逗号,3是整数(非元组,类型不同) t1 + (3) # TypeError: can only concatenate tuple (not "int") to tuple三、关键澄清:Python 「无拼接专用关键字」
✅ 结论重申
Python 的关键字列表中,不存在任何专门用于「拼接」的关键字 (比如
join/extend是「方法名」,不是关键字)。✨ 补充:Python 关键字是语言层面规定的特殊保留字(如
if/for/def/return),用于控制程序逻辑,而非数据操作;拼接属于「数据操作」,全部由运算符 / 内置方法实现。✅ 易混淆点区分
很多人会把「拼接相关方法」误认成关键字,这里明确区分:
- ✖
join:字符串的内置方法,不是关键字;- ✖
extend/append:列表的内置方法,不是关键字;- ✔ 真正的拼接核心:
+/+=(运算符) + 各类型专属方法。四、Python 拼接「高频易错点汇总」(避坑必看)
❌ 错误 1:不同类型直接用
+拼接(切记)python
python# 典型错误:字符串 + 整数 "num:" + 100 # 报错TypeError # 正确写法:先类型转换 "num:" + str(100)❌ 错误 2:列表用
append做拼接python
python# 错误:append追加列表,会嵌套 lst = [1,2] lst.append([3,4]) # [1,2,[3,4]] # 正确:用extend/+ lst.extend([3,4]) # [1,2,3,4]❌ 错误 3:元组拼接单个元素不加逗号(前文已经强调了)
python
python# 错误:(3)是整数,不是元组 (1,2) + (3) # 报错TypeError # 正确:加逗号,标识元组 (1,2) + (3,)❌ 错误 4:大批量字符串用
+拼接python
python# 低效:多次+拼接会生成多个新字符串,浪费内存 s = "" for i in range(1000): s += str(i) # 虽然Python做了优化,但仍不推荐 # 高效:用join批量拼接 s = "".join(str(i) for i in range(1000))五、完整总结(精华提炼,快速查阅)
✅ 一、核心拼接运算符
+:基础拼接,同类型序列可用,生成新对象;
+=:增强拼接,原地修改(可变对象),效率更高。✅ 二、各类型最优拼接方案
字符串 :少量拼接用
+,混合类型用f-string,批量拼接用join();列表 :少量拼接用
+=,批量追加用extend(),单个元素用append();元组 :仅用
+拼接(生成新元组),单个元素拼接加,;字节串 :直接用
+/+=即可。✅ 三、核心规则
✔ 仅同类型序列可拼接;
✔ Python 无拼接关键字,拼接靠「运算符 + 方法」;
✔ 不可变对象(字符串、元组)拼接生成新对象,可变对象(列表)优先原地修改。
三、新手必记的核心要点
- 类型判断 :用type
(变量)查看类型,比如type(18)→<class 'int'>;type本身也是一个关键字 - 不可变 vs 可变 :
- 不可变(数字 / 字符串 / 元组):改内容会生成新对象(比如
name = "小明"→name = "小红"是新建了字符串,不是改原来的); - 可变(列表 / 字典 / 集合):直接改内容,对象本身不变;
- 不可变(数字 / 字符串 / 元组):改内容会生成新对象(比如
- 常用场景 :
- 存单个数值 / 布尔 → 数字;
- 存文本 → 字符串;
- 存有序、可改的多个数据 → 列表;
- 存键值对(比如用户信息:姓名→小明,年龄→18)→ 字典;
- 存不重复的无序数据 → 集合;
- 存不可修改的有序数据 → 元组。
四、与C语言和C++基础数据之间的差别
4.1、定义方式和书写规则之间的差异
有时候就是这样,如果没有C或C++基础,直接学习Python,反而还不容易出现一些记忆错乱,和肌肉记忆导致错误的地方。
1、千万不要被python中数据类型的英文名,干扰,如list 、truple、dictionary、set等干扰,记住非常重要的一条【所有python类型,包含基本数据和复合数据一律,不需要写数据类型】
2、所有复合类型如 list 、truple、dictionary、set均不需要指定长度,这点需要记住,不要因为肌肉记忆,直接给这些数据类型定义一个长度,尤其是set,不注意的话,书写方式和数组特别像
3、python是不支持C和C++中的,数组,结构体,枚举。共用体的,此外python书写方式上也没有指针(只有self这种类指针的用法)(但是python依然是可以导入第三方包,来定义c中的数组和结构体,这是属于一些高阶用法,我们暂时不需要关注)
4、注意python所有复合数据类型,所有元素之间的分隔符都是","(注意是英文符号下的逗号),这点需要和C结构体定义中使用的";"作出区分
5、python中所有数据类型的所有成员,都是可以是任意基础数据类型。这句话有点拗口,解释就是python不像C或C++中的数组,要求成员的基本数据类型一致,可以是任意类型。
6、python中的所有复合数据类型,都可以使用以下形式进行检索:
符合类型变量名称+方括号+检索值:如
my_list[3]
my_truple[4]
my_dictionary["one"],注意这里的双引号也是必须得
my_set[4]
看到这里,我们似乎也就明白了,以前说的一句话,python是为了方便易学,易上手,维持牺牲一部分性能,而C/C++则是严谨的,但是比较难学,知识点零碎。
第6条,则说明了,python中的各种复合数据类型,结合了c、C++中数组和结构体的共同优点。c中的结构体,是不是不能使用for循环去遍历。但是python中除了字典,其他值都是可以使用for循环遍历的,这就是一种优势
4.3 、python的数据嵌套
我们知道,C或C++中是支持符合数据类型的嵌套的,如整形数组中嵌套整形数组,结构体中嵌套结构体,嵌套数组,、。
而Python的符合数据嵌套,则看起来更随意 ,方式也更加多样化 。从理论上来说,python是支持无限嵌套的,但是实际上没有编译器是支持无限嵌套的,也没有硬件是支持无限嵌套的。
Python 嵌套的唯一语法约束是「集合 / 字典的键必须是可哈希类型」(不可变类型:int/str/tuple/frozenset;可变类型:list/dict/set 不可哈希),其余场景无限制。总结为图表,如下图:
| 类型 | 可哈希性 | 能否作为集合元素 / 字典键 | 能否被嵌套(作为值) |
|---|---|---|---|
| list | ❌ 不可 | ❌ 不能 | ✅ 可以(任意位置) |
| tuple | ✅ 可 | ✅ 能 | ✅ 可以 |
| dict | ❌ 不可 | ❌ 不能 | ✅ 可以(任意位置) |
| set | ❌ 不可 | ❌ 不能 | ✅ 仅能作为其他类型的值(需用 frozenset) |
| frozenset | ✅ 可 | ✅ 能 | ✅ 可以 |
说人话就是字典中,key值不能是list,也不能是dict 或set,但是注意常量(数字,字符)都是可以作为dict的key,也可以作为set的元素
4.3.1、什么是可哈希值
只有先了解这一点,才可以理解嵌套。
"可哈希" 是 Python 中对不可变对象的一种特性描述:如果一个对象在其生命周期内哈希值(hash value)永不改变 ,且能通过 __hash__() 方法返回唯一的整数哈希值,同时支持通过 __eq__() 方法判断是否相等,那么这个对象就是「可哈希的」。
简单说:可哈希 = 不可变 + 能生成唯一哈希值 + 可比较相等性。
4.3.2 实际案例讲解嵌套-同类型嵌套
首先同类型进行嵌套是最简单的。
list内嵌套list:
python
# 基础:二维列表(类比 C 的 int arr[2][3])
matrix = [
[1, 2, 3], # 内层列表(行)
[4, 5, 6] # 内层列表(行)
]
# 访问:和 C 多维数组语法一致(索引从 0 开始)
print(matrix[0][1]) # 2(第一行第二列)
# 进阶:三维列表(无限嵌套)
cube = [
[[1, 2], [3, 4]], # 第一层
[[5, 6], [7, 8,9]] # 第二层,注意这里第二层的第二个list中,包含了3个元素
]
print(cube[1][0][1]) # 6(第二层、第一行、第二列)
# 动态修改(C 需手动扩容,Python 一键搞定)
matrix.append([7, 8, 9]) # 新增一行 → [[1,2,3],[4,5,6],[7,8,9]]
matrix[1][2] = 10 # 修改元素 → [[1,2,3],[4,5,10],[7,8,9]]
但是注意:
1:、一般我们不建议>2维的数组,代码中也可以看出来,查找时是非常混乱的。
2、每一个最小List的元素数量不是固定不变的
字典嵌套字典(dict in dict)------ 类比 C/C++ "结构体嵌套结构体"
python
# 基础:二级字典(用户信息)
user = {
"name": "小明",
"age": 18,
"address": { # 内层字典(地址结构体)
"city": "北京",
"street": "朝阳路",
"zip": 100000
}
}
# 访问:通过键逐层取值
print(user["address"]["city"]) # 北京
# 进阶:三级字典(系统配置)
config = {
"server": {
"ip": "127.0.0.1",
"port": 8080,
"timeout": { # 第三层字典
"connect": 5,
"read": 10
}
}
}
print(config["server"]["timeout"]["read"]) # 10
# 动态增删内层字典
user["address"]["district"] = "朝阳区" # 新增地址字段
del config["server"]["timeout"]["connect"] # 删除字段
这里关注一下,(1)字典读取的方式,和字典缩进。(2)因为上面说的字典的key值必须是可哈希值,所以被嵌入的字典都被安排在了value的位置
truple元组嵌套元组
4.4、嵌套class的实例对象
先看下面一段代码
python
class Fruit:
def __init__(self, name, price):
self.name = name
self.price = price
fruits = [Fruit("apple", 5), Fruit("banana", 3), Fruit("cherry", 7)]
# key=lambda x: x.price:用Fruit对象的price属性作为排序依据
fruits.sort(key=lambda x: x.price)
print([f.name for f in fruits]) # 输出:['banana', 'apple', 'cherry'](价格从低到高)
这里可以看出来,这里将class实例对象作为list的元素,这里又引申出两个概念,匿名对象和显示对象。
匿名对象,即没有名字的对象(C++中通过vector也可以实现匿名对象的创建)
4.5、python中的数据类型,兼有方法
学到这里,看了这么多代码,我们也看出来了python这些复合数据类型,兼有方法,也就是函数的性质。和c++中的对象一样,可以通过 . 来调用函数。
如果从复合类型数据,能调用方法的属性来看,这是有点类似class实例化的特征了
复合数据可调用的方法
| 数据类型 | 方法名称 | 功能详细说明 | 典型应用场景示例 |
|---|---|---|---|
| 列表 (list) | .append(x) |
在列表末尾添加单个元素 x | 动态收集用户输入的数据 |
| 列表 (list) | .extend(iterable) |
将可迭代对象的所有元素追加到列表末尾 | 合并两个列表 |
| 列表 (list) | .pop([i]) |
移除并返回指定索引 i 的元素(无参数时默认最后一个) | 实现栈结构的弹出操作 |
| 列表 (list) | .sort(key=None, reverse=False) |
对列表原地排序,key 指定排序依据,reverse=True 为降序 | 数据统计前的排序 |
| 列表 (list) | .reverse() |
原地反转列表元素顺序 | 需要倒序遍历的场景 |
extend()参数必须是可迭代的对象,即list,truple,set(注意dict是不能被添加到list中去的),不会改变添加list的类型(即依然保持list),被添加对象也不会改变自身数据属性 append则是添加单个对象,可以是常量和字符,也可以是其他基本数据中的一个元素,甚至是class实例对象所引用的属性。 pop方法,则是弹出该元素,会改变list本身的数据结构,其实更可以理解为删除。 sort和reverse方法都是排序函数,sort函数存在两个参数 key,和reserve两个参数。从本质上来说,并不是两个参数,而是两个表达式:以下是sort()的具体方法: python lst = ["apple", "banana", "cherry", "date"] lst.sort(key=len, reverse=True) print(lst) # 输出:['cherry', 'banana', 'apple', 'date'](长度从长到短) key是指定排序的方式,reverse=True是默认升序,reserve=false |
|||
| 字典 (dict) | .keys() |
返回字典所有键的视图对象(可迭代) | 遍历字典的键 |
| 字典 (dict) | .values() |
返回字典所有值的视图对象(可迭代) | 遍历字典的值 |
| 字典 (dict) | .items() |
返回键值对元组的视图对象(可迭代) | 同时遍历键和值 |
| 字典 (dict) | .get(key[, default]) |
安全获取键对应的值,若键不存在则返回 default(默认 None) | 防止 KeyError 异常 |
| 字典 (dict) | .update(other) |
用另一个字典或键值对更新当前字典 | 合并配置参数 |
| 字符串 (str) | .lower() |
返回字符串的小写形式 | 用户输入标准化 |
| 字符串 (str) | .upper() |
返回字符串的大写形式 | 标题格式化 |
| 字符串 (str) | .split(sep=None, maxsplit=-1) |
按分隔符 sep 分割字符串为列表(maxsplit 限制分割次数) | 解析 CSV 数据 |
| 字符串 (str) | .join(iterable) |
用字符串连接可迭代对象中的元素 | 拼接路径、URL 参数 |
| 字符串 (str) | .replace(old, new[, count]) |
替换字符串中 old 子串为 new,count 限制替换次数 | 文本清理、数据预处理 |
| 集合 (set) | .add(x) |
向集合中添加元素 x(若已存在则无操作) | 去重收集数据 |
| 集合 (set) | .remove(x) |
移除元素 x,若不存在则抛出 KeyError | 精确删除特定元素 |
| 集合 (set) | .discard(x) |
移除元素 x,若不存在则不抛异常 | 安全删除元素 |
| 集合 (set) | .union(other) / |
` | 返回当前集合与 other 的并集 |
| 集合 (set) | .intersection(other) / & |
返回当前集合与 other 的交集 | 寻找共同元素 |
| 元组 (tuple) | .count(x) |
统计元组中元素 x 出现的次数 | 数据分析 |
| 元组 (tuple) | .index(x[, start[, end]]) |
返回元素 x 首次出现的索引,可指定搜索范围 | 查找特定值位置 |
五、函数定义的区别
5.1、定义返回类型的区别
C或C++,需定义返回值类型
// 格式:返回值类型 函数名(参数类型1 参数名1, 参数类型2 参数名2) { 函数体 }
int add(int a, int b) { // 必须声明:返回值类型+参数类型
return a + b;
}
- 必须写返回值类型 (无返回值用
void); - 必须写参数类型 + 参数名;
- 函数体必须用
{}包裹,结尾可加分号(C++ 允许,C 不强制)。
python,不需要定义返回值类型,使用关键字def,形参后的括号后加上:
# 格式:def 函数名(参数名1, 参数名2): 函数体(缩进)
def add(a, b): # 无需声明任何类型
return a + b
- 用
def关键字开头,无返回值类型(返回值类型由实际返回值决定); - 参数仅写参数名 ,无需指定类型,小括号后必须加上":";
- 函数体靠缩进 区分(不用
{}),结尾无分号。
5.2、一个靠"{}+;"(;并不是必须得)代表函数结束,一个靠缩进(identation)规则代表函数体结束
先详细介绍一下python的缩进:
Python 中的缩进 是python中语法级别的核心规则,是必须需要遵守的规则,核心规则总结如下:
记住「"冒号 + 4 个空格" 触发缩进,回归外层结束缩进,不使用空格和 Tab混用」,就能避免 99% 的缩进错误。
六、正常语句和空语句的区别
6.1 正常语句的区别
此外还有一点非常容易引起错误,大家都知道,python一行只写一条语句,然后按下回车换行键,继续编写下一条语句,编译器也会根据读取到的回车换行键,认定这条语句结束。即语句结束后,不需要强制写";"
但是必须要知道以下几点:
(1)python本身的语法并没有删除";" 只要你愿意,你在每行加上 ; 或部分语句加上都可以
(2)必须要加的情况,如果在一行中,写多条语句
小结,python判断语句结束,优先根据回车换行键判断,同时兼容;
6.2 空语句的区别和联系
| 对比维度 | C/C++ 空语句 | Python 空语句 |
|---|---|---|
| 核心写法 | 单独的; |
pass(推荐)或"..." |
| 语法本质 | 语句结束符单独存在,代表 "无执行逻辑" | 显式关键字占位,填补 "必须有代码块" 的语法要求 |
| 代码块 vs 语句 | {}是空代码块,;是空语句(效果等价) |
无 "空代码块" 概念 ,缩进内必须有pass/... |
| 错误场景 | 少写;会编译报错(比如for循环) |
无pass会语法报错(比如空函数) |
| 可读性 | ;易被忽略(比如while(1);),推荐用{} |
pass/...语义明确,一眼看出是 "空逻辑" |
七、C++中Class的定义和Python中定义的区别和联系
7.1、先从最基本的定义说起
先看以下两段代码:分别为C++定义模板类,和Python定义模板类
代码段1:C++代码
cpp#include <string> using namespace std; class Car { private: // 默认private,显式声明更清晰 string brand; // 必须声明类型(string) int price; // 必须声明类型(int) public: // 成员方法声明(需指定参数/返回值类型) Car(string b, int p); // 构造函数声明 void run(); // 普通方法声明 int get_price(); // 普通方法声明 }; // 源文件(类实现) // 构造函数实现(需绑定类名) Car::Car(string b, int p) { brand = b; price = p; } void Car::run() { cout << brand << " is running" << endl; } int Car::get_price() { return price; }
代码段2:Python代码
python
class Car:
# 类属性(无需声明类型)
category = "automobile"
# 构造方法(无需声明参数/返回值类型)
def __init__(self, brand, price):
# 实例属性(动态绑定,无需提前声明)
self.brand = brand
self.price = price
# 普通方法(self是隐含的实例引用,无需声明类型)
def run(self):
print(f"{self.brand} is running")
def get_price(self):
return self.price
相同点:
(1)基本的结构框架都是一样的,都包含属性和方法,
(2)都采取class作为关键字,来定义类
(3)都具有初始化函数
实现细节上,有一些不同:
首先说一些:通用的不同,方法和属性都不需要指定类型,都不需要使用{},而是使用缩进,这些都不需要多说
(1)一个初始化函数,C++是与类名相同的函数(不写返回值类型),Python是__init__()函数来执行初始化
(2)python,是存在一个默认形参self(注意这个self不是python的关键字,但是大家一般都这么写)
(3)函数,也就是方法定义的地方,C++一般建议是写在class类模板的外面,且使用:类模板名称::函数名称(形参),来定义。而python则不需要这样的写法,利用缩进规则,写在定义时即可
(4)C++中存在3个关键字public,private,和protect,python没有这样的规则
7.2 、类实例化的区别和联系
C++实例化,采取 类模板名 + 实例名+(实参)或者使用new,以7.1中的代码为例:
Car SUV_1("星愿",30000);或者 Car *SUV = new Car("星愿",30000);
Car SUV_2("五菱Mini",25000);或者 Car *SUV = new Car("五菱Mini",25000);
而python则是采取了,另外一种方式:实例化名= 类模板名称(形参),
SUV_1 = Car ("星愿",30000)
SUV_2 = Car("五菱Mini",25000)
7.3 python与C++深层次Class的用法
7.3.1. 初始化的区别
(1)C++ 的初始化
C++ 的初始化分为构造函数和初始化列表,规则严格且多态不生效:
- 必须显式定义构造函数(默认生成无参构造、拷贝构造等,C++11 后支持默认构造
=default); - 支持初始化列表(优先于构造函数体执行,用于初始化 const 成员、引用成员、父类成员);
- 构造函数不能返回值,且编译期确定调用哪个构造函数;
- 构造函数中多态不生效(子类构造时先调用父类构造,此时子类对象未完全创建,虚函数不会调用子类实现);
- 支持拷贝构造 、移动构造(C++11 新增),用于对象拷贝 / 移动场景。
示例代码:
cpp
cpp
#include <iostream>
using namespace std;
class Parent {
public:
Parent(int a) : m_a(a) { // 初始化列表
cout << "Parent构造" << endl;
}
private:
int m_a;
};
class Child : public Parent {
public:
// 必须通过初始化列表调用父类构造,即在构造函数的:后直接调用父类的构造函数
Child(int a, string b) : Parent(a), m_b(b) {
cout << "Child构造" << endl;
}
private:
const string m_b; // const成员必须在初始化列表初始化
};
int main() {
Child c(10, "test"); //这里将child实例化,并将父类people中的实参传递进去
return 0;
}
(2)Python 的初始化
Python 的初始化是分层执行的,__init__并非真正的构造函数:
- 真正的构造函数是
__new__(负责创建对象实例,返回实例对象),__init__是初始化函数(接收__new__创建的实例,进行属性初始化); - 无需显式调用父类初始化(可通过
super().__init__()主动调用),支持动态传递参数; - 初始化时可动态添加属性(即使不在
__init__中定义,后续也可通过obj.attr = value添加); - 支持默认参数、关键字参数,初始化逻辑更灵活;
- 无专门的拷贝构造 / 移动构造,对象拷贝通过
copy模块实现(浅拷贝copy.copy()、深拷贝copy.deepcopy())。
示例代码:
python
python
class Parent:
def __init__(self, a):
self.a = a
print("Parent初始化")
class Child(Parent):
def __init__(self, a, b):
super().__init__(a) # 主动调用父类初始化
self.b = b
print("Child初始化")
# 创建实例
C = Child(10, "test")
# 动态添加属性,这里给实例对象,添加了一个变量c,或者属性c
C.c = 20
print(C.a, C.b, C.c)
以上代码中,可以看出python中,
(1)继承写法和C++很不一样(语法上),写在子类名称后的括号里面。
(2)从此处还有一个非常重要的特性,即通过实例对象,还能添加属性。被添加的属性只属于实例C,而通过child模板创建的其他实例对象,则不会存在这个属性
(3)和C++不一样的地方在于,对属性赋值,C++是可以直接利用初始化列表实现,而python则只能使用赋值语句实现
(4)子类中如何实现父类初始化,python使用super()init(形参)来实现,(这里有必要多说一句,__是两个下划线,这类写法目前我所知道的也是python中独有的)
(4.1)、super()是python内部的一个内置class(也就是build-in class)
(4.2)、如果子类存在多个父类,或者存在多重继承的情况下,如何区别调用哪一个父类的初始化方法,这里又牵扯出MRO链条。
(4.3)、详细解释MRO链条
初始化核心差异总结
| 特性 | C++ | Python |
|---|---|---|
| 真正构造函数 | 构造函数(编译期确定) | __new__(运行时执行) |
| 初始化逻辑 | 初始化列表(优先)+ 构造函数体 | __init__(初始化实例属性) |
| 父类初始化 | 必须在初始化列表显式调用 | 可通过super()主动调用,可选 |
| 动态属性添加 | 不支持(编译期固定成员) | 支持(运行时动态添加) |
| 拷贝 / 移动构造 | 原生支持(拷贝 / 移动语义) | 无原生支持,依赖copy模块 |
| 多态生效 | 构造函数中多态不生效 | __init__中可调用子类重写方法 |
7.3.2、继承的区别
(1)C++ 的继承
C++ 继承是静态编译期继承,支持多继承和访问权限控制,规则严格:
- 支持多继承 (一个子类可继承多个父类),但会带来菱形继承问题(需通过
virtual虚继承解决); - 继承权限控制:
public/protected/private继承(影响父类成员在子类中的访问权限); - 虚继承:用于解决多继承的菱形问题,确保父类只被初始化一次;
- 多态实现:基于虚函数 (
virtual关键字),通过虚函数表(vtable)实现运行时多态; - 子类不能新增父类没有的虚函数(编译期确定虚函数表);
- 支持纯虚函数 (
virtual void func() = 0;),用于定义抽象类(不能实例化)。
示例代码(多继承 + 虚继承):
cpp
#include <iostream>
using namespace std;
// 虚基类
class Base {
public:
Base() { cout << "Base构造" << endl; }
virtual void show() = 0; // 纯虚函数,抽象类
};
class A : virtual public Base { // 虚继承
public:
void show() override { cout << "A::show" << endl; }
};
class B : virtual public Base { // 虚继承
public:
void show() override { cout << "B::show" << endl; }
};
// 多继承
class C : public A, public B {
public:
void show() override { cout << "C::show" << endl; } // 重写虚函数
};
int main() {
C c;
c.show(); // 调用C::show
Base* ptr = &c;
ptr->show(); // 多态:调用C::show
return 0;
}
(2)Python 的继承
Python 继承是动态运行时继承,支持多继承和灵活的方法解析,无严格权限控制:
- 支持多继承 (一个子类可继承多个父类),通过 **MRO(方法解析顺序)** 解决菱形继承问题(
class.__mro__可查看顺序); - 无继承权限控制(仅通过属性命名规范区分公开 / 私有,无编译期限制);
- 多态实现:无需关键字,子类重写父类方法即可实现多态(运行时动态绑定);
- 支持动态继承 (运行时可修改类的父类,如
Child.__bases__ = (Parent1, Parent2)); - 支持抽象类 (通过
abc模块的ABCMeta和@abstractmethod装饰器实现); - 支持Mixin 模式(一种特殊的多继承,用于复用功能,不单独实例化);
super()函数:基于 MRO 顺序查找父类方法,而非直接调用父类。
示例代码(多继承 + MRO):
python
python
class Base:
def show(self):
print("Base::show")
class A(Base):
def show(self):
print("A::show")
class B(Base):
def show(self):
print("B::show")
# 多继承
class C(A, B):
pass
# 查看MRO顺序
print(C.__mro__) # (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)
c = C()
c.show() # 按MRO调用A::show
# 动态修改父类
C.__bases__ = (B, A)
print(C.__mro__) # 父类顺序改变
c.show() # 调用B::show
继承核心差异总结
| 特性 | C++ | Python |
|---|---|---|
| 多继承支持 | 支持,存在菱形继承问题(需虚继承解决) | 支持,通过 MRO 解决菱形继承问题 |
| 继承权限控制 | 支持(public/protected/private 继承) | 不支持(无编译期权限限制) |
| 多态实现 | 基于 virtual 虚函数 + 虚函数表 | 基于运行时动态绑定,无需关键字 |
| 动态修改父类 | 不支持(编译期固定继承关系) | 支持(修改__bases__属性) |
| 抽象类实现 | 基于纯虚函数(virtual ... = 0) |
基于abc模块(@abstractmethod) |
| 方法解析顺序 | 编译期确定,多继承需手动处理 | 运行时 MRO 顺序,自动解析 |
| Mixin 模式 | 无原生支持(可通过多继承模拟) | 原生支持(常用功能复用方式) |
| 父类方法调用 | 直接通过父类名调用(如Parent::func()) |
通过super()基于 MRO 调用 |
3. 属性读写权限的区别
(1)C++ 的属性权限
C++ 的属性权限是编译期严格控制,通过访问修饰符限制,无法绕过:
- 访问修饰符:
public(公开,任意位置可访问)、protected(保护,子类 + 本类可访问)、private(私有,仅本类可访问); - 权限是编译期强制限制:若试图访问 private 成员,编译器直接报错,无法运行;
- 友元机制:
friend关键字可突破权限限制(友元函数 / 友元类可访问类的 private/protected 成员); - const 成员:
const修饰的成员变量必须在初始化列表初始化,且只能读不能写; - 静态成员:
static修饰的成员属于类本身,而非实例,可通过类名直接访问。
示例代码:
cpp
cpp
#include <iostream>
using namespace std;
class Test {
public:
Test(int a) : m_private_a(a), m_protected_b(10) {}
static int m_public_c; // 静态公开成员
void show() {
cout << m_private_a << endl; // 本类可访问private成员
}
// 友元函数声明
friend void friendFunc(Test& t); /**这里注意友元函数的形参,必须是需要绑定的CLASS类型的引 用,从另外一个方面来理解,就是friend,就是告诉编译器,这里的函数形参**/
private:
int m_private_a; // 私有成员
protected:
int m_protected_b; // 保护成员
};
int Test::m_public_c = 20; // 静态成员初始化,
// 友元函数:可访问private/protected成员
void friendFunc(Test& t) {
cout << t.m_private_a << " " << t.m_protected_b << endl;
}
class ChildTest : public Test {
public:
ChildTest(int a) : Test(a) {}
void showProtected() {
cout << m_protected_b << endl; // 子类可访问protected成员
// cout << m_private_a << endl; // 编译报错:无法访问private成员
}
};
int main() {
Test t(5);
t.show();
cout << Test::m_public_c << endl; // 访问静态公开成员,注意这里class的静态成员,引用与初始化 Test::m_public_c
friendFunc(t); // 友元函数访问私有/保护成员
ChildTest ct(8);
ct.showProtected();
return 0;
}
(2)Python 的属性权限
Python 的属性权限是约定俗成 ,无编译期强制限制,通过特殊机制实现 "伪私有":
- 无严格访问修饰符:通过命名规范区分:
- 公开属性:普通命名(如
attr),任意位置可访问; - 保护属性:单下划线开头(如
_attr),约定子类可访问,外部不建议访问(但可直接访问); - 私有属性:双下划线开头(如
__attr),通过 ** 名称改写(name mangling)** 实现伪私有(实际变为_类名__attr,仍可绕过访问);
- 公开属性:普通命名(如
- 属性读写控制:通过属性装饰器 (
@property、@attr.setter、@attr.deleter)实现灵活的读写权限控制(如只读、只写、自定义校验逻辑); - 动态属性拦截:通过
__getattr__、__setattr__、__delattr__方法,可拦截所有属性的读写 / 删除操作,实现自定义逻辑; - 无友元机制:无法通过特殊关键字突破属性权限约定;
- 静态成员:通过
@staticmethod(静态方法)、@classmethod(类方法)实现,静态属性直接定义在类中。
示例代码:
python
python
class Test:
m_public_c = 20 # 静态公开属性
def __init__(self, a):
self.attr = a # 公开属性
self._attr = 10 # 保护属性(约定)
self.__attr = 20 # 私有属性(名称改写)
# 属性装饰器:只读属性
@property
def read_only_attr(self):
return self.attr + 10
# 属性装饰器:可写属性(带校验)
@property
def write_attr(self):
return self._attr
@write_attr.setter
def write_attr(self, value):
if isinstance(value, int):
self._attr = value
else:
raise ValueError("必须是整数")
# 拦截属性访问
def __getattr__(self, name):
return f"属性{name}不存在"
class ChildTest(Test):
def show(self):
print(self._attr) # 子类可访问保护属性
# print(self.__attr) # 直接访问报错
print(self._Test__attr) # 绕过名称改写,访问私有属性
# 实例化
t = Test(5)
print(t.attr) # 访问公开属性
print(t._attr) # 访问保护属性(不建议,但可行)
print(t._Test__attr) # 绕过访问私有属性
print(t.read_only_attr) # 访问只读属性
# t.read_only_attr = 100 # 报错:无法修改只读属性
t.write_attr = 30 # 修改可写属性
print(t.write_attr)
# 访问不存在的属性(触发__getattr__)
print(t.xxx)
ct = ChildTest(8)
ct.show()
属性权限核心差异总结
| 特性 | C++ | Python |
|---|---|---|
| 访问修饰符 | 原生支持(public/protected/private) | 无原生支持,依赖命名规范(_/__) |
| 权限强制程度 | 编译期强制限制(报错无法运行) | 运行时约定俗成(可绕过) |
| 友元机制 | 支持(突破权限限制) | 不支持 |
| 属性读写控制 | 需通过 get/set 方法实现 | 支持@property装饰器,灵活控制 |
| 动态属性拦截 | 不支持(编译期固定成员) | 支持(__getattr__等魔法方法) |
| 私有属性实现 | 真正私有(无法绕过) | 伪私有(名称改写,可绕过) |
| const 只读属性 | 原生支持(const关键字) |
需通过@property实现(无 setter) |
4. C++ class 有而 Python 没有的特性
C++ 的 class 存在不少 Python 不具备的特性,核心是基于静态编译和严格封装的设计:
- 严格的访问权限控制
- C++ 的
public/protected/private是编译期强制限制,private 成员无法被外部访问(友元除外),而 Python 仅为约定,无强制约束; - 继承权限控制(public/protected/private 继承):可改变父类成员在子类中的访问权限,Python 无此概念。
- 虚函数与虚函数表
- C++ 通过
virtual关键字实现虚函数,编译期生成虚函数表(vtable),运行时通过 vtable 找到子类实现,实现高效多态; - Python 无虚函数概念,多态基于运行时动态绑定,效率低于 C++ 的虚函数表。
- 纯虚函数与抽象类(原生支持)
- C++ 可直接通过
virtual void func() = 0;定义纯虚函数,生成抽象类(无法实例化),无需额外模块; - Python 需依赖
abc模块实现抽象类,非原生支持。
- 构造函数初始化列表
- C++ 的初始化列表优先于构造函数体执行,可初始化 const 成员、引用成员、父类成员,Python 无对应的语法(
__init__仅为初始化,无法初始化 const 类似的不可变成员)。
- 拷贝构造与移动构造(原生语义)
- C++ 原生支持拷贝构造(
Class(const Class& other))和移动构造(Class(Class&& other)),分别对应对象的拷贝和移动语义,效率更高; - Python 无原生拷贝 / 移动构造,需通过
copy模块实现,效率较低。
- 友元机制
- C++ 的
friend关键字可让外部函数 / 类访问类的私有 / 保护成员,用于特殊场景(如运算符重载),Python 无此机制。
- 模板类(泛型编程)
- C++ 的
template关键字支持模板类,可实现编译期泛型编程(类型无关的代码),如std::vector<T>; - Python 本身是动态类型,无需模板类即可实现泛型功能,无对应的语法。
- const 成员与 const 成员函数
- C++ 的
const关键字可修饰成员变量(只读,必须初始化列表初始化)和成员函数(void func() const,保证不修改成员变量); - Python 无原生 const 语法,只读属性需通过
@property模拟,无法保证成员函数不修改属性。
5. Python class 有而 C++ 没有的特性
Python 的 class 基于动态特性,拥有不少 C++ 不具备的灵活特性:
- 类本身是对象(元类)
- Python 的 class 是
type的实例(元类的对象),可动态修改类的属性 / 方法(如Class.attr = value、Class.__bases__ = (Parent,)); - C++ 的 class 是编译期的类型定义,不是对象,无法动态修改。
- 动态属性与方法
- Python 的实例和类都可在运行时动态添加 / 删除属性 / 方法(如
obj.attr = value、def func(): pass; Class.func = func); - C++ 的类成员在编译期固定,无法动态增删。
- 属性装饰器(
@property)
- Python 的
@property装饰器可将方法伪装成属性,灵活实现读写权限控制、数据校验、懒加载等功能; - C++ 需通过手动编写 get/set 方法实现类似功能,语法繁琐。
- 魔法方法(特殊方法)
- Python 的 class 支持大量魔法方法(如
__init__、__str__、__getattr__、__add__等),可拦截对象的各类操作(属性访问、运算符重载、序列化等); - C++ 仅支持部分运算符重载,无类似的统一魔法方法体系。
- MRO 方法解析顺序
- Python 的多继承通过 MRO(C3 算法)自动解决菱形继承问题,无需手动处理;
- C++ 的多继承需通过虚继承手动解决菱形继承问题,语法复杂。
- 动态继承(修改
__bases__)
- Python 可在运行时动态修改类的父类(
Class.__bases__ = (Parent1, Parent2)),改变继承关系; - C++ 的继承关系在编译期固定,无法修改。
- Mixin 模式(原生支持)
- Python 常用 Mixin 模式实现功能复用(如
class MyClass(Parent, Mixin1, Mixin2)),Mixin 类不单独实例化,仅提供功能; - C++ 无 Mixin 的原生概念,需通过多继承模拟,灵活性不足。
- 无需显式声明成员变量
- Python 的 class 无需提前声明成员变量,在
__init__中直接赋值即可,甚至可在后续动态添加; - C++ 的 class 必须提前声明成员变量(public/protected/private),否则编译报错。
- 元类(Metaclass)
- Python 的元类(如
type、自定义元类)可控制类的创建过程,实现类的个性化定制(如自动添加方法、校验类的结构); - C++ 无元类概念,无法干预类的编译创建过程。
三、 核心差异总结
| 对比维度 | C++ class 特性 | Python class 特性 |
|---|---|---|
| 本质 | 静态类型,编译期类型检查 | 动态类型,运行时动态绑定 |
| 初始化 | 构造函数 + 初始化列表,const / 引用必须初始化列表 | __new__(创建)+ __init__(初始化),动态添加属性 |
| 继承 | 多继承(需虚继承解决菱形问题),继承权限控制 | 多继承(MRO 自动解析),动态修改父类 |
| 多态 | 基于 virtual 虚函数 + 虚函数表 | 基于运行时动态绑定,无需关键字 |
| 属性权限 | public/protected/private(编译期强制) | 命名规范(_/__)+ @property(约定) |
| 独有的特性 | 初始化列表、友元、模板类、const 成员、虚函数 | 元类、魔法方法、动态属性 / 继承、@property、Mixin |
| 灵活性 | 低(编译期固定,规则严格) | 高(运行时动态,灵活扩展) |
| 效率 | 高(编译期优化,虚函数表高效) | 较低(动态绑定,额外开销) |
| 抽象类 | 纯虚函数原生支持 | 依赖abc模块实现 |
7.4、另外C++class中一些特有修饰关键字,python也是没有的
如C++中常见的
friend(友源)
static(静态)
virtual(虚继承)
const (只读)
Auto,此外访问控制符 (public/private/protected),函数重载,析构函数也都是C++中独有的,python中都是没有的。
7.5、python独有,而C++没有特性
Python 的这些特性是 "动态、灵活" 的体现,C++(静态编译型)无法原生支持:
-
动态添加 / 删除类 / 实例属性 Python 运行时可给类 / 实例新增属性(如
obj.new_attr = 10)、删除属性(del obj.attr);C++ 类的属性是编译期固定的,无法动态修改。 -
动态修改实例类型 Python 可通过
obj.__class__ = 新类修改实例的类型;C++ 实例的类型是编译期确定的,无法动态变更。 -
__init__外的动态初始化 Python 可在任意方法 / 外部代码中给实例绑定属性(无需在__init__中声明);C++ 实例属性必须在类中声明,且只能在构造函数中初始化。 -
元类(Metaclass) Python 的元类是 "类的类",可自定义类的创建逻辑(如
type动态创建类);C++ 无元类概念,类的结构由编译器静态解析。 -
属性装饰器(
@property) Python 用@property可将方法伪装成属性(实现 getter/setter 的简洁语法);C++ 需手动写get_xxx()/set_xxx()方法,无原生装饰器语法。