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

  • python类型对象
  • python中对象通用的部分有共同的定义
  • 但是还要知道在内存中分配的空间,存储这数据的信息在哪呢?
  • 在pyobject中没有这样的信息,其实这种说法是不对的,这个信息虽然不显见与pyobject中,但是隐身于pyobject中,占用内存空间的大小是对象的元信息,这种元信息与对象所属的类型密切相关
  • 在typeobject的定义中包含了许多的信息,主要分为4类
  • 类型名:tp_name
  • 创建对象时分配内存空间的大小信息:tp_basicsize和tp_itemsize
  • 与该对象相关联的操作信息
  • pytypeobject
  • 对象就是python中对面向对象理论中类的概念
  • 基于pytypeobject的python对象体系后面会深度分析
  • 对象的创建:我们命令python创建一个整数对象,python内部究竟如何从无到有创建出一个整数对象呢?
  • 一般来说有两种方法:python C api 和pyint_type
  • python的C API分为两类:一类称为范型的API,或者称为AOL
  • 另一类是与类型相关的API或者称为COL。这类API通常只作用在某一种类型的对象上。
  • 类型的类型,其实python中类型也是一种对象类型的类型是type类型
  • python的多态特性,python在内部各个函数之间传递都是一种范性指针------pyobject*
  • python中的引用计数:在现代开发的编程语言中一般选择了由语言本身来维护和管理,即采用了垃圾收集机制,ob_refcnt变量决定这对象的创建和消亡
  • 如果熟悉设计模式中的观察者模式,就可以隐约看到其中的影子,让ob_refcnt减为0时就会触发对象销毁事件
  • 各个对象提供了不同的事件处理函数,而事件的注册动作正是在各个对象对应了类型对象中静态完成的
  • ob_refcnt是一个32位整形变量,说明一个对象的引用不会超过一个整形变量的最大值。
  • 每一个对象指向类型对象的指针不能视为对类型对象的引用
  • 当引用为0时并不意味这就要free内存,因为这样太消耗资源,python中会引入内存对象池的概念,避免了频繁申请和释放内存空间,通常是将空间归还到内存池中
  • python中的对象大致分为5类(不一定准确)
  • 类型对象、数值对象、容纳其他对象的序列集合对象、map关联对象、python虚拟机在运行时内部使用的对象
  • python中的整数对象int就是对c中原生类型long的简单的包装
  • python中的整数对象池,几乎所有的內建对象都会有自己的对象池机制
  • 两个int类型对象的比较实际上是long值的比较
  • 以整数对象为例pynumbermethods中定义了一个对象作为数值对象时的所有可选的操作信息。
  • 一共有39个函数指针,包括加减法等
  • python的所有的数据都躺在内存堆中
  • python支持调整小整数池的边界大小,但是需要自己改源码再重新编译
  • 在intobject.c蕴含着小整数对象池的所有代码,不过1000行,small_ints就是
  • 在python2.5后默认就是[-5,257),不过可以修改NSMALLPOINTS和NSMALLNEGINTS重新编译Python可以延伸小整数对象池
  • 就是一次性都放入内存,而不用每次都申请释放资源
  • 时间换空间思想
  • 如果小整数池机制被激活尝试用小整数对象池,如果不能使用小整数对象池则使用通用的整数对象池
  • python中的字符串对象,对于变长的对象在对象创建时是不知道的
  • int类型对象在对象在对象创建时长度就已经确定了
  • 在定义时不知道,在创建时知道
  • string和tuple是不可变对象
  • string是变长对象对象中的不可变对象
  • string类型变长很好理解,但是不可变对象???
  • 不可变对象使得string类型可以作为dict的键值,也使得字符串的操作效率大大降低
  • ob_shash是用来缓存对象的hash值,默认的初始值为-1,在dict中这个hash发挥了很大的作用
  • 字符串的hash值和python的intern机制使得python虚拟机运行的效率提高了20%
  • tp_itemsize指明了变长对象保存长度(一个对象在内存中的长度)
  • 和ob_size共同决定了应该额外申请的内存的大小
  • 字符串长度有限制,与所属的平台有关
  • 还需要判断字符串的长度是否为空,如果不是空字符串才创建申请内存
  • 字符串的最后为\0为了便于判断终止条件
  • 字符串对象的intern机制,当字符组长度为0时触发intern机制
  • 类似于享元模式,共享短小的字符串,下次不用再次申请
  • 这个机制即节省了空间又简化了对象之间的比较
  • 在python中因为一切都是对象,所以所有的=都是引用
  • 当a和b创建的字符串相同时,b申请空间会直接返回a的引用,而不是重新再次申请内存空间
  • intern机制只有字符串中有,基于字符串派生出来的数据结构都没有intern的机制
  • intern机制的核心在于interned,在stringobject.c中被定义为static project *interned
  • 内部就是一个map字典,用来处理是否被intern机制处理过
  • pytho对于字符串对象也创建了对象池,字符串缓冲池
  • 小整数缓冲池是python初始化时创建的,字符串缓冲池是以静态变量形式存在的
  • python初始化之后所有的stringobj指针都为空
  • 创建字符串对象先进行intern操作,将结果放入缓冲池,下次再次创建,先检查是否是字符串对象,如果检查是否在缓冲池中,如果有直接返回
  • string的效率问题,因为python的string为不可变对象,当字符串相加时就会再次申请内存空间,所以在数据量大的时候用join操作,一次性申请内存
  • python的list和c++的vector非常相似
  • list存放的都是pyobject的指针
  • list是变长的,和string不同的是其支持删除、插入等操作,可以在运行时动态的维护内存和元素,所以是可变对象
  • list中allocated和ob_size都与对象的内存管理有关
  • 和cpp的vector的管理方式是一样的,并不是存多少东西就申请多大的内存,这样的管理

第五章 dict

  • 一种关联式容器,关联式容器会极大的关注键的搜索效率,在cpp中的map会用设计良好的数据结构:RB-tree(红黑树)搜索的时间复杂度为O(log2N)
  • Python中也提供关联式的容器dict,没有利用红黑树用的散列表,在最优的情况下可以提供O(1)的搜索效率
  • 散列表的思想是通过一定的函数将需要搜索的键值映射为整数,然后访问连续内存的第n个位置就可以拿到,核心是将key映射成整数,再通过这个整数访问内存中的区域里的值,对于映射函数称为散列函数,映射后的值为散列值
  • 选择散列函数的优劣直接决定了散列表的搜索效率
  • 不同的对象经过散列函数的运算可能被映射为相同的散列值,随着存储的增加散列冲突会发生的越来越频繁
  • 当装载率大于2/3时散列冲突发生的概率就会大大增加
  • 解决散列冲突可以使用二次探测函数,Python中为开放地址法
  • hashtable中采用的是开链法
  • Python中删除对象时是伪删除,后面会重点讨论
  • dict是一大堆的entry集合

第六章 Python虚拟机

  • Python的虚拟机不像c#和Java,而是一种更高级的虚拟机
  • 通过dll文件编译成PyCodeObject字节码最后在变成机器码
  • pyc文件只是对外的一种表现形式,并不是真正的字节码
  • python虚拟机有很大部分时间浪费在确定一个符号的对象是什么
  • py文件经过Python编译器编译后共得到3个PyCodeObject对象

你可能感兴趣的:(Python源码剖析----深度探索动态语言核心技术读书笔记)