为什么 SimpleNamespace 的大小与空类的大小不同?

jua*_*aga 2 python

考虑以下:

In [1]: import types

In [2]: class A:
    ...:     pass
    ...:

In [3]: a1 = A()

In [4]: a1.a, a1.b, a1.c = 1, 2, 3

In [5]: a2 = types.SimpleNamespace(a=1,b=2,c=3)

In [6]: sys.getsizeof(a1)
Out[6]: 56

In [7]: sys.getsizeof(a2)
Out[7]: 48
Run Code Online (Sandbox Code Playgroud)

这种尺寸差异从何而来?看着:

In [10]: types.__file__
Out[10]: '/Users/juan/anaconda3/lib/python3.5/types.py'
Run Code Online (Sandbox Code Playgroud)

我发现:

import sys

# Iterators in Python aren't a matter of type but of protocol.  A large
# and changing number of builtin types implement *some* flavor of
# iterator.  Don't check the type!  Use hasattr to check for both
# "__iter__" and "__next__" attributes instead.

def _f(): pass
FunctionType = type(_f)
LambdaType = type(lambda: None)         # Same as FunctionType
CodeType = type(_f.__code__)
MappingProxyType = type(type.__dict__)
SimpleNamespace = type(sys.implementation)
Run Code Online (Sandbox Code Playgroud)

好吧,这里什么都没有:

>>> import sys
>>> sys.implementation
namespace(cache_tag='cpython-35', hexversion=50660080, name='cpython', version=sys.version_info(major=3, minor=5, micro=2, releaselevel='final', serial=0))
>>> type(sys.implementation)
<class 'types.SimpleNamespace'>
Run Code Online (Sandbox Code Playgroud)

我似乎在这里追逐自己的尾巴。

我能够找到这个相关问题,但没有回答我的特定查询。

我在 64 位系统上使用 CPython 3.5。对于某些我无法确定的错误参考,这 8 个字节的大小似乎恰到好处。

don*_*mus 5

考虑以下具有不同大小的类:

class A_dict:
    pass

class A_slot_0:
    __slots__ = []

class A_slot_1:
    __slots__ = ["a"]

class A_slot_2:
    __slots__ = ["a", "b"]
Run Code Online (Sandbox Code Playgroud)

每一个都有不同的基本内存占用:

>>> [cls.__basicsize__ for cls in [A_dict, A_slot_0, A_slot_1, A_slot_2]]
>>> [32, 16, 24, 32]
Run Code Online (Sandbox Code Playgroud)

为什么?在type_new(in typeobject.c)的源代码中,它负责创建底层类型并计算实例的基本大小,我们看到它tp_basicsize被计算为:

  • tp_basicsize基础类型的(object... 16个字节);
  • 另一个sizeof(PyObject *)用于每个槽;
  • asizeof(PyObject *)如果__dict__需要 a;
  • asizeof(PyObject *)如果__weakref__定义了a ;

一个普通的类,例如A_dict将有 a__dict__和 a__weakref__定义,而带有槽的类在__weakref__默认情况下没有。因此,plain 的大小A_dict是 32 字节。您可以认为它实际上由PyObject_HEAD两个指针组成。

现在,考虑在SimpleNamespace中定义的 a namespaceobject.c。这里的类型很简单:

typedef struct {
    PyObject_HEAD
    PyObject *ns_dict;
} _PyNamespaceObject;
Run Code Online (Sandbox Code Playgroud)

并且tp_basicsize被定义为sizeof(_PyNamespaceObject),使它比普通对象大一个指针,因此是 24 个字节。

笔记:

这里的区别是有效地A_dict提供了对弱引用的支持,而types.SimpleNamespace没有。

>>> weakref.ref(types.SimpleNamespace())
TypeError: cannot create weak reference to 'types.SimpleNamespace' object
Run Code Online (Sandbox Code Playgroud)