python 对象
python 对象
在python中,对象就是为C中的结构体在堆上申请的一块内存,一般来说,对象是不能被静态初始化的,并且不能再栈空间上生存。本文主要对Python的基本数据类型做简单的介绍。
PyObject
在python中,所有东西都是对象,而所欲的对象都拥有一些共性(object.h/PyObject)。
PyObject是整个python对象机制的核心。
typedef struct _object {
PyObject_HEAD
} PyObject;
在release模式下编译python时,PyObject如下:
typedef struct _object {
int ob_refcnt; //引用计数
struct _typeobject *ob_type; //类型
} PyObject;
其中,ob_refcnt为int整型,实现了基于引用计数的垃圾收集机制。对于某一对象A,当有一个新的PyObject引用该对象时,A的引用计数应该增加;而当这个PyObject被删除时,A的引用计数应该减少;当A的引用计数减少到0时,A就可以从堆上被删除,以释放出内存供别的对象使用。
引用计数
PyObject中的ob_refcnt是一个32位的整形变量,这实际蕴含着Python所有的一个假设,既对一个对象的引用不会超过一个整形变量的最大值。
整数对象
小整数对象
在python中,所有对象都存活在堆上,python重复地使用malloc申请空间,大大降低了运行效率,造成大量内存碎片,影响整体性能。因此,在python中,对小整数对象使用了对象池技术,用来缓存所有的小整形对象(对重复的对象不需要重复的malloc)。
NSMALLPOSINTS和NSMALLNEGINTS来修改小整数对象池的范围(默认分别为257和-5)。
大整数对象
对于小整数,使用对象池技术完全缓存其PyIntObject对象,而对于其他整数,python提供了一种PyIntBlock结构供大整形对象使用。PyIntBlock是通过维护一块内存(Block)来供大整数使用,并通过单向列表block_list来维护。
Python的设计者为了提高代码执行效率放弃了类型安全使用了宏来代替函数。
当一个PyIntObject对象被销毁时,它所占用的内存并不会被释放,而是继续被python保留着,加入到free_list所维护的自有内存链表,为其他需要创建对象的内存使用。
字符串对象
在python中,PyStringObject是字符串对象的实现,它是一个拥有可变长度内存的对象。
trpdef struct {
PyObject_VAR_HEAD //ob_size字符串长度
long ob_shash; //字符串hash值
int ob_sstate;
char ob_sval[1];
} PyStringObject;
在PyStringObject中,还使用了intern机制和缓存池技术。
intern机制和缓存池
intern机制的目的:保证被intern之后的字符串在python整个运行期间只对应唯一的一个PyStringObject对象。
intern机制的关键是在系统中有一个(key,value)映射的集合,记录所有被intern机制处理过的PyStringObject对象。当python在创建一个字符串时,会首先在interned中检查是否已经有该字符串对应的PyStringObject对象了,如果有,则不用创建新的,这样可以节省内存空间。其实,Python始终会为字符串创建PyStringObject对象,intern机制是在创建之后才会生效的,通常python在运行时创建一个PyStrhingObject对象temp后,基本就会销毁该对象(引用计数减1)。
类似小整形的缓存池,python为PyStringObject中的一个字节的字符对应的PyStringObject对象设计对象池characters。
Python中的处理顺序:
- 判断是否为一个字符
- 创建PyStringObject对象进行intern操作
- 将intern结果缓存到字符缓冲池中
PyStringObject效率问题
在Python中"+"操作符进行字符串连接的方法效率极其低下,其根源在于python中的PyStringObject对象是一个不可变对象
。这就意味着当进行字符串连接时,实际上必须要创建一个新的PyStringObject对象。因此,如果需要连接N个PyStringObject对象,就必须进行N-1次内存申请及内存搬运工作,严重影响python的执行效率。
官方推荐利用PyStringObject对象的join操作来对存储在list和tuple中一组PyStringObject对象进行连接操作,只需要分配一次内存,执行效率大大提高。
join操作会统计出list中所有PyStringObject对象的字符串长度,然后申请内存。
列表对象
PyListObject是Python提供的对列表的抽象。它类似于C++中的vector,而不是list。
typedef struct {
PyObject_VAR_HEAD
PyObject **ob_item; //元素首地址
int allocated; //实际申请内存的个数(部分没有被使用,类似于vector)
} PyListObject;
其中,ob_item为指向元素列表的指针。
python所采用的内存管理策略和c++中vector采取的内存管理策略一样,并不是寸多少数据就要申请对应大小的空间,这样内存管理的效率较低,因此,在每一次申请内存时PyListObject总会申请一大块内存,该内存的大小就记录在allocated中,而实际被使用的内存数量记录在ob_size中。
对象缓存池
在创建一个新的list时,首先创建PyListObject对象,然后创建PyListObject对象所维护的元素列表;在销毁一个List时,首先销毁PyListObject所维护的列表,然后释放掉PyListObject本身,但是在释放之前python会检查缓冲池free_lists,查看其中缓存的PyListObject的数量是佛已经满了,如果没有,就将该数据对象加入到穿存池中,否则删除。
Dict对象
与map不同,PyDictObject采用散列表(hash table)。在最优情况下,散列表能提供O(1)复杂度的搜索效率。
散列表
散列表的基本思想是通过一个函数将需搜索的键值映射为一个整数,将这个整数视为索引值访问某片连续性的内存区域。
在使用散列表的过程中,不同的对象经过散列函数的作用,可能被映射为相同的散列值。随着需要存储的数据的增多,这样的冲突就会发生得越来越频繁。散列冲突是散列表不可避免的问题,当散列表的装载率(已使用空间和总空间的比值)大于2/3时,散列冲突发生的概率就会大大增加。
在STL库的hash table采用开链法来解决冲突,而python中采用开放地址法。当产生冲突时,python会通过一个二次探测函数f计算下一个候选位置,如果该候选位置可用,则可将待插入元素放到该位置,否则再次调用探测函数f,寻找可用位置。
关联容器entry
typedef struct {
Py_ssize me_hash; //散列值
PyObject *me_key;
PyObject *me_value;
}
在PyDictObject对象生存变化的过程中,其中entry会在不同的状态间转换:Unused、Active和Dummy。
- Unused:当一个entry的me_key和me_value都是NULL;
- Active:当entry中存储了一个(key,value)对时;
- dummy:当entry中存储的(key,value)被删除后,entry的状态不能直接从Active转到unused(伪删除),否则会出现冲突探测链中断。
python 对象的更多相关文章
- python征程3.0(python对象)
1.python使用对象模型来存储数据.构造任何类型的值都是一个对象.”尽管python被当成一种面向对象的脚本的编程语言“,但你完全能够写出不使用任何类和实例的脚本. python对象都拥有三个特性 ...
- Python 对象的引用计数和拷贝
Python 对象的引用计数和拷贝 Python是一种面向对象的语言,包括变量.函数.类.模块等等一切皆对象. 在python中,每个对象有以下三个属性: 1.id,每个对象都有一个唯一的身份标识自己 ...
- Python对象(译)
这是一篇我翻译的文章,确实觉得原文写的非常好,简洁清晰 原文链接:http://effbot.org/zone/python-objects.htm ------------------------- ...
- 《Python核心编程》 第四章 Python对象- 课后习题
练习 4-1. Python对象.与所有Python对象有关的三个属性是什么?请简单的描述一下. 答:身份.类型和值: 身份:每一个对象都有一个唯一的身份标识自己,可以用id()得到. 类型:对象的 ...
- Python对象体系揭秘
Guido用C语言创造了Python,在Python的世界中一切皆为对象. 一.C视角中的Python对象 让我们一起追溯到源头,Python由C语言实现,且向外提供了C的API http://doc ...
- 【2】python核心编程 第四章-python对象
1.python对象 所有的Python 对像都拥有三个特性:身份,类型和值. 身份: 每一个对象都有一个唯一的身份标识自己,任何对象的身份可以使用内建函数id()来得到. 这个值可以被认为是该对象的 ...
- python学习笔记:python对象
一.python对象 python使用对象模型来存储数据,构造任何类型的值都是一个对象.所有的python对象都拥有三个特性:身份.类型和值. 身份:每个对象都有一个唯一的身份标识自己,对象的身份可以 ...
- Python对象类型及其运算
Python对象类型及其运算 基本要点: 程序中储存的所有数据都是对象(可变对象:值可以修改 不可变对象:值不可修改) 每个对象都有一个身份.一个类型.一个值 例: >>> a1 = ...
- 判断python对象是否可调用的三种方式及其区别
查找资料,基本上判断python对象是否为可调用的函数,有三种方法 使用内置的callable函数 callable(func) 用于检查对象是否可调用,返回True也可能调用失败,但是返回False ...
随机推荐
- C语言之数组,字符串,指针
一. 数组的定义 1. 数组初始化 初始化方式 int a[3] = {10, 9, 6}; int a[3] = {10,9}; int a[] = {11, 7, 6}; int a[4] = ...
- winfrom 捕获是否点击关闭按钮关闭的窗体
const int WM_SYSCOMMAND = 0x0112; const int SC_CLOSE = 0xF060; protected override void WndProc(ref M ...
- 【Mail】JavaMail介绍及发送邮件(一)
JavaMail介绍 JavaMail是SUN提供给开发人员在应用程序中实现邮件发送和接收功能而提供的一套标准开发类库,支持常用的邮件协议,如SMTP.POP3.IMAP,开发人员使用JavaMail ...
- [django]在virtualenv下安装的第三方库的使用方法
在virtualenv下安装的第三方库,例如south, requests等,如果想在django中使用,需要先将库添加到settings.py的INSTALLED_APPS中, 以south, re ...
- 【转】25个必须记住的SSH命令
1.复制SSH密钥到目标主机,开启无密码SSH登录 ssh-copy-id user@host 如果还没有密钥,请使用ssh-keygen命令生成. 2.从某主机的80端口开启到本地主机2001端口的 ...
- 1032: [JSOI2007]祖码Zuma
链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1032 Description 这是一个流行在Jsoi的游戏,名称为祖玛.精致细腻的背景,外加神 ...
- spring-aop学习
SpringAOP学习 author:luojie 1. AOP中的基本概念 AOP的通用术语,并非spring java所特有.很遗憾AOP的术语不是特别的直观.但如果让Spring java来 ...
- Linux图片批处理
通过imagemagick的convert命令来处理. 将多个图片横向拼接(宽图): convert +append 1.jpg 2.jpg all.jpg #人为指定顺序 convert +appe ...
- js生成验证码并检验
<html> <head> <title>验证码</title> <style type="text/css"> #co ...
- [LeetCode] 452 Minimum Number of Arrows to Burst Balloons
There are a number of spherical balloons spread in two-dimensional space. For each balloon, provided ...