内建对象—float

一. 内部结构

1.1 实例对象结构

float类型的定义写在 include/floatobject.h 文件中,类型定义十分简单,

typedef struct {
    PyObject_HEAD
    double ob_fval;
} PyFloatObject;

float实例对象由一个定长头部和一个double类型字段ob_fval构成。示意图如下,
Alt

1.2 类型对象结构

类型对象全局唯一,作为全局变量定义在Objects/floatobject.c中的PyFloat_Type

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 */
    PyObject_GenericGetAttr,                    /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,   /* tp_flags */
    float_new__doc__,                           /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    float_richcompare,                          /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    float_methods,                              /* tp_methods */
    0,                                          /* tp_members */
    float_getset,                               /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    float_new,                                  /* tp_new */
};

类型对象中保存float对象很多关键的元信息,这些信息决定float实例对象的创建、销毁和行为

字段名解释
tp_name保存类型名称,值为float
tp_dealloc、tp_init、tp_alloc和tp_new对象创建销毁相关函数,float_dealloc/0/0/float_new
tp_repr生成语法字符串表示形式的函数,float_repr
tp_str字段是生成普通字符串表示形式的函数,float_repr
tp_as_number字段是数值操作集,对应代码float_as_number
tp_hash字段是哈希值生成函数,对应float_hash

二. 创建和销毁

通过调用类型对象中的方法实现实例对象的创建和销毁。

2.1 对象的创建

Python执行type类型对象中的tp_call函数。tp_call函数进而调用float类型对象的 tp_new函数创建实例对象,再调用tp_init函数对其进行初始化。
Alt
float类型对象的tp_init函数指针为空。因为float是很简单的对象,初始化赋值语句,在 tp_new中完成即可。两个函数根据输入值类型的不同选择调用下面两个创建对象API,

PyObject * PyFloat_FromDouble(double fval); // 通过浮点值创建浮点对象

PyObject * PyFloat_FromString(PyObject *v); // 通过字符串创建浮点对象

以double创建浮点数为例,

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();
    }
    // 初始化对象类型字段ob_type以及引用计数字段 ob_refcnt
    (void)PyObject_INIT(op, &PyFloat_Type);
    // ob_fval字段初始化为指定值
    op->ob_fval = fval;
    return (PyObject *) op;
}

上方代码中,宏PyObject_INIT在include/objimpl.h 中定义,

#define PyObject_INIT(op, typeobj) \
    ( Py_TYPE(op) = (typeobj), _Py_NewReference((PyObject *)(op)), (op) )

// 宏_Py_NewReference将引用计数初始化为1,在include/Object.h中定义
#define _Py_NewReference(op) (                          \
    _Py_INC_TPALLOCS(op) _Py_COUNT_ALLOCS_COMMA         \
    _Py_INC_REFTOTAL  _Py_REF_DEBUG_COMMA               \
    Py_REFCNT(op) = 1)

2.2 对象的销毁

Python 通过Py_DECREF或者Py_XDECREF宏减少引用计数,当计数降为0时,通过_Py_Dealloc宏回收对象。

对象销毁调用的是类型对象的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);
}

float_dealloc函数在_Py_Dealloc宏中调用,

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

2.3 创建与销毁过程图示

Alt

三. 空闲对象池

3.1 空闲对象池概念

浮点运算背后涉及大量临时对象创建以及销毁,如

area = pi * r ** 2
  1. 计算 r 2 r^2 r2的结果由一个临时对象来保存,假设是t
  2. 计算pi与t的乘积结果赋值给变量area
  3. 销毁临时对象t

大量临时对象创建销毁,意味着大量内存分配回收操作。Python在浮点对象销毁后,并不急于回收内存,而是将对象放入一个空闲链表。后续需要创建浮点对象时,先到空闲链表中取,省去分配内存的开销。

浮点对象的空闲链表在Objects/floatobject.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指针来用,将空闲对象串成链表。
Alt

3.2 创建过程的使用

在创建float对象的过程中,2.1节PyFloat_FromDouble方法里的调用过程,

PyFloatObject *op = free_list;
if (op != NULL) {
    free_list = (PyFloatObject *) Py_TYPE(op);
    numfree--;
} else {
    op = (PyFloatObject*) PyObject_MALLOC(sizeof(PyFloatObject));
    // ...
}

Alt

  1. 检查free_list是否为空(第 2 行)
  2. 如果free_list非空,取出头节点备用,并将numfree减一(第 3-4 行)
  3. 如果free_list为空,则调用PyObject_MALLOC分配内存(第 6 行)

3.3 销毁过程的使用

对象销毁时,Python将其缓存在空闲链表中,以备后用。考察float_dealloc函数,

if (numfree >= PyFloat_MAXFREELIST)  {
    PyObject_FREE(op);
    return;
}
numfree++;
Py_TYPE(op) = (struct _typeobject *)free_list;
free_list = op;
  1. 第1-4行,空闲链表长度达到限制值,调用PyObject_FREE回收对象内存
  2. 第5-7行,空闲链表长度暂未达到限制,将对象插到空闲链表头部

空闲对象缓存池在提高对象分配效率方面发挥着至关重要的作用,在其他内置对象中会经常看到它。

四. 对象行为

PyFloat_Type中定义了很多函数指针,包括tp_reprtp_strtp_hash等。 它们一起决定float对象的行为。

Python将常见的数值操作抽象成数值操作集PyNumberMethods。在头文件include/object.h中定义。函数根据参数个数可以分为:一元函数(unaryfunc)、二元函数(binaryfunc)等。

typedef struct {
    binaryfunc nb_add;
    binaryfunc nb_subtract;
    binaryfunc nb_multiply;
    binaryfunc nb_remainder;
    binaryfunc nb_divmod;
    ternaryfunc nb_power;
    unaryfunc nb_negative;
    // ...

    binaryfunc nb_inplace_add;
    binaryfunc nb_inplace_subtract;
    binaryfunc nb_inplace_multiply;
    binaryfunc nb_inplace_remainder;
    ternaryfunc nb_inplace_power;
    //...
} PyNumberMethods;

Objects/floatobject.c中浮点对象数值操作集float_as_number的初始化如下,

static PyNumberMethods float_as_number = {
    float_add,          /* nb_add */
    float_sub,          /* nb_subtract */
    float_mul,          /* nb_multiply */
    float_rem,          /* nb_remainder */
    float_divmod,       /* nb_divmod */
    float_pow,          /* nb_power */
    (unaryfunc)float_neg, /* nb_negative */
    // ...

    0,                  /* nb_inplace_add */
    0,                  /* nb_inplace_subtract */
    0,                  /* nb_inplace_multiply */
    0,                  /* nb_inplace_remainder */
    0,                  /* nb_inplace_power */
    // ...
};

其中,以float_add为例,该二元函数定义于Objects/floatobject.h中,

static PyObject * float_add(PyObject *v, PyObject *w)
{
    double a,b;
    CONVERT_TO_DOUBLE(v, a);
    CONVERT_TO_DOUBLE(w, b);
    PyFPE_START_PROTECT("add", return 0)
    a = a + b;
    PyFPE_END_PROTECT(a)
    return PyFloat_FromDouble(a);
}
  1. 将两个参数对象转化成浮点值( 5-6 行)
  2. 对两个浮点值求和( 8 行)
  3. 创建一个新浮点对象保存计算结果并返回
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值