Python源码剖析-深度探索动态语言核心技术

  • 内容简介
  • Python 总体架构
  • Python源代码的组织
  • 第一部分:Python内建对象
    • 第1章:Python对象初探
    • 1.1 Python内的对象
      • 1.1.1 对象的基石-----PyObject
      • 1.1.2 定长对象
      • 重要点总结
    • 1.2 类型对象
      • 1.2.1 对象的创建
      • 1.2.2 对象的行为
      • 1.2.3 类型的类型
    • 1.3 Python对象的多态性
    • 1.4 引用计数
    • 1.5 Python 对象的分类
    • 第2章:Python中的整数对象
    • 2.1 初识 PyIntObject 对象
    • 2.2 PyIntObject 对象的创建和维护
      • 2.2.1 对象创建的3种途径

内容简介

为了更好地利用python语言,无论是python本身还是和C/C++配合使用,深刻理解python的运行原理都是非常重要的,本文以CPython源码为研究对象,深入剖析Python的实现。主要包含:

  • Python内建对象
  • Python虚拟机
  • Python高级特性

本文参考: Python源码剖析一书

学习建议:唯有亲身尝试,才能深解其中三昧。 在真实的源码和文章的描述相比较揣摩,动手捣鼓捣鼓Python的源码,用真实的输出来验证文章描述和自己的理解。

Python 总体架构

首先我们应该了解python的整体架构,对python 有个宏观的认识。

Python整体架构分为三部分,如下图:

  • 左边是python提供的大量模块,库,以及用户自定义模块
  • 右边是python的运行时环境,包括对象/类型系统, 内存分配器, 运行时状态信息。
  • 中间部分为解释器(虚拟机), 包括词法分析,语法分析,编译器与 (虚拟机Code Evauator)

Python源代码的组织

获取源码网站: 源码网站

解压后我们将看到目录结构:

  • Includes : 该目录包含了python提供的所有头文件,我们如果用c、c++编写自定义模块扩展python,就需要用到这里提供的头文件
  • Lib:包含了python自带的所有标准库, lib 中的库都是用python编写而成
  • Modules: 该模块包含了所有用C语言编写的模块, 比如random,cstringIO等,其中模块都是那些对速度要求非常严格的模块, (对速度没有严格要求的放在了lib中)
  • Parser: 包含了python解释器中的词法分析Scanner和语法分析 Parser部分
  • Objects: 改目录中包含了所有Python的内建对象,包括整数,list, dict等, 同时包含了Python在运行时需要的所有的内部使用对象的实现。
  • Python:是Python解释器的 Compiler 和执行引擎部分,是其运行的核心所在。

第一部分:Python内建对象

第1章:Python对象初探

我们熟知在面向对象理论中的 “类” 和 “对象” 这两个概念在python中都是使用Python内对象来实现的,也可以理解为一切都是对象, 于是我们将会带着一些疑问进入本章~

  1. 对象在python内部是如何表示的? (在C的层面,是个什么模样)
  2. C不是一个面向对象的语言,那么python的对象机制又是如何实现的呢?

额外一些有意思的相关知识:

  • 在Python中,对象是C中结构体在堆上申请的一块内存,因此对象不能被静态初始化,也不能在栈上生存,例外是(Python的类型对象type,都是被静态初始化的 ~)
  • 在Python中,一个对象一旦被创建,其内存大小就是不变的了,于是python在对象内维护一个指向一块可变大小的内存区域的指针来容纳可变长度数据的对象,当然这也是因为遵循这样的规则是的通过指针维护对象的工作变得简单。(struct _typeobject *ob_type)

1.1 Python内的对象

1.1.1 对象的基石-----PyObject

所有的对象都拥有一些相同的内容, 这些内容就在PyObject中定义, PyObject是整个Python对象体系的核心。

而PyObject的秘密就在这个PyObject_HEAD中~

我们发现,其实在PyObject的定义中, 仅有两个东西:

  • int (py_ssize_t) 类型的 ob_refcnt , 对象引用计数(垃圾回收机制)
  • struct _typeobject *ob_type, 一个指针,来维护一个类型信息结构(后续会再次分析)

PyObject中定义了每一个Python对象都必须有的内容, 每一个Python对象除了拥有PyObject外,还会占据一些额外的内存,保存属于自己的特殊信息,比如 intobject就会多出一个 ob_ival 来表示其值。

1.1.2 定长对象

对于数组,这种 n 个 元素构成的东西也是一类Python对象的共同特征, 因此, 在PyObject对象之外
还有一个表示这类对象的结构体–PyVarObject


变长对象往往都是容器, ob_size 指明了所容纳元素的个数

重要点总结

从PyObject_VAR_HEAD的定义中我们发现, PyVarObject实际上只是对PyObject的扩展而已,其开始部分的字节的意义和 PyObject相同, 一个引用计数加上一个类型信息结构指针,这意味着在Python中对对象的引用变得十分的统一, 使用 PyObject* 指针能引用任意的一个对象。

                  不同的Python对象在内存布局上的关系

1.2 类型对象

在上文我们看到了Python中所有对象的共有信息的定义,但我们会思考,我们所创建的对象的真实描述信息在哪呢? 例如:Int 和 Str 对象占有的空间信息在哪~, 其实这些信息都被隐含在 PyObject 的 _typeobjct 指针指向的地方。

在_typeobjct 的定义中包含了很多的信息,主要分为4类:

  • 类型名
  • 创建该类型时分配的内存空间大小信息 (tp_basicsize 和 tp_itemsize)
  • 与该类型对象相关联的操作信息(诸如 tp_print这样的许多的函数指针)
  • 类型的类型信息

事实上, 一个 PyTypeObject对象就是 Python中对面向对象理论中“类”这个概念的实现,由于PyTypeObject是一个篇幅较大的话题,我们在第二部分来介绍构建在PyTypeObject之上的Python的类型和对象体系。

1.2.1 对象的创建

思考: Python内部究竟如何才能从无到有创建一个整数对象呢?

  1. 通过 Python C API 来创建
  2. 通过类型对象 来创建

Python对外提供了C API,分为两种:

  • AOL,(Abstract Object Layer),形式如 PyObject_***形式, 可以应用在任何Python对象上
PyObject * intObj = PyObject_New(PyObject, &PyInt_Type)
  • COL (Concrete Object Layer) 只能作用于某一种类型的对象上,对于内建对象都有一套API
PyObject *intObj = PyInt_FromLong(10)

对于自定义的类型,比如 Class A(object) 定义的类型A,要创建其对象,由于Python不可能事先提供
PyA_New这样的API, 他将会通过A所对应的类型对象 来创建实例对象。

下面我们举例创建整数对象的函数调用流程(如图):

  • PyInt_Type 中的 tp_new 会被调用,如果tp_new 是空,会到基类找tp_new
  • tp_new会访问PyInt_Type中记录的 tp_basicsize 信息,完成申请内存的操作
  • 之后调用 tp_init ,完成初始化的操作

tp_new, tp_init 对应 new操作符和类的构造函数

1.2.2 对象的行为

对象的行为是通过内置大量函数指针来实现的,这些函数指针就表现了类型对象所定义的操作(行为)。

同时,在PyTypeObject有三个重要的操作族需要介绍一下:

tp_as_number, tp_as_sequence, tp_as_mapping,
分别指向 PyNumberMethods, PySequenceMethods 和 PyMappingMethods 函数族。

这里我们以序列操作族为例子:
也可以说当 tp_as_sequence 指针不为空时(list,str),表现出的行为就是序列行为。

1.2.3 类型的类型

在我们的PyTypeObject定义的最开始也会存在 PyObject_Var_HEAD, 证明Python中的类型本身也是一个对象,其类型是 PyType_Type:

  • PyType_Type 在 Python的类型机制是非常关键的,所有用户自定义Class所对应的 PyTypeObject对象都是通过这个对象创建的。

这里我们来看一下Int类型的定义,它会调用 PyObject_HEAD_INIT这个宏来初始化公共的头部分:
本质上就是将 ob_type 指向 PyType_Type, 并将其引用计数设置为1

现在我们可以想象一个整数对象在运行时的形象表示:

1.3 Python对象的多态性

Python利用C语言实现了对象的多态性,Python内部在创建对象时会使用 PyObject * 取保存和维护这个对象,(所有对象的头部是相同的),因此直接可以使用该指针所指对象的 ob_type 域动态去判断,正是这个域的存在,Python实现了多态性。

我们来分析一下:

void Print(PyObject* object) {object->ob_type.tp_print(object);
}

如果指针本身是一个PyIntObject* ,就会调用到 PyIntObject的类型对象中定义的输出操作,
如果是一个PyStringObject*, 就会调用到 PyStringObject 对象对应的类型对象中定义的输出操作。

1.4 引用计数

Python内建了垃圾回收机制,进行较为繁重的内存管理工作,引用计数正是Python垃圾回收机制的一部分。

  • Python中每一个东西都有一个 ob_refcnt 变量,维护着引用计数,决定着对象的创建和消亡。
  • 通过 Py_INCREF(op) 和 Py_DECREF(op) 两个宏来增加和减少一个对象的引用计数。
  • 当引用计数为0,会调用该对象的 tp_dealloc 进行析构动作

注意: 析构函数并不意味着最终会释放内存, 频繁申请和释放会有效率上的问题,因此Python中大量采用了内存池的技术。

1.5 Python 对象的分类

我们将 Python 对象从概念上大致分为5类:

  • Fundamental 对象: 类型对象
  • Numeric 对象: 数值对象
  • Sequence对象:容纳其他对象的序列集合对象
  • Mapping对象:关联对象
  • Internal对象: Python 虚拟机在运行时内部使用的对象

第2章:Python中的整数对象

2.1 初识 PyIntObject 对象

在定义上,是对 c 语言 long 的扩展而已~

对于其 ob_type 的指向,则是指向了 PyInt_Type

在PyInt_Type 中保存了关于PyIntObject对象的丰富信息,不仅有其对象应占有的内存大小,文档信息,也包括了支持的操作。

操作定义: 需要注意的是 int_as_number 这个域,他是一个PyNumberMethods对象,PyNumberMethods 有39个函数指针,定义了39种可选的操作(加减乘除),而对于 int_as_number 确定了对于一个整数对象,数值操作是如何进行的。

小知识点: 在整数的相加 int_add 中检查了加法结果是否溢出,用了位运算。
原理:只有两个相同符号的整数相加时才会溢出, 一正一负相加不会溢出, 溢出后得到的结果符号一定与任意一个整数符号相反,即

x = a + b
if ((x^a) >= 0 || (x^b) >= 0)     # 和其中一个符号相同就算不溢出return False
return True

2.2 PyIntObject 对象的创建和维护

2.2.1 对象创建的3种途径

内建类型对象的tp_new, tp_init 操作来创建实例对象, 最终依旧是会调用Python为特定内建对象准备的
CAPI。 为了创建一个 PyIntObject对象,Python提供了3条途径。

Python源码剖析-深度探索动态语言核心技术相关推荐

  1. python源码深度剖析_Python源码剖析——深度探索动态语言核心技术 | 学步园

    8.3  Python虚拟机的运行框架 当Python启动后,首先会进行Python运行时环境的 初始化.注意这里的运行时环境是一个与上一节剖析的执行环境不同的概念.运行时环境是一个全局的概念,而执行 ...

  2. Python猫荐书系统之四:《Python源码剖析》

    大家好,新一期的荐书栏目如期跟大家见面了. 先来看看今天的主角是谁:<Python源码剖析--深度探索动态语言核心技术>,2008年出版,作者 @陈儒 ,评分8.7分. 是的,你没看错,出 ...

  3. Python源码剖析:前言

    第0章:前言 0.0 我的前言  在几个月学习的中,已经学习了python基本.进阶的语法,如果有读者不清楚的话,可以参考我之前的专栏<python进阶>.  而在这个专栏<pyth ...

  4. Python源码剖析[19] —— 执行引擎之一般表达式(2)

    Python源码剖析 --Python执行引擎之一般表达式(2) 本文作者: Robert Chen(search.pythoner@gmail.com ) 3.2     Simple.py 前面我 ...

  5. Python源码剖析[16] —— Pyc文件解析

    Python源码剖析[16] -- Pyc文件解析 2008-02-28 18:29:55|  分类: Python |举报 |字号 订阅 Python源码剖析 --Pyc文件解析 本文作者: Rob ...

  6. 《Python源码剖析》读书笔记

    <Python源码剖析>电子书下载 http://download.csdn.net/detail/xiarendeniao/5130403 Python源码在官网有下载链接,用ctags ...

  7. python源码剖析—— python中的字节码对象初探

    一.代码对象 每个初学python的人都会认为python是一种解释型语言,这个不能说错.但是python并不是真的对执行的python代码的每一行进行解释,虽然我们有一个所谓的"解释器&q ...

  8. Python发展的新时代—冯大辉先生谈《Python源码剖析》

    Python 3.0 beta 1终于在 6 月 18 号发布了,依照Python一贯主张的简洁,标准,统一精神,Beta版的新特性让我们惊喜的发现,Python正如我们所期望的那样已经进入了一个蓬勃 ...

  9. Python源码剖析[1] —— 编译Python

    [ 绝对原创,转载请注明出处] 注意 :第一部分Python总体架构采用了网络文档<The Architecture of Python>,这是网络上唯一可见的以剖析Python实现为己任 ...

  10. python源码剖析笔记1——Python对象初见

    本文简书地址:http://www.jianshu.com/p/763f6cec7a9b 工作整两年了,用python最多,然而对于python内部机制不一定都清楚,每天沉醉于增删改查的简单逻辑编写, ...

最新文章

  1. 从Netflix的Hystrix框架理解服务熔断和服务降级
  2. python求圆柱表面积_python基础知识-字典
  3. mysql报错 Incorrect table definition;there can be only one auto column and it must be defined as a key
  4. python javascript配合,在python部署时组合javascript文件
  5. Python 分析在德的中国程序员,告别 996 ?
  6. 常用应用层传输协议和端口
  7. c语言求阶乘的两种算法(递归和循环)
  8. 经典小游戏开发思路和算法之拼图(1)
  9. CAD一键统计所有线段长度
  10. java关闭自动更新_怎么关闭java自动更新
  11. 分享《模拟专升本考试排名》
  12. 如何启用计算机安全模式,怎么进入电脑安全模式
  13. JL 杰理 AC692N系列TWS 蓝牙音箱 开发
  14. Java微信支付API文档测试
  15. 三星S7edge从8.0降到6.0.1,只为流畅的飞一般的感觉
  16. 保留两位小数(四舍五入)
  17. hMailServer 使用教程 —— 手把手教你搭建自己的邮箱服务器
  18. 团队-中国象棋游戏-设计文档
  19. 好用的报表分析软件有哪些?
  20. Git系列(四)、在IDEA操作Git本地仓库与连接远程Git仓库(推送、合并、拉取、克隆操作)

热门文章

  1. 如何使用计算机中的导出,解决方案:如何使用Canon 2525i复印机将文档扫描到计算机中并生成PDF格式?...
  2. Allegro给一个网络赋默认值,取消默认值
  3. CSS半透明磨砂效果实现
  4. Java详细安装配置教程(Windows),从下载到配置——Java-1.8(jdk)安装
  5. 软件测试——bug相关知识
  6. ABBYY FineReader 12 破解版(附注册码)
  7. js把txt转为html,js格式化文本为html标签
  8. javaweb课程设计之XXX管理系统
  9. 怎样下载企业通讯录Excel模板
  10. 最佳的75个安全工具工具