万物皆对象

对象分类

  • Fundamental对象
    类型对象

    1
    type
  • Numeric对象
    数值对象

    1
    2
    3
    integer
    float
    boolean
  • Sequence对象
    容纳其他对象的序列集合对象

    1
    2
    3
    4
    list
    tuple
    string
    set
  • Mapping对象
    类似 C++中的 map 的关联对象

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

    1
    2
    3
    4
    5
    function
    code
    frame
    module
    method

对象三要素

对象三要素:

  • 双向链表
  • 引用计数
  • 类型对象指针

PyObject定义如下

1
2
3
4
5
6
7
8
9
10
// Include/object.h
#define _PyObject_HEAD_EXTRA \
struct _object *_ob_next; \
struct _object *_ob_prev;

typedef struct _object {
_PyObject_HEAD_EXTRA // 双向链表 垃圾回收 需要用到
Py_ssize_t ob_refcnt; // 引用计数
struct _typeobject *ob_type; // 指向类型对象的指针,决定了对象的类型
} PyObject;

定长与变长对象

1
2
3
4
5
6
7
8
9
10
11
12
// Include/object.h
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;

typedef struct {
PyObject ob_base;
Py_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;
/* PyVarObject比PyObject多出了一个用于存储元素个数的变量ob_size */

类型对象

PyObject 的 对象类型指针struct _typeobject *ob_type,
它指向的类型对象就决定了一个对象是什么类型的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
typedef struct _typeobject {
PyObject_VAR_HEAD
const char *tp_name; /* For printing, in format "<module>.<name>" */ // 类型名
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;
......
} PyTypeObject;

Python 中的所有对象都拥有一些相同的内容,而这些内容就定义在PyObject中。

类型对象创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Include/object.h
#ifdef Py_TRACE_REFS
#define _PyObject_EXTRA_INIT 0, 0,
#else
#define _PyObject_EXTRA_INIT
#endif

#define PyObject_HEAD_INIT(type) \
{ _PyObject_EXTRA_INIT \
1, type },

// Objects/longobject.c
static PyNumberMethods long_as_number = {
(binaryfunc)long_add, /*nb_add*/
(binaryfunc)long_sub, /*nb_subtract*/
(binaryfunc)long_mul, /*nb_multiply*/

......
};

PyTypeObject PyLong_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"int", /* tp_name */
offsetof(PyLongObject, ob_digit), /* tp_basicsize */
sizeof(digit), /* tp_itemsize */
long_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
long_to_decimal_string, /* tp_repr */
&long_as_number, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */

......
};

对象创建

  • 1.这类 API 通常形如PyObject_XXX这样的形式。可以应用在任何 Python 对象上,
    如PyObject_New。创建一个整数对象的方式
    1
    PyObject* longobj = PyObject_New(Pyobject, &PyLong_Type);
  • 2.与类型相关的 API 或称为 COL (Concrete Object Layer)
    这类 API 通常只能作用于某一种类型的对象上,对于每一种内建对象
    Python 都提供了这样一组 API。例如整数对象,我们可以利用如下的 API 创建
    1
    PyObject *longObj = PyLong_FromLong(10);

对象的多态性

Python 创建一个对象比如 PyLongObject 时,会分配内存进行初始化,
然后 Python 内部会用 PyObject* 变量来维护这个对象,其他对象也与此类似
所以在 Python 内部各个函数之间传递的都是一种范型指针 PyObject* 我们不
知道这个指针所指的对象是什么类型,只能通过所指对象的 ob_type 域 动态进
行判断,而 Python 正是通过 ob_type 实现了多态机制
考虑以下的 calc_hash 函数

1
2
3
4
5
6
Py_hash_t
calc_hash(PyObject* object)
{
Py_hash_t hash = object->ob_type->tp_hash(object);
return hash;
}

如果传递给 calc_hash 函数的指针是一个 PyLongObject*,那么它会调用 PyLongObject 对象对应的类型对象中定义的 hash 操作tp_hash,tp_hash可以在PyTypeObject中找到, 而具体赋值绑定我们可以在 PyLong_Type 初始化代码中看到绑定的是long_hash函数

1
2
3
4
5
6
7
8
9
10
// Objects/longobject.c
PyTypeObject PyLong_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"int", /* tp_name */
...

(hashfunc)long_hash, /* tp_hash */

...
};

如果指针是一个 PyUnicodeObject*,那么就会调用 PyUnicodeObject 对象对应的类型对象中定义的 hash 操作,查看源码可以看到 实际绑定的是 unicode_hash函数

1
2
3
4
5
6
7
8
9
10
11
// Objects/unicodeobject.c
PyTypeObject PyUnicode_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"str", /* tp_name */

...

(hashfunc) unicode_hash, /* tp_hash*/

...
};

引用计数

Python 通过引用计数来管理维护对象在内存中的存在与否
Python 中的每个东西都是一个对象, 都有ob_refcnt 变量,
这个变量维护对象的引用计数,从而最终决定该对象的创建与销毁
在 Python 中,主要通过 Py_INCREF(op)与Py_DECREF(op) 这两个宏
来增加和减少对一个对象的引用计数。当一个对象的引用计数减少到 0 之后,
Py_DECREF将调用该对象的tp_dealloc来释放对象所占用的内存和系统资源;
但这并不意味着最终一定会调用 free 释放内存空间。因为频繁的申请、释放
内存会大大降低 Python 的执行效率。因此 Python 中大量采用了内存对象
池的技术,使得对象释放的空间归还给内存池而不是直接free,后续使用可先
从对象池中获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 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)
分享到