88

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.

codeforester
  • 28,846
  • 11
  • 78
  • 104
Pyderman
  • 10,029
  • 9
  • 46
  • 85
  • 5
    There may be alternatives, but there's usually only one *obvious* way to do it - and, in this case, you seem to have already found it. – ekhumoro Oct 16 '15 at 02:23
  • if it *always* contains a single item, then maybe a `list` is not the best data type? – David Zemens Oct 16 '15 at 02:24
  • 14
    @ekhumoro: I'm actually partial to the sequence unpacking method, because it verifies the assumption that the sequence only has one element. `mylist[0]` succeeds when you have at least one element, but doesn't complain if you actually had 30 elements. `singleitem, = mylist` verifies that you've got exactly one element, no more, no less. – ShadowRanger Oct 16 '15 at 02:27
  • 1
    @ShadowRanger. The question is explicitly about accessing the only element in a list which is already *known* to contain a single item. – ekhumoro Oct 16 '15 at 02:34
  • 6
    @ekhumoro: No disagreement. I just prefer to program defensively, so violations of requirements don't pass silently (sure it fails, but it fails loudly, which is much easier to identify and fix than subtle misbehaviors). If I had a nickel for every time some "known" behavior in production code turned out to be dead wrong... Well, I probably wouldn't be _rich_, but I'd be able to take the family out to a _really_ nice dinner. – ShadowRanger Oct 16 '15 at 02:38
  • @ShadowRanger good point re: sequence unpacking. – Pyderman Oct 16 '15 at 16:46

2 Answers2

135

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

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

Many others (combining or varying bits of the above, or otherwise relying on implicit iteration), but you get the idea.

ShadowRanger
  • 108,619
  • 9
  • 124
  • 184
  • 7
    The first one is unique in that it also raises if the list contains more than one element. – Neil G Oct 16 '15 at 09:40
  • 8
    The first can also be written as `[singleitem] = mylist`. – Carsten S Oct 16 '15 at 10:00
  • @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 Oct 16 '15 at 12:57
  • 1
    @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! – ShadowRanger Oct 16 '15 at 14:47
  • 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. – Carsten S Oct 16 '15 at 14:50
  • You can also use `ast` and prevent having a list with one element in the first place. #yolo – Bharel Sep 23 '16 at 22:24
  • @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. – ShadowRanger Sep 23 '16 at 22:29
  • 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 Sep 23 '16 at 22:34
  • You may also override builtins.list.__new__ and hope for good. – Bharel Sep 23 '16 at 22:37
  • @Bharel: Okay, so as long as we're agreed that's batshit insane, we're good. :-) – ShadowRanger Sep 23 '16 at 23:15
  • `(singleitem,) = mylist` works too, of course. Somehow I can parse that one better. – Nathan Jun 14 '19 at 15:15
  • @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. – ShadowRanger Aug 01 '19 at 00:41
  • I'll add `singleitem, *_ = mylist` (it's derived from `head, *tail = mylist`). – David Waterworth Nov 30 '20 at 08:17
  • @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`. – ShadowRanger Nov 30 '20 at 11:46
  • True, the safest/most explicit method may be to use something like https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.one - numpy has item() which I use frequently. – David Waterworth Nov 30 '20 at 22:48
19

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

pylang
  • 28,402
  • 9
  • 97
  • 94