python源码剖析读书笔记总结_《Python源码剖析》 第一章 读书笔记

Python内建对象

对象是数据以及基于这些数据的操作的集合,在Python中,对象就是为C中的结构体在堆上申请的一块内存。

在Python中,一个对象一旦被创建,它在内存中的大小就是不变的了,那些需要容纳可变长度数据的对象只能在对象内维护一个指向一块可变大小的内存区域的指针。

1.1.1 对象机制的基石 -- PyObject

[object.h]

typedef struct _object{

int ob_refcnt; /* 引用计数器 */

struct _typeobject *ob_type; /* _typeobject这个结构体用来指定一个对象类型的类型对象 */

} PyObject

在Python中,对象机制的核心非常简单,一个是引用计数,一个是类型信息

在PyObject中定义了每一个Python对象都必须有的内容,除了PyObject以外,还应该有一些额外的内存,放置其他信息。

1.1.2 定长对象和变长对象

Python整数对象PyIntObject

typedef struct{

PyObject_HEAD

long ob_ival;

} PyIntObject

整数对象的特殊信息是一个C中的整形变量,无论这个整数对象的值有多大,都可以保存在这个整形变量中。

变长对象PyVarObject

#define PyObject_VAR_HEAD \

PyObject_HEAD \

int ob_size; /* 容器中元素的数量 */

typedef struct{

PyObject_VAR_HEAD

} PyVarObject;

我们把整形对象这样不包含可变长度数据的对象称为“定长对象”,而字符串对象这样包含可变长度数据的对象称为“变长对象”,它们的区别在于定长对象的不同对象占用内存大小是一样的,而变长对象的不同对象占用内存可能不一样。

PyVarObject只是对PyObject的一个扩展而已,在Python内部,每一个对象都拥有相同的对象头部。

1.2 类型对象

创建对象的时候,必须知道要申请多大的空间,所需空间大小是对象的一种元信息,这样的元信息与对象所属类型密切相关。我们考察一下类型对象_typeobject:

typedef struct _typeobject {

PyObject_VAR_HEAD

char *tp_name; /* tp_name 类型名 */

int tp_basicsize, tp_itemsize; /* 创建该类型对象时分配内存空间的大小 */

/* 与对象关联的操作信息 */

destructor tp_dealloc;

printfunc tp_print;

/* 更多操作信息 */

hashfunc tp_hash;

ternaryfunc tp_call;

...

} PyTypeObject;

一个PyTypeObject就是Python中面向对象理论中“类”这个概念的实现

1.2.1 对象的创建

Python创建对象的方法:

通过Python C API来创建

通过类型对象 PyInt_Type来创建

Python对外提供了C API让用户可以从C环境中与Python交互,Python的C API分为两类:

范型API,或称为AOL

另一类称为COL

从PyInt_Type创建整数对象

说明: 在Python完成运行环境的初始化后,符号'int'就对应着Python内部的PyInt_Type对象,object则对应着PyBaseObject_Type对象

使用init(10)创建对象时,就会调用PyInt_Type对象中的tp_new操作

假如这一个tp_new操作不存在,为NULL,那么就到tp_base指定的基类里去找tp_new操作

在Python2.2以后的新式类中,所有的类都是以object为基类的,所以最终一定可以找到一个不为NULL的tp_new

tp_new指向了object_new,object_new会访问PyInt_Type中记录的tp_basicsize信息,得到需要申请的内存大小,然后完成申请内存的操作。我们前面说了,“在Python中,对象就是为C中的结构体在堆上申请的一块内存”,所以内存申请完成就代表对象已经创建(但还未初始化)

tp_new创建完对象后,流程转向PyInt_Type的tp_init,完成初始化工作

当我们用int(10)创建整数对象10时,首先PyInt_Type中的tp_new会被调用,如果这个tp_new为NULL,那么会到tp_base指定的基类中去寻找tp_new操作

1.2.2 对象的行为

在PyTypeObject中定义了大量的函数指针,这些函数都最终会指向某个函数,或者指向NULL,这些函数指针可以视为类型对象中所定义的操作,而这些操作直接决定着一个对象在运行时所表现出的行为。

比如PyTypeObject中的tp_hash是一个函数指针(hashfunc)类型的变量,tp_hash指明对于该类型的对象如何生成其hash值。

在PyTypeObject中指定的不同的操作信息也正是一种对象区别于另一种对象的关键所在。

在这些操作族中,有三组非常重要:

tp_as_number

指向PyNumberMethods函数族,该族定义了一个数值对象应该支持的操作,典型对象如int,tp_as_number.nb_add指定了

对该对象进行加法操作时的具体行为。

tp_as_sequence

指向PySequenceMethods函数族,该族定义了作为一个序列对象应该支持的操作,典型对象list.

tp_as_mapping

指向PyMappingMethods函数族,该族定义了作为一个关联对象应该支持的操作,典型对象dict。

对一种类型来说,它完全可以同时定义三个函数族中的所有操作,只要定义相应的special_method

1.2.3 类型的类型

在Python中类型其实也是一个对象,类型的类对应Python内部的PyType_Type,它是所有class的class,在Python中称为metaclass,元类。

#define PyObject_HEAD_INIT(type) \

_PyObject_EXTRA_INIT \

1, type

PyType_Type 元类

PyTypeObject PyType_Type = {

PyObject_HEAD_INIT(&PyType_Type)

0, /* ob_size */

"type", /* tp_name */

sizeof(PyHeapTypeObject), /* tp_basicsize */

sizeof(PyMemberDef), /* tp_itemsize */

……

};

宏展开后

PyTypeObject PyType_Type = {

_PyObject_EXTRA_INIT \

1, /* ob_refcnt,引用计数置为1 */

&PyType_Type, /* 类型仍然为PyType_Type,即元类的类型还是元类 */

0, /* 元素个数 */

"type", /* 类型名称 */

sizeof(PyHeapTypeObject), /* tp_basicsize */

sizeof(PyMemberDef), /* tp_itemsize */

……

};

#### 番外篇: #define学习

\反斜杠把该定义延续到下一行,编译器将\分成的多个物理行转换为一个逻辑行,并删除\符号

#define预处理器指令以#号作为一行的开始,指令可以出现在源文件的任何地方,其定义从指令出现的地方到该文件末尾有效。

每行#define(逻辑行)都由3部分组成:

第1部分是#define指令本身

第2部分是选定的缩写,也称为宏,有些宏代表值,这些宏被称为类对象宏

第3部分称为替换列表或替换体

从宏变成最终替换文本的过程称为宏展开

在#define中使用参数可以创建外形和作用与函数类似的类函数宏

整理各对象定义以及关系

PyObject

typedef struct _object {

PyObject_HEAD

} PyObject;

PyObject是一个名为_object的结构体数据类型,结构成员为PyObject_HEAD宏

预处理

#define _PyObject_HEAD_EXTRA /* 未定义替换体的宏 */

#define _PyObject_EXTRA_INIT

PyObject_HEAD

#define PyObject_HEAD \

_PyObject_HEAD_EXTRA \

int ob_refcnt; \ /* 引用计数值 */

struct _typeobject *ob_type; /* 类型指针 */

PyObject_HEAD是一个宏,指明了引用计数变量_ob_refcnt和类型结构_typeobject及其指针*ob_type

宏转换后的PyObject:

typedef struct _object {

_PyObject_HEAD_EXTRA \ /* 在预处理中找 */

int ob_refcnt; \

struct _typeobject *ob_type;

} PyObject;

整型对象PyIntObject

typedef struct {

PyObject_HEAD /* 和基础PyObject一样 */

long ob_ival; /* 多了一个长整型值,储存int对象的值 */

} PyIntObject;

完成宏替换

typedef struct {

_PyObject_HEAD_EXTRA \

int ob_refcnt; \ /* 引用计数器 */

struct _typeobject *ob_type; /* 类型指针 */

long ob_ival; /* 多了一个长整型值,储存int对象的值 */

} PyIntObject;

可以看出PyIntObject对比PyObject多了一个 ob_ival,它代表了整形对象值的大小

变长对象PyVarObject

#define PyObject_VAR_HEAD \

PyObject_HEAD \

int ob_size; /* 序列对象的元素个数 */

typedef struct {

PyObject_VAR_HEAD

} PyVarObject;

转换一下可得

typedef struct {

PyObject_HEAD \

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

} PyVarObject;

再转换一次

typedef struct {

_PyObject_HEAD_EXTRA \

int ob_refcnt; \ /* 引用计数器 */

struct _typeobject *ob_type; /* 类型指针 */

int ob_size; /* 序列对象的元素个数 */

} PyVarObject;

可以看出PyVarObject对比PyObject多了一个 ob_size结构成员,它代表了序列对象里元素的个数

小结1:

在Python中所有对象都从基础对象PyObject发展而来,定长对象多了一个ob_ival表示具体值,变长对象多了一个ob_size表示元素个数。

我们接着考察Python对象的类型定义

回顾PyObject_HEAD,ob_refcnt代表引用计数值,_typeobject结构体代表对象类型,所以应该考察_typeobject

_typeobject

typedef struct _typeobject {

PyObject_VAR_HEAD

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

int tp_basicsize, tp_itemsize; /* For allocation */

/* Methods to implement standard operations */

destructor tp_dealloc;

printfunc tp_print;

……

/* More standard operations (here for binary compatibility) */

hashfunc tp_hash;

ternaryfunc tp_call;

……

} PyTypeObject;

进行完全的宏替换可得

typedef struct _typeobject {

_PyObject_HEAD_EXTRA \

int ob_refcnt; \ /* 引用计数值 */

struct _typeobject *ob_type; /* 类型指针 */

int ob_size; /* 序列对象的元素个数 */

char *tp_name; /* 类型名,可以通过.打印出来 */

int tp_basicsize, tp_itemsize; /* 创建该类型对象时分配内存空间的大小信息 */

/* 与该类型对象相关联的操作信息 */

destructor tp_dealloc;

printfunc tp_print;

……

/* More standard operations (here for binary compatibility) */

hashfunc tp_hash;

ternaryfunc tp_call;

……

} PyTypeObject;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值