How to use namedtuples in multiple inheritance

Is it possible to create a class that inherits from several instances of namedtuple , or to create something with the same effect (with an immutable type that combines the fields of the base types)? I did not find a way to do this.

This example illustrates the problem:

 >>> class Test(namedtuple('One', 'foo'), namedtuple('Two', 'bar')): >>> pass >>> t = Test(1, 2) TypeError: __new__() takes 2 positional arguments but 3 were given >>> t = Test(1) >>> t.foo 1 >>> t.bar 1 

The problem is that namedtuple does not use super to initialize its base class, as seen when creating:

 >>> namedtuple('Test', ('field'), verbose=True) [...] class Test(tuple): [...] def __new__(_cls, field,): 'Create new instance of Test(field,)' return _tuple.__new__(_cls, (field,)) 

Even if I decided to write my own version of namedtuple to fix this, it's not clear how to do this. If there are several namedtuple instances in the class MRO, they will have to split up one instance of the tuple base class. To do this, they would have to coordinate on which namedtuple uses the range of indices in the base tuple.

Is there an easier way to achieve multiple inheritance with namedtuple or something similar? Has anyone already implemented this somewhere?

+1
source share
3 answers

You can use a decorator or metaclass to combine the parent fields named tuple into a new named tuple and add it to the __bases__ class:

 from collections import namedtuple def merge_fields(cls): name = cls.__name__ bases = cls.__bases__ fields = [] for c in bases: if not hasattr(c, '_fields'): continue fields.extend(f for f in c._fields if f not in fields) if len(fields) == 0: return cls combined_tuple = namedtuple('%sCombinedNamedTuple' % name, fields) return type(name, (combined_tuple,) + bases, dict(cls.__dict__)) class SomeParent(namedtuple('Two', 'bar')): def some_parent_meth(self): return 'method from SomeParent' class SomeOtherParent(object): def __init__(self, *args, **kw): print 'called from SomeOtherParent.__init__ with', args, kw def some_other_parent_meth(self): return 'method from SomeOtherParent' @merge_fields class Test(namedtuple('One', 'foo'), SomeParent, SomeOtherParent): def some_method(self): return 'do something with %s' % (self,) print Test.__bases__ # ( # <class '__main__.TestCombinedNamedTuple'>, <class '__main__.One'>, # <class '__main__.SomeParent'>, <class '__main__.SomeOtherParent'> # ) t = Test(1, 2) # called from SomeOtherParent.__init__ with (1, 2) {} print t # Test(foo=1, bar=2) print t.some_method() # do something with Test(foo=1, bar=2) print t.some_parent_meth() # method from SomeParent print t.some_other_parent_meth() # method from SomeOtherParent 
+5
source

This code takes a similar approach to Francis Colas, although it is slightly longer :)

This is a factory function that takes any number of parent named elements and creates a new namedtuple that has all the fields in the parents to skip any duplicate field names.

 from collections import namedtuple def combined_namedtuple(typename, *parents): #Gather fields, in order, from parents, skipping dupes fields = [] for t in parents: for f in t._fields: if f not in fields: fields.append(f) return namedtuple(typename, fields) nt1 = namedtuple('One', ['foo', 'qux']) nt2 = namedtuple('Two', ['bar', 'baz']) Combo = combined_namedtuple('Combo', nt1, nt2) ct = Combo(1, 2, 3, 4) print ct 

Exit

 Combo(foo=1, qux=2, bar=3, baz=4) 
+4
source

Well, if you just need a named element with both fields, it's easy to just create one:

 One = namedtuple('One', 'foo') Two = namedtuple('Two', 'bar') Test = namedtuple('Test', One._fields+Two._fields) 
+3
source

All Articles