Getting only element from a single-element list in Python?

87,903

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

Share:
87,903
Pyderman
Author by

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, 2021

Comments

  • Pyderman
    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
    Neil G over 8 years
    The first one is unique in that it also raises if the list contains more than one element.
  • Carsten S
    Carsten S over 8 years
    The first can also be written as [singleitem] = mylist.
  • ShadowRanger
    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
    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
    Carsten S over 8 years
    I 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
    Bharel over 7 years
    You can also use ast and prevent having a list with one element in the first place. #yolo
  • ShadowRanger
    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 a list if the API is limited.
  • Bharel
    Bharel over 7 years
    In 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
    Bharel over 7 years
    You may also override builtins.list.__new__ and hope for good.
  • ShadowRanger
    ShadowRanger over 7 years
    @Bharel: Okay, so as long as we're agreed that's batshit insane, we're good. :-)
  • Nathan
    Nathan almost 5 years
    (singleitem,) = mylist works too, of course. Somehow I can parse that one better.
  • ShadowRanger
    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
    David Waterworth over 3 years
    I'll add singleitem, *_ = mylist (it's derived from head, *tail = mylist).
  • ShadowRanger
    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 empty list there if it's in fact single element); it's otherwise identical to singleitem, = mylist.
  • David Waterworth
    David Waterworth over 3 years
    True, 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
    pabouk - Ukraine stay strong over 2 years
    Very 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
    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); lambdas 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.