一、对象的存储结构
对象的引用计数
从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。
对象的类型
查看对象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的类型对象,那么它的类型应该是元类对象,我们尝试一下在源码解析一书上的知识。
元类的特殊性质
print(id(type)) type_obj = PyObject.from_address(id(type)) print(type_obj.typeid)
1825980464
1825980464
符合,str(对应字符串类)的类型、元类本身的类型的、元类本身,三者的地址都指向同一位置。