ruk·si

🐍 Python
Dictionaries

Updated at 2018-06-09 14:19

Dictionaries (dict) are key-value hash tables. They have much more efficient lookups than lists as long as the data fits in RAM. But regular computers should be able to handle dictionaries with more than ten million items.

Dictionaries are indexed by keyes. Key can be anything hashable (__hash__) that can be compared (__eq__), but strings are the most commonly used keys. Hashing is done to keep lookups fast and equality is used to protect against hash collisions.

phonebook = {
    'john': 123,
    'bob': 456,
}
phonebook['alice'] = 789
assert isinstance(phonebook, dict)
assert phonebook['bob'] == 456

Use dictionary comprehensions.

squares = {number: number * number for number in range(6)}
assert type(squares) == dict
assert squares == {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

d = {0: 'A', 1: 'B', 2: 'C', 3: 'D'}
inverted = {v: k for k, v in d.items()}
assert inverted == {'A': 0, 'B': 1, 'C': 2, 'D': 3}

Dictionary items don't have order. Use ordered dictionaries if order is important in your use-case.

from collections import OrderedDict

od = OrderedDict(one=1, two=2, three=3)
od['four'] = 4
assert od['two'] == 2

Or you can just sort the dictionary when it is read.

import operator

xs = {'a': 2, 'b': 3, 'c': 1}
assert sorted(xs.items()) == [('a', 2), ('b', 3), ('c', 1)]
assert sorted(xs.items(), key=operator.itemgetter(1)) == [('c', 1), ('a', 2), ('b', 3)]
assert sorted(xs.items(), key=lambda x: x[1]) == [('c', 1), ('a', 2), ('b', 3)]

Accessing missing key will raise KeyError. You can use .get(), .setdefault() or defaultdict to fetch keys safely.

phonebook = {
    'john': 123,
    'bob': 456,
    'alice': 789,
}
# phonebook['matt'] # => KeyError
assert phonebook.get('matt') is None
assert phonebook.get('matt', 0) == 0

from collections import defaultdict

dd_phonebook = defaultdict(lambda: 0)
dd_phonebook.update(phonebook)
assert dd_phonebook['john'] == 123
assert dd_phonebook['matt'] == 0
reservations = {
    'table1': [1],
    'table2': [2, 3],
}
incoming_reservations = [
    ('table1', 4),
    ('table3', 5),
    ('table4', 6),
]

for ir in incoming_reservations:
    reservations.setdefault(ir[0], []).append(ir[1])

assert reservations == {
    'table1': [1, 4],
    'table2': [2, 3],
    'table3': [5],
    'table4': [6],
}

Use ChainMap to search multiple dictionaries.

from collections import ChainMap

canines = {
    'grey wolf': 'canis lupus',
    'dire wolf': 'canis dirus',
    'maned wolf': 'chrysocyon brachyurus',
    'red wolf': 'canis rufus',
}
felines = {
    'bobcat': 'lynx rufus',
    'tiger': 'panthera tigris',
    'lion': 'panthera leo',
    'leopard': 'panthera pardus',
}
animals = ChainMap(canines, felines)
assert animals['dire wolf'] == 'canis dirus'
assert animals['bobcat'] == 'lynx rufus'
# animals['poodle'] => KeyError

Use dictionaries when you would require a switch statement.

from typing import Optional

def operate(operator, x, y) -> Optional[float]:
    return {
        'add': lambda: x + y,
        'sub': lambda: x - y,
        'mul': lambda: x * y,
        'div': lambda: x / y,
    }.get(operator, lambda: None)()

assert operate('mul', 2, 4) == 8.0
assert operate('mulz', 2, 4) is None

Merging dictionaries is done with update or **.

xs = {'a': 1, 'b': 2}
ys = {'b': 3, 'c': 4}

aa = {}
aa.update(xs)
aa.update(ys)
assert aa == {'a': 1, 'b': 3, 'c': 4}

bb = {**xs, **ys}
assert bb == {'a': 1, 'b': 3, 'c': 4}

Source

  • Python Tricks The Book, Dan Bader
  • Fluent Python, Luciano Ramalho