Python内建类型float源码学习

目录
  • 1 回顾float的基础知识
    • 1.1 PyFloatObject
    • 1.2 PyFloat_Type
    • 1.3 对象的创建
    • 1.4 对象的销毁
    • 1.5 小结
  • 2 空闲对象缓存池
    • 2.1 浮点对象的空闲链表
    • 2.2 空闲链表的使用
  • 3 其他

“深入认识Python内建类型”这部分的内容会从源码角度为大家介绍Python中各种常用的内建类型。

1 回顾float的基础知识

1.1 PyFloatObject

1.2 PyFloat_Type

C源码(仅列出部分字段):

PyTypeObject PyFloat_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "float",
    sizeof(PyFloatObject),
    0,
    (destructor)float_dealloc,                  /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_reserved */
    (reprfunc)float_repr,                       /* tp_repr */
    &float_as_number,                           /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    (hashfunc)float_hash,                       /* tp_hash */
    0,                                          /* tp_call */
    (reprfunc)float_repr,                       /* tp_str */
    // ...
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    float_new,                                  /* tp_new */
};

PyFloat_Type中保存了很多关于浮点对象的元信息,关键字段包括:

tp_name:保存类型名称,常量float

tp_dealloc、tp_init、tp_alloc和tp_new:对象创建和销毁的相关函数

tp_repr:生成语法字符串表示形式的函数

tp_str:生成普通字符串表示形式的函数

tp_as_number:数值操作集

tp_hash:哈希值生成函数

1.3 对象的创建

通过类型对象创建实例对象:

通过C API创建实例对象:

PyObject *
PyFloat_FromDouble(double fval);
PyObject *
PyFloat_FromString(PyObject *v);

1.4 对象的销毁

当对象不再需要时,Python通过Py_DECREF或者Py_XDECREF宏来减少引用计数;

当引用计数降为0时,Python通过_Py_Dealloc宏回收对象。(有关引用计数的内容后续会详细介绍)

_Py_Dealloc宏实际上调用的是*Py_TYPE(op)->tp_dealloc,对于float即调用float_dealloc:

#define _Py_Dealloc(op) (                               \
    _Py_INC_TPFREES(op) _Py_COUNT_ALLOCS_COMMA          \
    (*Py_TYPE(op)->tp_dealloc)((PyObject *)(op)))

1.5 小结

最后将对象从创建到销毁整个生命周期所涉及的关键函数、宏以及调用关系整理如下:

2 空闲对象缓存池

问题:浮点运算背后涉及大量临时对象创建和销毁。

area = pi * r ** 2

这个语句首先计算半径r的平方,中间结果由一个临时对象来保存,假设是t;然后计算pi与t的乘积,得到最终结果并赋值给变量area;

最后,销毁临时对象t。创建对象时需要分配内存,销毁对象时又要回收内存,大量临时对象创建和销毁,意味着大量内存分配回收操作,这是不能接受的。

因此,Python在浮点对象销毁之后,并不急于回收内存,而是将对象放入一个空闲链表,后续需要创建浮点对象时,先到空闲链表中取,省去了部分分配内存的开销。

2.1 浮点对象的空闲链表

C源码:

#ifndef PyFloat_MAXFREELIST
#define PyFloat_MAXFREELIST    100
#endif
static int numfree = 0;
static PyFloatObject *free_list = NULL;

源码解读:

free_list变量:指向空闲链表头节点的指针

numfree变量:维护空闲链表当前长度

PyFloat_MAXFREELIST宏:限制空闲链表的最大长度,避免占用过多内存

为了保持简洁,Python把ob_type字段当作next指针来用,将空闲对象串成链表。float空闲链表图示如下:

个人体会:

Python中这样的池技术很多地方都在用,并且在实际工程中,这也是一种广泛使用的方式,大家可以具体体会下。

“把ob_type字段当作next指针来用“,这种方式可以学习,但是也要结合实际情况:可读性,是否需要节省这部分内存等等。

2.2 空闲链表的使用

有了空闲链表之后,需要创建浮点对象时,可以从链表中取出空闲对象,省去申请内存的开销,以PyFloat_FromDouble()为例:(只列出部分代码)

PyObject *
PyFloat_FromDouble(double fval)
{
    PyFloatObject *op = free_list;
    if (op != NULL) {
        free_list = (PyFloatObject *) Py_TYPE(op);
        numfree--;
    } else {
        op = (PyFloatObject*) PyObject_MALLOC(sizeof(PyFloatObject));
        if (!op)
            return PyErr_NoMemory();
    }
    /* Inline PyObject_New */
    (void)PyObject_INIT(op, &PyFloat_Type);
    op->ob_fval = fval;
    return (PyObject *) op;
}

检查free_list是否为空

如果free_list非空,取出头节点备用,free_list指向第二个节点(这里看代码调用的是Py_TYPE(),

也就是op的ob_type字段,也就是第二个节点),并将numfree减1

如果free_list为空,则调用PyObject_MALLOC分配内存

最后会通过PyObject_INIT对op进行相应设置(包括修改ob_type),然后设置ob_fval为fval

图示如下:(对比2.1中的图示,可以看到free_list指向了第二个节点,并且第一个节点的ob_type字段也不再指向第二个节点,而是指向对应的类型对象)

对象销毁时,Python将其缓存在空闲链表中,以备后用。float_dealloc函数源码如下:

static void
float_dealloc(PyFloatObject *op)
{
    if (PyFloat_CheckExact(op)) {
        if (numfree >= PyFloat_MAXFREELIST)  {
            PyObject_FREE(op);
            return;
        }
        numfree++;
        Py_TYPE(op) = (struct _typeobject *)free_list;
        free_list = op;
    }
    else
        Py_TYPE(op)->tp_free((PyObject *)op);
}

若空闲链表长度达到限制值,调用PyObject_FREE回收对象内存

若空闲链表长度未达到限制值,则将对象插到空闲链表头部(这里可以顺带复习下头插法,hh)

3 其他

问题:以下例子中,变量e的id值为何与已销毁的变量pi相同?

>>> pi = 3.14
>>> id(pi)
4565221808
>>> del pi
>>> e = 2.71
>>> id(e)
4565221808

答:在3.14这个浮点数对象被销毁时,并没有直接回收其内存,而是将对象缓存在空闲链表中,此时3.14这个浮点数对象为空闲链表头节点;

当创建浮点对象2.71时,此时空闲链表非空,则取出空闲链表的头节点,修改ob_fval值为2.71,因此两个对象的id是一样的。

以上就是Python内建类型float源码学习的详细内容,更多关于Python内建类型float的资料请关注我们其它相关文章!

(0)

相关推荐

  • Python内建类型int源码学习

    目录 1 int对象的设计 1.1 PyLongObject 1.2 整数的布局 1.3 小整数静态对象池 1.4 示例 2 大整数运算 2.1 整数运算概述 2.2 大整数运算处理过程 1.long_add()源码: 2.绝对值加法x_add() 3 其他 大整数转float溢出 “深入认识Python内建类型”这部分的内容会从源码角度为大家介绍Python中各种常用的内建类型. 问题:对于C语言,下面这个程序运行后的结果是什么?是1000000000000吗? #include <stdio

  • Python内建类型str源码学习

    目录 引言 1 Unicode 2 Python中的Unicode 2.1 Unicode对象的好处 2.2 Python对Unicode的优化 3 Unicode对象的底层结构体 3.1 PyASCIIObject 3.2 PyCompactUnicodeObject 3.3 PyUnicodeObject 3.4 示例 4 interned机制 5 总结 引言 “深入认识Python内建类型”这部分的内容会从源码角度为大家介绍Python中各种常用的内建类型. 在介绍常用类型str之前,在上

  • Python内建类型bytes深入理解

    目录 引言 1 bytes和str之间的关系 2 bytes对象的结构:PyBytesObject 3 bytes对象的行为 3.1 PyBytes_Type 3.2 bytes_as_sequence 4 字符缓冲池 引言 “深入认识Python内建类型”这部分的内容会从源码角度为大家介绍Python中各种常用的内建类型. 在我们日常的开发中,str是很常用的一个内建类型,与之相关的我们比较少接触的就是bytes,这里先为大家介绍一下bytes相关的知识点,下一篇博客再详细介绍str的相关内容

  • Python内建类型list源码学习

    目录 问题: 1 常用方法 小结: 题外话: 2 list的内部结构:PyListObject 3 尾部操作和头部操作 3.1 尾部操作 3.2 头部操作 4 浅拷贝和深拷贝 4.1 浅拷贝 4.2 深拷贝 4.3 直接赋值 4.4 小结 个人总结: TODO: 5 动态数组 5.1 容量调整 5.2 append() 5.3 insert() 5.4 pop() 5.5 remove() 6 一些问题 问题: “深入认识Python内建类型”这部分的内容会从源码角度为大家介绍Python中各种

  • python基础入门详解(文件输入/输出 内建类型 字典操作使用方法)

    一.变量和表达式 复制代码 代码如下: >>> 1 + 1               2>>> print 'hello world' hello world>>> x = 1               >>> y = 2>>> x + y3 Python是强类型语言,无法根据上下文自动解析转换成合适的类型. Python是一种动态语言,在程序运行过程中,同一个变量名在运行的不同阶段可以代表不同形式的值(整型,浮

  • Python内建类型float源码学习

    目录 1 回顾float的基础知识 1.1 PyFloatObject 1.2 PyFloat_Type 1.3 对象的创建 1.4 对象的销毁 1.5 小结 2 空闲对象缓存池 2.1 浮点对象的空闲链表 2.2 空闲链表的使用 3 其他 “深入认识Python内建类型”这部分的内容会从源码角度为大家介绍Python中各种常用的内建类型. 1 回顾float的基础知识 1.1 PyFloatObject 1.2 PyFloat_Type C源码(仅列出部分字段): PyTypeObject P

  • Python对象的底层实现源码学习

    目录 1. PyObject:对象的基石 2. PyVarObject:变长对象的基础 2.1 浮点对象 2.2 列表对象 3. PyTypeObject:类型的基石 4. PyType_Type:类型的类型 5. PyBaseObject_Type:类型之基 6. 补充 在“Python源码学习笔记:Python万物皆对象”中,我们对Python的对象类型体系有了一定的认识,这篇博客将从源码层面来介绍Python中万物皆对象的底层实现. 1. PyObject:对象的基石 在Python解释器

  • Python作用域与名字空间源码学习笔记

    目录 作用域与名字空间 1. 名字绑定 1.1 赋值 1.2 模块导入 1.3 函数.类定义 1.4 as关键字 2. 作用域 2.1 静态作用域 2.2 划分作用域 2.3 闭包作用域 2.4 类作用域 2.5 复杂嵌套 2.5.1 函数嵌套类 2.5.2 类嵌套类 3. 名字空间 3.1 Globals 3.2 Locals 3.3 Enclosings 3.4 Builtin 4. 问题与总结 作用域与名字空间 问题: PI = 3.14 def circle_area(r): retur

  • python内建类型与标准类型

    目录 前言 理解对象和类型 关于不可变类型和可变类型 关于动态静态强弱类型 标准类型 其它内建类型 类型的类型 None ->空类型 内建类型的布尔值 前言 全可以访问相同的对象, 因此我们讲 这种变量名也叫对象的 "引用". 验证1: a = 2 b = 3 print(id(a),id(b))  #140734889681584 140734889681616 b = 2 print(id(b))    #140734889681584 验证2: b = 3 print(id

  • Python对象的生命周期源码学习

    目录 思考: 1 C API 2 对象的创建 2.1 两种创建对象的方式 2.2 由类型对象创建实例对象 3 对象的多态性 4 对象的行为 5 引用计数 思考: 当我们输入这个语句的时候,Python内部是如何去创建这个对象的? a = 1.0 对象使用完毕,销毁的时机又是怎么确定的呢? 下面,我们以一个基本类型float为例,来分析对象从创建到销毁这整个生命周期中的行为. 1 C API Python是用C写的,对外提供了API,让用户可以从C环境中与其交互,并且Python内部也大量使用了这

  • Python源码学习之PyType_Type和PyBaseObject_Type详解

    PyType_Type和PyBaseObject_Type PyObject和PyTypeObject内容的最后指出下图中对实例对象和类型对象的理解是不完全正确的, 浮点类型对象全局唯一,Python在C语言层面实现过程中将其定义为一个全局静态变量,定义于Object/floatobject.c中,命名为PyFloat_Type. PyTypeObject PyFloat_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "float&quo

随机推荐