第三章内建对象(2):浮点对象----面试题讲解

例题一:

以下例子中, area 计算过程中有临时对象创建吗?为什么?
>>> pi = 3.14
>>> r = 2
>>> area = pi * r ** 2
Python 如何优化临时对象创建效率?
解答:(看过上面一节讲的内容。其实这个问题的答案就已经了然于心了)

(第一问)会有临时对象创建。 这个语句首先计算半径 r 的平方,中间结果由一个临时对象来保存,假设是 t ; 然后计算圆周率 pi 与 t 的乘积,得到最终结果并赋值给变量 area ; 最后,销毁临时对象 t 。
(第二问)为了提高浮点对象创建效率, Python 引入了 空闲对象缓存池,浮点对象销毁后, Python 并不急于回收内存,而是将对象放入一个 空闲链表 。 后续需要创建浮点对象时,先到空闲链表中取,省去分配内存的开销。

解析:答案其实已经说得很清晰了,但是并没有提到C语言实现的细节。 在C语言底层实现,空闲对象缓存池 是一个 空闲链表,(1)free_list 变量,指向空闲链表 头节点 的指针;(2)numfree 变量,维护空闲链表 当前长度 ;(3)PyFloat_MAXFREELIST 宏,限制空闲链表的 最大长度 ,避免占用过多内存(最大长度源码里是 100),(4)ob_type字段 作为 next指针用

创建对象(两种方式)最终调用的都是
(1)PyFloat_FromDouble 函数通过浮点值创建浮点对象;(2)PyFloat_FromString 函数通过字符串对象创建浮点对象;
而销毁对象,实际上是调用 _Py_Dealloc 宏调用类型对象 PyFloat_Type 中的 tp_dealloc 函数指针: 实现将 对象放入 缓存池中。

另外关于空闲对象缓存池的细节,就自行参看上节。


例题二

以下例子中,变量 e 的 id 值为何与已销毁的变量 pi 相同?
>>> pi = 3.14
>>> id(pi)
4565221808
>>> del pi
>>> e = 2.71
>>> id(e)
4565221808

答案:
Python 为了优化浮点对象内存分配效率,引入了 空闲对象缓存池 。 浮点对象销毁后, Python 并不急于回收对象内存,而是将对象缓存在空闲链表中以备后用

例子中, pi 对象销毁后, Python 先不回收对象内存,而是将其插空闲对象链表头部。 当创建浮点对象 e 时, Python 从链表头取出空闲对象来用,省去了申请内存的开销。 换句话讲, pi 对象销毁后被 e 重新利用了,因此 id 值相同也就不奇怪了。

注释:

这一题就是缓存池的原理了,再讲下:

  • 对象销毁时, Python 将其缓存在空闲链表中,以备后用。考察 float_dealloc 函数:
if (numfree >= PyFloat_MAXFREELIST)  {
    PyObject_FREE(op);
    return;
}
numfree++;
Py_TYPE(op) = (struct _typeobject *)free_list;
free_list = op;
  • 1-4 行,空闲链表长度达到限制值,调用 PyObject_FREE 回收对象内存;
  • 第 5-7 行,空闲链表长度暂未达到限制,将对象插到空闲链表头部;
  • 下次使用时,便会从链表头部取出使用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值