烟雨对《Python源码剖析》的笔记(4)

烟雨
烟雨 (烟锁重楼千里尽,雨打芭蕉万点残)

读过 Python源码剖析

Python源码剖析
  • 书名: Python源码剖析
  • 作者: 陈儒
  • 副标题: 深度探索动态语言核心技术
  • 页数: 480
  • 出版社: 电子工业出版社
  • 出版年: 2008-6
  • 第17页
    [A.py] from B import D class C: pass [B.py] from A import C class D: pass
    上面这两段代码不能相互调用,因为循环引用了

    在图的左边,是Python提供的大量的模块、库以及用户自定义的模块。比如在执行import os时,这个os就是Python内建的模块,当然用户还可以通过自定义模块来扩展Python系统。
    右边,是Python的运行时环境,包括对象/类型系统(Object/Type structures)、内存分配器(Memory Allocator)和运行时状态信息(Current State of Python)。运行时状态维护了解释器在执行字节码时不同的状态(比如正常状态和异常状态)之间切换的动作,我们可以将它视为一个巨大而复杂的有穷状态机。内存分配器则全权负责Python中创建对象时,对内存的申请工作,实际上它就是Python运行时与C中malloc的一层接口。而对象/类型系统则包含了Python中存在的各种内建对象,比如整数、list和dict,以及各种用户自定义的类型和对象。
    在中间的部分,可以看到Python的核心——解释器(interpreter),或者称为虚拟机。在解析器中,箭头的方向指示了Python运行过程中的数据流方向。其中Scanner对应词法分析,将文件输入的Python源代码或从命令行输入的一行行Python代码切分为一个的token;Parser 使用运行时里的object/type 来解析语法,这就体现了先定义后使用的原则。对后来的语法解析要仰仗于前文的定义。Parser对应语法分析,在Scanner的分析结果上进行语法分析,建立抽象语法树(AST);Scanner 简单理解就是把代码的文本文件转化为parser可以解析的一个一个单元,其实就是个扫描的过程。Compiler是根据建立的AST生成指令集合——Python字节码(byte code),就像Java编译器和C#编译器所做的那样;最后由Code Evaluator来执行这些字节码。因此,Code Evaluator又可以被称为虚拟机。
    图中,在解释器与右边的对象/类型系统、内存分配器之间的箭头表示“使用”关系;而与运行时状态之间的箭头表示“修改”关系,即Python在执行的过程中会不断地修改当前解释器所处的状态,在不同的状态之间切换。
    2017-01-24 14:26:07 回应
  • 第20页
    Include :该目录下包含了Python提供的所有头文件,如果用户需要自己用C或C++来编写自定义模块扩展Python,那么就需要用到这里提供的头文件。 Lib :该目录包含了Python自带的所有标准库,Lib中的库都是用Python语言编写的。 Modules :该目录中包含了所有用C语言编写的模块,比如random、cStringIO等。Modules中的模块是那些对速度要求非常严格的模块,而有一些对速度没有太严格要求的模块,比如os,就是用Python编写,并且放在Lib目录下的。 Parser :该目录中包含了Python解释器中的Scanner和Parser部分,即对Python源代码进行词法分析和语法分析的部分。除了这些,Parser目录下还包含了一些有用的工具,这些工具能够根据Python语言的语法自动生成Python语言的词法和语法分析器,与YACC非常类似。 Objects :该目录中包含了所有Python的内建对象,包括整数、list、dict等。同时,该目录还包括了Python在运行时需要的所有的内部使用对象的实现。 Python :该目录下包含了Python解释器中的Compiler和执行引擎部分,是Python运行的核心所在。 PCBuild :包含了Visual Studio 2003的工程文件,研究Python源代码就从这里开始(本书将采用VS2003对Python进行编译)。 PCBuild8 :包含了Visual Stuido 2005使用的工程文件。
    Yacc (Yet Another Compiler Compiler), Unix, BNF, LALR(1); Lex (Lexical analyzer)
    导入库和静态库的区别很大,他们实质是不一样的东西。静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。
    简单的说,静态库和应用程序编译在一起,在任何情况下都能运行,而动态库是动态链接,顾名思义就是在应用程序启动的时候才会链接,所以,当用户的系统上没有该动态库时,应用程序就会运行失败。
    再看它们的特点:
    动态库: 1.共享:多个应用程序可以使用同一个动态库,启动多个应用程序的时候,只需要将动态库加载到内存一次即可; 2.开发模块好:要求设计者对功能划分的比较好。
    静态库:代码的装载速度快,执行速度也比较快,因为编译时它只会把你需要的那部分链接进去,应用程序相对比较大。但是如果多个应用程序使用的话,会被装载多次,浪费内存。
    [intobject.c]
    static int int_print(PyIntObject *v, FILE *fp, int flags)
    {
      //add by Robert
      PyObject* str = PyString_FromString(“i am in int_print”);
      PyObject_Print(str, stdout, 0);
      printf(“\n”);
    
      fprintf(fp, "%ld", v->ob_ival);
      return 0;
    }
    
    下面的代码显示了如何输出到重定向后的标准输出:
    static PyObject* int_repr(PyIntObject *v)
    {
        ……
      //add by Robert
      If(PyInt_AsLong(v) == -999) {
          PyObject* str = PyString_FromString("i am in int_repr");
          PyObject* out = PySys_GetObject("stdout");
          if(out != NULL) {
            PyObject_Print(str, stdout, 0);
            printf("\n");
          }
    
    2017-01-24 14:50:58 回应
  • 第30页
    malloc与new的不同点:
    从函数声明上可以看出。malloc 和 new 至少有两个不同: new 返回指定类型的指针,并且可以自动计算所需要大小。
    而 malloc 则必须由我们计算要字节数,并且在返回后强行转换为实际类型的指针。
    第一、malloc 函数返回的是 void * 类型,如果你写成:p = malloc (sizeof(int)); 则程序无法通过编译,报错:“不能将 void* 赋值给 int * 类型变量”。所以必须通过 (int *) 来将强制转换。
    第二、函数的实参为 sizeof(int) ,用于指明一个整型数据需要的大小。
    另外有一点不能直接看出的区别是,malloc 只管分配内存,并不能对所得的内存进行初始化,所以得到的一片新内存中,其值将是随机的。
    除了分配及最后释放的方法不一样以外,通过malloc或new得到指针,在其它操作上保持一致。
    对象是数据以及基于这些数据的操作的集合。
    对象的存储位置:
    global variables -------> data
    static variables -------> data
    constant data types -----> code and/or data. Consider string literals for a situation when a constant itself would be stored in the data segment, and references to it would be embedded in the code
    local variables(declared and defined in functions) --------> stack
    variables declared and defined in main function -----> stack
    pointers(ex: char *arr, int *arr) -------> data or stack, depending on the context. C lets you declare a global or a static pointer, in which case the pointer itself would end up in the data segment.
    dynamically allocated space(using malloc, calloc, realloc) --------> heap
    上文来自:http://stackoverflow.com/questions/14588767/where-in-memory-are-my-variables-stored-in-c
    PyObject是整个Python对象机制的核心。
    [object.h]
    typedef struct _object {
        PyObject_HEAD
        PyObject_HEAD
    } PyObject;
    
    typedef struct _object {
        _PyObject_HEAD_EXTRA
        Py_ssize_t ob_refcnt;
        struct _typeobject *ob_type;
    } PyObject;
    
    在PyObject的定义中,整型变量ob_refcnt与Python的内存管理机制有关,它实现了基于引用计数的垃圾收集机制。对于某一个对象A,当有一个新的PyObject *引用该对象时,A的引用计数应该增加;而当这个PyObject *被删除时,A的引用计数应该减少。当A的引用计数减少到0时,A就可以从堆上被删除,以释放出内存供别的对象使用。
    在Python中,对象机制的核心其实非常简单,一个是引用计数,一个就是类型对象指针。
    举个类型对象的例子:
    [intobject.h]
    typedef struct {
        PyObject_HEAD
        long ob_ival;
    } PyIntObject;
    
    2017-01-24 16:20:06 回应
  • 第38页
    对于字符串,列表之类的单一类型变量重复多次的变量,用PyVarObject来存放
    
    [object.h]
    #define PyObject_VAR_HEAD       \
        PyObject_HEAD           \
        int ob_size; /* Number of items in variable part */
    
    typedef struct {
        PyObject_VAR_HEAD
    } PyVarObject;
    
    可以说,变长对象都是定长对象的容器。对于字符串来说,是定长对象char的容器。
    ob_size指明的是所容纳元素的个数,而不是字节的数量
    PyDictObject的定义里有PyObject_HEAD,而不是PyObject_VAR_HEAD,所以dict是一个定长对象而不是变长对象
    在_typeobject的定义中包含了许多的信息,主要可以分为4类: □ 类型名,tp_name,主要是Python内部以及调试的时候使用; □ 创建该类型对象时分配内存空间大小的信息,即tp_basicsize和tp_itemsize; □ 与该类型对象相关联的操作信息(就是诸如tp_print这样的许多的函数指针); □ 我们在下面将要描述的类型的类型信息。
    API。Python的C API分成两类,一类称为范型的API,或者称为AOL(Abstract Object Layer)。这类API都具有诸如PyObject_***的形式,可以应用在任何Python对象身上,比如输出对象的PyObject_Print,你可以PyObject_Print(int object),也可以PyObject_Print(string object),API内部会有一整套机制确定最终调用的函数是哪一个。对于创建一个整数对象,我们可以采用如下的表达式:PyObject* intObj = PyObject_New(PyObject, &PyInt_Type)。 另一类是与类型相关的API,或者称为COL(Concrete Object Layer)。这类API通常只能作用在某一种类型的对象上,对于每一种内建对象,PyObject *intObj = PyInt_FromLong(10),这样就创建了一个值为10的整数对象。
    2017-01-24 16:54:21 回应

烟雨的其他笔记  · · · · · ·  ( 全部218条 )

Zabbix企业级分布式监控系统
1
深入理解Nginx
1
蒙台梭利教育法
1
编程之法
1
蒙台梭利儿童教育经典原著(套装6册)
1
3岁决定孩子的一生
1
小程序,巧应用
1
火鸟
1
Python自动化运维
3
写给孩子的山海经.鱼鸟篇
1
子弹笔记术
1
Unity 5.x游戏开发指南
1
通关!游戏设计之道
5
算法之美
1
Violent Python
1
每个孩子都能好好睡觉
1
玻璃球游戏
1
Python高手之路 第3版
3
编写高质量代码
1
Unity游戏设计与实现
1
骑鲸之旅
1
Unity 3D脚本编程
1
游戏编程模式
1
动起来更聪明
1
Vue2实践揭秘
1
设计模式之禅(第2版)
1
Vue.js权威指南
1
前端架构设计
1
郑玉巧育儿经·婴儿卷
5
轻量级Web应用开发
1
算法图解
1
算法精解
3
响应式Web设计
4
Python高手之路
1
精简社交
1
张公案
1
好人难寻
1
讲出一个精彩故事
1
短篇小说写作指南
1
盐铁论
1
0-3岁越玩越聪明的亲子益智游戏大全
1
吃的真相
1
你看了,但你没看见
2
拖延心理学
1
善恶之源
1
貓乘 貓苑
2
妈妈,我要吃辅食
1
婴幼儿睡眠圣经
1
舌尖上的中式点心
1
斜屋犯罪
1
当鞋合脚时
1
游戏改变世界
2
知日!知日!这次彻底了解日本 01
1
王尔德童话
1
红瓦黑瓦
1
Linux Shell脚本攻略
4
宫泽贤治童话
1
百鬼夜行全图鉴
1
方寸指间
1
学会创新:创新思维的方法和技巧
1
和宝宝一起做的游戏大全
1
电影编剧学
9
1984
1
Debug Hacks中文版
6
深入理解LINUX内核(第三版)
1
独白者3:同行
2
Binary Hacks
11
写给Web开发人员看的HTML5教程
1
莉莉和章鱼
1
第一本Docker书
1
先秦古国志
1
Python编程
1
趣味逻辑学
1
硝烟中的Scrum和XP
2
XSS跨站脚本攻击剖析与防御
4
Docker 技术入门与实战
2
护生画集
1
重构
1
Python核心编程(第二版)
3
Python Cookbook
1
岛上书店
1
Web安全测试
1
SQL注入攻击与防御(第2版)
1
Web应用漏洞侦测与防御
1
黑客攻防技术宝典(第2版)
1
习惯的力量
1
0day安全 (第2版)
1
用眼动追踪提升网站可用性
1
微博控•控微博
1
Web入侵安全测试与对策
1
秩序之美
1
幻影城主
1
众妙之门
1
别对我撒谎
1
认知与设计
1
汉口的沧桑往事
1
优势说服力
1
阅读的故事
1
沉重的翅膀
1
移动设计
1
常识
1
写给大家看的设计书(第3版)
1
蔡澜谈美食
1
30天写小说
1
产品经理的第一本书
1
交互设计指南
1
傾城
1
奥斯曼帝国闲史
1
冲突与悬念
1
双阙
2
兰陵王
1
魍魉之匣(上)
2
经典常谈
1
上帝掷骰子吗
1
定格时光·请写信给我
1
亭长小武
1
中国的妖怪
3
一年通往作家路
3
万历范儿
1
日本茶道文化概论
1
一部手机打天下
3
没有色彩的多崎作和他的巡礼之年
6
北京秘境
1
日本蜡烛图技术
4
山海经校注
1
幻夜
1
惜别
2
WordPress 3 Plugin Development Essentials
3
Foundation HTML5 with CSS3
1
全世界人民都知道
1
神们自己
1
林徽因传
1
香识
1
中国史读本
1
江湖行帮趣话
1
怪诞行为学2
1
《程序员》(杂志)
1
怪诞行为学
1
上下班路上能做的95件事
1
天才在左 疯子在右
1
Java 2 参考大全
1