# Add safe \`.get\` method to List

**URL:** <https://discuss.python.org/t/add-safe-get-method-to-list/32555>\
**Category:** Ideas\
**Created:** [August 29, 2023, 6:27pm UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555 "2023-08-29T18:27:03Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![NoahTheDuke](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/noahtheduke/32/14372_2.png) [@NoahTheDuke](https://discuss.python.org/u/NoahTheDuke)\
**Post date:** [August 29, 2023, 6:27pm UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/1 "2023-08-29T18:27:03Z")

</div>

To access an item in a dictionary, you use indexing: `d["some_key"]`. However, if the key doesn’t exist in the dictionary, a `KeyError` is raised. To avoid this, you can use `.get` and pass in a default value to return instead: `d.get("some_key", "default value")`.

Lists don’t have such a method for safely indexing. There are many ways around it (as documented in many SO threads: [thread 1](https://stackoverflow.com/questions/5125619/why-doesnt-list-have-safe-get-method-like-dictionary), [thread 2](https://stackoverflow.com/questions/2492087/how-to-get-the-nth-element-of-a-python-list-or-a-default-if-not-available)), but they’re all pretty awkward and they aren’t composable or chain-able the same way that `.get()` is.

This could maybe be applied to Tuples, but I’d prefer to keep scope small. And I don’t think this applies to Iterables in general, but I don’t know enough details about those to say.

I searched the threads and PEPs but not the mailing lists and I haven’t seen anything recommending this. I looked at the code and I think this shouldn’t be _too_ hard to implement. I’d be willing to take a swing at it or writing a PEP if there’s any interest.

---

<div class="post-metadata">

**Author:** ![barry-scott](https://avatars.discourse-cdn.com/v4/letter/b/e9c0ed/32.png) [@barry-scott](https://discuss.python.org/u/barry-scott)\
**Post date:** [August 29, 2023, 6:31pm UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/2 "2023-08-29T18:31:55Z")

</div>

This comes up every now and again.

The argument against this is that lists are iterated or code knows the length so no safe get is useful.

Do you have a case where that argument is not valid and not a rare use case?

---

<div class="post-metadata">

**Author:** ![NoahTheDuke](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/noahtheduke/32/14372_2.png) [@NoahTheDuke](https://discuss.python.org/u/NoahTheDuke)\
**Post date:** [August 29, 2023, 7:59pm UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/3 "2023-08-29T19:59:42Z")

</div>

I understand that argument, but it’s not true that lists are only iterated. For example, I have a list of objects inside of a nested dictionary. I want to get a property of the first item in the list:

```python
state = {"corp": {"hand": [{"title": "Sure Gamble"}]}}

state.get("corp", {}).get("hand", [])[0].get("title", "")

```

The `.get()` calls allow me to write a somewhat fluent and safe getter for the nested objects. But this blows up if the list is empty. To work around that, I have to store in a variable and then either check the length of the list or wrap the indexing in a `try/except`. If I’m using a variable index instead of a literal `0`, then I need to use `try/except` because of negative indexes.

That’s a fairly complex example, but I think even the simple examples would benefit. For example, I want to look at the current item and the next item in a for loop. The following code blows up but with a safe get, it would do the “right” thing without blowing up:

```python
lst = list(range(10))
for idx, item in enumerate(lst):
    next_item = lst[idx + 1]
    print(item, next_item)

```

I know the argument that there are ways around these situations (see the SO threads linked above) or that a simple `if/then` is easy to use, so we shouldn’t bloat the standard library. I think that such an argument exists for dictionaries as well. Why can’t people just use `if "some_key" in d: ret = d["some_key"] else: ret = "default value"`?

Given that `dict.get()` is both used and loved enough to make people want it for list, I see great value in adding it to list.

---

<div class="post-metadata">

**Author:** ![pf\_moore](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pf_moore/32/35_2.png) [@pf\_moore](https://discuss.python.org/u/pf_moore)\
**Post date:** [August 29, 2023, 8:09pm UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/4 "2023-08-29T20:09:21Z")

</div>

> [@NoahTheDuke](#):
>
> The `.get()` calls allow me to write a somewhat fluent and safe getter for the nested objects. But this blows up if the list is empty. To work around that, I have to store in a variable and then either check the length of the list or wrap the indexing in a `try/except`. If I’m using a variable index instead of a literal `0`, then I need to use `try/except` because of negative indexes.

Maybe look at the `glom` library on PyPI, which as far as I know is designed to handle this sort of use case specifically.

As far as the proposal here is concerned, I find the chained `get` expression incredibly difficult to read, and I’m -1 on adding features to the language that encourage this type of thing.

---

<div class="post-metadata">

**Author:** ![Rosuav](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rosuav/32/3429_2.png) [@Rosuav](https://discuss.python.org/u/Rosuav)\
**Post date:** [August 29, 2023, 8:33pm UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/5 "2023-08-29T20:33:58Z")

</div>

> [@NoahTheDuke](#):
>
> ```auto
> state.get("corp", {}).get("hand", [])[0].get("title", "")
> 
> ```
> 
> The `.get()` calls allow me to write a somewhat fluent and safe getter for the nested objects. But this blows up if the list is empty. To work around that, I have to store in a variable and then either check the length of the list or wrap the indexing in a `try/except`. If I’m using a variable index instead of a literal `0`, then I need to use `try/except` because of negative indexes.

Honestly, this sounds like a great use for PEP 463…

---

<div class="post-metadata">

**Author:** ![NoahTheDuke](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/noahtheduke/32/14372_2.png) [@NoahTheDuke](https://discuss.python.org/u/NoahTheDuke)\
**Post date:** [August 29, 2023, 8:40pm UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/6 "2023-08-29T20:40:46Z")

</div>

I don’t think the chained `.get()` is common, just something I ran into today. I suspect the majority of use-cases are single calls.

If that’s your final opinion tho, I’ll trust your opinion and leave it be. Thanks for the feedback.

---

<div class="post-metadata">

**Author:** ![NoahTheDuke](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/noahtheduke/32/14372_2.png) [@NoahTheDuke](https://discuss.python.org/u/NoahTheDuke)\
**Post date:** [August 29, 2023, 8:42pm UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/7 "2023-08-29T20:42:25Z")

</div>

Thanks for the reference PEP. It’s interesting to see this situation referenced but a completely alternate approach taken (and subsequently rejected).

---

<div class="post-metadata">

**Author:** ![Rosuav](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rosuav/32/3429_2.png) [@Rosuav](https://discuss.python.org/u/Rosuav)\
**Post date:** [August 29, 2023, 8:56pm UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/8 "2023-08-29T20:56:13Z")

</div>

> [@NoahTheDuke](#):
>
> It’s interesting to see this situation referenced but a completely alternate approach taken (and subsequently rejected).

Yeah. Part of the reasoning is that the situations where this would be useful are relatively rare, and are often best handled with a path-traversal function, such as:

```auto
def get(state, *path):
    try:
        for step in path:
            state = state[step]
    except LookupError:
        return None
    return state

get(state, "corp", "hand", 0, "title")

```

If you wanted, you could even make this a ` __getitem__ ` method on a class that always returns another instance of itself, although personally, I’d go for the simpler option of a helper function (since this sort of data often gets loaded/saved in JSON and it’s easier to use real dicts and lists).

---

<div class="post-metadata">

**Author:** ![NoahTheDuke](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/noahtheduke/32/14372_2.png) [@NoahTheDuke](https://discuss.python.org/u/NoahTheDuke)\
**Post date:** [August 30, 2023, 2:52pm UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/9 "2023-08-30T14:52:39Z")

</div>

That’s true, and the alternatives are fairly easy. It’s just nice to provide a built-in that makes specific use-cases easier, cover what looks like a gap in the api. Given that the first reply said it comes up “every now and again” and I found two heavily upvoted threads on Stack Overflow, I’d expect that to weigh a little bit for adding _something_ to make this easier on folks (like me lol).

---

<div class="post-metadata">

**Author:** ![NeilGirdhar](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/neilgirdhar/32/5776_2.png) [@NeilGirdhar](https://discuss.python.org/u/NeilGirdhar)\
**Post date:** [August 30, 2023, 4:01pm UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/10 "2023-08-30T16:01:53Z")

</div>

> [@NoahTheDuke](#):
>
> ```auto
> lst = list(range(10))
> for idx, item in enumerate(lst):
> next_item = lst[idx + 1]
> print(item, next_item)
> 
> ```

Just FYI, there’s a nice way to write this:

```auto
from itertools import pairwise
lst = list(range(10))
for item, next_item in pairwise(lst):
    print(item, next_item)

```

---

<div class="post-metadata">

**Author:** ![flyinghyrax](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/flyinghyrax/32/12394_2.png) [@flyinghyrax](https://discuss.python.org/u/flyinghyrax)\
**Post date:** [August 30, 2023, 4:47pm UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/11 "2023-08-30T16:47:30Z")

</div>

I would be on board, and IMO this should just be added to `collections.abc.Sequence` as a generic method of indexable collections.

Like OP I run into this paper cut fairly often, and feel existing solutions have shortcomings compared to the proposed method.

Besides the symmetry with `dict.get`, I think this utility method mirrors similar collection APIs in other language standard libraries (but I’ll need to cite that later when I’m off work).

---

<div class="post-metadata">

**Author:** ![storchaka](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/storchaka/32/217_2.png) [@storchaka](https://discuss.python.org/u/storchaka)\
**Post date:** [August 30, 2023, 5:01pm UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/12 "2023-08-30T17:01:55Z")

</div>

Just use an external function:

```auto
def get(mapping, key, default=None):
    if key in mapping:
        return mapping[key]
    else:
        return default

```

---

<div class="post-metadata">

**Author:** ![flyinghyrax](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/flyinghyrax/32/12394_2.png) [@flyinghyrax](https://discuss.python.org/u/flyinghyrax)\
**Post date:** [August 30, 2023, 6:43pm UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/13 "2023-08-30T18:43:29Z")

</div>

As has been pointed out already,

1. You could just as well say the same for `dict.get`, or for any other non-dunder collection method.
2. We already know how to write wrapper functions and other utilities for this use case.

The point is that one writes this same function _over and over again_. I literally just noticed I am staring at a version of this function _right now_ at work:

```python
def eval_escape_sequences(token_stream: Iterable[Token]) -> Iterable[Token]:
  output_queue: deque[Token] = deque()

  def peek_previous() -> Optional[Token]:
    if len(output_queue) > 0:
      return output_queue[0]
    else:
      return None

  …
    

```

I’ve likely written a function like this - or a more generic version - for every project of significant size I’ve ever worked on.

---

<div class="post-metadata">

**Author:** ![pf\_moore](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pf_moore/32/35_2.png) [@pf\_moore](https://discuss.python.org/u/pf_moore)\
**Post date:** [August 30, 2023, 7:42pm UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/14 "2023-08-30T19:42:06Z")

</div>

… and yet, the proposed `.get` method on lists wouldn’t help, because you’re using a deque not a list.

---

<div class="post-metadata">

**Author:** ![elis.byberi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/elis.byberi/32/35937_2.png) [@elis.byberi](https://discuss.python.org/u/elis.byberi)\
**Post date:** [August 30, 2023, 11:46pm UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/15 "2023-08-30T23:46:13Z")

</div>

> [@NoahTheDuke](#):
>
> don’t have such a method for safely indexing

You can use the “+” operator:

```python
state = {"corp": {"hand": [{"title": "Sure Gamble"}]}}
a = (state.get("corp", {}).get("hand", []) + [{}])[0].get("title", "")
print(a)

```

---

<div class="post-metadata">

**Author:** ![flyinghyrax](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/flyinghyrax/32/12394_2.png) [@flyinghyrax](https://discuss.python.org/u/flyinghyrax)\
**Post date:** [August 31, 2023, 12:55am UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/16 "2023-08-31T00:55:30Z")

</div>

> [@pf\_moore](#):
>
> … and yet, the proposed `.get` method on lists wouldn’t help, because you’re using a deque not a list.

Fair enough, but that’s why I mentioned Sequence. That said, I’m not really familiar with how `collections.abc` registration works for built in / native collections like deque. Would it even “inherit” a method added to `Sequence`, or does registering only declare what interfaces it implements for `isinstance` checks?

Additionally I have been skimming docs for some other languages \[1\], and so far the proposed behavior isn’t as widespread as I assumed! At least not for a prominent method name like `get`.

- C#/dotnet’s `List<T>` has the [`Enumerable.ElementAtOrDefault`](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.elementatordefault?view=net-7.0), but that’s an extension method, not defined directly on List or any of its parent interfaces
- F#'s [`List.item`](https://fsharp.github.io/fsharp-core-docs/reference/fsharp-collections-listmodule.html#item) throws an exception for out of bounds access; [`List.tryItem`](https://fsharp.github.io/fsharp-core-docs/reference/fsharp-collections-listmodule.html#tryItem) will return `Optional.None` but is clearly the secondary interface of the two
- Java’s [`ArrayList.get`](https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/ArrayList.html#get(int)) throws, and none of the parent types in the collections package implement a ‘safe get’ method that return null as far as I can tell. The `Collections` helper class doesn’t include one either.
- Swift’s [`Array` indexing](https://developer.apple.com/documentation/swift/array/subscript(_:)-25iat) returns `Self.Element` not `Self.Element?` - i.e. is not nillable - and I don’t see any “safe get” method that supports specifying a default or returning `nil`. (The `first` and `last` properties are nillable, but that’s not arbitrary index access.)
- Rust’s `std::vec::Vec<T>` _does_ have [a `get` method that returns an `Option`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.get)
- Kotlin’s `kotlin.collections.List` has a `get` method (that backs the indexing operator `[]`, I think?) that requires in-bounds and I assume throws an exception otherwise. It does have extension methods similar to dotnet’s: [`getOrElse`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/get-or-else.html)/`getOrNull`, _and also_ (??? \[2\]) [`elementAtOrElse`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/element-at-or-else.html)/`elementAtOrNull`.
- Scala’s `Vector[+A]` is… it’s wild, and now I need to go learn Scala. \[3\]
- Ruby’s [`Array`](https://ruby-doc.org/3.2.2/Array.html) seems to have nillable indexing by default (how fun!), and two alternative methods `at` and `fetch` - [`fetch`](https://ruby-doc.org/3.2.2/Array.html#method-i-fetch) can either throw an error _or_ return a default, `at` seems under-documented \[4\]
- JavaScript’s array indexing and [`Array.at`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at) return `undefined` for out-of-bounds indices \[5\], but you can’t specify a default.

So far, only Ruby’s `fetch` seems to make ‘get or default’ a prominent operation. Rust and JavaScript have prominent methods to return ‘nothing’ instead of throwing an error, but not with a specified default value. \[6\]

TL;DR: I’ve convinced myself the proposed function isn’t as common as I thought.

* * *

1. …no methodology to speak of, suggestions welcome 

2. If someone can say why this is, I’d love to know 

3. `apply` works like ‘regular’ indexing (requires in-bounds), `applyOrElse` returns an Optional, and it also inherits `orElse`,and `lift` from PartialFunction that might be relevant? 

4. and Ruby is another hit list language that I don’t know much about yet… 

5. How “safe” that is in the context of the _rest_ of JavaScript is debatable I guess, but I’m biased 

6. Rust’s `get` lets you chain functions from `Option`, so I guess there’s an obvious design reason they wouldn’t bother with a ‘default’ parameter overload for `get`.

---

<div class="post-metadata">

**Author:** ![ajoino](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ajoino/32/6907_2.png) [@ajoino](https://discuss.python.org/u/ajoino)\
**Post date:** [August 31, 2023, 7:32am UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/17 "2023-08-31T07:32:56Z")

</div>

> [@NoahTheDuke](#):
>
> ```auto
> state.get("corp", {}).get("hand", [])[0].get("title", "")
> 
> ```

In this particular case, you could populate that empty list with a meaningful value, then that line wouldn’t fail.

---

<div class="post-metadata">

**Author:** ![NoahTheDuke](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/noahtheduke/32/14372_2.png) [@NoahTheDuke](https://discuss.python.org/u/NoahTheDuke)\
**Post date:** [August 31, 2023, 7:52pm UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/18 "2023-08-31T19:52:45Z")

</div>

Thanks for the overview!

I am primarily a Clojure developer and it has the built-in functions [`get`](https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/get) and [`get-in`](https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/get-in). Because Clojure is function-based, not method-based, `get` and `get-in` are polymorphic and work on any associative data structure (which includes Clojure’s vectors). They return `nil` when accessing out of bounds and they can be passed default values.

To prospective commenters, I know about the alternative ways to write my example code. I appreciate the enthusiasm but I linked to two SO threads that contain every suggestion posted so far. I created this thread to discuss whether to add something new to cover these alternatives. Seems prudent to stay on topic here.

---

<div class="post-metadata">

**Author:** ![elis.byberi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/elis.byberi/32/35937_2.png) [@elis.byberi](https://discuss.python.org/u/elis.byberi)\
**Post date:** [September 1, 2023, 1:20am UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/19 "2023-09-01T01:20:24Z")

</div>

> [@NoahTheDuke](#):
>
> ```auto
> state = {"corp": {"hand": [{"title": "Sure Gamble"}]}}
> 
> state.get("corp", {}).get("hand", [])[0].get("title", "")
> 
> ```

That’s not very idiomatic in Python; you might want to consider using a try/except block instead:

```python
state = {"corp": {"hand": [{"title": "Sure Gamble"}]}}

item = ''
try: item = state["corp"]["hand"][0]["title"]
except (KeyError, IndexError): pass

print(item)

```

> [@NoahTheDuke](#):
>
> but they’re all pretty awkward

I haven’t read any Stack Overflow threads because the discussion is taking place here. What specifically do you find awkward?

---

<div class="post-metadata">

**Author:** ![ruro](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ruro/32/7340_2.png) [@ruro](https://discuss.python.org/u/ruro)\
**Post date:** [September 3, 2023, 1:51pm UTC](https://discuss.python.org/t/add-safe-get-method-to-list/32555/20 "2023-09-03T13:51:55Z")

</div>

Interestingly, PEP 463 was rejected, because

> I disagree with the position that EAFP is better than LBYL, or “generally recommended” by Python.

And yet `list.get` and similar proposals are frequently criticized for not using the “Pythonic” way (EAFP).

IMHO, something like

```python
print(state["corp"]["hand"][0]["title"] except LookupError: "default")

```

is significantly easier to read than the suggested

```python
item = "default"
try:
    item = state["corp"]["hand"][0]["title"]
except LookupError:
    pass

print(item)

```

[Next page](https://discuss.python.org/t/add-safe-get-method-to-list/32555.md?page=2)
