In Python, how can I simply do the equivalent of dictionary.get(key, default)
for lists - i.e., how can I simply get the nth element of a list, or a default value if not available?
For example, given a list myList
, how can I get 5
if myList
is empty, or myList[0]
otherwise?
14 Answers 14
l[index] if index < len(l) else default
To support negative indices we can use:
l[index] if -len(l) <= index < len(l) else default
-
11Doesn't work if your list doesn't have a name, though - for instance, in a list comprehension.Xiong Chiamiov– Xiong Chiamiov2011年07月20日 01:12:05 +00:00Commented Jul 20, 2011 at 1:12
-
10You seem to forgot the case of negative index. Eg.
index == -1000000
should returndefault
.nodakai– nodakai2012年10月04日 03:50:18 +00:00Commented Oct 4, 2012 at 3:50 -
3@nodakai, good point. I've sometimes been bitten by this.
x[index] if 0 <= index < len(x) else default
would be better ifindex
can ever be negative.Ben Hoyt– Ben Hoyt2013年07月28日 21:51:02 +00:00Commented Jul 28, 2013 at 21:51 -
3@nodakai wow - that is a great example of why it may be better to use try/except, than to try to correctly code the test so it never fails. I still don't like to rely on try/except, for a case that I know will happen, but this increases my willingness to consider it.ToolmakerSteve– ToolmakerSteve2013年12月15日 03:27:31 +00:00Commented Dec 15, 2013 at 3:27
-
6@ToolmakerSteve: Your formula for
valid_index()
is incorrect. Negative indices are legal in Python - it should be-len(l) <= index < len(l)
.Tim Pietzcker– Tim Pietzcker2014年01月11日 06:54:45 +00:00Commented Jan 11, 2014 at 6:54
try:
a = b[n]
except IndexError:
a = default
Edit: I removed the check for TypeError - probably better to let the caller handle this.
-
4I like this. Just wrap it around a function so that you can call it with an iterable as an argument. Easier to read.Noufal Ibrahim– Noufal Ibrahim2010年03月22日 12:27:25 +00:00Commented Mar 22, 2010 at 12:27
Just discovered that :
next(iter(myList), 5)
iter(l)
returns an iterator on myList
, next()
consumes the first element of the iterator, and raises a StopIteration
error except if called with a default value, which is the case here, the second argument, 5
This only works when you want the 1st element, which is the case in your example, but not in the text of you question, so...
Additionally, it does not need to create temporary lists in memory and it works for any kind of iterable, even if it does not have a name (see Xiong Chiamiov's comment on gruszczy's answer)
-
9Combined with the other answers:
next(iter(myList[n:n+1]), 5)
Now it works for then
th element.Alfe– Alfe2018年10月29日 13:31:09 +00:00Commented Oct 29, 2018 at 13:31 -
That would not work with non lists. At this point, try:except-ing an IndexError reads like a better idea IMHO. Also, it creates a list in memory.Joachim Jablon– Joachim Jablon2018年11月06日 20:30:07 +00:00Commented Nov 6, 2018 at 20:30
-
The
try
variant isn't a one-liner (ask asked for by OP). It only works for lists becausemyList
is a list as specified by OP (to be precise, it is something indexable). Creating a copy in memory isn't costly here because I'm creating a list of one single (or none) element here. Sure, a little overhead, but not worth mentioning unless you do that millions of times in a loop. Btw, I guess creating and catching anIndexError
exception is probably more costly.Alfe– Alfe2018年11月07日 10:32:07 +00:00Commented Nov 7, 2018 at 10:32 -
readability counts :) If you're at the point where raising an IndexError has a significant cost, maybe you should be doing the whole thing in Python.Joachim Jablon– Joachim Jablon2019年01月12日 14:19:15 +00:00Commented Jan 12, 2019 at 14:19
(a[n:]+[default])[0]
This is probably better as a
gets larger
(a[n:n+1]+[default])[0]
This works because if a[n:]
is an empty list if n => len(a)
Here is an example of how this works with range(5)
>>> range(5)[3:4]
[3]
>>> range(5)[4:5]
[4]
>>> range(5)[5:6]
[]
>>> range(5)[6:7]
[]
And the full expression
>>> (range(5)[3:4]+[999])[0]
3
>>> (range(5)[4:5]+[999])[0]
4
>>> (range(5)[5:6]+[999])[0]
999
>>> (range(5)[6:7]+[999])[0]
999
-
9Would you write this in actual code without a comment to explain it?Peter Hansen– Peter Hansen2010年03月22日 13:41:34 +00:00Commented Mar 22, 2010 at 13:41
-
2@Peter Hansen, only if I was golfing ;) However it does work on all versions of Python. The Accepted answer only works on 2.5+John La Rooy– John La Rooy2010年03月22日 20:45:24 +00:00Commented Mar 22, 2010 at 20:45
-
4It invloves creating 3 temporary lists and accessing 2 indices in order to select an item, though.Joachim Jablon– Joachim Jablon2014年04月03日 09:48:17 +00:00Commented Apr 3, 2014 at 9:48
-
1While I'd never do this in "real" code, it's a nice concise one-liner that I can easily use in a python REPL, so you get my upvote.Cookyt– Cookyt2017年02月21日 05:42:51 +00:00Commented Feb 21, 2017 at 5:42
-
next(iter(lst[i:i+1]), default)
-- just another entry in the cryptic ugly one-liners competition.jfs– jfs2018年03月04日 10:55:07 +00:00Commented Mar 4, 2018 at 10:55
(L[n:n+1] or [somedefault])[0]
-
1+1 because this made me go learn what
[] or ...
did. However, I would personally just use the accepted solution, as it reads easily (for novices). Granted, wrapping it in a 'def' with comment would make that largely a non-issue.ToolmakerSteve– ToolmakerSteve2013年12月15日 03:20:52 +00:00Commented Dec 15, 2013 at 3:20 -
What if
L[n] == False
orL[n] == None
orL[n] == []
or more globally anything that evaluates to False ?Joachim Jablon– Joachim Jablon2014年04月03日 09:44:39 +00:00Commented Apr 3, 2014 at 9:44 -
2@JoachimJablon: Still works. The slice returns a list, and
[False]
is true.Ignacio Vazquez-Abrams– Ignacio Vazquez-Abrams2014年04月03日 14:36:25 +00:00Commented Apr 3, 2014 at 14:36 -
Oh, didn't realised the list was checked and not the value, indeed.Joachim Jablon– Joachim Jablon2014年04月04日 14:15:07 +00:00Commented Apr 4, 2014 at 14:15
-
Why do you wrap this in a tuple? I think
myval = l[n:n+1] or [somedefault]
would work just fine?Rotareti– Rotareti2018年03月01日 18:03:56 +00:00Commented Mar 1, 2018 at 18:03
... looking for an equivalent in python of
dict.get(key, default)
for lists
There is an itertools recipes that does this for general iterables. For convenience, you can > pip install more_itertools
and import this third-party library that implements such recipes for you:
Code
import more_itertools as mit
mit.nth([1, 2, 3], 0)
# 1
mit.nth([], 0, 5)
# 5
Detail
Here is the implementation of the nth
recipe:
def nth(iterable, n, default=None):
"Returns the nth item or a default value"
return next(itertools.islice(iterable, n, None), default)
Like dict.get()
, this tool returns a default for missing indices. It applies to general iterables:
mit.nth((0, 1, 2), 1) # tuple
# 1
mit.nth(range(3), 1) # range generator (py3)
# 1
mit.nth(iter([0, 1, 2]), 1) # list iterator
# 1
Using Python 3.4's contextlib.suppress(exceptions)
to build a getitem()
method similar to getattr()
.
import contextlib
def getitem(iterable, index, default=None):
"""Return iterable[index] or default if IndexError is raised."""
with contextlib.suppress(IndexError):
return iterable[index]
return default
A cheap solution is to really make a dict with enumerate and use .get()
as usual, like
dict(enumerate(l)).get(7, my_default)
-
3This is very slow to run because whole list is converted to a dictionary only to get later just one element.Arty– Arty2020年09月28日 12:41:03 +00:00Commented Sep 28, 2020 at 12:41
-
Very nice one; definitely works for my use case; thank you!Farzad– Farzad2024年08月07日 16:49:40 +00:00Commented Aug 7, 2024 at 16:49
With unpacking:
b, = a[n:n+1] or [default]
After reading through the answers, I'm going to use:
(L[n:] or [somedefault])[0]
-
1Probably you want
(L[n:n+1] or [somedefault])[0]
(n+1 added) instead, becauseL[n:]
creates long list and copies all elements fromn
to the end. Only to get just one element afterwards. WhileL[n:n+1]
creates a list of just 1 element at most.Arty– Arty2020年09月28日 12:43:58 +00:00Commented Sep 28, 2020 at 12:43 -
I believe I chose the form I give because I need to compute
n
, and I do not want to compute it twice, nor introduce a variable. My lists are short. -- Also, the answerL[n:n+1]
has already been provided by Ignacio Vazquez-AbramsMathieu CAROFF– Mathieu CAROFF2020年09月29日 07:43:16 +00:00Commented Sep 29, 2020 at 7:43 -
I don't get it. If you need to compute
n
, you will have it in the context, there's no need to compute it twice. Why not sum one and prevent the rest of the list from being copied?Victor Schröder– Victor Schröder2021年07月05日 00:31:31 +00:00Commented Jul 5, 2021 at 0:31
Combining @Joachim's with the above, you could use
next(iter(my_list[index:index+1]), default)
Examples:
next(iter(range(10)[8:9]), 11)
8
>>> next(iter(range(10)[12:13]), 11)
11
Or, maybe more clear, but without the len
my_list[index] if my_list[index:index + 1] else default
-
if my_list is a very large list, this could behave badly. Maybe next(iter(my_list[index:index + 1]), default)Joachim Jablon– Joachim Jablon2021年02月06日 21:42:06 +00:00Commented Feb 6, 2021 at 21:42
Althought this is not a one-liner solution, you can define a function with a default value like so:
def get_val(myList, idx, default=5):
try:
return myList[idx]
except IndexError:
return default
-
Thanks @VictorSchröder. Edited the answer!ezzeddin– ezzeddin2021年07月05日 12:50:22 +00:00Commented Jul 5, 2021 at 12:50
-
I removed my previous comment, as the issue was fixed. The solution still has limitations in case
myList
is not ofList
type or ifidx
is not anint
, but that's out of the scope of this question.Victor Schröder– Victor Schröder2021年07月06日 07:41:40 +00:00Commented Jul 6, 2021 at 7:41
For a small index, such as when parsing up to k
arguments, I'd build a new list of length k
, with added elements set to d
, as follows:
def fill(l, k, d):
return l + (k - len(l)) * [d]
Typical usage:
N = 2
arg_one, arg_two = fill("first_word and the rest".split(maxsplit=N - 1), N, None)
# arg_one == "first_word"
# arg_two == "and the rest"
Same example, with a short list:
arg_one, arg_two = fill("one_word_only".split(maxsplit=N - 1), N, None)
# arg_one == "one_word_only"
# arg_two == None
I like it more inline, therefore I created a little streams-like library (https://pypi.org/project/tinystream/).
my_list = ["a", "b", "c"]
stream = Stream(my_list)
assert stream[4].absent
assert stream[0].get() == "a"
Since the index element is of type Opt
, you can also do more stuff like:
assert stream[4].get('X') == 'X' # default if absent
assert stream[0].map(str.upper).filter(lambda x: x == 'A').present