7

Is there anyway to do the equivalent of this?

my_list = [try: my_dict["some_key"] except KeyError: 0 for my_dict in my_list]

Since dictionaries throw KeyErrors I want to catch the error if the element in the list does not have a "some_key" property. I know I could create a defaultdict by importing collections and sidestepping the exception, but I want to know if this is possible with out of the box dictionaries.

3 Answers 3

12

No, you can't. You can put only for loops, if's and else's.

What you can do, though, is use .get(), which never throws a KeyError:

my_list = [my_dict.get("some_key", 0) for my_dict in my_list]

The second argument is the default value in case the key does not exist. If you don't specify a default, the default default is None.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks I ended up using this
Would it be faster with an if argument to check if the key is in dict instead of trying all keys with get?
@Varlor Using the value if the key exists and 0 if not is exactly what get does. Doing it manually might not be noticeably slower, but I don't think it would be faster.
4

No, you can not do this directly in a list comprehension. You must factor the try/except logic out into a separate function, and then call the function.

There is an easy alternative for the use case shown in your question, though, using dict.get:

my_list = [my_dict.get('some_key', 0) for my_dict in my_list]

Comments

2

As other answers mentioned you can not use an try-except inside a list comprehension. But as a tricky approach you can use collections.defaultdict() and override the __missing__ attribute in order to catch the exceptions. Here is an example:

from collections import defaultdict


class Mydefaultdict(defaultdict):
    def __init__(self, *args, **kwargs):
        super(Mydefaultdict, self).__init__(*args, **kwargs)

    def __getitem__(self, key):
        try:
            return defaultdict.__getitem__(self, key)
        except KeyError:
            return self.__missing__(key)

    def __missing__(self, key):
        # Instead of printing you can catch your exceptions in any way you like
        print("{} doesn't exist".format(key))
        if self.default_factory:
            return self.default_factory()  # You can rturn whatever you want here

Demo:

d = Mydefaultdict(None, {4: 'b', 1: 'a'})
print([d[i] for i in [1, 2]])

2 doesn't exist
['a', None]

3 Comments

Or merely use defaultdict(int)
@zondo No! we just want to override the __missing__ method. Using int as the default factory function won't give us the desire behavior.
The desired behavior is to get a 0 when the key doesn't exist. Adding the key may be undesired, though.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.