在Python3中print是函数
Python --> bltinmodule.c --> builtin_print() line:1850
了解编译python源代码:在PCbuild同目录层次下会生成Lib文件夹,在PCbuild目录下会生成win32文件夹,里面根据你编译的是debug还是release版本,会生成python_d.exe或者python.exe,打开它,就能运行了。
2. Object在Python中,对象就是为C中的结构体在堆上申请一块内存,一般来说是不能被静态初始化的,并且也不能在栈空间上生存。但是Python中的类型对象是例外(相当于类),都是被静态初始化的。
在Python中一个对象一旦被创建,在内存中的大小就是不变的。所以维护容纳一个可变长度数据的对象(如List类型)就必须在对象内维护一个指向一块可变大小的内存区域的指针。
2.1 PyObjectPyObject 是整个 Python 对象机制的核心。
在 Python 的世界一切皆对象,不论是数字,还是字符串,甚至连数据类型、函数等都是一种对象。
// 宏定义:有参宏、无参宏 #define PI 3.14 #define STU struct Student typedef int INT1;
// Include/object.h
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
在PyObject的定义中,变量ob_refcnt与Python的内存管理机制有关,实现了基于引用计数的垃圾收集机制。对象A的引用计数减少至0时,A就可以从堆上被删除以释放内存。
前提:了解类型对象(如List对象)与实例对象的区别
结构体_typeobject指向对象类型的类型对象
// Include/object.h
#ifdef Py_LIMITED_API
typedef struct _typeobject PyTypeObject;
#else
typedef struct _typeobject {
PyObject_VAR_HEAD
const char *tp_name;
Py_ssize_t tp_basicsize, tp_itemsize;
destructor tp_dealloc;
printfunc tp_print;
getattrfunc tp_getattr;
setattrfunc tp_setattr;
PyAsyncMethods *tp_as_async;
reprfunc tp_repr;
PyNumberMethods *tp_as_number;
PySequenceMethods *tp_as_sequence;
PyMappingMethods *tp_as_mapping;
hashfunc tp_hash;
ternaryfunc tp_call;
reprfunc tp_str;
getattrofunc tp_getattro;
setattrofunc tp_setattro;
PyBufferProcs *tp_as_buffer;
unsigned long tp_flags;
const char *tp_doc;
traverseproc tp_traverse;
inquiry tp_clear;
richcmpfunc tp_richcompare;
Py_ssize_t tp_weaklistoffset;
getiterfunc tp_iter;
iternextfunc tp_iternext;
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;
inquiry tp_is_gc;
PyObject *tp_bases;
PyObject *tp_mro;
PyObject *tp_cache;
PyObject *tp_subclasses;
PyObject *tp_weaklist;
destructor tp_del;
unsigned int tp_version_tag;
destructor tp_finalize;
#ifdef COUNT_ALLOCS
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;
#endif
2.2 定长对象与变长对象
// Include/object.h
typedef struct {
PyObject ob_base;
Py_ssize_t ob_size;
} PyVarObject;
各种对象都拥有相同的对象头部,因此只需要一个PyObject *指针就可以引用任意一个对象,而不论该对象实际是一个什么对象。
思考以下两个问题,下一章节将详细解析。
- 怎么理解整型(LongObject)是变长对象?整型在内存中的字节数是多少?
总结:不可变数据类型更改后地址发生改变,可变数据类型更改地址不发生改变
不可变数据:Number, String, Tuple, None
(Number 数据类型用于存储数值。数据类型是不允许改变的,这就意味着如果改变 Number 数据类型的值,将重新分配内存空间。)
可变数据:List, Dictionary, Set
①Number:bool, long, float, complex.
②String:单引号与双引号使用完全相同;使用三引号可以指定一个多行字符串;转义符‘’,但使用r可以让反斜杠不发生转义;字符串可以用+运算符连接在一起,用*运算符重复。
**注:**word[0] = 'm’会导致错误。
③Tuple: 元组中元素类型可以不相同;虽然tuple元素不可改变,但它可以包含可变的对象,比如list列表。
注: 构造包含0个或1个元素的元组(tuple(),tuple(20,)),元组中只包含一个元素时,需要在元素后面添加逗号,否则括号会被当作运算符使用。
④List:列表中的元素类型可以不相同,可以嵌套列表。
注: 列表元素可以改变,但不可越界赋值。例如:List = [] # true,List[0] = 2 # False
⑤Set: 可以用来删除重复元素。
⑥Dictionary:字典是一种映射类型,是一个无序的键(key):值(value)对集合,并且键必须是惟一的;字典的关键字必须为不可变类型,且不能重复。
**注意:**为什么要设计str、None这样的不变对象呢?因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。
2.4 PyLongObject// Includre/longobject.h
typedef struct _longobject PyLongObject;
// Include/longintrepr.h
struct _longobject {
PyObject_VAR_HEAD
digit ob_digit[1];
};
// Include/object.h
// --------------这里非常重要-----------------
#define PyObject_VAR_HEAD PyVarObject ob_base;
#define Py_INVALID_SIZE (Py_ssize_t)-1
// 等价于下列表达
typedef struct {
int ob_refcnt; //引用计数
struct _typeobject *ob_type; //变量类型
int ob_size; //用来记录变长对象PyLongObject一共由多少bit位组成
digit ob_digit[1]; //digit类型的数组,默认长度为1,具体大小取决于PyLongObject
} PyLongObject;
// 从这个角度看PyLongObject是一个变长对象,因为其具有变长对象的头部。
思考:1. 如何实现整数相加而内存不溢出?2. 不可变对象指什么?与变长对象有联系吗?
// 参考代码 // Include/longintrepr.h #if PYLONG_BITS_IN_DIGIT == 30 typedef uint32_t digit; typedef int32_t sdigit; typedef uint64_t twodigits; typedef int64_t stwodigits; #define PyLong_SHIFT 30 #define _PyLong_DECIMAL_SHIFT 9 #define _PyLong_DECIMAL_base ((digit)1000000000) #elif PYLONG_BITS_IN_DIGIT == 15 typedef unsigned short digit; typedef short sdigit; typedef unsigned long twodigits; typedef long stwodigits; #define PyLong_SHIFT 15 #define _PyLong_DECIMAL_SHIFT 4 #define _PyLong_DECIMAL_base ((digit)10000) #else #error "PYLONG_BITS_IN_DIGIT should be 15 or 30" #endif #define PyLong_base ((digit)1 << PyLong_SHIFT) #define PyLong_MASK ((digit)(PyLong_base - 1)) #if PyLong_SHIFT % 5 != 0 #error "longobject.c requires that PyLong_SHIFT be divisible by 5" #endif2.5 PyTypeObject
类型对象存储对象的四种元信息
类型名:tp_name,主要是Python内部以及调试时使用。
占用内存空间大小:创建该对象时分配,即Py_ssize_t tp_basicsize, tp_itemsize。
与该类型对象相关联的操作信息(类似tp_print这样的函数指针)。
类型(其实是一个对象)的类型信息。
struct _typeobject *ob_type; // PyTypeObject
// Objects/typeobject.c line:3553
PyTypeObject PyType_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"type",
sizeof(PyHeapTypeObject),
sizeof(PyMemberDef),
(destructor)type_dealloc,
0,
// .......
}
所以PyType_Type 在Python中被称为 metaclass(元类)。
创建对象的方法:
通过Python C API来创建
C API分为两类:AOL(范型的API,用于任何python对象)、COL(与类型相关的API,用于某一种类型的对象)
直接分配内存
PyObject* longObj = PyObject_New(PyObject, &PyLong_Type)
通过设计的类型对象创建实例对象
class A(object) --> new A()
创建对象的流程
tp_new对应到 C++ 中 , 可以视为new操作符 , Python中则是__new__操作符。
tp_init则是 Python 中的__init__ (相当于类的构造函数 , 即对创建的新对象进行初始化)。
__new__ does object creation and __init__ does object initialization.
分别对应Python源码中的tp_new与tp_init
// tp_new
// typeobject.c line:3669
static PyObject *
object_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
// ...
}
// tp_init
static int
object_init(PyObject *self, PyObject *args, PyObject *kwds)
{
PyTypeObject *type = Py_TYPE(self);
if (excess_args(args, kwds)) {
if (type->tp_init != object_init) {
PyErr_SetString(PyExc_TypeError, "object.__init__() takes no arguments");
return -1;
}
if (type->tp_new == object_new) {
PyErr_Format(PyExc_TypeError, "%.200s().__init__() takes no arguments",
type->tp_name);
return -1;
}
}
return 0;
}
2.5.2 对象的行为
在PyTypeObject中定义了大量的函数指针,这些函数指针最终都会指向某个函数或者null。这些函数指针可以视为类型对象中所定义的操作,而这些操作直接决定一个对象在运行时所表现出的行为。
// object.h hashfunc tp_hash; typedef Py_hash_t (*hashfunc)(PyObject *);
也正是这些不同的操作信息,使得对象之间存在区别。非常重要的操作族:
tp_as_number (指向PyNumberMethods)
tp_as_sequence(指向PySequenceMethods)
tp_as_mapping(指向PyMappingMethods)
PyNumberMethods *tp_as_number;
PySequenceMethods *tp_as_sequence;
PyMappingMethods *tp_as_mapping;
#ifndef Py_LIMITED_API
typedef struct {
binaryfunc nb_add;
binaryfunc nb_subtract;
binaryfunc nb_multiply;
binaryfunc nb_remainder;
binaryfunc nb_divmod;
ternaryfunc nb_power;
unaryfunc nb_negative;
unaryfunc nb_positive;
unaryfunc nb_absolute;
inquiry nb_bool;
unaryfunc nb_invert;
binaryfunc nb_lshift;
binaryfunc nb_rshift;
binaryfunc nb_and;
binaryfunc nb_xor;
binaryfunc nb_or;
unaryfunc nb_int;
void *nb_reserved;
unaryfunc nb_float;
binaryfunc nb_inplace_add;
binaryfunc nb_inplace_subtract;
binaryfunc nb_inplace_multiply;
binaryfunc nb_inplace_remainder;
ternaryfunc nb_inplace_power;
binaryfunc nb_inplace_lshift;
binaryfunc nb_inplace_rshift;
binaryfunc nb_inplace_and;
binaryfunc nb_inplace_xor;
binaryfunc nb_inplace_or;
binaryfunc nb_floor_divide;
binaryfunc nb_true_divide;
binaryfunc nb_inplace_floor_divide;
binaryfunc nb_inplace_true_divide;
unaryfunc nb_index;
binaryfunc nb_matrix_multiply;
binaryfunc nb_inplace_matrix_multiply;
} PyNumberMethods;
typedef struct {
lenfunc sq_length;
binaryfunc sq_concat;
ssizeargfunc sq_repeat;
ssizeargfunc sq_item;
void *was_sq_slice;
ssizeobjargproc sq_ass_item;
void *was_sq_ass_slice;
objobjproc sq_contains;
binaryfunc sq_inplace_concat;
ssizeargfunc sq_inplace_repeat;
} PySequenceMethods;
typedef struct {
lenfunc mp_length;
binaryfunc mp_subscript;
objobjargproc mp_ass_subscript;
} PyMappingMethods;
typedef struct {
unaryfunc am_await;
unaryfunc am_aiter;
unaryfunc am_anext;
} PyAsyncMethods;
typedef struct {
getbufferproc bf_getbuffer;
releasebufferproc bf_releasebuffer;
} PyBufferProcs;
#endif
比如对于一个整数对象,自然对应到数值对象方法(即PuNumberMethods),所以可以通过tp_as_number.nb_add指定对该对象进行加法操作时的具体行为。(其实就是类似通过类决定函数调用的过程)
思考:设计一种类型同时支持int|list|dict三种特性?
// 提示
PyObject *
PyDict_GetItem(PyObject *op, PyObject *key)
{
Py_hash_t hash;
Py_ssize_t ix;
PyDictObject *mp = (PyDictObject *)op;
PyThreadState *tstate;
PyObject *value;
if (!PyDict_Check(op))
return NULL;
if (!PyUnicode_CheckExact(key) ||
(hash = ((PyASCIIObject *) key)->hash) == -1)
{
hash = PyObject_Hash(key);
if (hash == -1) {
PyErr_Clear();
return NULL;
}
}
tstate = PyThreadState_GET();
if (tstate != NULL && tstate->curexc_type != NULL) {
PyObject *err_type, *err_value, *err_tb;
PyErr_Fetch(&err_type, &err_value, &err_tb);
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value);
PyErr_Restore(err_type, err_value, err_tb);
if (ix < 0)
return NULL;
}
else {
ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value);
if (ix < 0) {
PyErr_Clear();
return NULL;
}
}
return value;
}
2.5.3 类型的类型
每个对象对应着一种类型,类型的类型就是指类型对象的类型。
所有用户自定义class所对应的PyTypeObject对象都是通过PyType_Type这个对象创建的。
PyType_Type与MeatClass后续章节会详细阐述。
PyLongObject如何与PyType_Type建立关系?
每一个对象将自己的引用计数、类型信息保存在开始部分中,方便对这部分内存的初始化。
PyTypeObject PyLong_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"int",
offsetof(PyLongObject, ob_digit),
sizeof(digit),
long_dealloc,
0,
// ......
0,
0,
long_new,
PyObject_Del,
};
2.6 多态性
多态(函数重写与重载)
函数之间传递的都是一种范型指针-PyObject *,这个指针所指对象的类型从对象的ob_type域动态进行判断(从而实现多态机制)。
同一个函数在不同情况下表现出不同的行为,比如print()函数会根据传进去的参数的不同调用对应类型对象中的输出操作。
2.7 引用计数Python垃圾收集机制,代替程序进行繁重的内存管理工作。
Python通过对一个对象的引用计数的管理来维护对象在内存中的存在与否。(ob_refcnt)
// 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_ForgetReference(op) _Py_INC_TPFREES(op)
#ifdef Py_LIMITED_API
PyAPI_FUNC(void) _Py_Dealloc(PyObject *);
#else
// 类似C++中的析构函数,销毁对象(在类型对象中定义函数指针tp_dealloc指定)
#define _Py_Dealloc(op) (
_Py_INC_TPFREES(op) _Py_COUNT_ALLOCS_COMMA
(*Py_TYPE(op)->tp_dealloc)((PyObject *)(op)))
#endif
#endif
#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)
类型对象时超越引用计数规则的,永远不会被析构,每一个对象中指向类型对象的指针不被视为对类型对象的引用。
内存对象池计数,析构之后将内存空间归还到内存池中,避免频繁地申请和释放内存空间。
2.8 Python对象的分类从概念上大致分为五类:
Fundamental对象:类型对象(Type)Numeric对象:数值对象Sequence对象:容纳其他对象的序列集合对象(String、List、Tuple)Mapp对象:类似C++中的map关联对象(Dict)Internal对象:Python虚拟机在运行时内部使用的对象



