Getting only element from a single-element list in Python?
Solution 1
Raises exception if not exactly one item:
Sequence unpacking:
singleitem, = mylist
# Identical in behavior (byte code produced is the same),
# but arguably more readable since a lone trailing comma could be missed:
[singleitem] = mylist
Rampant insanity, unpack the input to the identity lambda
function:
# The only even semi-reasonable way to retrieve a single item and raise an exception on
# failure for too many, not just too few, elements as an expression, rather than a
# statement, without resorting to defining/importing functions elsewhere to do the work
singleitem = (lambda x: x)(*mylist)
All others silently ignore spec violation, producing first or last item:
Explicit use of iterator protocol:
singleitem = next(iter(mylist))
Destructive pop:
singleitem = mylist.pop()
Negative index:
singleitem = mylist[-1]
Set via single iteration for
(because the loop variable remains available with its last value when a loop terminates):
for singleitem in mylist: break
There are many others (combining or varying bits of the above, or otherwise relying on implicit iteration), but you get the idea.
Solution 2
I will add that the more_itertools
library has a tool that returns one item from an iterable.
from more_itertools import one
iterable = ["foo"]
one(iterable)
# "foo"
In addition, more_itertools.one
raises an error if the iterable is empty or has more than one item.
iterable = []
one(iterable)
# ValueError: not enough values to unpack (expected 1, got 0)
iterable = ["foo", "bar"]
one(iterable)
# ValueError: too many values to unpack (expected 1)
more_itertools
is a third-party package > pip install more-itertools
Pyderman
Some day, I'll be one of the ones answering questions. Until then, I endeavour to submit articulate, well thought-out questions. If you find issue with any of my questions, please add a comment stating why (rather than simply downvoting). That way, I and other submitters can improve.
Updated on September 16, 2021Comments
-
Pyderman over 2 years
When a Python list is known to always contain a single item, is there a way to access it other than:
mylist[0]
You may ask, 'Why would you want to?'. Curiosity alone. There seems to be an alternative way to do everything in Python.
-
Neil G over 8 yearsThe first one is unique in that it also raises if the list contains more than one element.
-
Carsten S over 8 yearsThe first can also be written as
[singleitem] = mylist
. -
ShadowRanger over 8 years@NeilG: Sadly, it's not wholly unique; the "rampant insanity" version in the comments does that too (as does the unpack via list syntax, though that's actually identical to the first example behavior-wise). I feel a bit dirty now.
-
ShadowRanger over 8 years@CarstenS: Usually, I frown on using
[]
for sequence unpacking (for [i, x] in enumerate(iterable):
drives me batty), but yes, in the case of unpacking a single value, the trailing comma can easily be missed, and using brackets is justifiable for readability reasons. I've added it to the answer. Thanks! -
Carsten S over 8 yearsI agree that it is not very pythonic, so I do not use it much any more, either. Only the old Haskeller in me wants to use it.
-
Bharel over 7 yearsYou can also use
ast
and prevent having a list with one element in the first place. #yolo -
ShadowRanger over 7 years@Bharel: Huh? I don't see how the
ast
module is related here, and can't think what else you're referring to... It's not always possible to avoid alist
if the API is limited. -
Bharel over 7 yearsIn every node you encounter a list creation, you can prevent creating it in the first place. (hoping you won't wreck havoc). That way there will never be any single-element list. Who needs them anyway.
-
Bharel over 7 yearsYou may also override builtins.list.__new__ and hope for good.
-
ShadowRanger over 7 years@Bharel: Okay, so as long as we're agreed that's batshit insane, we're good. :-)
-
Nathan almost 5 years
(singleitem,) = mylist
works too, of course. Somehow I can parse that one better. -
ShadowRanger almost 5 years@Nathan: Sure. The lone trailing comma is still relatively easy to miss though; if the code was written by an inexperienced programmer, and I was maintaining it, I'd be half inclined to read it as another manifestation of unnecessary parentheses disease (the same disease that leads to
return(retval)
,if (x in y):
, etc.). The problem isn't parsing once you notice it, it's noticing it in the first place, and the square brackets (without a comma) are visible, and have no subtle details to miss. -
David Waterworth over 3 yearsI'll add
singleitem, *_ = mylist
(it's derived fromhead, *tail = mylist
). -
ShadowRanger over 3 years@DavidWaterworth: That silences the exception you'd get for passing a sequence with >1 item by silently collecting all the other items into
_
(making an emptylist
there if it's in fact single element); it's otherwise identical tosingleitem, = mylist
. -
David Waterworth over 3 yearsTrue, the safest/most explicit method may be to use something like more-itertools.readthedocs.io/en/stable/… - numpy has item() which I use frequently.
-
pabouk - Ukraine stay strong over 2 yearsVery useful answer! Could you please just confirm (in the answer) that there is no solution in a form of an expression (not assignment) raising an exception if the sequence does not contain only just single item? ... not counting the tools not in the standard library like
more_itertools.one()
. -
ShadowRanger over 2 years@pabouk: There technically is (someone just deleted the comment out of this comment chain apparently), it's just an insane solution I came up with (when I reference the "rampant insanity" version, that's the one), which is why I didn't put it in the answer. You'd just do:
(lambda x: x)(*mylist)
;lambda
s are function definitions as expressions, so you can define one and then pass your unpacked iterable to one in a single line, and it either dies with an exception (if the unpacking doesn't match the argument count precisely) or calls the function successfully and returns the argument.