前言
- 这个系列的文章,以python为主,会从编程语言的四类知识范畴去总结对比python和C++。这部分文章共四篇,分别是:数据类型和数据结构、语法规则和执行流程、内存模型和运行机制、编程范式和语言特性,这篇文章是从数据类型和数据结构的角度对比两者的不同。
- 这部分总结,是通过总分的架构实现的,目的是展现两种编程语言的区别,对比着学习,理解他们之间的本质差异,加深记忆。
- 这个系列的每一篇文章,后面会继续修改,完善。
- python和C++本质不同:Python中万物皆对象,所有数据、变量本质都是对象引用,统一面向对象设计;C++兼顾原生值类型 + 对象类型,分为两套独立体系,并非一切都是对象。
- 数据类型和数据结构:将编程语言比喻成一个产品加工厂的话,数据类型和数据结构就是这个工厂生成时需要的原材料。
- 数据类型:编程语言中对数据的分类定义,规定了一类数据的取值范围、存储形式以及可进行的合法操作,是构成程序数据的基础单元。
- 数据结构:数据的组织、存储与排列方式,基于基础数据类型构建而成,用于高效地管理、访问和操作批量数据,核心是数据的布局逻辑与操作效率。
摘要
| 序号 | 对比概念 | python | C++ | 补充 |
|---|---|---|---|---|
| 1 | 自带数据类型 | √ | √ | × |
| 2 | 衍生数据类型 | √ | √ | √ |
| 3 | 数据类型特性 | √ | √ | × |
概念对比
一、自带数据类型:语言内核 / 编译器底层硬编码实现、无需引入任何库、无需自定义。声明后可直接使用的基础数据类型,是语言最底层的基础存储单元。
1.1、在python中的定义:
- python自带数据类型一共五类:int、float、bool、str、None
- int:内置基础类型;无限精度整数,不分长短整型,自动适配数值大小,二进制/八进制/十六进制均可表示。
- float:内置基础类型;64位双精度浮点数,存在浮点精度误差,支持科学计数法。
- bool:内置基础类型,int子类;仅True、False两个取值,True等价1,False等价0。
- str:内置序列类型;不可变字符序列,单/双/三引号定义,支持切片。不可变,不能增删改,仅可查询。
- None:内置基础类型;唯一值None,表示空对象,区别0、空字符串。
1.2、具体案例:
python
# -*- coding: utf-8 -*-
# Python五大自带基础数据类型演示
num_int = 9999999999999999999999999999 # int 无限大整数
num_float = 3.14e-2 # float 科学计数浮点数
flag_bool = True # bool布尔值
text_str = "hello python" # str字符串
empty_none = None # None空对象
# 打印值与对应类型
print(num_int, type(num_int)) # 9999999999999999999999999999 <class 'int'>
print(num_float, type(num_float)) # 0.0314 <class 'float'>
print(flag_bool, type(flag_bool)) # True <class 'bool'>
print(text_str, type(text_str)) # hello python <class 'str'>
print(empty_none, type(empty_none)) # None <class 'NoneType'>
# 补充特性演示
print(True + 5) # bool是int子类,True等价1 6
print(text_str[0:3]) # str切片读取,不可修改 hel
print(None == 0) # None不等于数字0 False
print(None == "") # None不等于空字符串 False
1.3、与C++对比:
- C++ 标准原生基础标量只有四类:整型、浮点型、布尔型、空类型void;
- Python 的 str 是原生基础类型;C++ 无原生字符串基础类型,对应能力由标准库 std::string 提供(属于衍生类型)
- C++的整型包含 char、short、int、long、long long 及对应无符号版本,固定位宽、存在数值溢出;Python int 为无限精度对象,无溢出限制,二者语义相似但底层实现完全不同。
- C++ 提供 float、double、long double 三种精度浮点;Python 仅一种浮点,底层等价 C++ double,缺少单精度与扩展长浮点。
- C++布尔型和python类似,仅有真 / 假两个取值,但底层模型不同。Python bool 是 int 子类;C++ bool 为独立原生类型,标准未强制规定字节大小,主流编译器实现为 1 字节。
- C++的空类型 void和Python的空类型None 完全不互通、含义不同。void 无法创建变量,仅作函数返回、万能指针修饰;Python None 是可赋值的空对象。
二、衍生数据类型:以自带数据类型为底层基础,通过不同衍生方法(语法原生衍生、标准库衍生、自定义衍生等),生成的复合、拓展型数据结构,无法脱离基础原生类型独立存在。
2.1、在python中的定义:
2.1.1、语法原生衍生(内置容器,无需导入):
- 一共有四类分别是:list、tuple、dict、set
- 特点如下:
| 类型 | 定义 | 是否允许重复元素 | 是否有序 | 是否可变 | 是否支持混合类型元素 |
|---|---|---|---|---|---|
| list | 有序可变的序列容器,通过[]字面量或list()创建,支持索引、切片访问 |
允许,可存放完全相同的元素 | 是,严格按插入顺序保存,支持下标索引访问 | 是,创建后可随时增、删、改元素 | 支持,可同时存放任意不同类型的数据 |
| tuple | 有序不可变的序列容器,通过()字面量或tuple()创建,支持索引、切片访问 |
允许,可存放完全相同的元素 | 是,严格按插入顺序保存,支持下标索引访问 | 否,创建后无法增、删、改元素 | 支持,可同时存放任意不同类型的数据 |
| dict | 键值对映射型容器,通过{key:value}或dict()创建,通过键访问对应的值 |
键不允许重复,重复键会覆盖原值;值允许重复 | Python 3.7及以上版本按插入顺序有序,低版本无序 | 是,可随时增删改键值对 | 值支持任意类型;键必须是可哈希的不可变类型 |
| set | 无序不重复的集合容器,通过{}或set()创建,用于去重与集合运算 |
不允许,自动对元素去重,重复元素仅保留一份 | 否,内部无序存储,不支持下标索引访问 | 普通set可变,可增删元素;frozenset不可变 |
支持混合可哈希类型,元素必须为不可变的可哈希对象 |
2.1.2、标准库衍生(内置模块拓展,需 import 引入)
- Python 官方标准库针对特定场景封装的专用数据结构,在功能、性能上做了定向优化,底层仍完全基于基础类型实现。
- 代表类型:collections 系列(deque、OrderedDict、defaultdict、namedtuple)、array.array(同类型数值数组)、队列 queue、字节序列 bytes/bytearray 等
2.1.3、用户自定义衍生
- 开发者可自由组合基础类型与已有衍生类型,封装属性与方法,实现业务定制化的数据结构与行为逻辑,本质是对原生类型体系的进一步抽象封装。
- 例如:通过 class 关键字定义的自定义类与实例对象
2.2、具体案例:
python
# -*- coding: ANSI -*-
# ========== 1. 列表 list ==========
# 特性:支持混合类型、允许重复元素、有序、可变
my_list = [1, "hello", 3.14, 1, "hello"] # 混合类型 + 重复元素
my_list[0] = 999 # 可变:可修改元素
print("列表:", my_list) # 列表: [999, 'hello', 3.14, 1, 'hello']
print("列表有序,支持索引访问第2个元素:", my_list[1]) # 列表有序,支持索引访问第2个元素: hello
print("-" * 30)
# ========== 2. 元组 tuple ==========
# 特性:支持混合类型、允许重复元素、有序、不可变
my_tuple = (1, "world", 2.718, 1, "world") # 混合类型 + 重复元素
# my_tuple[0] = 999 # 不可变:取消注释运行会报错 TypeError
print("元组:", my_tuple) # 元组: (1, 'world', 2.718, 1, 'world')
print("元组有序,支持索引访问第2个元素:", my_tuple[1]) # 元组有序,支持索引访问第2个元素: world
print("-" * 30)
# ========== 3. 集合 set ==========
# 特性:元素需可哈希、自动去重、无序、可变
my_set = {1, "python", 3.14, 1, "python"} # 重复元素会被自动去重
# print(my_set[0]) # 无序:不支持下标索引,取消注释运行会报错
print("集合(自动去重):", my_set) # 集合(自动去重): {1, 3.14, 'python'}
print("-" * 30)
# ========== 4. 字典 dict ==========
# 特性:键唯一不可重复、值可混合类型、3.7+按插入顺序有序、可变
my_dict = {"name": "张三", "age": 25, "age": 30} # 重复键会覆盖旧值
print("字典:", my_dict) # 字典: {'name': '张三', 'age': 30}
print("字典通过键访问值:", my_dict["name"]) # 字典通过键访问值: 张三
2.3、与C++对比:
2.3.1、C++语法原生衍生(纯语法构造,无需引入标准库)
- 一共有六类分别是:原生数组、指针、引用、结构体 struct、枚举 enum、联合体 union
- 和python的不同点在于:底层构造逻辑完全不同;
- python中原生衍生类,本身就是完整的功能对象,自带大量成员方法(如append、keys),容器内可混合存放任意类型的数据,元素类型无强制约束,长度动态可变,内存由解释器自动管理,本质是引用语义;
- C++ 原生语法的 struct 常用于数据聚合,默认成员公开(C++中Class默认成员私有),其他方面与 class 语义能力一致。
2.3.2、标准库衍生(STL 标准库提供,需包含对应头文件)
- C++ 标准模板库(STL)通过模板泛型机制封装的通用容器与工具类型;
- 代表类型:std::string、std::vector、std::map、std::unordered_map、std::set、std::deque、std::list、智能指针等
- 和python的不同点在于:两者都是通过内置模块或者内置标准库引入,python中是动态对象,C++实例化必须显示指定元素类型,还有就是内存管理模式不同。
2.3.3、用户自定义衍生
- 开发者可基于原生基础类型、语法衍生类型、标准库类型进行封装,自定义成员变量、成员函数与访问控制,实现面向对象的自定义数据结构与业务逻辑。
- 代表形式:自定义 class 类、模板类、继承派生类
- 和python的不同点在于:两者都是面向对象的实现模式,但具体实现方式不同。python是动态类型,C++是静态类型。C++中语义丰富,python中都是引用语义。
2.4、补充
2.4.1、语法和语义:语法就是编程语言规定的合法书写规则,语义是代码所表达的含义、执行时的行为规则,以及最终产生的效果。
- 最典型的例子就是赋值语句 a = b:
在 C++ 里,默认是「把 b 的值拷贝一份给 a」;
在 Python 里,是「让 a 和 b 指向同一个对象」;
写法语法几乎一致,但底层行为完全不同,这就是语义不同。 - 值语义: 变量直接存储数据本身,赋值、传参时会拷贝一份完整的新数据;两个变量相互独立,修改其中一个不会影响另一个。
- 引用语义:变量存储的是数据的地址 / 引用,赋值、传参时只拷贝地址,两个变量指向同一份真实数据;通过其中一个修改数据,另一个也会同步变化。
2.4.2、深拷贝和浅拷贝:两者都是针对包含嵌套子对象的复合对象(比如列表套列表、字典套列表)的复制行为,核心区别只在于:嵌套的内层对象有没有被一起独立复制。
- 浅拷贝(Shallow Copy)只复制对象的最外层结构,内层嵌套的子对象仍然共享同一份内存引用。修改内层数据,原对象和拷贝对象会同步变化。
- 深拷贝(Deep Copy):递归复制对象的所有层级,从外到内每一层子对象都会创建全新的独立副本。两边完全独立,修改任何一层都互不影响。
- Python 所有变量存的都是对象的引用(内存地址),复制一个容器(比如 list)时:默认的浅拷贝,只是把容器里的地址列表复制了一遍;所以修改内层子对象时,两边都会同步变化。Python 官方标准库 copy 模块提供了 deepcopy() 函数,专门做递归深拷贝。它会自动遍历对象的所有嵌套层级,遇到可变对象就创建新副本,最终得到一个完全独立的新对象。Python 深浅拷贝是运行时的通用行为,由标准库统一处理,不需要开发者自己手写拷贝逻辑。
- C++ 默认值语义下,普通对象拷贝会调用默认拷贝构造函数,本质是逐成员拷贝(类似浅拷贝);如果类内有指针成员指向堆内存,默认拷贝会出现双重释放问题,C++需要手动重写拷贝构造函数实现深拷贝。
三、数据类型特性:两种语言中造成数据类型和数据结构差异最本质的原因
3.1、在python中的定义:
- Python:动态强类型,Python 仅在数值体系内部(int/float/bool)做隐式提升,字符串与数字、列表与数字等跨类型运算会直接报错,拒绝隐式转换,因此属于强类型;
- Python 遵循万物皆对象,所有变量本质都是对象的引用,全程只有引用语义,没有原生的值语义。 像 int、str 这些不可变类型,看起来 "改一个不影响另一个",很像值语义,但本质还是引用语义,不是拷贝了值,而是对象本身不可修改,一旦 "修改" 就会生成一个全新的对象,变量重新指向新对象,原对象不受影响。
- Python 中动态类型 + 统一引用存储路线,天然兼容异构元素;
3.2、具体案例:
python
# 运行时确定类型:
a = 10
print(type(a)) # <class 'int'>,此时是整型
a = "hello"
print(type(a)) # <class 'str'>,运行中直接变成字符串
a = [1, 2, 3]
print(type(a)) # <class 'list'>,又变成列表
# 自动隐式类型转换:
# int + float → 自动转float
res = 5 + 2.5
print(res, type(res)) # 7.5 <class 'float'>
# bool本质是int子类,True=1、False=0,可直接和数字运算
print(True + 3) # 4
print(False * 10) # 0
# python中只有引用语义:
a = 10
b = a
a = 20 # 不是修改原对象,是让 a 指向了新的 int 对象 20
print(b) # b 还是指向原来的 10,所以不变
3.3、与C++对比:
- C++:静态强类型,编译期固定类型,强制显式转换。
- C++:语义丰富,多套规则并存,C++ 之所以说语义丰富,是因为它同时支持多套不同的语义规则,开发者可以按需选择,不同语义对应不同的内存行为和性能表现。核心包含四类语义:
- 值语义:普通栈变量、结构体、类对象的默认行为,赋值 / 传参全量拷贝,数据独立。
- 引用语义(& 引用):给已有变量起别名,不拷贝数据,绑定后不能改指向,常用于函数传参避免拷贝。
- 指针语义:变量存内存地址,可空、可随时修改指向,支持指针运算,是 C++ 底层内存操作的核心。
- 移动语义(C++11 新增):通过 std::move 转移资源所有权,不拷贝数据,专门用于大对象高效传递。
- 除此之外还有左值、右值、右值引用等细分语义,对应不同的编译期规则和优化场景。
- C++ 中静态强类型 + 值语义连续存储路线,编译期必须确定元素类型与大小,因此不天然支持混合类型。
- 示例代码
cpp
#include <iostream>
using namespace std;
int main() {
int num = 10;
num = "hello"; // 编译直接报错,int不能存字符串
string s = "123";
int x = 4;
int err = s + x; // 编译报错,string和int不能直接相加
// 必须手动显式转换才能处理
int num1 = stoi(s);
int ans = num1 + x;
cout << ans << endl;
return 0;
}