python set union operation is not behaving well with named tuples

808 views Asked by At

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.

4

There are 4 answers

0
Danil Speransky On BEST ANSWER

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})
0
Moses Koledoye On

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.

0
NPE On

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

s = s.union([b3])
0
MSeifert On

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)}