Python基础 | 1.Python对象初探
文章内容部分引自:陈儒——《Python源码剖析》
https://github.com/python/cpython/blob/master/Include/object.h
https://docs.python.org/3/c-api/typeobj.html
一、简述Python中的对象
在学习Python时,脑子里一定要有清晰的概念,那就是————在Python世界中,一切都是对象。
1.在Python世界中,一切皆对象。
- 对象是Python中最核心的一个概念,在Python的世界中,一切都是对象。
- 一个整数是一个对象,一个字符串是一个对象,等等;
- 更为奇妙的是,整数类型是一个对象,字符串类型也是一个对象。
- 换句话说,面向对象理论中的“类”和“对象”这两个概念在Python中都是通过Python内的对象来实现的。
2.内建类型对象
- 在Python中,已经预先定义了一些类型对象。
- 比如int类型、string类型、dict类型等等。
- 上述这些类型对象实现了面向对象中“类”的概念,这些内建类型对象通过“实例化”,可以创建内建类型对象的实例对象,比如int对象、string对象、dict对象等。
- 我的理解:
number = 1000
- 这句代码是对 int类型对象(类) 的实例化,创建了一个 int对象,这个 int对象 存在于申请的内存中,而 变量number 是这个 int对象(值为1000) 的引用。
- 图片形式展示为:
3.自定义类型对象
- Python允许程序员通过 class A(object) 这样的表达式自己定义类型对象。
- 这样的类型对象,同样可以实例化操作,创建的对象叫作“实例对象”。
- Python中有着许多千差万别的对象,这些对象之间又存在着各种复杂的关系,从而构成了我们称之为“类型系统”或“对象体系”的东西。
二、Python对象初探
1.对象机制的基石——PyObject
- 在Python中,一切都是对象,而所有的对象都有相同的东西,这些内容在PyObject中定义,因此PyObject是整个Python对象机制的核心。
- 我们可以理解为所有的类都是继承于 基类PyObject
[object.h] typedef struct _object { _PyObject_HEAD_EXTRA //详细介绍在下述 a.关于 _PyObject_HEAD_EXTRA Py_ssize_t ob_refcnt; //详细介绍在下述 b.关于 Py_ssize_t ob_refcnt PyTypeObject *ob_type; //详细介绍在下述 c.关于 PyTypeObject *ob_type } PyObject;
- 现在我们来分析PyObject:
- a.关于_PyObject_HEAD_EXTRA
/* 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;
- 环状双向链表 refchain,在python程序中创建的任何对象都会放在refchain链表中。( _ob_next = refchain中的上一个对象;_ob_prev = refchain中的下一个对象)
- _PyObject_HEAD_EXTRA这个宏的作用仅仅是在DEBUG模式下,将全部的对象使用双链表链起来方便DEBUG而已。因此暂忽略不考虑。(下划线开头的一般内部使用)
- b.关于Py_ssize_t ob_refcnt
- 在Python2.5中,这段是 int ob_refcnt,因此我们认为Py_ssize_t和int效果是一样的,那么为什么使用Py_ssize_t呢???
- Python在后来的版本中创建了 Py_ssize_t 类型用作索引的类型,它是为了容纳64位体系结构。(这也是与int的不同,64位系统的计算机越来越多)
- 也就是说在64位系统中, 它是64位int整型, 32位系统就是int. _W64,是为了兼容64位系统存在的。 所以 Py_ssize_t 的本质就是 int.
- 也就可以认为ob_refcnt 是一个 整型变量。用作 引用计数 的计数器。引用计数与Python的垃圾收集机制有关。
- c.关于PyTypeObject *ob_type
- ob_type 是一个指向 PyTypeObject 的指针,用来指定一个对象类型的类型对象。
- 也就是说,表示了对象的类型信息, 诸如int, string, function等。
- d.因此,Python 的对象机制十分简单,就是 引用计数 + 类型信息。
- a.关于_PyObject_HEAD_EXTRA
- 每一个Python的对象除了必须有PyObject这个内容以外,还要有一些额外的东西,也就是每个对象独特的东西,这样才能使得Python有许多个千差万别的对象。
2.定长对象和变长对象
(1)定长对象与变长对象的直观理解
- a.定长对象与变长对象的含义
- 定长对象:这类对象在内存中的占用大小相同。例如:数字1和数字1000在内存中的占用大小是相同的,都是28字节。
- 变长对象:这类对象在内存中的占用大小不相同。例如:字符串'a'和字符串'ab'和字符串'abc'在内存中的占用全部是不能相同的。
- b.定长对象和变长对象的例子
- 定长对象(以int类型为例)
import sys a = 1 b = 1000 c = 2 ** 30 - 1 print('the memory size of a is:',sys.getsizeof(a),'byte') print('the memory size of b is:',sys.getsizeof(b),'byte') print('the memory size of c is:',sys.getsizeof(c),'byte') # 输出结果为 the memory size of a is: 28 byte the memory size of b is: 28 byte the memory size of c is: 28 byte
- 变长对象(以str类型为例)
import sys a = 'a' ab = 'ab' abc = 'abc' print('the memory size of a is:',sys.getsizeof(a),'byte') print('the memory size of ab is:',sys.getsizeof(ab),'byte') print('the memory size of abc is:',sys.getsizeof(abc),'byte') # 输出结果为 the memory size of a is: 50 byte the memory size of ab is: 51 byte the memory size of abc is: 52 byte
- 定长对象(以int类型为例)
(2)定长对象和变长对象的结构体
- a.定长对象的结构体就是 基类PyObject
- 上面已经详细介绍过,不再介绍
- b.变长对象的结构体——PyVarObject
- PyVarObject的结构体
[object.h] typedef struct { PyObject ob_base; Py_ssize_t ob_size; /* Number of items in variable part */ } PyVarObject;
- 可以看到,PyVarObject继承了基类PyObject,并添加了 Py_ssize_t ob_size
- 关于Py_ssize_t上面已经介绍过,不再介绍。简单理解为就是 int
- 变长对象通常都是容器,ob_size这个成员实际上指明了变长对象中一个容纳了多少个元素。【特别注意:是元素的个数,而不是字节数】
- PyVarObject的结构体
- c.PyVarObject 实际上只是对 PyObject 的一个扩展而已,因此,对于任何一个 PyVarObject,其所占的内存,开始部分的字节的意义和 PyObject 是一样的。换句话说,在 Python 内部,每一个对象都拥有相同的对象头部。这就使得在 Python 中,对对象的引用变得非常的统一,我们只需要用一个 PyObject* 指针就可以引用任意的一个对象。而不论该对象是一个什么对象。
(3)定长对象和变长对象在内存布局上的关系
- 从图片中反应了上述内容:
- 在Python中,每一个对象都拥有相同的对象头部(蓝色部分)
- PyVarObject是对PyObject的扩展,多了ob_size(绿色部分)
- 定长对象的Other是固定的(某一范围内,如int在每 230内相同),变长对象的Other只要ob_size(即元素个数)不相同,大小就不相同。(灰色部分)
3.类型对象
前面已讲:Python 的对象机制十分简单,就是 引用计数 + 类型信息。
a.类型对象 _typeobject
- 当在内存中分配空间、创建对象的时候,毫无疑问我们需要知道申请多大的空间,显然这不是一个定值。因为不同的对象需要不同的空间,那么,对象所需的内存空间的大小的信息在哪里呢?
- 对象所需的内存空间的大小信息,不明显的存在与PyObject的定义中,而是隐身于PyObject中。
- 实际上,占用内存空间的大小是对象的一种元信息,这样的元信息是与对象所属类型息息相关的,因为它一定会出现在与对象所对应的类型对象中。
- 类型对象 _typeobject 的源代码:
[object.h] 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; Py_ssize_t tp_vectorcall_offset; 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 */ /* call function for all accessible objects */ traverseproc tp_traverse; /* delete references to contained objects */ inquiry tp_clear; /* 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; } PyTypeObject;
- 可能你看不懂,我现在的水平也tm看不懂。但是当前阶段可以知道分为哪些组成部分。官网翻译
- 类型名 tp_name
- 创建该类型对象时分配内存空间的大小信息 tp_basicsize, tp_itemsize
- 与该类型对象相关联的操作信息
- 类型的类型信息
- 可能你看不懂,我现在的水平也tm看不懂。但是当前阶段可以知道分为哪些组成部分。官网翻译
4.类型的类型
仔细观察PyTypeObject,发现在其定义的最开始,有PyObject_VAR_HEAD,这意味着Python中的类型实际上也是一种对象。那么问题来了,类型对象的类型是什么呢?对于其他对象,可以通过与其关联的类型对象确定其类型,那么通过什么来确定一个对象是类型对象呢?————答案:PyType_Type。
(1)PyType_Type源码
[typeobject.c]
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 */
offsetof(PyTypeObject, tp_vectorcall), /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
(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 |
Py_TPFLAGS_HAVE_VECTORCALL, /* 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 */
};
(2)PyTypeObject与PyType_Type的关系
PyType_Type 在 Python 的类型机制中是一个至关重要的对象,所有用户自定义 class 所对应的 PyTypeObject 对象都是通过这个对象创建的。
- 一般PyTypeObject 与 PyType_Type 的关系
>>> class Dog(object): ... pass ... >>> Dog.__class__ <class 'type'> >>> type.__class__ <class 'type'> >>>
- 上面的输出 <class 'type'> 就是Python内部的 PyType_Type ,它是所有 class 的 class,所以它在Python 中被称为 metaclass。