所以,看上去是a改变了值,实际上a是引用了不同的整数对象。

注释下面就是PyIntObject的定义了。

typedef struct {

PyObject_HEAD

long ob_ival;

} PyIntObject;

所以展开来的话,主要就包含了引用计数、对象类型和表示值的long ob_ival。

每一种类型有独有的特性和操作,对象的很多信息都放在对应的类型中,比如PyIntObject很多信息放在PyInt_Type中。

PyInt_Type定义在intobject.c中:

PyTypeObject PyInt_Type = {

PyVarObject_HEAD_INIT(&PyType_Type, 0)

"int",

sizeof(PyIntObject),

0,

(destructor)int_dealloc, /* tp_dealloc */

(printfunc)int_print, /* tp_print */

0, /* tp_getattr */

0, /* tp_setattr */

(cmpfunc)int_compare, /* tp_compare */

(reprfunc)int_to_decimal_string, /* tp_repr */

&int_as_number, /* tp_as_number */

0, /* tp_as_sequence */

0, /* tp_as_mapping */

(hashfunc)int_hash, /* tp_hash */

0, /* tp_call */

(reprfunc)int_to_decimal_string, /* tp_str */

PyObject_GenericGetAttr, /* tp_getattro */

0, /* tp_setattro */

0, /* tp_as_buffer */

Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |

Py_TPFLAGS_BASETYPE | Py_TPFLAGS_INT_SUBCLASS, /* tp_flags */

int_doc, /* tp_doc */

0, /* tp_traverse */

0, /* tp_clear */

0, /* tp_richcompare */

0, /* tp_weaklistoffset */

0, /* tp_iter */

0, /* tp_iternext */

int_methods, /* tp_methods */

0, /* tp_members */

int_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 */

int_new, /* tp_new */

(freefunc)int_free, /* tp_free */

};

所以前面几项宏展开来是:

PyTypeObject PyInt_Type = {

_PyObject_EXTRA_INIT

1, //引用计数

PyType_Type, //对象类型

0, //可变部分中items的数目,PyIntObject为不可变对象

"int", //类型名称

sizeof(PyIntObject), //该类型的基本大小

0, //类型包含的item的大小

......

}

后面的信息就是一些类型拥有的操作,显而易见的有创建对象和删除对象、输出、比较、整数操作和成员函数。

比如int_as_numbers的定义如下:

static PyNumberMethods int_as_number = {

(binaryfunc)int_add, /*nb_add*/

(binaryfunc)int_sub, /*nb_subtract*/

(binaryfunc)int_mul, /*nb_multiply*/

(binaryfunc)int_classic_div, /*nb_divide*/

(binaryfunc)int_mod, /*nb_remainder*/

(binaryfunc)int_divmod, /*nb_divmod*/

(ternaryfunc)int_pow, /*nb_power*/

(unaryfunc)int_neg, /*nb_negative*/

(unaryfunc)int_int, /*nb_positive*/

(unaryfunc)int_abs, /*nb_absolute*/

(inquiry)int_nonzero, /*nb_nonzero*/

(unaryfunc)int_invert, /*nb_invert*/

(binaryfunc)int_lshift, /*nb_lshift*/

(binaryfunc)int_rshift, /*nb_rshift*/

(binaryfunc)int_and, /*nb_and*/

(binaryfunc)int_xor, /*nb_xor*/

(binaryfunc)int_or, /*nb_or*/

......

}

这里列出了一些基本运算,如加减乘除,还有位移、逻辑、幂运算等。

考虑一下加法运算过程。

static PyObject *

int_add(PyIntObject *v, PyIntObject *w)

{

register long a, b, x;

CONVERT_TO_LONG(v, a);

CONVERT_TO_LONG(w, b);

/* casts in the line below avoid undefined behaviour on overflow */

x = (long)((unsigned long)a + b);

if ((x^a) >= 0 || (x^b) >= 0)

return PyInt_FromLong(x);

return PyLong_Type.tp_as_number->nb_add((PyObject *)v, (PyObject *)w);

}

实际上是long类型的加法运算,并对运算结果进行判断是否溢出。

采取异或运算判断与0的比较结果,是为了判断符号位的变化。

考虑:正数+正数,负数+负数,正数+负数。

第一种情况下溢出,那么结果x将为负数,不满足if判断。

第二种情况下溢出,那么结果x将为正数,不满足if判断。

第三种情况,不会溢出,并且x的符号位必然和其中一个数相同,满足或的if判断。

最后是关于小整数和大整数的管理问题。

Python为小整数建立了内存池,小整数的范围可以自己定义(修改源码重新编译),因为小整数是使用十分频繁的对象。

而对于大整数,Python也采取了一定的措施。

先来看看创建一个整数对象的过程。

参考如下函数:

PyObject *

PyInt_FromLong(long ival)

{

register PyIntObject *v;

#if NSMALLNEGINTS + NSMALLPOSINTS > 0

if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {

v = small_ints[ival + NSMALLNEGINTS];

Py_INCREF(v);

#ifdef COUNT_ALLOCS

if (ival >= 0)

quick_int_allocs++;

else

quick_neg_int_allocs++;

#endif

return (PyObject *) v;

}

#endif

if (free_list == NULL) {

if ((free_list = fill_free_list()) == NULL)

return NULL;

}

/* Inline PyObject_New */

v = free_list;

free_list = (PyIntObject *)Py_TYPE(v);

PyObject_INIT(v, &PyInt_Type);

v->ob_ival = ival;

return (PyObject *) v;

}

该函数会对传进来的参数判断是否在小整数范围内(-NSMALLNEGINT ~ NSMALLPOSINTS),如果在就直接在small_ints数组里取。

staticPyIntObject*small_ints[NSMALLNEGINTS+NSMALLPOSINTS];

如果不是小整数,那么涉及的就是对大整数的处理。

在对大整数的处理中,首先涉及的是free_list,它被如下初始化:

staticPyIntObject*free_list=NULL;

所以,当第一次遇到大整数的时候,会调用函数fill_free_list()来新建一个链表。

#define BLOCK_SIZE 1000 /* 1K less typical malloc overhead */

#define BHEAD_SIZE 8 /* Enough for a 64-bit pointer */

#define N_INTOBJECTS ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject))

struct _intblock {

struct _intblock *next;

PyIntObject objects[N_INTOBJECTS];

};

typedef struct _intblock PyIntBlock;

static PyIntBlock *block_list = NULL;

static PyIntObject *free_list = NULL;

static PyIntObject *

fill_free_list(void)

{

PyIntObject *p, *q;

/* Python's object allocator isn't appropriate for large blocks. */

p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock));

if (p == NULL)

return (PyIntObject *) PyErr_NoMemory();

((PyIntBlock *)p)->next = block_list; //在成功分配内存后,将next指针指向上一个块(第一次为NULL)

block_list = (PyIntBlock *)p; //让block_list始终指向最新的块

/* Link the int objects together, from rear to front, then return

the address of the last int object in the block. */

p = &((PyIntBlock *)p)->objects[0]; //指针p指向数组中第一个元素

q = p + N_INTOBJECTS; //指针q指向数组中最后一个元素的下一个(越界了)

while (--q > p) //利用(滥用)对象的ob_type指针来将所有元素链接在一起

Py_TYPE(q) = (struct _typeobject *)(q-1);

Py_TYPE(q) = NULL; //最后一个(实际是第一个,因为是从最后向前遍历)的ob_type指针指向NULL

return p + N_INTOBJECTS - 1; //返回数组的最后一个元素的地址

}

注释里面说明了Python的对象分配器不适合多个大内存块,所以一次获取的大小是sizeof(PyIntBlock)。

PyIntBlock里面包含了一个next指针和一个PyIntObject数组。

为了方便,将说明放在代码中进行注释。

接着回到PyInt_FromLong函数

{

......

v = free_list; //v指向数组的最后一个元素,最新未使用的PyIntObject

free_list = (PyIntObject *)Py_TYPE(v); //free_list指向最新未使用的PyIntObject,目前未倒2个数组元素,注意,这里是用ob_type指针来链接的

PyObject_INIT(v, &PyInt_Type); //对v进行初始化,即刚得到的整数对象

v->ob_ival = ival; //设置整数对象的值

return (PyObject *) v; //返回整数对象

}

补充。

intobject.c开头的一段注释值得注意。

/* Integers are quite normal objects, to make object handling uniform.

(Using odd pointers to represent integers would save much space

but require extra checks for this special case throughout the code.)

Since a typical Python program spends much of its time allocating

and deallocating integers, these operations should be very fast.

Therefore we use a dedicated allocation scheme with a much lower

overhead (in space and time) than straight malloc(): a simple

dedicated free list, filled when necessary with memory from malloc().

block_list is a singly-linked list of all PyIntBlocks ever allocated,

linked via their next members. PyIntBlocks are never returned to the

system before shutdown (PyInt_Fini).

free_list is a singly-linked list of available PyIntObjects, linked

via abuse of their ob_type members.

*/

这一段主要是说为了避免过多的内存分配开销,采用了特定的策略来管理整数对象的内存分配。

block_list是一个单向链表,保存着所有分配过的PyIntBlock,通过next指针链接,它会一直存在着,直到系统关闭(PyInt_Fini)。

free_list是PyIntObject的单向链表,通过对象的ob_type成员链接,这是忽略类型安全的“滥用”。

小整数也是通过这两个数据结构来管理的,只不过它是在一开始就初始化好,而大整数是运行时需要才创建的。

下面是大整数创建过程的草图。

pythonobject转int_[笔记]Python的整数对象:PyIntObject相关推荐

  1. python整数池_对Python中小整数对象池和大整数对象池的使用详解

    1. 小整数对象池 整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间. Python 对小整数的定义是 [-5, 256] 这些整数对象是提 ...

  2. Python中小整数对象池和大整数对象池

    1.小整数对象池 整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间. Python 对小整数的定义是 [-5, 256] 这些整数对象是提前 ...

  3. python整数运算_深入 Python (6) 整数对象的数学运算

    整数的基本运算 上一节讲到,在 PyLong_Type 中定义了整数类型的各种属性,比如整数类型的名称 "int".整数对象最常用的是一些数学运算,整数对象当然也是支持这些方法的, ...

  4. python求长整数_python 长整数

    Python 的整数与 Numpy 的数据溢出 某位 A 同学发了我一张截图,问为何结果中出现了负数? 看了图,我第一感觉就是数据溢出了.数据超出能表示的最大值,就会出现奇奇怪怪的结果. 然后,他继续 ...

  5. python内建函数测试对象身份_Python学习笔记 03 Python对象

    1.Python对象 Python对象都拥有三个特性:身份.类型和值. 身份:每一个对象都有一个唯一的身份标识自己,任何对象的身份都可以使用内建函数id()来得到.这个值可以被认为是该对象的内存地址. ...

  6. 《流畅的Python第二版》读书笔记——函数作为一等对象

    引言 这是<流畅的Python第二版>抢先版的读书笔记.Python版本暂时用的是python3.10.为了使开发更简单.快捷,本文使用了JupyterLab. 函数是Python的一等( ...

  7. python求5_python(五)——运算符,小整数对象池

    1.成员运算符,判断某个东西是否在某个东西里包含:in,not in name = "abcd" if "ac" inname:print("ok&q ...

  8. python整数池_【Python】Python中神奇的小整数对象池和大整数对象池

    小整数对象池 整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间. Python 对小整数的定义是 [-5, 256] 这些整数对象是提前建立 ...

  9. Python的小整数对象池

    1. 小整数对象池 整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间. Python 对小整数的定义是 [-5, 257) 这些整数对象是提 ...

最新文章

  1. python 字符串前加r和f
  2. linux版_微软爱 Linux:安全杀毒软件 Defender ATP 要出 Linux 版了! | Linux 中国
  3. php 打印请求体,php如何获取原生请求体
  4. react学习笔记(9)表单控件
  5. 阿里小米获运营商牌照;罗永浩吐槽苹果;谷歌曾私下求情欧盟 | 极客头条
  6. 物理机安装linux系统,物理机安装linux的三种方法
  7. mysql 图文安装_mysql安装图解mysql图文安装教程(详细说明)
  8. python open() r和rb
  9. IDEA插件系列(45):UUID Generator插件——UUID生成器
  10. Matplotlib——线图_axis()函数
  11. [常微分方程的数值解法系列五] 龙格-库塔(RK4)法
  12. 领导说要搞微服务,我该怎么搭建开发和测试环境?
  13. 【C++】DISALLOW_COPY_AND_ASSIGN
  14. 63套js ,html .css效果页面(点个小星星免费下载)
  15. 74HC238引脚定义 使用方法
  16. [C++]auto类型说明符
  17. tan5度用计算机怎么算,tan5度(tan5度怎么求)
  18. 网络综合测试仪 的功能和参数
  19. java 生成UUID字符串工具类 UUIDUtil
  20. PJ331 PJ501超小型封装PFM DC/DC升压稳压器

热门文章

  1. 被风吹过的夏天,与往事和解。
  2. mybatis——.xml映射文件中不同情况下的resultType(记录)
  3. 大数据技术之_16_Scala学习_09_函数式编程-高级
  4. 一个90后关于ZG足球的思考【卡塔尔世界杯】
  5. 我眼中的微商为什么要用微商系统
  6. 教你炒股票1:不会赢钱的经济人,只是废人!
  7. 伪静态规则写法RewriteRule-htaccess详细语法使用
  8. Swift官方入门教程系列--一--使用Swift创建UI【翻译版,源代码】
  9. 数据可视化,我的数据分析成长之旅
  10. MySQL 之 事务、存储过程、索引