3

Consider the following:

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

Where is this size discrepancy coming from? Looking at:

In [10]: types.__file__
Out[10]: '/Users/juan/anaconda3/lib/python3.5/types.py'

I find:

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)

Ok, well, here goes nothing:

>>> 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'>

I seem to be chasing my own tail here.

I was able to find this related question, but no answer to my particular query.

I am using CPython 3.5 on a 64-bit system. These 8 bytes seem just the right size for some errant reference that I cannot pin-point.

Community
  • 1
  • 1
juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
  • 1
    Its coming because they are different types ... and `sys.getsizeof` doesn't tell you the total memory consumed by an object, so why should it be the same for these two types? eg In your example, `getsizeof(A())` (an "empty" `A`) is also 56. – donkopotamus Feb 06 '17 at 23:21
  • Right, adding attributes is a red herring. But even if I just have an empty class `class A: pass; a = A()` and `s = SimpleNamespace()`, there is still the discrepancy. – juanpa.arrivillaga Feb 06 '17 at 23:27
  • Sure ... but again, why do you expect two empty types to have the same size? Do you expect a `set()` and `[]` to have the same size? – donkopotamus Feb 06 '17 at 23:40
  • @donkopotamus not necessarily, but I want to understand *why*. – juanpa.arrivillaga Feb 06 '17 at 23:47
  • 1
    The definition of the `SimpleNamespace` class is in the [`namespaceobject.c`](https://hg.python.org/cpython/file/3.6/Objects/namespaceobject.c#l198) source file. – martineau Feb 07 '17 at 00:35
  • OT: gracias-Thanks-Danke! – ΦXocę 웃 Пepeúpa ツ Aug 08 '17 at 07:28

1 Answers1

5

Consider the following classes, which have different sizes:

class A_dict:
    pass

class A_slot_0:
    __slots__ = []

class A_slot_1:
    __slots__ = ["a"]

class A_slot_2:
    __slots__ = ["a", "b"]

Each of these has a differing fundamental memory footprint:

>>> [cls.__basicsize__ for cls in [A_dict, A_slot_0, A_slot_1, A_slot_2]]
>>> [32, 16, 24, 32]

Why? In the source of type_new (in typeobject.c), which is responsible for creating the underlying type and computing the basic size of an instance, we see that tp_basicsize is computed as:

  • the tp_basicsize of the underlying type (object ... 16 bytes);
  • another sizeof(PyObject *) for each slot;
  • a sizeof(PyObject *) if a __dict__ is required;
  • a sizeof(PyObject *) if a __weakref__ is defined;

A plain class such as A_dict will have a __dict__ and a __weakref__ defined, whereas a class with slots has no __weakref__ by default. Hence the size of plain A_dict is 32 bytes. You could consider it to effectively consist of PyObject_HEAD plus two pointers.

Now, consider a SimpleNamespace, which is defined in namespaceobject.c. Here the type is simply:

typedef struct {
    PyObject_HEAD
    PyObject *ns_dict;
} _PyNamespaceObject;

and tp_basicsize is defined as sizeof(_PyNamespaceObject), making it one pointer larger than a plain object, and thus 24 bytes.

NOTE:

The difference here is effectively that A_dict provides support for taking weak references, while types.SimpleNamespace does not.

>>> weakref.ref(types.SimpleNamespace())
TypeError: cannot create weak reference to 'types.SimpleNamespace' object
donkopotamus
  • 22,114
  • 2
  • 48
  • 60