tags:

views:

181

answers:

3

I recently encountered a scenario in which if a set only contained a single element, I wanted to do something with that element. To get the element, I settled on this approach:

element = list(myset)[0]

But this isn't very satisfying, as it creates an unnecessary list. It could also be done with iteration, but iteration seems unnatural as well, since there is only a single element. Am I missing something simple?

+9  A: 

tuple unpacking works.

(element,) = myset

(By the way, python-dev is exploding at the moment discussing the possible addition of myset.get() to return an arbitrary element from a set. Discussion here)

My personal favorite for getting arbitrary element is (when you have an unknown number, but also works if you have just one):

element = next(iter(myset)) ¹

1: in Python 2.5 and before, you have to use iter(myset).next()

kaizer.se
Very nice! I like that this fails if the number of elements isn't 1.
Laurence Gonsalves
@Laurence: That's a fine observation. Catch errors early, right?
kaizer.se
+1  A: 

you can use element = tuple(myset)[0] which is a bit more efficient, or, you can do something like

element = iter(myset).next()

I guess constructing an iterator is more efficient than constructing a tuple/list.

Oren S
Why do you guess constructing an iterator is more efficient?
Craig McQueen
luckily, I point you to Alex's response :)
Oren S
+8  A: 

Between making a tuple and making an iterator, it's almost a wash, but iteration wins by a nose...:

$ python2.6 -mtimeit -s'x=set([1])' 'a=tuple(x)[0]'
1000000 loops, best of 3: 0.465 usec per loop
$ python2.6 -mtimeit -s'x=set([1])' 'a=tuple(x)[0]'
1000000 loops, best of 3: 0.465 usec per loop
$ python2.6 -mtimeit -s'x=set([1])' 'a=next(iter(x))'
1000000 loops, best of 3: 0.456 usec per loop
$ python2.6 -mtimeit -s'x=set([1])' 'a=next(iter(x))'
1000000 loops, best of 3: 0.456 usec per loop

Not sure why all the answers are using the older syntax iter(x).next() rather than the new one next(iter(x)), which seems preferable to me (and also works in Python 3.1).

However, unpacking wins hands-down over both:

$ python2.6 -mtimeit -s'x=set([1])' 'a,=x'
10000000 loops, best of 3: 0.174 usec per loop
$ python2.6 -mtimeit -s'x=set([1])' 'a,=x'
10000000 loops, best of 3: 0.174 usec per loop

This of course is for single-item sets (where the latter form, as others mentioned, has the advantage of failing fast if the set you "knew" had just one item actually had several). For sets with arbitrary N > 1 items, the tuple slows down, the iter doesn't:

$ python2.6 -mtimeit -s'x=set(range(99))' 'a=next(iter(x))'
1000000 loops, best of 3: 0.417 usec per loop
$ python2.6 -mtimeit -s'x=set(range(99))' 'a=tuple(x)[0]'
100000 loops, best of 3: 3.12 usec per loop

So, unpacking for the singleton case, and next(iter(x)) for the general case, seem best.

Alex Martelli
Why Python2.x syntax? I only do real programming there, and `python` is Python 2.5 on my system, and hence what comes up when I press my keybinding to open a python console.
kaizer.se
2.6 is perfectly usable for "real programming" and richer than 2.5; the only reason to stick with 2.5 is if your external environment constrains you (App Engine, Civilization 4, etc). next(x) and x.next() both work in 2.6, but next(x) is better (lets you specify a default value rather than catching StopIteration exceptions, requires one fewer character;-).
Alex Martelli
@Alex: No need to convince me, I would much rather use python 2.6. I use debian, debian has not yet finished transitioning to Python 2.6! (Pyhthon 2.6 itself is available but none of the distributions 3rd part libraries.)
kaizer.se
but since you mention that `next(..)` is available in Python 2.6, I now understand your point much better! That's good news.
kaizer.se
@kaizer.se, yep -- similarly App Engine, as I mentioned, is also 2.5-only, as is Civilization IV (other scriptable games but yep, 2.6 is appetizing when usable (the next built-in function is just one tidbit, but a juicy one;-).
Alex Martelli