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. python框架之Django(15)-contenttype模块

    假如有一个书城系统,需要给作者和书籍加上评论功能.如果给每个表单独建一个评论表,那么我们以后要扩展其它模块评论功能的时候,还需要随之新建一张评论表,会显得很冗余.对于这种情况,Django 给我们提供 ...

  2. Java 基础 IO流(转换流,缓冲)

    一,前言 在学习字符流(FileReader.FileWriter)的时候,其中说如果需要指定编码和缓冲区大小时,可以在字节流的基础上,构造一个InputStreamReader或者OutputStr ...

  3. json to entity in api

    using (var client = new HttpClient()) { var WVMId = DB.Vehicles.Where(v => v.Id == new Guid(vehic ...

  4. reactjs中使用高德地图计算两个经纬度之间的距离

    第一步下载依赖 npm install --save react-amap 第二步,在组件中使用 import React, { Component } from 'react' import { L ...

  5. Mybatis入门及于hibernate的区别

    pojo:不按mvc分层,只是java bean有一些属性,还有get set方法domain:不按mvc分层,只是java bean有一些属性,还有get set方法po:用在持久层,还可以再增加或 ...

  6. vue和jQuery嵌套实现异步ajax通信

    <!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8&qu ...

  7. 今天整理了一下Winform用的UI插件信息

    平时主要用了一下几个比较好的UI控件: 1:IrisSkin2 皮肤插件.这是一款与编程开发相关的素材资源,主要是提供一些采用IrisSkin2.dll控件进行软件窗口换肤的素材文件,包括一些GIF图 ...

  8. 【软件工程1916|W(福州大学)_助教博客】团队第一次作业成绩公示

    题目 第一次作业 评分准则: 队名(最好能够体现项目内容,要求有亮点与个性):(1分) 拟作的团队项目描述:一句话(中英文不限):(1分) 队员风采:介绍每一名队员,包括成员性格.擅长的技术.编程的兴 ...

  9. 我设计的电脑usb红外遥控键盘原理图

    我设计的电脑usb红外遥控键盘,orcad原理图备份如下:

  10. 石家庄铁道大学课程信息管理系统(javaWeb+servlet+Mysql)

    实现网页版的课程管理系统,具有增删改查的功能. 1.首先连接数据库,具体数据库的使用及如何连接eclipse,参考     https://blog.csdn.net/lrici/article/de ...