python源码剖析_Python源码剖析 - 对象初探

1.jpg

01 前言

对象是 python 中最核心的一个概念,在python的世界中,一切都是对象,整数、字符串、甚至类型、整数类型、字符串类型,都是对象。

02 什么是PyObject

Python 中凡事皆对象,而其中 PyObject 又是所有对象的基础,它是 Python 对象机制的核心。因为它是基类,而其他对象都是对它的继承。

打开 Include/python.h 中声明如下:

#define PyObject_HEAD \

_PyObject_HEAD_EXTRA \

Py_ssize_t ob_refcnt; \

struct _typeobject *ob_type;

typedef struct _object {

PyObject_HEAD

} PyObject;

PyObject 有两个重要的成员对象:

ob_refcnt - 表示引用计数,当有一个新的 PyObject * 引用该对象时候,则进行 +1 操作;同时,当这个 PyObject * 被删除时,该引用计数就会减小。当计数为0时,该对象就会被回收,等待内存被释放。

ob_type 记录对象的类型信息,这个结构体含有很多信息,见如下代码分析。

03 类型对象

在python中,预先定义了一些类型对象,比如 int 类型、str 类型、dict 类型等,这些我们称之为内建类型对象,这些类型对象实现了面向对象中"类"的概念。

这些内建对象实例化之后,可以创建类型对象所对应的实例对象,比如 int 对象、str 对象、dict 对象。这些实例对象可以视为面向对象理论中的“对象"这个概念在python中的体现。

#define PyObject_VAR_HEAD \

PyObject_HEAD \

Py_ssize_t ob_size; /* Number of items in variable part */

typedef struct _typeobject {

PyObject_VAR_HEAD

const char *tp_name; /* For printing, in format "." */

Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */

/* Methods to implement standard operations */

destructor tp_dealloc;

printfunc tp_print;

getattrfunc tp_getattr;

setattrfunc tp_setattr;

cmpfunc tp_compare;

reprfunc tp_repr;

/* Method suites for standard classes */

PyNumberMethods *tp_as_number;

PySequenceMethods *tp_as_sequence;

PyMappingMethods *tp_as_mapping;

/* Attribute descriptor and subclassing stuff */

struct PyMethodDef *tp_methods;

struct PyMemberDef *tp_members;

struct PyGetSetDef *tp_getset;

struct _typeobject *tp_base;

PyObject *tp_dict;

descrgetfunc tp_descr_get;

descrsetfunc tp_descr_set;

Py_ssize_t tp_dictoffset;

initproc tp_init;

allocfunc tp_alloc;

newfunc tp_new;

freefunc tp_free; /* Low-level free-memory routine */

inquiry tp_is_gc; /* For PyObject_IS_GC */

PyObject *tp_bases;

PyObject *tp_mro; /* method resolution order */

PyObject *tp_cache;

PyObject *tp_subclasses;

PyObject *tp_weaklist;

destructor tp_del;

...

} PyTypeObject;

这当中,我们需要关注几个重点成员变量:

tp_name 即类型名称,例如 'int', tuple', 'list'等,可以标准输出

tp_basicsize 与 tp_itemsize, 创建该对象的内存信息

关联操作

描述该类型的其他信息

04 定长对象与变长对象

定长对象比较好理解,例如一个整数对象,无论这个数值多大,它的存储长度是一定的,这个长度由 _typeobject 来指定,不会变化。

typedef struct {

PyObject_HEAD

long ob_ival;

} PyIntObject;

变长对象在内存中的长度是不一定的,所以需要 ob_size 来记录变长部分的个数,需要注意的是,这个并不是字节的数目。

#define PyObject_VAR_HEAD \

PyObject_HEAD \

Py_ssize_t ob_size; /* Number of items in variable part */

typedef struct {

PyObject_VAR_HEAD;

long ob_shash;

int ob_sstate;

char ob_sval[1];

/* Invariants:

* ob_sval contains space for 'ob_size+1' elements.

* ob_sval[ob_size] == 0.

* ob_shash is the hash of the string or -1 if not computed yet.

* ob_sstate != 0 iff the string object is in stringobject.c's

* 'interned' dictionary; in this case the two references

* from 'interned' to this object are *not counted* in ob_refcnt.

*/

} PyStringObject;

05 创建一个定长对象的例子

代码如下:

a = int(10)

Python 主要做了以下操作:

第一步:分析需要创建的类型,如上,则是 PyInt_Type

第二步:根据 PyInt_Type 中的 int_new 函数来构造对象

第三步:识别上述代码中的 10 为字符传,然后调用 PyInt_FromString() 函数来构造

第四步:最后调用 PyInt_FromLong(long ival) 函数来进行整数对象的内存分配和赋值。

我们先看一下 PyInt_Type的代码实现:

tp_name 被赋值为“int”,这样在 type() 函数时,就会显示该字符串

指定 “int” 类的关联操作,如释放、打印、比较等

tp_basicsize 赋值为 sizeof(PyIntObject)

tp_itemsize 赋值为 0

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

};

这里我们对 int_new 方法进行展开, int_new 方法就是创建函数,类似于 C++ 中的构造函数,用来生成PyIntObject 代码如下:

static PyObject *

int_new(PyTypeObject *type, PyObject *args, PyObject *kwds)

{

PyObject *x = NULL;

int base = -909;

static char *kwlist[] = {"x", "base", 0};

if (type != &PyInt_Type)

return int_subtype_new(type, args, kwds); /* Wimp out */

if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi:int", kwlist,

&x, &base))

return NULL;

if (x == NULL) {

if (base != -909) {

PyErr_SetString(PyExc_TypeError,

"int() missing string argument");

return NULL;

}

return PyInt_FromLong(0L);

}

if (base == -909)

return PyNumber_Int(x);

if (PyString_Check(x)) {

/* Since PyInt_FromString doesn't have a length parameter,

* check here for possible NULs in the string. */

char *string = PyString_AS_STRING(x);

if (strlen(string) != PyString_Size(x)) {

/* create a repr() of the input string,

* just like PyInt_FromString does */

PyObject *srepr;

srepr = PyObject_Repr(x);

if (srepr == NULL)

return NULL;

PyErr_Format(PyExc_ValueError,

"invalid literal for int() with base %d: %s",

base, PyString_AS_STRING(srepr));

Py_DECREF(srepr);

return NULL;

}

return PyInt_FromString(string, NULL, base);

}

#ifdef Py_USING_UNICODE

if (PyUnicode_Check(x))

return PyInt_FromUnicode(PyUnicode_AS_UNICODE(x),

PyUnicode_GET_SIZE(x),

base);

#endif

PyErr_SetString(PyExc_TypeError,

"int() can't convert non-string with explicit base");

return NULL;

}

最后通过 PyInt_FromLong 方法对新产生的对象的type信息就行赋值为 PyInt_Type,并设置整数的具体数值。其中如果是小整数,则可以从 small_ints 数组中直接放回。

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

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

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

static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];

struct _intblock {

struct _intblock *next;

PyIntObject objects[N_INTOBJECTS];

};

typedef struct _intblock PyIntBlock;

static PyIntBlock *block_list = NULL;

static PyIntObject *free_list = NULL;

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);

(void)PyObject_INIT(v, &PyInt_Type);

v->ob_ival = ival;

return (PyObject *) v;

}

06 展开

为了性能考虑,python 中对小整数有专门的缓存池,这样就不需要每次使用小整数对象时去用 malloc 分配内存以及free释放内存。

小整数之外的大整数怎么避免重复分配和回收内存呢?

Python 的方案是 PyIntBlock。PyIntBlock 这个结构就是一块内存,里面保存 PyIntObject 对象。一个 PyIntBlock 默认存放 N_INTOBJECTS 对象。

PyIntBlock 链表通过 block_list 维护,每个block中都维护一个 PyIntObject 数组 objects,block 的 objects 可能会有些内存空闲,因此需要另外用一个 free_list 链表串起来这些空闲的项以方便再次使用。objects 数组中的 PyIntObject 对象通过 ob_type 字段从后往前链接。

小整数的缓存池最终实现也是生存在 block_list 维护的内存上,在 python 初始化时,会调用 PyInt_Init 函数申请内存并创建小整数对象。

更多内容

如果你对Python语言感兴趣,可以关注我,或者关注我的微信公众号:xtuz666

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

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

相关文章

html点线面制作,利用HTML5绘制点线面组成的3D图形的示例

玩Canvas玩了有两三个礼拜了&#xff0c;平面的东西玩来玩去也就那样&#xff0c;所以就开始折腾3D了。因为Canvas画布终究还是平面的&#xff0c;所以要有3D就得抽象出一个Z轴。然后再把3D坐标转换成2D坐标&#xff0c;画到画布上&#xff0c;再通过旋转等变换效果来产生3D感。…

javapanel根据内部组件_[译]避免在unmounted组件上调用setState

[译]避免在unmounted组件上调用setState原文 : https://www.robinwieruch.de/react-warning-cant-call-setstate-on-an-unmounted-component很多人在开发 React 的过程中&#xff0c;会遇到下面这些警告。github上很多issue都和这些警告相关。因此&#xff0c;我想在这篇文章里…

如何写一个脚本语言_零基础小白如何学会写文案?文案写作技巧之一:如何写一个吸引读者的文案开头...

我文笔不好怎样才能够写出优质的文案&#xff1f;很多人都有这个疑问&#xff0c;包括我自己在学习新媒体运营之前&#xff0c;这也是我最大的困惑。现在是内容为王的时代&#xff0c;你的文章质量决定了你的KPI。后来我学习了文章的写作结构和技巧后&#xff0c;就能够写出一篇…

计算机硬件的维护知识,电脑放了一年开不了机 电脑硬件维护常识要点有哪些...

随着经济的发展&#xff0c;人们走到哪里都是拿着手机&#xff0c;对于家里已有电脑的&#xff0c;完全不会去用下&#xff0c;很多人都觉得手机更加方便&#xff0c;但长时间不用电脑就会导致电脑当中的一些设备出现问题&#xff0c;都知道电脑它主要就会由一些硬件与软件进行…

计算机学院寝室文明风景线活动,小猿关注 | 营造良好学风 打造和谐宿舍 ——计算机学院开展学风主题教育暨文明宿舍评选活动...

原标题&#xff1a;小猿关注 | 营造良好学风 打造和谐宿舍 ——计算机学院开展学风主题教育暨文明宿舍评选活动计算机学院记者团讯(通讯员 苏婉静 编辑 王鸿宇)12月15日晚&#xff0c;计算机学院于九号楼9201教室开展“ 营造良好学风&#xff0c;打造和谐宿舍”主题教育活动&am…

c++ 结构体初始化_STM32入门系列-使用库函数点亮LED,LED初始化函数

要点亮LED&#xff0c;需要完成LED的驱动&#xff0c; 在工程模板上新建一个led.c和led.h文件&#xff0c;将其存放在led文件夹内。这两个文件需要我们自己编写。 通常xxx.c文件用于存放编写的驱动程序&#xff0c;xxx.h文件用于存放xxx.c内的stm32头文件、管脚定义、全局变量声…

swift-UITextfield控件的基本属性设置

//1.初始化UITextField let userNameUITextField(frame: CGRectMake(0, 100, 100, 100)); //2.将文本框userName添加到当前视图中 self.view.addSubview(userName); //3.文本框默认显示文字 userName.placeholder"请输入手机号"; //4.设置字体大小 userName.fontUIFo…

vue openlayer单击地图事件循环多次执行_12道vue高频原理面试题,你能答出几道?

前言本文分享 12 道 vue 高频原理面试题,覆盖了 vue 核心实现原理,其实一个框架的实现原理一篇文章是不可能说完的,希望通过这 12 道问题,让读者对自己的 Vue 掌握程度有一定的认识(B 数),从而弥补自己的不足,更好的掌握 Vue ❤️1. Vue 响应式原理vue-reactive核心实现类:Obse…

大型网站架构演化发展历程

前面已经描述了大型网站系统的特点&#xff0c;而对一个大型网站系统&#xff0c;其架构也是重要的一个环节。 大型网站技术主要的挑战来自于庞大的用户、高并发以及海量的数据这三个方面。大型网站的形成就像一颗大树的成长&#xff0c;历尽长时间的磨练&#xff0c;最后枝繁叶…

dts数据库迁移工具_传统数据库迁移上云利器-ADAM

自1970年关系型数据库被提出以来&#xff0c;至今已有50年历史。但在关系型数据库领域正在发生着巨大的变化&#xff0c;首先是互联网的发展&#xff0c;使得开源数据库越来越受欢迎&#xff0c;可扩展性成为支撑业务发展的重要特性&#xff0c;比如WebScaleSQL就是几个互联网公…

python解zuobiaoxi方程_吴恩达《Machine Learning》精炼笔记 2:梯度下降与正规方程

作者&#xff1a;Peter红色石头的个人网站&#xff1a;红色石头的个人博客-机器学习、深度学习之路​www.redstonewill.com今天带来第二周课程的笔记&#xff1a;梯度下降与正规方程。主要内容&#xff1a;多维特征多变量梯度下降梯度下降法实践正规方程多维特征Multiple Featu…

计算机房按几类防雷,计算机机房防雷方案

常见问题计算机机房防雷方案2461一键分享这些微电子网络设备的普遍应用&#xff0c;使得防雷的问题显得越来越重要。由于微电子设备具有高密度、高速度、低电压、和低功耗等特性&#xff0c;这就使其对各种诸如雷电过电压、电力系统操作过电压、静电放电、电磁辐射等电磁干扰非…

webpack最新版本_webpack小结-开发环境构建优化

刚刚对我们前端项目做了一顿分析优化操作&#xff0c;因为接手时每次构建要花两分钟左右的时间&#xff0c;实在忍受不了&#xff0c;只能动手了。通过这次优化&#xff0c;重新温习了下 webpack 的一些知识。接下来会关于 webpack 展开写几篇心得&#xff1a;构建分析开发环境…

Xamarin Mono for VS开发窗体标题(Title)乱码解决方案

利用mono for VS开发一个手机程序&#xff0c;结果只有窗体的标题 title部分是乱码&#xff0c;其他所有地方中文都显示正常&#xff0c;很郁闷。百度很久无果。最后发现只要在 VS菜单中 的 文件->高级保存选型中奖编码设置为 Unicode或者UTF8就行了。更改所有有乱码的文件&…

nlp 命名实体识别 算法_中文命名实体识别算法 Lattice LSTM

中文命名实体识别 (NER) 算法按照输入的类型&#xff0c;可以大致分为 Character-based (字符) 和 Word-based (单词) 两种。这两种方法都存在一些缺陷&#xff0c;Character-based 不能利用词汇和词汇序列信息&#xff1b;Word-based 需要分词&#xff0c;分词的错误会对 NER …

margin background_margin:auto与布局展示

margin:auto 的作用机制。使用margin:auto居中&#xff0c;是css的基本操作。但会发现时不时的失灵。 这篇文章是对该属性的深度分析。首先&#xff0c;以下事实&#xff08;自动填充&#xff09;必须明确&#xff08;1&#xff09;有时候元素就算没有设置width 或height&#…

智能客户端ios_为什么现在的客户端开发越来越不吃香了

这是Kevin的第 672 篇原创&#xff0c;持续日更&#xff0c;做产品经理的创业斜杠青年。年底上线APP&#xff0c;是我们今年PMTalk在产品研发上的一个最终目标。启动这个项目前&#xff0c;团队、和身边创业朋友都不看好这件事&#xff0c;因为客户端开发太重了&#xff0c;小公…

用计算机录制声音让音质更好,电脑如何录屏?电脑录屏如何把声音也录制下来...

电脑如何录屏&#xff1f;电脑录屏如何把声音也录制下来2019年09月24日 14:25作者&#xff1a;黄页编辑&#xff1a;黄页分享电脑如何录屏?当你看到精彩的视频内容时&#xff0c;往往经常会出现&#xff0c;无法下载视频或者是下载很麻烦的问题。那么这个时候&#xff0c;可以…

第九周 10.25-10.31

10.25 HDU 4117 GRE Words 卡了很久的一个题目。比较综合。 看了很久题解还是各种写挫。 毕竟除了模拟题都没敲过那么长的。 题意&#xff1a;按顺序给N个单词&#xff0c;每个单词有权值&#xff0c;删去其中任意单词&#xff0c;使得前面的单词为后面单词的子串&#xff0c;求…

芯片设计中的latch_Latch-up (闩锁效应)

------------------------------------WeChat subscription account&#xff1a;Analog CMOS------------------------------------每周分享Analog IC学习资料/笔记。关注获取。。。。。00 - 本文内容CMOS 中的闩锁效应&#xff08;latch-up&#xff09;的来源具体的避免 latch…