python源码剖析类机制_Python源码剖析阅读笔记 :第一章:Python对象初探

Python对象初探

对象是python中的核心概念,Python中一切皆对象。一种内置数据类型也是一种对象,我们称之为内置类型对象(int,string,list等)。这些类型对象实现了面向对象中‘类’的概念。使用时,就可以实例化出这些内置类型对象。

第一章只需要了解python对象是如何被c实现的。python对象整个体系在第二章会有完整讲解。

1.1python内的对象

在python中,对象就是为C中的结构体在堆上申请的一块内存,一般来说,对象是不能被静态初始化的,并且也不能在栈空间上生存。唯一的例外就是类型对象,Python中所有的内建的类型对象都是被静态初始化的。

python对象创建后,内存中的大小是不变的。如果那些需要容纳可变长度数据的对象(list,dict等可变类型的实例化对象)

,只能维护一个指向一块可变大小的内存区域的指针。这样会使维护对象的工作变得简单。从对数据的操作变成了对指针的操作,工作由繁化简了。

1.1.1对象机制的基石——PyObject

在python中,所有的东西都是对象,而所有的对象都拥有一些相同的内容,这些内容在pyobject中定义。

[object.h]

typedef struct_object{

PyObject_HEAD

};

这个struct就是对象机制中的基石,从代码中可以看到,Python对象的秘密都隐藏在PyObject_HEAD这个宏中。

#ifdef Py_TRACE_REFS

/* Define pointers to support a doubly-linked list of all live heap objects. */

#define _PyObject_HEAD_EXTRA \

struct _object *_ob_next; \

struct _object *_ob_prev;

#define _PyObject_EXTRA_INIT 0, 0,

#else

#define _PyObject_HEAD_EXTRA

#define _PyObject_EXTRA_INIT

#endif

实际发布python中,PyObject的定义非常简单:

typedef struct _object {

_PyObject_HEAD_EXTRA

Py_ssize_t ob_refcnt;

struct _typeobject *ob_type;

} PyObject;

ob_refcnt是与python的内存管理机制有关,它实现了基于引用计数的垃圾收集机制。另一个是指向结构体的指针*ob_type,用来说明创建的对象是什么类型的一个类型对象。

定长对象和变长对象

整数对象的特殊信息是一个C中的整型变量,无论这个整数对象的值有多大,都可以保存在这个整形变量(ob_ival)中。但是并不是所有对象都可以这么定义。比如字符串。所以需要添加‘变量的个数’这一变量(ob_size)来控制个数。

typedef struct {

PyObject ob_base;

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

} PyVarObject;

我们把整数对象这样不包含可变长度数据的对象成为‘定长对象’,而字符串对象这样包含可变长度数据的对象成为‘变长对象’。在python内部,每一个对象都拥有相同的对象头部。这就使得在python中,对对象的引用变得非常统一,我们只需要一个PyObject*指针就可以引用任意一个对象。

类型对象

上面大概的表述,python所有对象共有信息的定义。python对象的主要部分。当内存中分配空间,创建对象的时候,必须要知道申请多大的空间。然而它是不确定的。

typedef struct _typeobject {

PyObject_VAR_HEAD

const char *tp_name; /* For printing, in format "." 调试时使用输出格式. */

Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation python对象创建时内存大小的信息 */

....

1.2.1对象的创建

有两种方式来创建python对象,第一种:python C API来创建,第二种:类型对象PYInet_Type。

Python的C API分成两类,一类成为范型的API,或者称为AOL(abstract object layer)。这类API都形如:PyObject_***。可以应用任何python对象身上。对于创建一个整数对象,我们可以采用如下表达式:PyObject* intObj = PyObject_New(PyObject, &PyInt_Type)。

另一类是与类型相关的API,COL(Concrete Object layer)。这类通常只能作用在某一种类型的对象上,对于每一种内建对象,都会有对应的API。

例子:PyObject *intObj = PyInt_FromLong(10)创建了一个整数为10的变量。

1.2.2对象的行为

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

#ifdef Py_LIMITED_API

typedef struct _typeobject PyTypeObject; /* opaque */

#else

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;

PyAsyncMethods *tp_as_async; /* formerly known as tp_compare (Python 2)

or tp_reserved (Python 3) */

reprfunc tp_repr;

/* Method suites for standard classes */

PyNumberMethods *tp_as_number;

PySequenceMethods *tp_as_sequence;

PyMappingMethods *tp_as_mapping;

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

hashfunc tp_hash;

ternaryfunc tp_call;

reprfunc tp_str;

getattrofunc tp_getattro;

setattrofunc tp_setattro;

/* Functions to access object as input/output buffer */

PyBufferProcs *tp_as_buffer;

/* Flags to define presence of optional/expanded features */

unsigned long tp_flags;

const char *tp_doc; /* Documentation string */

/* Assigned meaning in release 2.0 */

/* call function for all accessible objects */

traverseproc tp_traverse;

/* delete references to contained objects */

inquiry tp_clear;

/* Assigned meaning in release 2.1 */

/* rich comparisons */

richcmpfunc tp_richcompare;

/* weak reference enabler */

Py_ssize_t tp_weaklistoffset;

/* Iterators */

getiterfunc tp_iter;

iternextfunc tp_iternext;

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

/* Type attribute cache version tag. Added in version 2.6 */

unsigned int tp_version_tag;

destructor tp_finalize;

#ifdef COUNT_ALLOCS

/* these must be last and never explicitly initialized */

Py_ssize_t tp_allocs;

Py_ssize_t tp_frees;

Py_ssize_t tp_maxalloc;

struct _typeobject *tp_prev;

struct _typeobject *tp_next;

#endif

} PyTypeObject;

在这些操作信息中,有三组非常重要的操作族,他们是

/* Method suites for standard classes */

PyNumberMethods *tp_as_number; /*数值对象 */

PySequenceMethods *tp_as_sequence; /*序列对象 */

PyMappingMethods *tp_as_mapping; /* 关联对象*/

对于一种类型来说,它完全可以同时定义三个函数中的所有操作。换句话说,一个对象可以即表现出数值对象的特性,也可以表现出关联对象的特性。举个例子:

class MyInt(int):

def __getitem__(self, key):

return key + str(self)

a = MyInt(1)

b = MyInt(2)

print(a + b)

print(a['key'])

结果:

1229991-20200627131011946-1635154473.png

看上去a['key']这样的操作时一个类似于dict这样的对象才会支持的操作。从int继承出来的Myint应该自然就是一个数值对象,重写上面的方法之后,可以视为指定MyInt在Python内部对应的PyTypeObject对象的tp_as_mapping.mp_subscript操作。最终这个Myint的实例对象可以“表现”得像一个关联对象。本质上就是在于PyTypeObject中允许一种类型同事指定三种不同行为的对象。

1.2.3 类型的类型

PyTypeObject中,定义的最开始,可以发现PyObject_VAR_HEAD,这意味着Python中的类型实际上也是一种对象。每一个对象都是对应一种类型的。而类型对象是什么类型呢?对于其他对象,可以通过与其关联的类型对象确定其类型,那么通过什么来确定一个对象是类型对象呢?

答案就是PyType_Type:

PyTypeObject PyType_Type = {

PyVarObject_HEAD_INIT(&PyType_Type, 0)

"type", /* tp_name */

sizeof(PyHeapTypeObject), /* tp_basicsize */

sizeof(PyMemberDef), /* tp_itemsize */

(destructor)type_dealloc, /* tp_dealloc */

0, /* tp_print */

0, /* tp_getattr */

0, /* tp_setattr */

0, /* tp_reserved */

(reprfunc)type_repr, /* tp_repr */

0, /* tp_as_number */

0, /* tp_as_sequence */

0, /* tp_as_mapping */

0, /* tp_hash */

(ternaryfunc)type_call, /* tp_call */

0, /* tp_str */

(getattrofunc)type_getattro, /* tp_getattro */

(setattrofunc)type_setattro, /* tp_setattro */

0, /* tp_as_buffer */

Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |

Py_TPFLAGS_BASETYPE | Py_TPFLAGS_TYPE_SUBCLASS, /* tp_flags */

type_doc, /* tp_doc */

(traverseproc)type_traverse, /* tp_traverse */

(inquiry)type_clear, /* tp_clear */

0, /* tp_richcompare */

offsetof(PyTypeObject, tp_weaklist), /* tp_weaklistoffset */

0, /* tp_iter */

0, /* tp_iternext */

type_methods, /* tp_methods */

type_members, /* tp_members */

type_getsets, /* tp_getset */

0, /* tp_base */

0, /* tp_dict */

0, /* tp_descr_get */

0, /* tp_descr_set */

offsetof(PyTypeObject, tp_dict), /* tp_dictoffset */

type_init, /* tp_init */

0, /* tp_alloc */

type_new, /* tp_new */

PyObject_GC_Del, /* tp_free */

(inquiry)type_is_gc, /* tp_is_gc */

};

PyType_Type在python的类型机制中是一个至关重要的对象,所有用户自定义的class所对应的PyTypeObject对象都是通过这个对象创建的。图中显示了一般的PyType_Object和PyType_Type的关系:

1229991-20200627194236447-2070084367.png

是python内部的PyType_Type,它是所有class的class,所以它在Python中被称为metaclass。

Py-nt_Type是怎么和PyType_Type联系上的呢。前面提到,python中,每一个对象都将自己的引用计数、类型信息保存在开始的部分中。为了方便对内存的初始化,python提供了几个宏。

1229991-20200627202845711-2002906374.png

1.3 Python对象的多态性

Python 创建一个对象比如PyLongObject 时,会分配内存进行初始化,然后 Python 内部会用 PyObject* 变量来维护这个对象,其他对象也与此类似

所以在 Python 内部各个函数之间传递的都是一种范型指针 PyObject* 我们不知道这个指针所指的对象是什么类型,只能通过所指对象的 ob_type 域 动态进行判断,而 Python 正是通过 ob_type 实现了多态机制

比如:

void Print(PyObject* object)

{

object->ob_type->tp_print(object);

}

传进来的指针可以是int型也可以是string类型,他们都会调用他们相应的类型对象中定义的输出操作。这就是多态性的展现。

1.4引用计数

Python 通过引用计数来管理维护对象在内存中的存在与否

Python 中的每个东西都是一个对象, 都有ob_refcnt 变量,这个变量维护对象的引用计数,从而最终决定该对象的创建与销毁

在 Python 中,主要通过 Py_INCREF(op)与Py_DECREF(op) 这两个宏 来增加和减少对一个对象的引用计数。当一个对象的引用计数减少到 0 之后, Py_DECREF将调用该对象的tp_dealloc来释放对象所占用的内存和系统资源;

但这并不意味着最终一定会调用 free 释放内存空间。因为频繁的申请、释放内存会大大降低 Python 的执行效率。因此 Python 中大量采用了内存对象池的技术,使得对象释放的空间归还给内存池而不是直接free,后续使用可先从对象池中获取

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

#define Py_INCREF(op) ( \

_Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA \

((PyObject *)(op))->ob_refcnt++)

#define Py_DECREF(op) \

do { \

PyObject *_py_decref_tmp = (PyObject *)(op); \

if (_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA \

--(_py_decref_tmp)->ob_refcnt != 0) \

_Py_CHECK_REFCNT(_py_decref_tmp) \

else \

_Py_Dealloc(_py_decref_tmp); \

} while (0)

1.5Python对象的分类

大致分为5类。

Fundanmental对象:类型对象

Numeric对象:数值对象

Sequence对象:容纳其他对象的序列集合对象

Mapping对象:具有映射关系的对象

Internal对象: Python虚拟机在运行时内部使用的对象

1229991-20200627210220804-1033941351.png

这些是对python对象的一些基本了解。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值