Package: cheapnamedtuple
Cheap namedtuple implementation (Python 2.7)Namedtuples are a neat goody of Python, but they have one big caveat. In
order to be precise with the type definition, they compile with exec a
string template, formated with the variables given in the namedtuple()
function factory. This compilation is very expensive computation-wise,
and in practice can be spared if needed for performance reasons.Anissuewas opened years ago in
the Python bug tracker but it got rejected, arguing that the official
implementation is clearer and more maintainable. This is true, but there
are cases where performance is key, and the current implementation using
exec is just not an option.If you need to define new namedtuple types dynamically, and you have
high performance constraints, this is for you.There are multiple versions out there, usingmetaclasses, ormetaclasses,
orABCThis versions is simpler. Just define a new class closed-over by the
factory function.Unittests from cPython2.7 implementation are copied here to assert that
the same expected behaviour is honored.InstallpipinstallcheapnamedtupleUsageFor the purist, thenamedtupleimplementation is 3.75x faster than
Python’s implementation, while honouring 100% of the Python’s
implementation behaviour, including docstrings and everything else
(corroborated by unit tests):>>>fromcheapnamedtupleimportnamedtuple>>>Point=namedtuple('Point',['x','y'])>>>Point.__doc__# docstring for the new class'Point(x, y)'>>>p=Point(11,y=22)# instantiate with positional args or keywords>>>p[0]+p[1]# indexable like a plain tuple33>>>x,y=p# unpack like a regular tuple>>>x,y(11,22)>>>p.x+p.y# fields also accessible by name33>>>d=p._asdict()# convert to a dictionary>>>d['x']11>>>Point(**d)# convert from a dictionaryPoint(x=11,y=22)>>>p._replace(x=100)# _replace() is like str.replace() but targets named fieldsPoint(x=100,y=22)For the practical, acheapnamedtupleimplementation is 26.6x faster
than the Python’s implementation, while still honouring all the public
behaviour (corroborated by doctests), and still supporting copy and
pickle. The only caveats identified so far are:The docstring of the type generated by the cheapnamedtuple factory
cannot be generated ad-hoc for itSome typechecking has been trade in favor of performance (see
test_name_fixer test case)>>>fromcheapnamedtupleimportcheapnamedtuple>>>Point=cheapnamedtuple('Point',['x','y'])>>>Point.__doc__# docstring for the new class'Point(x, y)'>>>p=Point(11,y=22)# instantiate with positional args or keywords>>>p[0]+p[1]# indexable like a plain tuple33>>>x,y=p# unpack like a regular tuple>>>x,y(11,22)>>>p.x+p.y# fields also accessible by name33>>>d=p._asdict()# convert to a dictionary>>>d['x']11>>>Point(**d)# convert from a dictionaryPoint(x=11,y=22)>>>p._replace(x=100)# _replace() is like str.replace() but targets named fieldsPoint(x=100,y=22)CompatibilityCurrently only tested in Python 2.7BenchmarkingPython’s stdlib implementation:$python-mtimeit-vvvv"from collections import namedtuple""A = namedtuple('A', ['foo', 'bar', 'foobar'])""a = A(1, 2, 3)""a.bar"10loops->0.00922394secs100loops->0.0595999secs1000loops->0.350676secsrawtimes:0.3289640.331690.3275191000loops,bestof3:327.519usecperloopnamedtupleimplementation:$python-mtimeit-vvvv"from cheapnamedtuple import namedtuple""A = namedtuple('A', ['foo', 'bar', 'foobar'])""a = A(1, 2, 3)""a.bar"10loops->0.00332594secs100loops->0.01106secs1000loops->0.09164secs10000loops->0.955008secsrawtimes:0.9294550.8728040.90487710000loops,bestof3:87.2804usecperloopcheapnamedtupleimplementation:$python-mtimeit-vvvv"from cheapnamedtuple import cheapnamedtuple""A = cheapnamedtuple('A', ['foo', 'bar', 'foobar'])""a = A(1, 2, 3)""a.bar"10loops->0.00241184secs100loops->0.00281secs1000loops->0.0245481secs10000loops->0.156194secs100000loops->1.25612secsrawtimes:1.234591.231591.23687100000loops,bestof3:12.3159usecperloopUsing metaclass version foundhere:$python-mtimeit-vvvv"from metanamedtuple import namedtuple""A = namedtuple('A', ['foo', 'bar', 'foobar'])""a = A(1, 2, 3)""a.bar"10loops->0.00334907secs100loops->0.0108609secs1000loops->0.088969secs10000loops->1.25756secsrawtimes:1.28681.240041.2538310000loops,bestof3:124.004usecperloop