Typed List and Dict

Numba · Practical · 10 min

Algorithms that need growing collections or lookup tables can stay fully compiled.

Arrays have a fixed size, but many algorithms need a growable list or a hash map: building adjacency lists, counting occurrences, memoisation. Numba provides typed, compiled versions in numba.typed:

  • typed.List: a homogeneous list. You can create one in Python (List() then append, or List([1.0, 2.0])) and pass it into compiled functions, or create a list inside an @njit function with an ordinary [] literal and append.
  • typed.Dict: a homogeneous hash map. Create it empty with explicit types, Dict.empty(key_type=types.int64, value_type=types.float64), or inside compiled code with {} and assignments, where the types are inferred from the first insertion.

Every element and key must have the same Numba type. Values can be numbers, tuples, arrays (as long as all have the same dtype and ndim) or other typed containers.

Why not just pass a Python list? Numba used to support "reflected" Python lists by converting them on every call, which was slow and is deprecated. Typed containers live in native memory and cross the Python boundary without conversion. Accessing them from plain Python, however, is slower than a normal list, because every access goes through a compiled helper. Use them as the data structure for compiled code, not as a general replacement.

Typed dicts and lists are usable from both sides, so you can build data in a compiled function and inspect it in Python.

Example

import numpy as np
from numba import njit, types
from numba.typed import Dict, List

@njit
def count_values(a):
    counts = Dict.empty(key_type=types.int64, value_type=types.int64)
    for x in a:
        counts[x] = counts.get(x, 0) + 1
    return counts

c = count_values(np.array([3, 1, 3, 3, 2]))
print(dict(c))                      # {3: 3, 1: 1, 2: 1}

@njit
def evens_up_to(n):
    out = []                        # inferred as a typed list of int64
    for i in range(n):
        if i % 2 == 0:
            out.append(i)
    return out

print(list(evens_up_to(7)))         # [0, 2, 4, 6]
lst = List([1.5, 2.5])
print(type(lst).__name__)           # List

Remember

  • typed.List and typed.Dict hold one Numba type per container
  • Create them in Python or with literals inside @njit code
  • They are for compiled code; plain Python access is slower than built-ins

Watch out for

  • Passing a regular Python list of lists into @njit code (reflected lists are deprecated and slow).

Check yourself

  1. Which value can be inserted into Dict.empty(key_type=types.int64, value_type=types.float64)?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: numba.readthedocs.io/en/stable/reference/pysupported.html