Best way to extract an item from a set of len 1

T

Tim Chase

When you have a set, known to be of length one, is there a "best"
("most pythonic") way to retrieve that one item?

# given that I've got Python2.3.[45] on hand,
# hack the following two lines to get a "set" object
>>> import sets
>>> set = sets.Set

>>> s = set(['test'])
>>> len(s)
1
>>> s[0]
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: unindexable object

(which is kinda expected, given that it's unordered...an index
doesn't make much sense)

To get the item, i had to resort to methods that feel less than
the elegance I've come to expect from python:

>>> item = [x for x in s][0]

or the more convoluted two-step

>>> item = s.pop()
>>> s.add(item)

or even worse, intruding into private members

>>> item = s._data.keys()[0]

Is any of these more "pythonic" than the others? Is there a more
elegant 2.3.x solution? If one upgrades to 2.4+, is there
something even more elegant? I suppose I was looking for
something like

>>> item = s.aslist()[0]

which feels a little more pythonic (IMHO). Is one solution
preferred for speed over others (as this is happening in a fairly
deeply nested loop)?

Any tips, preferences, input, suggestions, pointers to obvious
things I've missed, or the like?

Thanks,

-tkc
 
P

Peter Otten

Tim said:
When you have a set, known to be of length one, is there a "best"
("most pythonic") way to retrieve that one item?
s = set(["one-and-only"])
item, = s
item
'one-and-only'

This works for any iterable and guarantees that it contains exactly one
item. The comma may easily be missed, though.

Peter
 
A

Alex Martelli

Tim Chase said:
To get the item, i had to resort to methods that feel less than
the elegance I've come to expect from python:
item = [x for x in s][0]

A shorter, clearer expression of the same idea:

item = list(s)[0]

or

item = list(s).pop()
or the more convoluted two-step

which in turn suggests

item = set(s).pop()

Similar ideas include iter(s).next() and s.copy().pop().

Basically: s has no way to get the item non-destructively, so, either
make a copy (and use the destructive-get 'pop' on the copy) or build
from s a type which DOES have ways to get the item (iterator, list, etc)
be they destructive or not. As for speed, measuring is the only way,
and timeit is your friend. As for elegance, the most concise readable
form is "set(s).pop()" and that's what I would use.


Alex
 
F

Fredrik Lundh

Peter said:
When you have a set, known to be of length one, is there a "best"
("most pythonic") way to retrieve that one item?
s = set(["one-and-only"])
item, = s
item
'one-and-only'

This works for any iterable and guarantees that it contains exactly one
item. The comma may easily be missed, though.

you can make this a bit more obvious:

this is almost twice as fast as the fastest alternative from my previous
post.

</F>
 
A

Alex Martelli

Rene Pijlman said:
Peter Otten:
s = set(["one-and-only"])
item, = s
...
The comma may easily be missed, though.

You could write:

(item,) = s

But I'm not sure if this introduces additional overhead.

Naah...:

helen:~ alex$ python -mtimeit -s's=set([23])' 'x,=s'
1000000 loops, best of 3: 0.689 usec per loop
helen:~ alex$ python -mtimeit -s's=set([23])' '(x,)=s'
1000000 loops, best of 3: 0.652 usec per loop
helen:~ alex$ python -mtimeit -s's=set([23])' '[x]=s'
1000000 loops, best of 3: 0.651 usec per loop

....much of a muchness.


Alex
 
P

Peter Otten

Alex said:
Rene Pijlman said:
Peter Otten:
s = set(["one-and-only"])
item, = s ...
The comma may easily be missed, though.

You could write:

(item,) = s

But I'm not sure if this introduces additional overhead.

Naah...:

helen:~ alex$ python -mtimeit -s's=set([23])' 'x,=s'
1000000 loops, best of 3: 0.689 usec per loop
helen:~ alex$ python -mtimeit -s's=set([23])' '(x,)=s'
1000000 loops, best of 3: 0.652 usec per loop
helen:~ alex$ python -mtimeit -s's=set([23])' '[x]=s'
1000000 loops, best of 3: 0.651 usec per loop

...much of a muchness.

And that is no coincidence. All three variants are compiled to the same
bytecode:
import dis
def a(): x, = s ....
def b(): (x,) = s ....
def c(): [x] = s ....
dis.dis(a)
1 0 LOAD_GLOBAL 0 (s)
3 UNPACK_SEQUENCE 1
6 STORE_FAST 0 (x)
9 LOAD_CONST 0 (None)
12 RETURN_VALUE 1 0 LOAD_GLOBAL 0 (s)
3 UNPACK_SEQUENCE 1
6 STORE_FAST 0 (x)
9 LOAD_CONST 0 (None)
12 RETURN_VALUE 1 0 LOAD_GLOBAL 0 (s)
3 UNPACK_SEQUENCE 1
6 STORE_FAST 0 (x)
9 LOAD_CONST 0 (None)
12 RETURN_VALUE

Peter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top