What is the fastest (to access) struct-like object in Python?

One thing to bear in mind is that namedtuples are optimised for access as tuples. If you change your accessor to be a[2] instead of a.c, you’ll see similar performance to the tuples. The reason is that the name accessors are effectively translating into calls to self[idx], so pay both the indexing and the name lookup price.

If your usage pattern is such that access by name is common, but access as tuple isn’t, you could write a quick equivalent to namedtuple that does things the opposite way: defers index lookups to access by-name. However, you’ll pay the price on the index lookups then. Eg here’s a quick implementation:

def makestruct(name, fields):
    fields = fields.split()
    import textwrap
    template = textwrap.dedent("""\
    class {name}(object):
        __slots__ = {fields!r}
        def __init__(self, {args}):
            {self_fields} = {args}
        def __getitem__(self, idx): 
            return getattr(self, fields[idx])
    """).format(
        name=name,
        fields=fields,
        args=",".join(fields), 
        self_fields=",".join('self.' + f for f in fields))
    d = {'fields': fields}
    exec template in d
    return d[name]

But the timings are very bad when __getitem__ must be called:

namedtuple.a  :  0.473686933517 
namedtuple[0] :  0.180409193039
struct.a      :  0.180846214294
struct[0]     :  1.32191514969

ie, the same performance as a __slots__ class for attribute access (unsurprisingly – that’s what it is), but huge penalties due to the double lookup in index-based accesses. (Noteworthy is that __slots__ doesn’t actually help much speed-wise. It saves memory, but the access time is about the same without them.)

One third option would be to duplicate the data, eg. subclass from list and store the values both in the attributes and listdata. However you don’t actually get list-equivalent performance. There’s a big speed hit just in having subclassed (bringing in checks for pure-python overloads). Thus struct[0] still takes around 0.5s (compared with 0.18 for raw list) in this case, and you do double the memory usage, so this may not be worth it.

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)