I've got a use case for namedtuple that requires some of the fields to be floats. I could require the code that's constructing the namedtuple to supply floats, but I'd like for the namedtuple class to be smart enough to convert strings it gets into floats for me (but only for a specified subset of fields). I have code to do this, but it's ugly:
from collections import namedtuple, OrderedDict # This list is built at runtime in my application _FIELDS_AND_TYPES = [("num1", True), ("num2", True), ("label", False)] FIELDS_FORCE_FLOAT = OrderedDict(_FIELDS_AND_TYPES) _MyNamedTuple = namedtuple("_MyNamedTuple", FIELDS_FORCE_FLOAT.keys()) class TypedNamedTuple(_MyNamedTuple): fields_force_float = FIELDS_FORCE_FLOAT def __new__(cls, *values_in): super_obj = super(_MyNamedTuple, cls) superclass = super_obj.__thisclass__ values = [float(value) if cls.fields_force_float[fieldname] else value for fieldname, value in zip(superclass._fields, values_in)] self = super_obj.__new__(cls, values) return self print TypedNamedTuple("1.0", "2.0", "3.0") # _MyNamedTuple(num1=1.0, num2=2.0, label='3.0')
Things I don't like about this code:
- The output of
print TypedNamedTuple("1.0", "2.0", "3.0")starts with
TypedNamedTuple. I think this is a general problem with subclassing namedtuples, so fair enough. I could give both classes the same name to solve this.
- My code has to pull in
FIELDS_FORCE_FLOATfrom a global variable.
TypedNamedTupleis inefficient (it runs
zipand does a bunch of dictionary lookups). This is not so bad in my context but I'd like to know how to handle this "right."
- If I wanted to create another namedtuple subclass that forces some of its args to be float, I'd basically be starting from scratch. My
TypedNamedTupleis not reusable.
Is there a cleaner way to do this?
As for why I'm attempting to use namedtuples here at all (H/T @jonrsharpe), it's not set in stone but here are some things that pushed me in this direction:
- The list of fields I'm interested in isn't known until runtime.
- Immutability: I do not, in fact, have a reason to change any of these objects once they're constructed.