4

I want to create a set of namedtuple in python, with the ability to add elements dynamically using the union operation.

The following code snippet creates a set of namedtuple, which is behaving nicely.

from collections import namedtuple

B = namedtuple('B', 'name x')

b1 = B('b1',90)
b2 = B('b2',92)
s = set([b1,b2])
print(s)

which prints

{B(name='b1', x=90), B(name='b2', x=92)}

Now if I create another namedtuple and add it to my set with the union operations it is not behaving as expected.

b3 = B('b3',93)
s = s.union(b3)
print(s)

The code snippet prints the following output.

{93, B(name='b1', x=90), B(name='b2', x=92), 'b3'}

The expected output should be:

{B(name='b1', x=90), B(name='b2', x=92), B(name='b3', x=93)}

Am I mis-understanding the API? Both python2 and 3 are showing the same behaviour.

MSeifert
  • 145,886
  • 38
  • 333
  • 352
Jayendra Parmar
  • 702
  • 12
  • 30

4 Answers4

5

A namedtuple instance is an iterable of items. set.union simply merges the current set with the items in the namedtuple.

However, what you want is put the namedtuple in another container/iterable, so the merge is done with the item (the namedtuple) contained in the new parent iterable:

s.union((b3,))

It becomes more obvious if you actually think of the operator equivalent:

s = s | set(b3) # set(b3) -> {93, 'b3'}

As compared to what we actually want:

s = s | {b3}

The union is performed with the outer iterable.

Moses Koledoye
  • 77,341
  • 8
  • 133
  • 139
3

union expects a set (or a list or another iterable), but you pass a named tuple, which is an iterable by itself, but it provides values, so you merge the set with the values. Try this:

s = s.union({b3})
Danil Speransky
  • 29,891
  • 5
  • 68
  • 79
1

Since b3 is iterable, union works on its elements rather than on the tuple itself. Replace that with:

s = s.union([b3])
NPE
  • 486,780
  • 108
  • 951
  • 1,012
0

The documentation on set.union actually explains this:

union(*others)

Return a new set with elements from the set and all others.

So it will create a new set including all elements from the others:

>>> set(b3)  # these are the unique elements in your `b3`
{93, 'b3'}

>>> s.union(b3)   # the union of the unique elements in "s" and "b3"
{B(name='b1', x=90), 93, 'b3', B(name='b2', x=92)}

In your case (because you assign it back to s) you can simply add the item thus avoiding creating a new set entirely:

>>> s.add(b3)
>>> s
{B(name='b1', x=90), B(name='b3', x=93), B(name='b2', x=92)}
Community
  • 1
  • 1
MSeifert
  • 145,886
  • 38
  • 333
  • 352