Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Performance Comparison of namedtuple, namedlist, and recordclass
import sys
class Record(object):
__slots__ = ()
def __init__(self, *args):
assert len(self.__slots__) == len(args)
for field, value in zip(self.__slots__, args):
setattr(self, field, value)
def __getitem__(self, index):
return getattr(self, self.__slots__[index])
def __len__(self):
return len(self.__slots__)
def __eq__(self, that):
if not isinstance(that, type(self)):
return NotImplemented
return all(item == iota for item, iota in zip(self, that))
def __repr__(self):
args = ', '.join(repr(item) for item in self)
return '%s(%s)' % (type(self).__name__, args)
def __getstate__(self):
return tuple(self)
def __setstate__(self, state):
self.__init__(*state)
from collections import namedtuple
from namedlist import namedlist
from recordclass import recordclass
NTTest = namedtuple('NTTest', 'a b c d e f')
NLTest = namedlist('NLTest', 'a b c d e f')
RCTest = recordclass('RCTest', 'a b c d e f')
class RETest(Record):
__slots__ = 'a', 'b', 'c', 'd', 'e', 'f'
nttest = NTTest(1, 2, 3, 4, 5, 6)
nltest = NLTest(1, 2, 3, 4, 5, 6)
rctest = RCTest(1, 2, 3, 4, 5, 6)
retest = RETest(1, 2, 3, 4, 5, 6)
print 'Attribute Access'
print 'namedtuple'
%timeit nttest.c
print 'namedlist'
%timeit nltest.c
print 'recordclass'
%timeit rctest.c
print 'Record'
%timeit retest.c
print 'Object Size'
print 'namedtuple'
print sys.getsizeof(nttest)
print 'namedlist'
print sys.getsizeof(nltest)
print 'recordclass'
print sys.getsizeof(rctest)
print 'Record'
print sys.getsizeof(retest)
@grantjenks

This comment has been minimized.

Copy link
Owner Author

@grantjenks grantjenks commented Dec 20, 2016

Output:

Attribute Access
namedtuple
The slowest run took 16.59 times longer than the fastest. This could mean that an intermediate result is being cached.
10000000 loops, best of 3: 115 ns per loop
namedlist
10000000 loops, best of 3: 41.5 ns per loop
recordclass
10000000 loops, best of 3: 49 ns per loop
Record
10000000 loops, best of 3: 41.7 ns per loop
Object Size
namedtuple
104
namedlist
96
recordclass
104
Record
96

My advice is to prefer the Record recipe above. Thirty lines of code and it pretty prints, pickles, copies, and unpacks. You can also easily customize to your liking.

@C0oo1D

This comment has been minimized.

Copy link

@C0oo1D C0oo1D commented May 5, 2019

#python 3.7
import sys
from lib import exec_time  # based on get min time of timeit.Timer and partial().repeat()

from collections import namedtuple
from namedlist import namedlist
from recordclass import recordclass, structclass


class Record(object):
    __slots__ = ()

    def __init__(self, *args):
        assert len(self.__slots__) == len(args)
        for field, value in zip(self.__slots__, args):
            setattr(self, field, value)

    def __getitem__(self, index):
        return getattr(self, self.__slots__[index])

    def __len__(self):
        return len(self.__slots__)

    def __eq__(self, that):
        if not isinstance(that, type(self)):
            return NotImplemented
        return all(item == iota for item, iota in zip(self, that))

    def __repr__(self):
        args = ', '.join(repr(item) for item in self)
        return '%s(%s)' % (type(self).__name__, args)

    def __getstate__(self):
        return tuple(self)

    def __setstate__(self, state):
        self.__init__(*state)


params_count = 6
fields = tuple(hex(_)[1:] for _ in range(params_count))
args = tuple(range(params_count))

NTTest = namedtuple('NTTest', fields)
NLTest = namedlist('NLTest', fields)
RCTest = recordclass('RCTest', fields)
SCTest = structclass('SCTest', fields)


class RETest(Record):
    __slots__ = fields


print('\nCreate:')
print(f'\tnamedtuple: {exec_time(NTTest, *args)}')
print(f'\tnamedlist: {exec_time(NLTest, *args)}')
print(f'\trecordclass: {exec_time(RCTest, *args)}')
print(f'\tstructclass: {exec_time(SCTest, *args)}')
print(f'\tRecord: {exec_time(RETest, *args)}')


nttest = NTTest(*args)
nltest = NLTest(*args)
rctest = RCTest(*args)
sctest = SCTest(*args)
retest = RETest(*args)

print('\nAccess:')
print(f'\tnamedtuple: {exec_time(lambda: nttest.x1)}')
print(f'\tnamedlist: {exec_time(lambda: nltest.x1)}')
print(f'\trecordclass: {exec_time(lambda: rctest.x1)}')
print(f'\tstructclass: {exec_time(lambda: sctest.x1)}')
print(f'\tRecord: {exec_time(lambda: retest.x1)}')

print('\nSize:')
print(f'\tnamedtuple: {sys.getsizeof(nttest)}')
print(f'\tnamedlist: {sys.getsizeof(nltest)}')
print(f'\trecordclass: {sys.getsizeof(rctest)}')
print(f'\tstructclass: {sys.getsizeof(sctest)}')
print(f'\tRecord: {sys.getsizeof(retest)}')

Create:
namedtuple: 496 ns
namedlist: 4.07 µs
recordclass: 422 ns
structclass: 509 ns
Record: 1.94 µs

Access: (lambda used, can be not so valid)
namedtuple: 217 ns
namedlist: 186 ns
recordclass: 180 ns
structclass: 186 ns
Record: 183 ns

Size:
namedtuple: 52
namedlist: 48
recordclass: 36
structclass: 32
Record: 48

@C0oo1D

This comment has been minimized.

Copy link

@C0oo1D C0oo1D commented May 6, 2019

print('\nAccess:')
print(f'\tnamedtuple: {exec_time(lambda: (nttest.x0, nttest.x1, nttest.x2, nttest.x3, nttest.x4, nttest.x5))}')
print(f'\tnamedlist: {exec_time(lambda: (nltest.x0, nltest.x1, nltest.x2, nltest.x3, nltest.x4, nltest.x5))}')
print(f'\trecordclass: {exec_time(lambda: (rctest.x0, rctest.x1, rctest.x2, rctest.x3, rctest.x4, rctest.x5))}')
print(f'\tstructclass: {exec_time(lambda: (sctest.x0, sctest.x1, sctest.x2, sctest.x3, sctest.x4, sctest.x5))}')
print(f'\tRecord: {exec_time(lambda: (retest.x0, retest.x1, retest.x2, retest.x3, retest.x4, retest.x5))}')

Access:
namedtuple: 621 ns
namedlist: 496 ns
recordclass: 434 ns
structclass: 481 ns
Record: 465 ns

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment