After watching Nina Zahkarenko's Python Memory Management talk at Pycon2016 (link), it seemed like the dunder method __slots__
was a tool to reduce object size and speed up attribute lookup.
My expectation was that a normal class would be the largest, while a __slots__
/namedtuple
approach would save space. However, a quick experiment with sys.getsizeof()
seems to suggest otherwise:
from collections import namedtuple
from sys import getsizeof
class Rectangle:
'''A class based Rectangle, with a full __dict__'''
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
class SlotsRectangle:
'''A class based Rectangle with __slots__ defined for attributes'''
__slots__ = ('x', 'y', 'width', 'height')
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
NamedTupleRectangle = namedtuple('Rectangle', ('x', 'y', 'width', 'height'))
NamedTupleRectangle.__doc__ = 'A rectangle as an immutable namedtuple'
print(f'Class: {getsizeof(Rectangle(1,2,3,4))}')
print(f'Slots: {getsizeof(SlotsRectangle(1,2,3,4))}')
print(f'Named Tuple: {getsizeof(NamedTupleRectangle(1,2,3,4))}')
Terminal Output:
$ python3.7 example.py
Class: 56
Slots: 72
Named Tuple: 80
What is going on here? From the docs on Python's Data Model it appears that descriptors are used for __slots__
which would add function overhead to classes implementing it. However, why are the results so heavily skewed towards a normal class?
Channeling my inner Raymond H.: there has to be a harder way!