这取决于什么样的对象,以及哪个Python实现:-)

在CPython中,这是大多数人在使用python时使用的,所有Python对象都由C结构PyObject表示. “存储对象”的所有内容实际上都存储了PyObject *. PyObject结构保存最基本的信息:对象的类型(指向另一个PyObject的指针)及其引用计数(ssize_t大小的整数.)C中定义的类型扩展了这个结构,它们需要存储在对象本身中的额外信息,有时分开分配额外的数据.

例如,元组(实现为PyTupleObject“扩展”PyObject结构)存储它们的长度以及它们包含在结构本身内的PyObject指针(结构在定义中包含1长度数组,但实现分配了一块内存保持PyTupleObject结构的正确大小以及与元组应该保持的项目完全相同的数量.)同样,字符串(PyStringObject)存储它们的长度,它们的缓存哈希值,一些字符串缓存(“实习”)簿记,以及他们数据的实际char *.因此,元组和字符串是单个内存块.

另一方面,列表(PyListObject)存储它们的长度,PyObject **用于存储它们的数据,另一个ssize_t用于跟踪它们为数据分配的空间.因为Python在任何地方存储PyObject指针,所以一旦分配了PyObject结构就不能生成它 – 这样做可能需要移动结构,这意味着找到所有指针并更新它们.因为列表可能需要增长,所以它必须与PyObject结构分开分配数据.元组和字符串不能增长,因此它们不需要这样. Dicts(PyDictObject)以相同的方式工作,尽管它们存储密钥的值,值和缓存的哈希值,而不仅仅是项目. Dict还有一些额外的开销来容纳小的dicts和专门的查找功能.

但是这些都是C中的类型,你通常可以通过查看C源来查看它们将使用多少内存.用Python而不是C定义的类实例并不那么容易.最简单的情况,经典类的实例,并不是那么困难:它是一个PyObject,它将PyObject *存储到它的类中(这与PyObject结构中存储的类型不同),PyObject *到它的__dict__属性(它包含所有其他实例属性)和PyObject *到其weakreflist(由weakref模块使用,并且只在必要时才初始化.)实例的__dict__通常对实例是唯一的,因此在计算“内存大小”时这样的实例你通常也想要计算属性dict的大小.但它不必具体针对实例! __dict__可以分配到很好.

新式课程复杂化.与经典类不同,新样式类的实例不是单独的C类型,因此它们不需要单独存储对象的类.它们确实有__dict__和weakreflist引用的空间,但与经典实例不同,它们不需要__dict__属性用于任意属性.如果类(及其所有基类)使用__slots__来定义严格的属性集,并且这些属性都没有命名为__dict__,则实例不允许任意属性,也不分配dict.另一方面,__slots__定义的属性必须存储在某处.这是通过直接在PyObject结构中存储这些属性的值的PyObject指针来完成的,就像用C编写的类型一样.__slots__中的每个条目因此将占用PyObject *,无论该属性是否已设置或不.

所有这一切,问题仍然存在,因为Python中的所有东西都是一个对象,而拥有一个对象的所有内容都只是一个引用,有时很难在对象之间绘制线条.两个对象可以引用相同的数据位.他们可能只拥有该数据的两个引用.摆脱两个对象也摆脱了数据.他们都拥有这些数据吗?只有其中一个,但如果是这样,哪一个?或者你会说他们拥有一半的数据,即使摆脱一个对象不会释放一半的数据? Weakrefs可以使这更加复杂:两个对象可以引用相同的数据,但删除其中一个对象可能会导致另一个对象也摆脱对该数据的引用,导致数据被清除.

幸运的是,常见的情况很容易弄明白.有一些用于Python的内存调试器可以很好地跟踪这些事情,比如heapy.只要你的类(及其基类)相当简单,你就可以有条不紊地猜测它会占用多少内存. – 特别是大量的.如果您真的想知道数据结构的确切大小,请咨询CPython源代码;大多数内置类型是Include /< type> object.h中描述的简单结构,并在Objects /< type> object.c中实现. PyObject结构本身在Include / object.h中描述.请记住:它一直指向下方;那些也占用了房间.