dict.get(key, default) evaluates default even if key exists

Chris Angelico rosuav at gmail.com
Sun Dec 20 09:10:30 EST 2020


On Mon, Dec 21, 2020 at 12:47 AM <2QdxY4RzWzUUiLuE at potatochowder.com> wrote:
>
> On 2020-12-19 at 22:29:34 +0100,
> Roel Schroeven <roel at roelschroeven.net> wrote:
>
> > What could be useful in some use cases, I think, is a wrapper function
> > that evaluates the function lazily:
> >
> >     def dict_get_lazily(d, key, fnc, *args, **kwargs):
> >         try:
> >             return d[key]
> >         except KeyError:
> >             return fnc(*args, **kwargs)
> >
> >     some_var = dict_get_lazily(d, 'spam', some_function, 31, 11)
>
> There's no need to pass all those arguments through dict_get_lazily, and
> the language already has a way to evaluate an expression lazily:
>
>     def dict_get_lazily(d, key, f):
>         try:
>             return d[key]
>         except KeyError:
>             return f()
>
>     some_var = dict_get_lazily(d, 'spam', lambda: some_function(31, 11))
>

class LazyDict(dict):
    def __missing__(self, key):
        # Pretend this is slow
        print("slow function called")
        return 31 + 11

d = LazyDict({
    'spam': 0,
    'ham': 1,
    'parrot': 2,
})
print(d['spam'])
print(d['eggs'])

Guaranteed to be called when and only when the key is missing. This
does imply a variant architecture in which the data structure itself
is aware of the calculations necessary to generate the data, so it may
not be appropriate to all situations, but it's clean, easy, and
efficient.

If you can't do this, I'd recommend using the try/except method, maybe
wrapped up in a function if you want to. In the case where the key is
found, the cost is one try block and one lookup - barely more than any
other option (for the record, a try block is pretty cheap in CPython,
and it's only costly when you hit an exception); and where it isn't
found, it's two lookups plus the cost of generating the default (and
we've already established that generating the default is expensive,
otherwise setdefault would be appropriate).

ChrisA


More information about the Python-list mailing list