Python虽然没有指针类型,但是处处离不开指针,我们要认识到一点,只要操作系统是用C语言写的,就一定会使用到指针,因为使用某种语言,我们一定会定义变量,就必须申请进程的地址空间,也就注定会用到指针。
所以Python虽然没有指针类型,但是在底层是会大量使用到指针的!下面我们主要来分析元组在底层是如何来实现的,其他关于元组的内容都和list大差不差。
一、元组的底层原理(简单理解)
在学习元组的时候,我们知道元组具有以下的特性
- 定长的
- 元组的元素是不可改变的
- 元组的元素可以是各种类型
- 若元组中包含可变对象,则可变对象的元素是可以改变的
思考:我们已经知道元组中的元素是不可以被改变的,但是为什么元组中包含一个可改变对象,
如(列表),我们就可以去改变它呢?
其实要理解这个现象,就要从元组的底层原理来展开了,我们可以先简单理解元组直接存的并不是元素对象本身,而是对元素对象的索引,也就是指向每个元素对象的指针!这也解释了为什么Python虽然没有指针类型,却处处离不开指针的原因。
我们现在理解了元组本质上存的是每个元素的指针,那如何来实现元素不变呢?
在C/C++语言中要想实现类似元组的数据结构,就要先考虑元组元素不可变,但前提就必须是元组的每个指针的指向不能变,必须是一个const pointer,只有恒定的指向每个元素才能继续保证指向的元素不变,所以我们会采用指针数组来实现,同时还要保证元组可以存储任意数据类型
C语言模拟元组的结构:
const void* const tuple[];
- tuple是一个指针数组,每个元素的类型都是 const void* const 类型
- 第一个const保证其指向的对象不可变性
- 第二个const保证指针自身的不可变性
- void*保证可以接收各种数据类型的对象的地址,届时只需做强转来增大访问权限即可
【注】事实上,元组的实现比这个复杂的多,我们只是简单理解!
思考:我们理解了元组的不可变性和支持各种类型,那元组是如何保证定长的呢?
其实这个很容易了,在C/C++中,我们可以直接不提供修改、删除、增加的接口就行
思考:为什么元组元素为可变对象(如列表),可变对象的内部元素是可以改变的?
在C语言中就类似这样的场景:
int a = 3;
double d = 3.14;
int arr[] = {1,2,3};
const void* const tuple[] = {&a, &d, arr};
其实这里并不能很好的诠释可变对象的内部元素可以改变,因为上面的代码是都不可改变的
我们可以这样理解:当我们使用Python的元组中存放了一个可变对象时,在底层就会强转为可以改变的权限也就是void* const ,理解成底层自动识别上层用户存放的是否是可变对象即可,不是看可变对象就强转为const type* const ,反之 type* const。
思考:为什么元组采用存放元素对象的指针的方式?
首先元组的元素是不可变的,一旦创建,无法改变,所以当我们创建大量相同的数据时,存放指针的优势就展现出来了,可以减少内存空间的开辟,多次复用相同空间的值
二、元组简单的补充
当我们的元组中只有一个元素时,必须在元素后面➕逗号,而不是括号➕元素
(防止Python解释器认为其是一个冗余的括号)