HOWTO · Python

Lookup Table in Python

Learn how to build Python lookup tables with dictionaries, lists, NumPy arrays, and Pandas DataFrames, handle absent keys and indexes, and choose a suitable structure.

On this page

A lookup table stores values so code can retrieve them using a key or position. It is useful when the same relationship is queried repeatedly, such as mapping product codes to prices. Precomputing a result can avoid repeating a calculation, but the table takes memory and must be updated when its source data changes.

Choose the structure from the shape of the keys and the work you need to do: dictionaries accept arbitrary hashable keys, lists and arrays use integer positions, and a DataFrame supports labeled columns and table operations. The examples below show how each structure handles a practical lookup.

Choose a lookup-table structure for your data

First ask what identifies a value. Use a dictionary for names, codes, or other keys that are not dense integer positions. Use a list when positions are consecutive integers beginning at zero and the values can live in a Python sequence. Use a NumPy array when the data is numeric and array operations matter. Use a Pandas DataFrame when each record has multiple named fields or you need filtering and other tabular operations. NumPy and Pandas are third-party dependencies; install them in your project environment before using their examples.

Use a dictionary for key-based lookups

A dictionary maps each distinct, hashable key to a value. It works well for non-sequential keys and makes a fallback explicit with get. For an absent key, get returns the default you provide; bracket access instead raises KeyError when the key is absent.

Run this example:

prices = {"tea": 2.5, "coffee": 3.0, "cocoa": 2.75}
print(prices["tea"])
print(prices.get("juice", "not listed"))
2.5
not listed

Dictionary retrieval is average-case O(1), not a guarantee for every possible workload. Keys must be hashable, and assigning a value for an existing key replaces its previous value. If the source records change, update the dictionary too so the lookup does not return stale data.

Use a list for dense integer indexes

A list is a simple table when valid indexes are dense and start at zero: index i refers to the (i + 1)th item. It is a poor fit for sparse identifiers such as customer numbers, because gaps would require unused slots or a separate mapping. Indexing a list is O(1); the values remain ordinary Python objects.

labels = ["bronze", "silver", "gold"]

def label_for(index):
    if 0 <= index < len(labels):
        return labels[index]
    return "index out of range"

print(label_for(2))
print(label_for(5))
gold
index out of range

The explicit range check handles both indexes below zero and indexes at or beyond the list length. Python lists also allow negative indexes to count backward from the end, so validate first when your table’s keys are defined only as nonnegative positions.

Use a NumPy array for numeric data

A NumPy array is useful when the table contains numeric values and you also need vectorized calculations, slicing, or other array operations. Install NumPy separately; it is not part of the Python standard library. A scalar lookup is still just an indexed retrieval, so an array is not automatically a faster replacement for a dictionary.

import numpy as np

rates = np.array([1.0, 1.5, 2.0, 2.5])
index = 2
if 0 <= index < len(rates):
    print(rates[index])
else:
    print("index out of range")
print((rates * 2).tolist())
2.0
[2.0, 3.0, 4.0, 5.0]

The bounds check is useful when an index comes from external input. NumPy arrays generally use a single element type, which can make numerical operations convenient; choose the element type and shape to match the data you need to store.

Use a Pandas DataFrame for tabular data

A DataFrame fits records with named columns, especially when lookup is part of a larger filtering or analysis task. Install Pandas separately. Filtering by a column compares rows and returns a DataFrame when you select rows alone. In the expression below, selecting the single price column with .loc returns a Series; the filter may scan the rows. For repeated key lookup, consider setting a meaningful index and using .loc instead.

import pandas as pd

items = pd.DataFrame({
    "code": ["A", "B", "C"],
    "price": [1.25, 2.50, 3.75],
})
match = items.loc[items["code"] == "B", "price"]
if not match.empty:
    print(match.iloc[0])
else:
    print("not found")
2.5

This pattern handles the absence of a matching code without indexing an empty result. If codes are unique and queried repeatedly, items.set_index("code").loc["B", "price"] expresses a label lookup; ensure the code exists before accessing it, and decide how duplicate labels should be handled.

Handle missing keys and invalid indexes

Choose a missing-value policy that suits the caller. dict.get(key, default) returns a fallback, while key in mapping lets you distinguish absence from a stored value that happens to equal the fallback. Use try/except KeyError when missing keys are exceptional in the surrounding logic. For lists and arrays, validate the index against 0 <= index < len(table) if negative positions should not count from the end. For a DataFrame filter, check whether the result is empty before selecting its first row. Keep the policy consistent across functions so callers know whether absence returns a sentinel, a default, or an error.

Compare performance and practical trade-offs

Dictionary lookup is average-case O(1) by key, while a list or one-dimensional array takes O(1) by position. A DataFrame boolean filter is generally O(n) because it tests rows; an index can support label-oriented access, but its cost and behavior depend on index construction and data shape. These complexity descriptions do not predict wall-clock speed for every input size: setup costs, key types, data layout, library operations, and memory all matter.

Use the simplest structure that matches the key and the operations: dictionary for arbitrary keys, list for dense positions, NumPy array for numeric array work, and DataFrame for labeled tabular work. Benchmark representative data if performance is a deciding factor. Account for memory as well: dictionaries store key-value mappings, lists hold references to Python objects, and NumPy arrays store typed elements; actual use depends on data and implementation. If a lookup table is derived from changing source data, rebuild or update it whenever that data changes to avoid stale answers.