Python元组底层剖析(缓存机制)及面试题

再Python中创建一个元组时,本质上就是创建一个结构体对象。

元组的核心结构体简化后如下:

typedef struct{
    struct _object *_ob_next;
    struct _object *_ob_prev; //双向环状链表中上一个和下一个,python内部将对象

    Py_ssize_t ob_refcnt;    //引用计数器,即:有多少变量使用了这个列表对象放到链表中便于进行内存管理
    Py_ssize_t ob_size;      //元素个数
    pyObject  *ob_item[1];  //存储元组中的元素 [指针,]
}PyTupleObject;

 

 

 *ob_item[1]是只有一个元素的指针数组,怎么存储元素中的多个元素呢?

 

 

 

 

 

 1.创建元组

v1 = tuple()
v2 = tuple(['amy','alex','david'])

 

 

 1.1 空元组

内部源码的执行过程如下:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 1.2 非空元组

 

1.2.1 元组转换为元组

  因为元组创建开辟内存的过程中,会检查元组中元素,如果元素本身就是元组,就会直接返回元素的内存地址。

1.2.2 列表转换为元组

  如果元组里面是列表,会检查元组中元素,如果元素本身就是元组,就会直接返回元素的内存地址。

1.2.3 迭代器转换为元组(或可迭代对象)

  如果元组里面是迭代器,会先猜想它的元素个数,然后根据猜想个数创建元组的结构体对象(开辟内存),再将迭代器的每个元素放到元组中(放的过程中,会进行判断猜想数字是否正确,如果猜小了会扩容(原长度*0.25+2.5),猜大会进行缩容。)。

 

2.切片取值

 (1)执行过程:元组切片只拷贝元组结构体,不拷贝数据。切片过程是根据切片的元素个数,创建元组(开辟内存),然后再将原来元组中的元素指针(地址)复制给元组。

3.销毁&缓存(只缓存元素个数20个以内的)

v1 = ('amy','david','bob')
v2 = (11,22,3,44)

del v1  //引用计数器减一置为0
del v2

元组的删除并非真正删除,会缓存起来(存在对应的free_list里),如果下一次出现相同数量元组,会进行利用。

三个元素存3,四个元素存4,根据元素个数存到对应free_list,并且num_free变为1

销毁两个相同元素个数的元组时,根据后来居上原则,将后来的元组地址缓存到对应的free_list中,而先来的元组地址存到后来元组的ob_item的第一个位置中。并且num_free变为2.

如果此时创建新的相同元素元组时,会将(11,22,33)拿走,并且num_free变为1。

 

 

 

 

3.1 空元组

 

3.2 非空元组

(1)为什么元组内存地址一样?

(2)为什么元组转换为元组内存地址一样?

  因为元组创建开辟内存的过程中,会检查元组中元素,如果元素本身就是元组,就会直接返回元素的内存地址。

(3)为什么列表转换为元组,元素的内存地址是一样的?

   如果元素本身是列表,会循环列表中每一个元素,将列表中的元素的指针(地址)赋值给元组

(4)迭代器转换为元组(或可迭代对象)

  如果是迭代器或者可迭代对象,则先猜想它长度,一般是10,如果长度不够则扩容(原长度*0.25+2.5),长度超了则缩容。然后再去创建元组结构体对象,再将迭代器中的每个元素放到元组中,

内部有缓存则优先去缓存中获取对应个数的free_list。

(5)为什么销毁元组后,再创建元组内存地址有时相同,有时不相同?

  因为销毁的元组个数可能相同,排在前一个元组的和创建的元组个数相同,但并不是之前销毁的,故地址不同,相反则相同。

(6)元组删除缓存范围?

  0-2000

(7)元组缓存限制有哪些?

  元素个数20以内、缓存个数2000以内

(8)元组创建流程是什么?

  对于空元组,其一创建就缓存到free_list[0]中。(故空元组地址都是一样的)

  对于非空元组,如果本身就是元组,则直接返回原来元组内存地址;(元组转元组)

  如果是列表,不拷贝数据本身,而是将列表中的元素地址放到元组对应的ob_item中;(列表转元组)

  如果是迭代器或者可迭代对象,则先猜想它长度,一般是10,如果长度不够则扩容(原长度*0.25+2.5),长度超了则缩容。然后再去创建元组结构体对象,再将迭代器中的每个元素放到元组中,

内部有缓存则优先去缓存中获取对应个数的free_list。(迭代器转为元组)

(9)创建两个元组并进行销毁,随后再创建两个元组,第一个元组跟第二个销毁元组内存地址一样,第二个元组地址为什么和第一个元组地址不一样?

 

  因为创建的第一个元组拿到第二个销毁元组地址后,pyhon内部会销毁元组,然后排到了第一个销毁元组前面,所以第二个创建的元组地址虽然也从free_list中获取内存地址,

但并不是原第一个销毁元组地址。

 

posted @ 2021-04-06 20:57  Eliphaz  阅读(572)  评论(0编辑  收藏  举报