
Python里有一句广为流传的话叫“Everything is object, except keywords”。就如同字面意思,抛去诸如if else之类的关键字,其余的东西皆是对象。我也一直比较好奇Python的对象是怎么样存在于内存的,于是就查看相关书籍和源码,在此做一下笔记。
PyObject
// file:object.h
/* PyObject_HEAD defines the initial segment of every PyObject. */
/* HEAD_EXTRA是Python Debug 模式下使用的,不影响我们理解 */
/* ob_refcnt 是Python耳熟能详的引用计数 */
/* ob_type指针指向一个 类型 变量,这个变量表明了当前Object在Python里是什么类型 */
#define PyObject_HEAD
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
/* Nothing is actually declared to be a PyObject, but every pointer to
* a Python object can be cast to a PyObject*. This is inheritance built
* by hand. Similarly every pointer to a variable-size Python object can,
* in addition, be cast to PyVarObject*.
*/
typedef struct _object {
PyObject_HEAD
} PyObject;从上方官方注释理解一下,就是说这个PyObject是在C实现中所有Object的基类(不过基类这种解释并不准确,这是纯C语言)。要注意的是,它并不是我们在python中的object类,因此,我们在Python中是找不到与之对应的对象的。
英文注释中写了很重要的一句:This is inheritance built by hand。就是说其他Python对象是可以继承这个PyObject的,但是这又是C,因此需要Python开发者手动操作。
那Python源码是如何实现类继承的效果呢?我们写一段C看看。
struct A {
int a = 1;
int b = 1;
};
struct B
{
int a = 2;
int b = 2;
int c = 1;
};
int main()
{
A a;
B b;
A* pa = &a;
B* pb = &b;
pa = (A*)pb;
printf("a=%d, b=%d", pa->a, pa->b);
}
/*
output:a=2, b=2
*/我把声明为B类型的指针强制转换成A类型的指针,这时候就可以用A的指针去访问B结构体了,这就跟B继承了A似的。
从底层来说,A类型指针不过是告诉编译器pa->a要从这个结构体所在内存的第1~4个字节(32位)去读数据而已。即使B结构体比A结构体本身多出一个int,也不妨碍。
但是,这样的操作的安全性需要完全由开发者自己去保证。比如下面这段代码就会出现意想不到的结果(我把结构体B中a的类型改成了float)。
struct A {
int a = 1;
int b = 1;
};
struct B
{
float a = 2.0;
int b = 2;
int c = 1;
};
int main()
{
A a;
B b;
A* pa = &a;
B* pb = &b;
pa = (A*)pb;
printf("a=%d, b=%d", pa->a, pa->b);
}
/*
output:a=1073741824, b=2
*/再多说一句,这种方式是不支持隐式类型转换的。比如下面这样。
int main()
{
A a;
B b;
A* pa = &a;
B* pb = &b;
pa = pb; // 这里不能通过编译
}那么函数呢?如何实现类似C++的多态呢?Python源码中,并不会直接在结构体中定义一个函数,而是声明一个函数指针,函数的定义在外部,通过函数指针来实现多态,这就很C++了。比如下面这段代码。
#include <stdio.h>
void FuncA() {
printf("AAA");
}
void FuncB() {
printf("BBB");
}
struct A {
int a = 1;
int b = 1;
void (*p_func)(void) = &FuncA;
};
struct B
{
int a = 2.0;
int b = 2;
void(*p_func)(void) = &FuncB;
int c = 1;
};
int main()
{
A a;
B b;
A* pa = &a;
B* pb = &b;
pa = (A*)pb;
pa->p_func();
}
/*
output:BBB
*/回到PyObject。有了Python内部实现关于继承与多态的理解之后,PyObject的作用就差不多了,毕竟他所包含的信息非常之少,一个引用计数和一个类型指针,而类型指针,则是一个相当复杂的结构,下一篇再详细研究一波。
PyVarObject
除了PyObject,翻看object.h,还可以看到一个PyVarObject。它的定义如下:
//file:object.h
/*
无非就是比普通的PyObject多一个ob_size。
Py_ssize_t是一个Python定义的宏,它在不同环境下
会是不同的内容。按其语义来说就是ob_size是专门用来记数的。
不深究就理解为unsigned int就好,深究就展开宏研究研究。
*/
#define PyObject_VAR_HEAD
PyObject_HEAD
Py_ssize_t ob_size; /* Number of items in variable part */
typedef struct {
PyObject_VAR_HEAD
} PyVarObject;PyVarObject是Python中可变长度的对象的基类,比如python中的list。而PyObject则是定长对象的基类,比如python中的int。
细读PyVarObject的定义可以发现,按照上面记录的手动继承逻辑,PyVarObject就是PyObject的子类。