python源码笔记_python源码学习笔记(二)

(二) python 继承和多态

这非常类似C++的功能,只不过是是在C基础上开发的。由上一节知,python的所有对象的基础都是PyObject,所以例如创建一个PyIntObject对象,是通过PyObejct*变量来维护,所以在python内部各个函数之间传递的都是一种范型指针PyObject* ,是不是很像C++里面的基类。如果要Print(PyIntObject* ),由多态(polymophism)我们会知道,调用的实际上是PyIntObject对象对应的类型对象中定义的输出操作。

看代码:

longPyObject_Hash(PyObject*v) //注意是PyObject

{

PyTypeObject*tp = v->ob_type; //找到类型

if (tp->tp_hash !=NULL)return (*tp->tp_hash)(v); //调用相应类型的hash函数

/*To keep to the general practice that inheriting

* solely from object in C code should work without

* an explicit call to PyType_Ready, we implicitly call

* PyType_Ready here and then check the tp_hash slot again*/

//为了维持在C代码单继承中不直接调用PyType_Ready这一惯例,在这里间接地调用PyType_Ready(),并再次检查tp_hash槽

if (tp->tp_dict ==NULL) {if (PyType_Ready(tp) < 0)return -1;if (tp->tp_hash !=NULL)return (*tp->tp_hash)(v);

}if (tp->tp_compare == NULL && RICHCOMPARE(tp) ==NULL) {return _Py_HashPointer(v); /*Use address as hash value*/ //把地址作为hash值返回

}/*If there's a cmp but no hash defined, the object can't be hashed*/

//如果有cmp,但是hash没有被定义,返回这个对象不能被hash

returnPyObject_HashNotImplemented(v);

}

以PyIntObject为例,观察其实现过程。

1 [intobject.h]2 typedef struct{3 PyObject_HEAD4 longob_ival;5 } PyIntObject;6

7 [intobject.c]8 static PyObject * //注意这里是静态函数,而且是PyObject的指针,这个是多态的典型特征

9 int_add(PyIntObject *v, PyIntObject *w) //加

10 {11 register longa, b, x;12 CONVERT_TO_LONG(v, a);13 CONVERT_TO_LONG(w, b);14 /*casts in the line below avoid undefined behaviour on overflow*/

15 x = (long)((unsigned long)a +b);16 if ((x^a) >= 0 || (x^b) >= 0)17 returnPyInt_FromLong(x);18 return PyLong_Type.tp_as_number->nb_add((PyObject *)v, (PyObject *)w);19 }20

21 static PyObject *

22 int_sub(PyIntObject *v, PyIntObject *w) //减

23 {24 register longa, b, x;25 CONVERT_TO_LONG(v, a);26 CONVERT_TO_LONG(w, b);27 /*casts in the line below avoid undefined behaviour on overflow*/

28 x = (long)((unsigned long)a -b);29 if ((x^a) >= 0 || (x^~b) >= 0)30 returnPyInt_FromLong(x);31 return PyLong_Type.tp_as_number->nb_subtract((PyObject *)v,32 (PyObject *)w);33 }34

35 static PyObject *

36 int_mul(PyObject *v, PyObject *w) //乘

37 {38 longa, b;39 long longprod; /*a*b in native long arithmetic*/

40 double doubled_longprod; /*(double)longprod*/

41 double doubleprod; /*(double)a * (double)b*/

42

43 CONVERT_TO_LONG(v, a);44 CONVERT_TO_LONG(w, b);45 /*casts in the next line avoid undefined behaviour on overflow*/

46 longprod = (long)((unsigned long)a *b);47 doubleprod = (double)a * (double)b;48 doubled_longprod = (double)longprod;49

50 /*Fast path for normal case: small multiplicands, and no info51 is lost in either method.*/

52 if (doubled_longprod ==doubleprod)53 returnPyInt_FromLong(longprod);54

55 /*Somebody somewhere lost info. Close enough, or way off? Note56 that a != 0 and b != 0 (else doubled_longprod == doubleprod == 0).57 The difference either is or isn't significant compared to the58 true value (of which doubleprod is a good approximation).59 */

60 {61 const double diff = doubled_longprod -doubleprod;62 const double absdiff = diff >= 0.0 ? diff : -diff;63 const double absprod = doubleprod >= 0.0 ?doubleprod :64 -doubleprod;65 /*absdiff/absprod <= 1/32 iff66 32 * absdiff <= absprod -- 5 good bits is "close enough"*/

67 if (32.0 * absdiff <=absprod)68 returnPyInt_FromLong(longprod);69 else

70 return PyLong_Type.tp_as_number->nb_multiply(v, w);71 }72 }73

由此可知,python的int 实际上是C里面的long实现,所以加减乘除都是用long实现,又由于PyIntObject为一个Immutable对象,这个对象不可改变,因此在最后return的都是新的对象:PyInt_FromLong(num),即由long变量创建一个int变量。

(三)python整数的实现

在整数里,python分为大整数和小整数,为了加快计算,节省内存的分配时间,因为不论是什么对象,只要在堆上申请空间是非常费时的,所以在涉及到频繁的内存操作时需要做一些优化。python提供了一种比较原始的方法——设个阈值,无语了,有这么来的么,最起码来个动态阈值也好啊...

1 #ifndef NSMALLPOSINTS2 #define NSMALLPOSINTS 257

3 #endif

4 #ifndef NSMALLNEGINTS5 #define NSMALLNEGINTS 5

6 #endif 范围设定到(-5~257) 在这个区间里面都为小整数

7 #if NSMALLNEGINTS + NSMALLPOSINTS > 0

8 /*References to small integers are saved in this array so that they //保存在数组里面被共享9 can be shared.10 The integers that are saved are those in the range11 -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive). //[-5, 257)12 */

13 static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS]; //申请(NSMALLNEGINTS + NSMALLPOSINTS)个PyIntObject* 为以后所共享

14 #endif

15

然后是大整数,大整数采用块内存区间内缓存

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

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

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

4

5 struct_intblock {6 struct _intblock *next;7 PyIntObject objects[N_INTOBJECTS];8 };9

10 typedef struct_intblock PyIntBlock;11

12 static PyIntBlock *block_list =NULL;13 static PyIntObject *free_list = NULL;

N_INTOBJECTS    到底是多少呢,算一下PyIntObject的大小,PyIntObject宏展开(without Py_TRACE_REFS)后就是:

Py_ssize_t  ob_refnt;

PyTypeObject *ob_type;

long ob_ival;

字节大小为4+4+8 = 16 , N_INTOBJECTS  = (1000-8)/16 = 82,即一个PyIntBlock维护着的82个PyIntObeject,咋一看其实就是个单链表,因此这个82个objects相当于是82个数。

通过block_list 来维护,看代码:

1 static PyIntObject *

2 fill_free_list(void)3 {4 PyIntObject *p, *q;5 /*Python's object allocator isn't appropriate for large blocks.*/

6 p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock));7 if (p ==NULL)8 return (PyIntObject *) PyErr_NoMemory();9 ((PyIntBlock *)p)->next = block_list; //串联blocklist

10 block_list = (PyIntBlock *)p;11 /*Link the int objects together, from rear to front, then return12 the address of the last int object in the block.*/

13 p = &((PyIntBlock *)p)->objects[0]; //第一块即front,头部,注意是地址哦,有个“&”

14 q = p + N_INTOBJECTS; //从0开始计数,q不是尾指针,还要减去1才是

15 while (--q > p) // //最后--q 后即rear,尾部从后往前遍历

16 Py_TYPE(q) = (struct _typeobject *)(q-1); //将所有的PyIntObject 串起来

17 Py_TYPE(q) = NULL; //q现在为头指针,类型置为空

18 return p + N_INTOBJECTS - 1; //返回rear

19 }

由上可知,相当于对外是一个blocklist, 对内是一系列的PyIntObject,当需要重新开辟blocklist时,通过((PyIntBlock *)p)->next = block_list把这些链表串起来。

现在我们可以看看PyInt_FromLong的实现了

1 [intobject.c]2 PyObject *

3 PyInt_FromLong(longival)4 {5 register PyIntObject *v; //用寄存器操作,加快速度

6 #if NSMALLNEGINTS + NSMALLPOSINTS > 0

7 if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) { //在小数的范围区间,直接命中

8 v = small_ints[ival +NSMALLNEGINTS];9 Py_INCREF(v); //引用计数加1

10 #ifdef COUNT_ALLOCS //这是要统计了

11 if (ival >= 0)12 quick_int_allocs++; //正数命中的个数

13 else

14 quick_neg_int_allocs++; //负数命中的个数

15 #endif

16 return (PyObject *) v;17 }18 #endif

19 if (free_list == NULL) { //如果是大数,且free_list 没有被赋值,开始创建

20 if ((free_list = fill_free_list()) == NULL) //这里我们知道freelist指向的是链表的rear21 //和 block_list 是指向PyIntBlock 的指针相区别

22 returnNULL;23 }24 /*Inline PyObject_New*/

25 v =free_list;26 free_list = (PyIntObject *)Py_TYPE(v); //强制转换一下类型变成PyIntObject类型

27 PyObject_INIT(v, &PyInt_Type); //初始化为python的int类型

28 v->ob_ival =ival;29 return (PyObject *) v;30 }31

最后有

1 [intobject.c]2 #define PyInt_CheckExact(op) ((op)->ob_type == &PyInt_Type)

3 ...4 static void

5 int_dealloc(PyIntObject *v)6 {7 if(PyInt_CheckExact(v)) {8 Py_TYPE(v) = (struct _typeobject *)free_list; //如果是整数类对象,只是简单的把v置成free_list,即空闲链表的起点,相当于9 //覆盖的形式

10 free_list =v;11 }12 else

13 Py_TYPE(v)->tp_free((PyObject *)v); //如果不是整数类型,调用底层的释放函数

14 }15

16 static void

17 int_free(PyIntObject *v)18 {19 Py_TYPE(v) = (struct _typeobject *)free_list; //同上

20 free_list =v;21 }

不要搞混的是,上述实现不管是小数还是大数都是起着缓冲池的作用,不要误解为是实现大数字功能,只不过都是用long实现的,这个很容易误导。

再看小整数的换冲池:

1 static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS]; //声明为静态指针数组

2 .........3

4 int

5 _PyInt_Init(void)6 {7 PyIntObject *v;8 intival;9 #if NSMALLNEGINTS + NSMALLPOSINTS > 0

10 for (ival = -NSMALLNEGINTS; ival < NSMALLPOSINTS; ival++) {11 if (!free_list && (free_list = fill_free_list()) == NULL) //同样是申请空闲链表

12 return 0;13 /*PyObject_New is inlined*/

14 v =free_list;15 free_list = (PyIntObject *)Py_TYPE(v);16 PyObject_INIT(v, &PyInt_Type);17 v->ob_ival = ival; //赋值后加入small_ints这个缓冲池

18 small_ints[ival + NSMALLNEGINTS] = v; //相当于一个一一映射的关系

19 }20 #endif

21 return 1;22 }

我们知道的就是说所有的整数都在堆里面有内存,小整数使用通过small_ints[]数组一一映射加快查找速度,大数则需要通过链表来维护内存。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/337008.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux 延时一微秒_让我们暂停一微秒

linux 延时一微秒低延迟Java应用程序中的许多基准测试涉及必须在一定负载下测量系统。 这就要求保持事件进入系统的稳定吞吐量&#xff0c;而不是在没有任何控制的情况下以全油门将事件泵入系统。 我经常要做的任务之一是在事件之间将生产者线程暂停一小段时间。 通常&#xf…

C语言不是最好的,却是我最爱的~

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删以下为译文&#xff1a;虽然 C 语言并不是我所学的第一门语言&#xff0c;也不是我的最后一门语言&#xff0c;但是我仍然非常喜欢 C&#xff0c…

vue 传递多行数据_vue父组件向子组件传递多个数据的实例

在平时我们使用VUE组件的时候&#xff0c;经常需要将父组件的某些数据传递给子组件&#xff0c;这个时候&#xff0c;我们通常会有很多的办法&#xff0c;这里主要分为两种情况&#xff1a;第一种&#xff1a;静态数据传递&#xff1a;传递一个 字符串第二种&#xff1a;动态数…

lucene 源码分析_Lucene分析过程指南

lucene 源码分析本文是我们名为“ Apache Lucene基础知识 ”的学院课程的一部分。 在本课程中&#xff0c;您将了解Lucene。 您将了解为什么这样的库很重要&#xff0c;然后了解Lucene中搜索的工作方式。 此外&#xff0c;您将学习如何将Lucene Search集成到您自己的应用程序中…

ggplot2箱式图两两比较_R语言进阶笔记2 | 长数据与ggplot2

1. 长数据是什么鬼&#xff1f;之前介绍了如何将多个性状的箱线图放在一个图上&#xff0c;比如learnasreml包中的fm数据&#xff0c;它有h1~h5五年的株高数据&#xff0c;想对它进行作图。「数据预览&#xff1a;」> library(learnasreml)> data(fm)> head(fm) Tree…

面向对象,C语言实现简单工厂模式,思路+代码

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删1、简介简单工厂方法定义一个用于创建对象的类&#xff0c;该类接受一个参数&#xff0c;通过参数决定创建不同的对象。GOF并没有把简单工厂方法…

javaone_JavaOne和OOW 2015总结

javaone大家好&#xff01; 终于&#xff0c;我回来了一个很棒的JavaOne和OOW2015。在这篇文章中&#xff0c;我想分享我的经验&#xff0c;一些照片和我参加的演讲的摘要。 会议前 我于2015年6月24日星期六乘Copa航空公司CLO-PTY-SFO飞往旧金山。 从哥伦比亚出发&#xff08;…

如何导出久其报表所有数据_如何选择好的HR软件

相信HR朋友想要换HR系统的时候&#xff0c;一般都会在百度、360和搜狗上找&#xff0c;或者通过朋友介绍&#xff0c;而自己百度找的时候&#xff0c;就会出现很多HR软件的广告&#xff0c;一个一个的去问&#xff0c;也不一定能问出所以然&#xff0c;所以就约着面谈&#xff…

网站快速变灰色,几行代码就搞定了!

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删自从伟大的江爷爷走了后&#xff0c;全站和各个App的内容都变成了灰色&#xff0c;包括按钮、图片等等。这时候我们可能会好奇这是怎么做到的呢&…

java 递归 堆栈_Java中的堆栈安全递归

java 递归 堆栈在本文中&#xff0c;摘自《 Java中的函数编程 》一书&#xff0c;我解释了如何使用递归&#xff0c;同时避免了StackOverflow异常的风险。 Corecursion正在使用第一步的输出作为下一步的输入来构成计算步骤。 递归是相同的操作&#xff0c;但是从最后一步开始。…

cshtml中引用css_css基础必备-使用样式,前端小白一看就会

在HTML文档中包含CSSCSS可以作为单独的文档引用&#xff0c;也可以嵌入到HTML文档中。在HTML文档中包含CSS有三种方法&#xff1a;内联样式 - 使用元素起始标记的style属性指定样式嵌入样式 - 在文档的head部分使用style标记指定样式外部样式 - 在文档的head部分使用link标记引…

嵌入式软件分层框架设计,举个例子

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删前言为了能够使得产品得到更好的开发速度与以后更好的迭代和移植&#xff0c;框架分层是很有必要的。但如对于中小型项目严格遵循这些原则&#…

mockito入门_Mockito入门

mockito入门本文是我们名为“ 用Mockito测试 ”的学院课程的一部分。 在本课程中&#xff0c;您将深入了解Mockito的魔力。 您将了解有关“模拟”&#xff0c;“间谍”和“部分模拟”的信息&#xff0c;以及它们相应的存根行为。 您还将看到使用测试双打和对象匹配器进行验证的…

windows7安dns服务器_在Windows 7 上安装DNS服务器bind9方法详解

本文主要介绍在WIN7上利用ntbind部署DNS服务器的方法。ntbind是Bind的Windows版本&#xff0c;1.下载BIND9.11下载地址&#xff1a;http://ftp.isc.org/isc/bind9/9.11.0rc3/。我的系统是window 7 64位需要下载BIND9.11.0rc3.x64.zip&#xff0c;建议下载9.11以上的版本&#x…

腾讯大举退出美团!

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删11月16日&#xff0c;腾讯发布第三季度财报&#xff0c;同时表示将“分红式减持”美团。腾讯分派90.9%美团持股 腾讯系中概股美股盘前多数下跌腾…

csr8670 修改key_CSR8670 DFU user guide

DFU使用1)产生DFU keys&#xff1a;dfukeygenerate.exe -o keys或dfukeygenerate.exe -o keys -r random.txt生成keys.private.key和keys.public.key两个文件。2)loader和firmware签名&#xff1a;a)dfukeyinsert.exe -v -o loader_signed -lC:\ADK_CSR867x.WIN4.3.1.5\firmwar…

matchers依赖_Hamcrest Matchers教程

matchers依赖本文是我们名为“ 用Mockito测试 ”的学院课程的一部分。 在本课程中&#xff0c;您将深入了解Mockito的魔力。 您将了解有关“模拟”&#xff0c;“间谍”和“部分模拟”的信息&#xff0c;以及它们相应的存根行为。 您还将看到使用测试双打和对象匹配器进行验证…

谷歌开源替代 C++ 的编程语言:Carbon

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删谷歌工程师 Chandler Carruth 近日在多伦多举办的 CppNorth 大会上宣布①&#xff0c;正式开源谷歌内部打造的编程语言&#xff1a;Carbon&#…

mediumtext和string转换_数据库用varchar和text的差别

数据库用varchar和text的差别发布时间&#xff1a;2018-05-09 20:41,浏览次数&#xff1a;1268, 标签&#xff1a;varchartext最近有几个同学问我varchar和text有啥别吗&#xff0c;这个问题&#xff0c;以前说真的也没太多的整理&#xff0c;以前遇到text在设计中就是尽可能的…

eai app_EAI的Spring集成教程

eai app课程大纲 Spring Integration是用于企业应用程序集成的开源框架。 这是一个轻量级的框架&#xff0c;建立在核心Spring框架之上。 它旨在支持开发事件驱动的体系结构和以消息为中心的体系结构典型的集成解决方案。 Spring Integration扩展了Spring编程模型&#xff0c;…