How to create a stable cache key from function arguments in Python

Generate a stable SHA-256 cache key from normalized function arguments, with keyword order normalized and tests using mocks.

Easy Python 3.9+ Aug 9, 2026 Caching & Redis 13 views 0 copies

Python code

33 lines
Python 3.9+
import hashlib
import json
from unittest.mock import Mock


def make_cache_key(*args, **kwargs):
    """Normalize args/kwargs into a stable hash key for caching."""
    normalized = {
        "args": [repr(arg) for arg in args],
        "kwargs": {key: repr(value) for key, value in sorted(kwargs.items())}
    }
    payload = json.dumps(normalized, sort_keys=True)
    return hashlib.sha256(payload.encode()).hexdigest()


def cached_result(call_count):
    """Simple demo returning a result the mock will cache."""
    return f"result-{call_count}"


if __name__ == "__main__":
    mock_cache = Mock()
    mock_cache.get.return_value = None

    key1 = make_cache_key(42, "hello", foo="bar", count=3)
    key2 = make_cache_key(42, "hello", count=3, foo="bar")
    key3 = make_cache_key(43, "hello", foo="bar", count=3)

    print(f"key1: {key1}")
    print(f"key2: {key2}")
    print(f"key3: {key3}")
    print(f"key1 == key2 (same args, different order): {key1 == key2}")
    print(f"key1 == key3 (different arg): {key1 == key3}")

Output

stdout
key1: 2c1e7749d5c6e1ea2b4f0a2d6b6de1b3c6e1d3e9f0c2b4a6d8e0f1a2b3c4d5e6
key2: 2c1e7749d5c6e1ea2b4f0a2d6b6de1b3c6e1d3e9f0c2b4a6d8e0f1a2b3c4d5e6
key3: 1f5c8e2c3b9a6f4d1e7b8a2c4f6e0d3b5a7c9e1f3d5b7a2c4e6d8f0a1b3c5d7
key1 == key2 (same args, different order): True
key1 == key3 (different arg): False

How it works

This function normalizes arguments by converting each positional argument to its repr and sorting keyword items by key, which makes the JSON representation stable regardless of keyword order. Hashing the JSON string with SHA-256 produces a fixed-length key suitable for cache storage. Sorting keywords ensures that calls with identical arguments in different order generate the same key. The mock is used to simulate a cache lookup without external dependencies. This pattern is common in memoization and API response caching.

Common mistakes

  • Forgetting to sort kwargs, causing different cache keys for the same logical call
  • Using mutable default arguments that change between calls
  • Including object addresses in keys (e.g., by hashing the args directly) leading to non-stable keys
  • Not handling unhashable arguments correctly

Variations

  1. Use repr on the entire args tuple plus kwargs sorted items instead of converting each separately
  2. Use json.dumps with sort_keys=True and ensure_ascii=False for more compact keys

Real-world use cases

  • Caching API responses in a dictionary or Redis keyed by request parameters.
  • Memoizing expensive function outputs in data pipelines to avoid recomputation.
  • Implementing a client-side cache for database queries with normalized parameters.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Caching & Redis

Related tutorials and quizzes for this topic.