13

I want to merge two namedtuples without loosing the key names. If, I just do a merge with '+' operator I am getting a tuple as a result but without the names.

For instance:

n [1]: from collections import namedtuple

In [2]: A = namedtuple("A", "a b c")

In [4]: B = namedtuple("B", "d e")

In [5]: a = A(10, 20, 30)

In [6]: b = B(40, 50)

In [7]: a + b
Out[7]: (10, 20, 30, 40, 50)

As you can see in the above case, the result of a + b has no names associated with them.

But, I am able to achieve it by creating a third namedtuple, which has fields from both A and B.

In [8]: C = namedtuple("C", A._fields + B._fields)

In [9]: C(*(a + b))
Out[9]: C(a=10, b=20, c=30, d=40, e=50)

Is this the right way or is there a better way to do this?

Simeon Visser
  • 118,920
  • 18
  • 185
  • 180
Senthil Babu
  • 1,243
  • 2
  • 11
  • 20
  • 1
    You can use a dict and the `.update()` method or the `{ k:v for d in (a,b) for k,v in d.items() }` trick. `namedtuple` is all about defining a fixed set of attributes, which you seem to violate. – Niklas B. Aug 28 '12 at 14:36
  • A better question would be why you'd need this. –  Aug 28 '12 at 14:37
  • @delnan I require this as I am trying to join two lists of namedtuples. I guess that is fairly a normal usecase. – Senthil Babu Aug 28 '12 at 14:42
  • 2
    @Senthil: No, it's not a normal usecase. Typically you define some "types" using the `namedtuple` constructor and then create instances of it. It's a bit like `struct` in C. I think you want to use a dict – Niklas B. Aug 28 '12 at 14:44

4 Answers4

9

You've pretty much nailed it as far as vanilla Python is concerned, but there's an extra simplification you can do if you're using Python 3.5+.

>>> from collections import namedtuple
>>> A = namedtuple("A", "a b c")
>>> B = namedtuple("B", "d e")
>>> a = A(10, 20, 30)
>>> b = B(40, 50)
>>> C = namedtuple("C", A._fields + B._fields)
>>> C(*(a + b))
C(a=10, b=20, c=30, d=40, e=50)
>>> #Available in Python 3.5+
>>> C(*a, *b)
C(a=10, b=20, c=30, d=40, e=50)

Also, here's a function you can use to eliminate boilerplate code if you find yourself doing this frequently:

>>> from functools import reduce
>>> from itertools import chain
>>> from operator import add
>>> def namedtuplemerge(*args):
...     cls = namedtuple('_'.join(arg.__class__.__name__ for arg in args), reduce(add, (arg._fields for arg in args)))
...     return cls(*chain(*args))
...
>>> namedtuplemerge(a, b)
A_B(a=10, b=20, c=30, d=40, e=50)
John Crawford
  • 2,144
  • 2
  • 19
  • 16
7

Some observations:

  • In general Python would not know what to do when you try to merge two namedtuples that happen to have fields with the same name. Perhaps this is why there is no operator or function for this.

  • The documentation of _fields says:

Tuple of strings listing the field names. Useful for introspection and for creating new named tuple types from existing named tuples.

This suggests your approach is fine and perhaps even hinted at by the authors of the namedtuple code.

Simeon Visser
  • 118,920
  • 18
  • 185
  • 180
4

Python will not automagically create a new namedtuple, a new class. You will have to define the combined namedtuple yourself:

A = namedtuple("A", "a b c")
B = namedtuple("B", "d e")
AB = namedtuple("AB", "a b c d e")

a = A(1,2,3)
b = B(4,5)
ab = AB(*(a+b))

>>> a
A(a=1, b=2, c=3)
>>> b
B(d=4, e=5)
>>> ab
AB(a=1, b=2, c=3, d=4, e=5)
clacke
  • 7,688
  • 6
  • 46
  • 48
-4

how about do

>>> [a] + [b]
[A(a=10, b=20, c=30), B(d=40, e=50)]
lsheng
  • 3,539
  • 10
  • 33
  • 43