1、对象的引用计数

从c代码分析可知,python所有对象的内存有着同样的起始结构:引用计数+类型信息,实际上这些信息在python本体重也是可以透过包来一窥一二的,

from ctypes import *

class PyObject(Structure):
_fields_ = [("refcnt", c_size_t),
("typeid", c_void_p)] a = "this is a string" # 通过id(a)可以获得对象a的内存地址,而PyObject.from_address()可以将
# 指定的内存地址的内容转换为一个PyObject对象。通过此PyObject对象obj_a
# 可以访问对象a的结构体中的内容。
obj_a = PyObject.from_address(id(a))
print(obj_a.refcnt)
b = [a]*10
print(obj_a.refcnt)

查看对象a的引用次数,原文中返回值1,实际返回2,可能是python3相对2的改动使得多引用一次,接下来创建一个列表,此列表中的每个元素都是对象a,因此此列表应用了它10次,所以引用次数变为了12。

2、对象的类型

查看对象a的类型对象的地址,它和id(type(a))相同,而由于对象a的类型为str,因此也就是id(str),

print(obj_a.typeid)
print(id(type(a)))
print(id(str))

1825992064

1825992064

1825992064

验证一下,从这个地址读取python对象试试,

obj_s = PyObject.from_address(id(str))
print(obj_s.typeid)

1825980464

指向新的地址,如果这个地址对应python的类型对象,那么它的类型应该是元类对象,我们尝试一下在源码解析一书上的知识。

3、元类的特殊性质

print(id(type))
type_obj = PyObject.from_address(id(type))
print(type_obj.typeid)

1825980464

1825980464

符合,str(对应字符串类)的类型、元类本身的类型的、元类本身,三者的地址都指向同一位置。

4、不可变对象值存储

这种对象除了有PyObject中的两个字段之外,还有一个val字段保存实际的值,两者占用空间也就不同,这种不可变对象的内存值部分与头信息是相连的,所以使用sys.getsizeof可以一并查到:

import sys
print(sys.getsizeof(1))
print(sys.getsizeof(1.0))

28

24

5.1、扁平序列的创建

  • 指定创建元素类型(字符串为字符,数组为int或者float)
  • 获取目标对象长度(即几个数字,几个字符)
  • 有了这两个信息就可以划分内存创建对象了
    • 原数据就存储在内存中,我们指定结构体去赋予新格式,并加入新的对象头信息

模拟C语言创建扁平序列的过程,

# 在定义长度不固定的字段时,使用长度为0的数组定义一个不占内存的
# 伪字段_val。create_var_object()用来创建大小不固定的结构体对象
class PyVarObject(PyObject):
_fields_ = [("size", c_size_t)] class PyStr(PyVarObject):
_fields_ = [("hash", c_long),
("state", c_int),
("_val", c_char*0)] class PyLong(PyVarObject):
_fields_ = [("_val", c_uint16*0)] def create_var_object(struct, obj):
inner_type = None
for name, t in struct._fields_:
# 首先搜索名为_val的字段,并将其类型保存到inner_type中
if name == "_val":
inner_type = t._type_
print(inner_type)
if inner_type is not None:
# 然后创建一个PyVarObject结构体读取obj对象中的size字段
tmp = PyVarObject.from_address(id(obj))
size = tmp.size
# 再通过size字段的大小创建一个对应的Inner结构体类,它可
# 以从struct继承,因为struct中的_val字段不占据内存。
class Inner(struct):
_fields_ = [("val", inner_type*size)]
Inner.__name__ = struct.__name__
struct = Inner
return struct.from_address(id(obj)) s = 'asdfgh'
s_obj = create_var_object(PyStr, s)
s_obj.size
<class 'ctypes.c_char'>
6

由于只是模拟,我们使用一个新建的伪字符串结构去从真的字符串结构读取数据格式化解析,并创建新对象。

5.2、(伪)长整型数字

l = 0x1234567890abcd
l_obj = create_var_object(PyLong, l)
print(l_obj.size)
val = list(l_obj.val)
print(val)

<class 'ctypes.c_ushort'>

2

[43981, 14480]

不论是几进制,实际l的type就是int。这里面我们看到了,实际上我们使用了多个普通的整形数表达一个很大的值,长整型实际上是一个序列。

6、List列表类型

列表对象的长度是可变的,因此不能采用字符串那样的结构体,

而是使用了一个指针字段items指向可变长度的数组,而这个数组本身是一个指向PyObject的指针。allocated字段表示这个指针数组的长度,而size字段表示指针数组中已经使用的元素个数,即列表的长度。列表结构体本身的大小是固定的。

class PyList(PyVarObject):
# item字段是指针数组,这里表现为二级指针
_fields_ = [("items", POINTER(POINTER(PyObject))),
("allocated", c_size_t)] def print_field(self):
# size字段表示指针数组中已经使用的元素个数
# allocated字段表示这个指针数组的长度
# 指针字段items指向可变长度的数组
print (self.size, self.allocated, byref(self.items[0])) # 测试一下添加元素
def test_list():
alist = [1,2.3,"abc"]
alist_obj = PyList.from_address(id(alist)) for x in range(10):
alist_obj.print_field()
alist.append(x)
test_list()

一开始列表的长度和其指针数组的长度都是3,即列表处于饱和状态。因此向列表中添加新元素时,需要重新分配指针数组,因此指针数组的长度变为了7,而地址也发生了变化。这时列表的长度为4,因此指针数组中还有3个空位保存新的元素。由于每次重新分配指针数组时,都会预分配一些额外空间,因此往列表中添加元素的平均时间复杂度为O(1),

3 3 <cparam 'P' (000001E0A1ABB330)>
4 7 <cparam 'P' (000001E09E0B14D0)>
5 7 <cparam 'P' (000001E09E0B14D0)>
6 7 <cparam 'P' (000001E09E0B14D0)>
7 7 <cparam 'P' (000001E09E0B14D0)>
8 12 <cparam 'P' (000001E0A0832AF0)>
9 12 <cparam 'P' (000001E0A0832AF0)>
10 12 <cparam 'P' (000001E0A0832AF0)>
11 12 <cparam 'P' (000001E0A0832AF0)>
12 12 <cparam 'P' (000001E0A0832AF0)>

删除元素原理一致,

def test_list2():
alist = [1] * 10000
alist_obj = PyList.from_address(id(alist)) alist_obj.print_field()
del alist[10:]
alist_obj.print_field()
test_list2()
10000 10000 <cparam 'P' (000001E0A24E63B0)>
10 17 <cparam 'P' (000001E0A24E63B0)>

『Python』源码解析_从ctype模块理解对象的更多相关文章

  1. 『Python』源码解析_源码文件介绍

    本篇代码针对2.X版本,与3.X版本细节不尽相同,由于两者架构差别不大加之本人能力有限,所以就使用2.X体验python的底层原理了. 一.主要文件夹内容 Include :该目录下包含了Python ...

  2. 神经网络中 BP 算法的原理与 Python 实现源码解析

    最近这段时间系统性的学习了 BP 算法后写下了这篇学习笔记,因为能力有限,若有明显错误,还请指正. 什么是梯度下降和链式求导法则 假设我们有一个函数 J(w),如下图所示. 梯度下降示意图 现在,我们 ...

  3. Python简单源码解析

    主要为一些简单的源代码的解析以及一些方法的理解. 说明:这些文件都不是我写的,详情可参考Github上的内容. 批量修改文件类型 def batch_rename(work_dir, old_ext, ...

  4. jquery 1.7.2源码解析(二)构造jquery对象

    构造jquery对象 jQuery对象是一个类数组对象. 一)构造函数jQuery() 构造函数的7种用法: 1.jQuery(selector [, context ]) 传入字符串参数:检查该字符 ...

  5. 【Spring源码解析】—— 结合SpringMVC过程理解IOC容器初始化

    关于IOC容器的初始化,结合之前SpringMVC的demo,对其过程进行一个相对详细的梳理,主要分为几个部分: 一.IOC的初始化过程,结合代码和debug过程重点说明 1. 为什么要debug? ...

  6. jQuery 源码解析(三十) 动画模块 $.animate()详解

    jQuery的动画模块提供了包括隐藏显示动画.渐显渐隐动画.滑入划出动画,同时还支持构造复杂自定义动画,动画模块用到了之前讲解过的很多其它很多模块,例如队列.事件等等, $.animate()的用法如 ...

  7. jquery源码解析:val方法和valHooks对象详解

    这一课,我们将讲解val方法,以及对value属性的兼容性处理,jQuery中通过valHooks对象来处理. 首先,我们先来看下val方法的使用: $("#input1").va ...

  8. jQuery 源码解析(八) 异步队列模块 Callbacks 回调函数详解

    异步队列用于实现异步任务和回调函数的解耦,为ajax模块.队列模块.ready事件提供基础功能,包含三个部分:Query.Callbacks(flags).jQuery.Deferred(funct) ...

  9. 【Spring源码解析】—— 委派模式的理解和使用

    一.什么是委派模式 委派模式,是指什么呢?从字面含义理解,委派就是委托安排的意思,委派模式就是在做具体某件事情的过程中,交给其他人来做,这个事件就是在我的完整链路上的一部分,但是复杂度较高的情况下或者 ...

随机推荐

  1. Centos7.1环境下搭建SVN

    环境准备: 系统 配置 IP Centos7.1 1核2G+60GB硬盘 10.10.28.204 1.安装 sudo yum install subversion 查看版本 svnserve –-v ...

  2. git rest 的相关操作

    git rest [parameter] 1. --soft 如果想撤销commit,并且只回退commit的信息  git diff返回空 git diff –cached 和 git diff H ...

  3. AppFabric查询工作流实例

    安装 通过IIS查询工作流实例,可以操作挂起,首先打开WF+WCF的站点: 这里可以搜索工作流实例:例如根据工作流ID.创建日期.状态等查询 下方的搜索结果可以查看结果 资源 Windows Serv ...

  4. es6阮一峰读后感

    不经意间看了你一眼(阮一峰的es6读后感)我自己常用的 字符串篇:ES6 为字符串添加了遍历器接口(详见<Iterator>一章),使得字符串可以被for...of循环遍历.只要有遍历器接 ...

  5. navicat for mysql 数据库备份与还原

    一, 首先设置, 备份保存路径 工具 -> 选项 点开 其他 -> 日志文件保存路径 二. 开始备份 备份分两种, 一种是以sql保存, 一种是保存为备份 SQL保存 右键点击你要备份的数 ...

  6. Netty返回数据丢包的问题之一

    这个问题是在一个群友做压力测试的时候发现的.使用客户端和netty创建一条连接,然后写了一个for循环不停的给服务器发送1500条信息,发现返回只有几百条.另外几百条不知道哪去了.查看代码,发现在服务 ...

  7. mecacheq的配置

    在处理业务逻辑时有可能遇到高并发问题,例如商城秒杀.微博评论等.如果不做任何措施可能在高瞬间造成服务器瘫痪,如何解决这个问题呢?队列是个不错的选择.队列(Queue)又称先进先出(First In F ...

  8. Spring Cloud配置中心(Config)

    Spring Cloud配置中心(Config) Spring Cloud是现在流行的分布式服务框架,它提供了很多有用的组件.比如:配置中心.Eureka服务发现. 消息总线.熔断机制等. 配置中心在 ...

  9. Mysql关于时间排序的问题

    SELECT * FROM table_name WHERE deleted = 0 order by create_time DESC 当用户使用DESC(降序时),最终得到的结果集,时间早的在前面 ...

  10. vue项目初始化时npm run dev报错webpack-dev-server解决方法

    vue项目初始化时npm run dev报错webpack-dev-server解决方法 原因:这是新版webpack存在的BUG,卸载现有的新版本webpack,装老版本就好webpack-dev- ...