python源码解读_python代码解释

简介: python源码解读_python代码解释

PyObject

// object.h
#define PyObject\_HEAD \
 \_PyObject\_HEAD\_EXTRA \ // 一般情况下为空
    int ob_refcnt;  // 引用计数
    struct _typeobject \*ob_type; //指定一个对象类型的类型对象
typedef struct _object{
    PyObject_HEAD
} PyObject;

每一个python对象除了PyObject之外还需要额外的内存,PyObject定义了必须要的数据比如PyIntObject

// intobject.h
typedef struct _object{
    PyObject_HEAD
    long ob_ival;
w} PyIntObject;

对于变长对象python有新的抽象

// object.h
#define PyObject\_VAR\_HEAD \
 PyObject\_HEAD \
 long ob\_size; /一般指容器内元素数量
typedef struct _object{
    PyObject_VAR_HEAD
} PyVarObject;

类型对象

PyObject占用内存大小是对象的元信息,元信息和对象所属类型密切相关

// object.h
typedef struct _typeobject{
    PyObject_VAR_HEAD
    char \*tp_name;  //print 信息"<module>.<name>"
    int tp_basicsize, item_size; //为了分配内存大小
    destructor tp_dealloc;
    printfunc tp_print;
    hashfunc tp_hash;
    ternaryfunc tp_call;
    ...
} PyTypeObject;
对象的创建

python对象的创建主要有两种方式

  • C API(对于python的内建对象, 直接分配内存)
  • 泛型API AOL
  • 类型相关API COL
  • 类型对象创建(用户自定义对象,因为不可能事先提供这类C方法),大致流程如下
    会调用ob_type 所指定的PyTyoeObject类的tp_new方法,如果tp_new是NULL会追溯tp_base所指向的ob_typetp_new方法,最终定位的tp_new(因为所有类继承object会有保底的tp_new方法)负责内存的申请(类似c++的new)
    之后通过tp_init初始化(类似c++的构造函数)
类型的类型

PyTypeObect 实际也是有ob_type属性的,其为PyType_Type(即type, 负责PyTypeObect的创建,即metaclass)

下图以int 为例说明了这些关系,也就是所知的python里面class,object,type的关系

Python中的整数对象

在Python的所有对象中,整数对象最简单且使用最频繁,故我们首先学习整数对象。关于整数对象的源码在Objects.intobjects.c中,整数对象是通过PyIntObject对象来完成的,在创建一个PyIntObject对象之后,就再也不能改变该对象的值了。定义为:

typedef struct {
    prObject_HEAD;
    long ob_ival;
}PyIntObject;

主要是PyIntObjectPyInt_Type,其他和普遍的PyObject, PyTyoeObject没什么,值得关注的有

  • python2中稍小一点的数直接用C语言中的long去存储,稍大一点的数(超过long的承受范围)会使用python的long对象去存储,而python3不会作区分,统一用longObect去存储,实现用到了柔性数组,感兴趣可以查一下
  • 小整形数组的内存池和大整形对象的内存链的维护,避免频繁malloc

在Python中,整数的使用是很广泛的,对应的,它的创建和释放也将会很频繁,那么如何设计一个高效的机制,使得整数对象的使用不会成为Python的瓶颈?在Python中是使用整数对象的缓冲池机制来解决此问题。使用缓冲池机制,那意味着运行时的整数对象并不是一个个独立的,而是相关联结成一个庞大的整数对象系统了。

小整形对象

// [intobject.c]
#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS 257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS 5
#endif
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
/\* References to small integers are saved in this array so that they
 can be shared.
 The integers that are saved are those in the range
 -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
\*/
static PyIntObject \*small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
#endif

在实际的编程中,数值比较小的整数,比如1,2,等等,这些在程序中是频繁使用到的,而Python中,所有的对象都存活在系统堆上,也就是说,如果没有特殊的机制,对于小整数对象,Python将一次次的malloc在堆上申请空间,然后free,这样的操作将大大降低了运行效率。

那么如何解决呢?Python中,对小整数对象使用了对象池技术。

那么又有一个问题了,Python中的大对象和小对象如何区分呢?嗯,Python中确实有一种方法,用户可以调整大整数和小整数的分界点,从而动态的确定小整数的对象池中应该有多少个小整数对象,但是调整的方法只有自己修改源代码,然后重新编译。

大整数对象

对于小整数,小整数对象池中完全的缓存PyIntObject对象,对于其它对象,Python将提供一块内存空间,这些内存空间将由这些大整数轮流使用,也就是谁需要的时候谁使用。

比如,在Python中有一个PyIntBlock结构,维护了一块内存,其中保存了一些PyIntObject对象,维护对象的个数也可以做动态的调整。在Python运行的某个时刻,有一些内存已经被使用,而另一些内存则处于空闲状态,而这些空闲的内存必须组织起来,那样,当Python需要新的内存时,才能快速的获得所需的内存,在Python中使用一个单向链表(free_list)来管理所有的空闲内存。

// [intobject.c]
#define BLOCK\_SIZE 1000 /\* 1K less typical malloc overhead \*/
#define BHEAD\_SIZE 8 /\* Enough for a 64-bit pointer \*/
#define N\_INTOBJECTS ((BLOCK\_SIZE - BHEAD\_SIZE) / sizeof(PyIntObject))
struct _intblock {
    struct _intblock \*next;
    PyIntObject objects[N_INTOBJECTS];
};
typedef struct _intblock PyIntBlock;
static PyIntBlock \*block_list = NULL;
static PyIntObject \*free_list = NULL;

创建

如果小整数对象池机制被激活,则尝试使用小整数对象池;如果不能使用小整数对象池,则使用通用整数对象池。

可以创建int的代码理解这两块的使用

// [intobject.c]
PyObject \*
PyInt\_FromLong(long ival)
{
    register PyIntObject \*v;
#if NSMALLNEGINTS + NSMALLPOSINTS > 0 /\* 尝试使用小整数对象池 \*/
    if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {
        v = small_ints[ival + NSMALLNEGINTS];
        Py\_INCREF(v);
        return (PyObject \*) v;
    }
#endif
    if (free_list == NULL) {
        if ((free_list = fill\_free\_list()) == NULL)
            return NULL;
    }
    /\* Inline PyObject\_New \*/
    v = free_list;
    free_list = (PyIntObject \*)v->ob_type;  /\* 有一个看似不合适但是比较方便的地方,freelist会通过 ob\_type存放可用空间的pyObject的地址(类似链表的next),而不是 PyTyoeObject \*/
    (void)PyObject\_INIT(v, &PyInt_Type);
    v->ob_ival = ival;
    return (PyObject \*) v;
}

下面是关于freelist的申请,和freelist和block_list的维护有关的代码

// [intobject.c]
static PyIntObject \*
fill\_free\_list(void)
{
    PyIntObject \*p, \*q;
    /\* 申请大小为sizeof(PyIntBlock)的内存空间,并链接到已有的block list中 \*/
    p = (PyIntObject \*) PyMem\_MALLOC(sizeof(PyIntBlock));
    ((PyIntBlock \*)p)->next = block_list;
    block_list = (PyIntBlock \*)p;
    /\* 将PyIntBlock中的PyIntObject数组——objects转变成单向链表\*/
    p = &((PyIntBlock \*)p)->objects[0];
    q = p + N_INTOBJECTS;
    while (--q > p)
        q->ob_type = (struct _typeobject \*)(q-1); /\* 上一段代码中所提到的不合适的地方
 Py\_TYPE(q) = NULL;
 return p + N\_INTOBJECTS - 1;
}

这样,freelist会指向可以分配内存的地址,但是如果由之前分配的PyIntObject被释放了,freelist需要将被释放的地址重新使用才可以,这个是通过PyIntObect的析构函数来实现的

// [intobject.c]
static void
int\_dealloc(PyIntObject \*v)
{
    if (PyInt\_CheckExact(v)) {   // 如果不是派生类这么执行,保证freelist的完整性
        v->ob_type = (struct _typeobject \*)free_list;
        free_list = v;
    }
    else                        // 如果是派生类,则执行正常的析构流程
        v->ob_type->tp\_free((PyObject \*)v);
}

Python中的字符串对象

PyStringObject和PyString_Type

//[stringobject.h]
typedef struct{
    PyObject_VAR_HEAD
    long ob_shash;
    int ob_sstate;
    char ob_sval[1];
} PyStringObject;
//[stringobject.c]
PyTypeObject PyString_Type = {
    PyObject\_HEAD\_INIT(&PyType_Type)
    0,
    "str",
    sizeof(PyStringObject), // basic size
    sizeof(char), //itemsize
    // ...
}
  • ob_sval指的是一段长度为ob_size+1个字节的内存,必须满足ob_sval[ob_size] == ‘\0’
  • ob_shash缓存的该对象的hash
  • ob_sstate标记了该对象是否已经经过intern机制的处理

创建PyStringObject对象

//[stringobject.c]
// 从原生字符串创建
PyObject\* PyString\_FromString(const char \*str) {
    register size_t size;
    register PyStringObejct \*op;
    
    size = strlen(str);
    if (size > PY_SSIZE_T_MAX) {
        return NULL;
    }
    if (size == 0 && (op = nullstring )!= NULL) {  // intern机制: 和下面的一个分支都是为了缓存特定的对象,一个是空字符串,一个是单个字符字符串,第一次使用后会存在,之后不必再次创建PyObject对象(这些对象之前都被初始化成了NULL)
        return (PyObject \*) op;
    }
    if (size == 1 && (op = characters[\*str & UCHAR_MAX]) != NULL) {
        return (PyObject \*) op;
    }
    op = (PyStringObject \*)PyObject\_MALLOC(sizeof(PyStringObject) + size); // 加上包含'\0'的额外内存
    PyObject\_INIT\_VAR(ob, &PyString_Type, size);
    op->ob_shash=-1;
    op->ob_sstate=SSTATE_NOT_INTERNED;
    memcpy(op->ob_sval, str, size+1);
    if (size==0) {
        PyObject \*t = (PyObject \*) op;
        PyString\_InternInPlace(&t);
        op = (PyStringObject \*) t;
        nullstring = op;
    } else if (size == 1) {
        PyObject \*t = (PyObject \*) op;
        PyString\_InternInPlace(&t);
        op = (PyStringObject \*) t;
        characters[\*str & UCHAR_MAX] = op;
    }
    return (PyObejct \*) op;
}

字符串对象的intern机制

//[stringobject.c]
void PyString\_InternInPlace(PyObject \*\*p) {
    register PyStringObject \*s = (PyStringObject \*)(\*p);
    PyObject \*t;
    if (!PyString\_CheckExact(s)) return;
    if (PyString\_CHECK\_INTERNED(s)) return;
    if (interned == NULL) {
        interned = PyDict\_New();
    }
    t = PyDict\_GetItem(interned, (PyObject \*) s);
    if (t) {
        Py\_INCREF(t);
        Py\_DECREF(\*p);
        \*p = t;
        return;
    }
    PyDict\_SetItem(interned, (PyObject \*)s, (PyObject \*)s);
    s->ob_refcnt -= 2;  // 减去key 和 value的引用
    PyString\_CHECK\_INTERNED(s) = SSTATE_INTERNED_MORTAL;  // 当析构时会根据这个属性,做出在interned中删除的操作
}

characters 是静态变量static PyStringObject *characters[UCHAR_MAX+1]; 开始都是NULL指针

= join字符串效率比 + 好, 因为PyStringObject 是不可变对象(varObject只是因为是变长的),两者申请内存不同

join 计算所有 PyStringObject 的size 得出需要分配的内存,一次分配

= 而concat(+) 需要分配n-1次内存,并且伴有析构

python中的List对象

// [listobject.h]
typedef struct{
### 最后
Python崛起并且风靡,因为优点多、应用领域广、被大牛们认可。学习 Python 门槛很低,但它的晋级路线很多,通过它你能进入机器学习、数据挖掘、大数据,CS等更加高级的领域。Python可以做网络应用,可以做科学计算,数据分析,可以做网络爬虫,可以做机器学习、自然语言处理、可以写游戏、可以做桌面应用…Python可以做的很多,你需要学好基础,再选择明确的方向。这里给大家分享一份全套的 Python 学习资料,给那些想学习 Python 的小伙伴们一点帮助!
#### 👉Python所有方向的学习路线👈
Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
![](https://ucc.alicdn.com/images/user-upload-01/img_convert/604bae65027d4d67fb62410deb210454.png)
#### 👉Python必备开发工具👈
工欲善其事必先利其器。学习Python常用的开发软件都在这里了,给大家节省了很多时间。
![](https://ucc.alicdn.com/images/user-upload-01/img_convert/fa276175617e0048f79437bd30465479.png)
#### 👉Python全套学习视频👈
我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了。
![](https://ucc.alicdn.com/images/user-upload-01/img_convert/16ac689cb023166b2ffa9c677ac40fc0.png)
#### 👉实战案例👈
学python就与学数学一样,是不能只看书不做题的,直接看步骤和答案会让人误以为自己全都掌握了,但是碰到生题的时候还是会一筹莫展。
因此在学习python的过程中一定要记得多动手写代码,教程只需要看一两遍即可。
![](https://ucc.alicdn.com/images/user-upload-01/img_convert/0d8c31c50236a205928a1d8ae8a0b883.png)
#### 👉大厂面试真题👈
我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
![](https://ucc.alicdn.com/images/user-upload-01/img_convert/99461e47e58e503d2bc1dc6f4668534a.png)
**[需要这份系统化学习资料的朋友,可以戳这里无偿获取](https://bbs.csdn.net/topics/618317507)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**


相关文章
|
6天前
|
缓存 监控 程序员
Python中的装饰器是一种特殊类型的声明,它允许程序员在不修改原有函数或类代码的基础上,通过在函数定义前添加额外的逻辑来增强或修改其行为。
【6月更文挑战第30天】Python装饰器是无侵入性地增强函数行为的工具,它们是接收函数并返回新函数的可调用对象。通过`@decorator`语法,可以在不修改原函数代码的情况下,添加如日志、性能监控等功能。装饰器促进代码复用、模块化,并保持源代码整洁。例如,`timer_decorator`能测量函数运行时间,展示其灵活性。
15 0
|
1天前
|
数据采集 机器学习/深度学习 数据可视化
关于Python数据分析项目的简要概述:从CSV加载数据,执行数据预处理,进行数据探索,选择线性回归模型进行训练,评估模型性能并优化,最后结果解释与可视化。
【7月更文挑战第5天】这是一个关于Python数据分析项目的简要概述:从CSV加载数据,执行数据预处理(填充缺失值,处理异常值),进行数据探索(可视化和统计分析),选择线性回归模型进行训练,评估模型性能并优化,最后结果解释与可视化。此案例展示了数据科学的典型流程。
12 2
|
4天前
|
Linux iOS开发 MacOS
【超级简单】Python代码如何打包成exe文件
【超级简单】Python代码如何打包成exe文件
31 1
|
1天前
|
机器学习/深度学习 数据采集 算法
【2024泰迪杯】A 题:生产线的故障自动识别与人员配置 Python代码实现
【2024泰迪杯】A 题:生产线的故障自动识别与人员配置 Python代码实现
10 0
|
3天前
|
语音技术 开发者 Python
语音识别,python运行H ~W~,要使用英符,执行Python的流程是输入Python,回车,解释器的两大功能,翻译代码,提交计算机运算,多行代码运行,写一个py文件,pycharm安
语音识别,python运行H ~W~,要使用英符,执行Python的流程是输入Python,回车,解释器的两大功能,翻译代码,提交计算机运算,多行代码运行,写一个py文件,pycharm安
|
3天前
|
Python
惊呆了!Python 闭包与装饰器:解锁代码魔法的神秘钥匙🔑
【7月更文挑战第3天】Python中的闭包和装饰器是代码的神器。闭包是内嵌函数记住外部函数的变量,如`inner_function`记住`outer_function`的`x`。装饰器不修改原函数,增加额外功能,如`my_decorator`在`my_function`执行前后的打印。它们提升代码复用性,如`timeit_decorator`计时或`permission_required`控制访问权限。利用这些特性,编码变得更高效和优雅。
|
4天前
|
存储 程序员 Python
惊!Python数据类型竟藏着这些黑科技?列表推导式让你代码秒变炫酷!
【7月更文挑战第2天】Python的列表推导式是编程中的亮点,它以一行代码实现循环与条件判断,创建新列表。例如,要计算数字列表的平方,传统方法需循环,而列表推导式只需`[number ** 2 for number in numbers]`。它还能结合if语句筛选元素,如取偶数平方。嵌套使用时,能处理复杂数据结构,如展平并筛选偶数。列表推导式是Pythonic的体现,提升效率,简化代码。
|
6天前
|
算法 搜索推荐 开发者
优化Python代码执行效率的技巧与实践
提升Python代码执行效率是开发过程中的关键挑战之一。本文探讨了几种有效的优化策略,包括算法选择、数据结构优化以及代码优化技巧,帮助开发者在实际项目中提升程序性能和响应速度。
|
Linux C语言 开发者
源码安装Python学会有用还能装逼 | 解决各种坑
相信朋友们都看过这个零基础学习Python的开篇了
397 0
源码安装Python学会有用还能装逼 | 解决各种坑