128
a = ['123', '2', 4]
b = a[4] or 'sss'
print b

I want to get a default value when the list index is out of range (here: 'sss').

How can I do this?

zjm1126
  • 63,397
  • 81
  • 173
  • 221

13 Answers13

124

In the Python spirit of "ask for forgiveness, not permission", here's one way:

try:
    b = a[4]
except IndexError:
    b = 'sss'
Thomas
  • 174,939
  • 50
  • 355
  • 478
  • 68
    any simple way ?? – zjm1126 Apr 04 '10 at 14:14
  • 2
    If you want a one-liner, put that into a small helper function. – Matti Virkkunen Apr 04 '10 at 14:15
  • 11
    @zjm: this *is* a simple way – Eli Bendersky Apr 04 '10 at 14:18
  • 8
    Is it necessary to span into 3 answers? – kennytm Apr 04 '10 at 14:21
  • 14
    @KennyTM: They are three different options. I figured, this way the best answer could be voted to the top and the crappy ones disappear into oblivion. It was not intended as rep-baiting, and is actually accepted practice: http://meta.stackexchange.com/questions/21761/can-i-answer-a-question-multiple-times – Thomas Apr 04 '10 at 14:26
  • 1
    @zjm1126: there's a shorter way if you can use a dict instead of a list. See my answer. – Vlad the Impala Apr 04 '10 at 14:54
  • 7
    I would like to see the .get method of dicts also in lists, that when you use get and it doesn't exist it just returns None by default. – Sam Stoelinga Feb 17 '14 at 08:43
  • Shouldn't it be "ValueError"? – j_c Aug 20 '14 at 23:53
  • No, what makes you think that? – Thomas Aug 31 '14 at 10:21
  • 4
    Using exceptions just to return default values isn't a good practice even though this is an easy (not simple) way. – David Vartanian Sep 14 '16 at 08:56
  • 2
    Totally agree, exceptions should be reserved for logic/algorithm rather than datastructures/content. – Eric Jul 23 '17 at 10:35
  • 3
    so ugggly... really? no easy way? – Toskan Oct 15 '18 at 22:09
  • 2
    Note: this doesn't work for negative indexes, because in python negative indexes just give you the element which is the absolute value from the back – GenuinePlaceholder Feb 08 '19 at 01:25
  • 2
    This is not the *simple* way, but it's the *pythonic* way. I don't think it does as good a job of expressing the *simple* notion "return default value if item not in list" as a dict does with `get(key, default_value)`. I also fail to understand what makes the list so particular that a similar abstraction is taboo. Everybody has to come with their own lambda preamble. To me this looks like a language oversight perpetually excused due to traditionalism. – Michael Ekoka Apr 15 '21 at 15:32
107

In the non-Python spirit of "ask for permission, not forgiveness", here's another way:

b = a[4] if len(a) > 4 else 'sss'
Thomas
  • 174,939
  • 50
  • 355
  • 478
  • 4
    was using it, good to see reliability confirmed. I tend to avoid trying (and catching) as much as possible. – Thiago Macedo Sep 19 '13 at 04:54
  • 4
    why avoid try/catch? – Laizer May 03 '18 at 17:17
  • The only technical use-case to optimize against raising exception in Python is when the happypath results in the exception being raised. (in example, if len(a) is only rarely > 4, then it's cheaper to use if/then. ). If the happypath is that len(a) > 4, then the only real reason to use the above is shorter code (at the risk of worse readability), but then maep's answer using slices is even shorter... – cowbert Oct 20 '18 at 02:38
  • I can't remember the source, @ThiagoMacedo, but I believe Python encourages `try/except` for normal flow. This does seem unusual when coming from other languages but there's no reason to think of an exception as being some sort of critical error, especially if you expect it. You might have a single line of code that may suffer from `KeyError` and `IndexError` and more, and you can deal with all those exceptions clearly and quickly by using a few exception handlers. This can sometimes avoid nested `if/then/else` statements repeatedly testing for `DivideByZero` and other such edge cases. – NeilG Feb 17 '23 at 07:34
  • My reason to avoid `try/except` is to keep the code cleaner. Too many tries make the whole algorithm unnecessarally verbose IMO. Nowadays I agree that the standard recommendation is a good choice but still use many one-line statements like this. – Thiago Macedo Jul 18 '23 at 22:22
55

In the Python spirit of beautiful is better than ugly

Code golf method, using slice and unpacking (not sure if this was valid 4 years ago, but it is in python 2.7 + 3.3)

b, = a[4:5] or ['sss']

Nicer than a wrapper function or try-catch IMHO, but intimidating for beginners.

Using slicing without unpacking:

b = a[4] if a[4:] else 'sss'

or, if you have to do this often, and don't mind making a dictionary

d = dict(enumerate(a))
b = d.get(4, 'sss')
00500005
  • 3,727
  • 3
  • 31
  • 38
25

another way:

b = (a[4:]+['sss'])[0]
maep
  • 1,318
  • 1
  • 12
  • 15
  • 4
    Okay, so I like the lack of verbosity. Could you explain the syntax a little? – froggythefrog Mar 15 '15 at 23:30
  • 4
    `a[4:]` creates a list of elements, from (including) index 4. This results in an empty list. `[1, 2, 3][999:] == []` It's how the slicing operation works. If `a` was `['123', '2', 4, 5, 6]`, it would return [5, 6] . He then adds the element 'sss', creating a new list, which is `['sss']` in this case. The final step `[0]` just takes the first element, thus returning `'sss'`, but would have returned `a[4]` if there was such an element` – vlad-ardelean Apr 01 '15 at 15:57
  • 7
    an interesting way to achieve this. a bit on the unreadable side. – Alexander Oh Jan 19 '16 at 12:37
  • 1
    This is the only way it works at least in Python 3.5. For some reason calling a[4] if 4 in a else 'default value' returns always the default value. But it's very interesting that slicings return always a list (even empty). Thanks Alex! – David Vartanian Sep 14 '16 at 08:53
  • `4 in a` asks whether `4` is in the list as a value, not a key! – yo' Dec 17 '20 at 15:17
18

You could create your own list-class:

class MyList(list):
    def get(self, index, default=None):
        return self[index] if -len(self) <= index < len(self) else default

You can use it like this:

>>> l = MyList(['a', 'b', 'c'])
>>> l.get(1)
'b'
>>> l.get(9, 'no')
'no'
Jav
  • 1,445
  • 1
  • 18
  • 47
gecco
  • 17,969
  • 11
  • 51
  • 68
11

For a common case where you want the first element, you can do

next(iter([1, 2, 3]), None)

I use this to "unwrap" a list, possibly after filtering it.

next((x for x in [1, 3, 5] if x % 2 == 0), None)

or

cur.execute("SELECT field FROM table")
next(cur.fetchone(), None)
Pepijn
  • 4,145
  • 5
  • 36
  • 64
8

You could also define a little helper function for these cases:

def default(x, e, y):
    try:
        return x()
    except e:
        return y

It returns the return value of the function x, unless it raised an exception of type e; in that case, it returns the value y. Usage:

b = default(lambda: a[4], IndexError, 'sss')

Edit: Made it catch only one specified type of exception.

Suggestions for improvement are still welcome!

Thomas
  • 174,939
  • 50
  • 355
  • 478
  • 4
    @Thomas: IMHO it's inelegant, and the all-catching `except` is worrisome – Eli Bendersky Apr 04 '10 at 14:25
  • 2
    There's a discussion on the mailing list about a construct like this: http://mail.python.org/pipermail/python-dev/2009-August/091039.html – Thomas Apr 04 '10 at 14:35
  • 1
    Old answer, but still. Python is one of the few languages where exceptions is seen on as a good thing. It is often used for flow-control where it creates readability. – Etse Jun 23 '14 at 12:24
5
try:
    b = a[4]
except IndexError:
    b = 'sss'

A cleaner way (only works if you're using a dict):

b = a.get(4,"sss") # exact same thing as above

Here's another way you might like (again, only for dicts):

b = a.setdefault(4,"sss") # if a[4] exists, returns that, otherwise sets a[4] to "sss" and returns "sss"
Vlad the Impala
  • 15,572
  • 16
  • 81
  • 124
2

I’m all for asking permission (i.e. I don’t like the tryexcept method). However, the code gets a lot cleaner when it’s encapsulated in a method:

def get_at(array, index, default):
    if index < 0: index += len(array)
    if index < 0: raise IndexError('list index out of range')
    return array[index] if index < len(a) else default

b = get_at(a, 4, 'sss')
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 2
    `mylist[-1]` should just get the last element, but your "asking permission" `get_at` code _doesn't_ behave like that. This shows one of the many reasons "asking permission" is just the wrong philosophy: what you're checking might not match what the system would do. I.e., not only are you systematically trying to duplicate work the system does for you, but you can easily get that extra, duplicated work wrong. "asking forgiveness" is **much** better. – Alex Martelli Apr 04 '10 at 15:35
  • @Alex: good catch, didn’t think of that. Python’s poweful list slices syntax makes this slightly more complicated … *however*, I don’t agree with your extrapolation from this special case that “asking forgiveness” in general is good. I’m of course biased since I’m a huge proponent of static typing. But anyway, that’s just the wrong conclusion. A better conclusion would be that my requirement analysis was insufficient (*and* perhaps that there’s no good way to get the internal behaviour in a modifiable way without either code duplication or error triggering. Which points to a bad API). – Konrad Rudolph Apr 04 '10 at 15:55
  • You could get away with it if you do `def get_at(array, index, default): try: return array[index] except IndexError: return default` – Esteban Küber Apr 04 '10 at 16:54
  • @voyager: I’m aware of that. But as I said in the very first sentence, I’m opposed to deliberately triggering exceptions. I prefer explicitness over implicitness since it reduces sources of confusion (and hence errors) and this means keeping tab on what my methods can and cannot do. – Konrad Rudolph Apr 05 '10 at 11:49
1

If you are looking for a maintainable way of getting default values on the index operator I found the following useful:

If you override operator.getitem from the operator module to add an optional default parameter you get identical behaviour to the original while maintaining backwards compatibility.

def getitem(iterable, index, default=None):
  import operator
  try:
    return operator.getitem(iterable, index)
  except IndexError:
    return default
Alexander Oh
  • 24,223
  • 14
  • 73
  • 76
1

If you are looking for a quick hack for reducing the code length characterwise, you can try this.

a=['123','2',4]
a.append('sss') #Default value
n=5 #Index you want to access
max_index=len(a)-1
b=a[min(max_index, n)]
print(b)

But this trick is only useful when you no longer want further modification to the list

Daniel
  • 9,491
  • 12
  • 50
  • 66
Vikas Kumar
  • 2,978
  • 3
  • 14
  • 24
0

Since this is a top google hit, it's probably also worth mentioning that the standard "collections" package has a "defaultdict" which provides a more flexible solution to this problem.

You can do neat things, for example:

twodee = collections.defaultdict(dict)
twodee["the horizontal"]["the vertical"] = "we control"

Read more: http://docs.python.org/2/library/collections.html

Jonathan Mayer
  • 1,432
  • 13
  • 17
-1

Using try/catch?

try:
    b=a[4]
except IndexError:
    b='sss'
zoli2k
  • 3,388
  • 4
  • 26
  • 36