Skip to main content

Pure Python mutable keys dictionary class.

Project description

MutableKeysDict

Tests PyPI version Python 3.8+ License: MIT

A pure Python dictionary class that allows the keys to be mutated after insertion.

Why MutableKeysDict?

Standard Python dictionaries use the hash of a key at insertion time. If you mutate a hashable object after using it as a key, the dictionary can't find it anymore because the hash has changed. MutableKeysDict solves this problem by tracking key mutations and maintaining access to your values even after the keys change.

Perfect for:

  • Using mutable hashable objects as dictionary keys
  • Tracking objects that change over time
  • Building data structures where key identity matters more than key value

Note: Keys must still be hashable (implement __hash__). If you need truly unhashable keys, check out dictanykey.

Installation

pip install mutablekeysdict

Requirements: Python 3.8+

Quick Start

from mutablekeysdict import MutableKeysDict

# Create a hashable list class
class HashableList(list):
    def __hash__(self):
        return hash(tuple(self))

# Use it as a dictionary key
h = HashableList([1, 2, 3])
d = MutableKeysDict({h: 6})

# Mutate the key
h.append(4)

# Still works! 🎉
print(d[h])  # Output: 6

# Standard dict would raise KeyError here
standard_dict = {h: 6}
h.append(5)
# standard_dict[h]  # KeyError!

Features

  • Full dict API compatibility - Drop-in replacement for standard dictionaries
  • Mutable key support - Access values even after keys are mutated
  • Type hints - Full mypy compatibility
  • Thoroughly tested - 95%+ code coverage with 52+ tests
  • Modern Python - Supports Python 3.8+
  • Pure Python - No dependencies
  • Dict union operators - Supports | and |= operators (Python 3.9+)

Usage Examples

Basic Operations

from mutablekeysdict import MutableKeysDict

# Create from dict
d = MutableKeysDict({'a': 1, 'b': 2})

# All standard dict operations work
d['c'] = 3
print(len(d))  # 3
print('a' in d)  # True
print(list(d.keys()))  # ['a', 'b', 'c']

# Update with another dict
d.update({'d': 4})

# Use dict union operators (Python 3.9+)
d2 = d | {'e': 5}

Working with Mutable Keys

class MutableKey:
    def __init__(self, value):
        self.value = value
        self.metadata = []
    
    def __hash__(self):
        return hash(self.value)
    
    def __eq__(self, other):
        return self.value == other.value

# Create dictionary with mutable keys
k1 = MutableKey("key1")
k2 = MutableKey("key2")
d = MutableKeysDict({k1: "value1", k2: "value2"})

# Mutate the keys
k1.metadata.append("modified")
k2.metadata.append("also modified")

# Dictionary still works!
print(d[k1])  # "value1"
print(d[k2])  # "value2"

# Can iterate over mutated keys
for key in d.keys():
    print(f"{key.value}: {key.metadata}")

Copy and Create Methods

# Create from keys
d = MutableKeysDict.fromkeys(['a', 'b', 'c'], 0)
# {'a': 0, 'b': 0, 'c': 0}

# Shallow copy
d2 = d.copy()

# Replace key
d.replace_key('a', 'new_a')

How It Works

MutableKeysDict maintains an internal mapping between keys and stable indices. When you access a key:

  1. It checks if any keys have been mutated (hash changed)
  2. If mutations are detected, it rebuilds the internal mapping
  3. Returns the value associated with the stable index

This allows the dictionary to track the same key object even when its hash changes, as long as you use the same object reference.

API Reference

MutableKeysDict implements the full collections.abc.MutableMapping interface. All standard dictionary methods are supported:

Standard Methods:

  • __getitem__, __setitem__, __delitem__
  • keys(), values(), items()
  • get(), pop(), popitem()
  • update(), setdefault(), clear()
  • copy(), fromkeys() (class method)

Additional Methods:

  • replace_key(old_key, new_key) - Replace a key with a new one while preserving the value

Operators:

  • ==, != - Equality comparison
  • |, |= - Dict union operators (Python 3.9+)
  • in - Membership testing
  • len() - Get size
  • iter() - Iterate over keys

Version History

v0.1.0 (2025-10-22)

  • 🐛 Fixed critical bugs in __setitem__, popitem(), get(), and update()
  • ✨ Added missing dict methods: copy(), fromkeys(), __or__(), __ior__()
  • 🎯 Full mypy type checking support
  • ✅ Comprehensive test suite with 95%+ coverage
  • 📦 Modernized packaging with pyproject.toml
  • 🔧 Python 3.8+ support

Contributing

Contributions are welcome! Here's how to set up your development environment:

Setting Up Development Environment

  1. Clone the repository:
git clone https://github.com/eddiethedean/mutablekeysdict.git
cd mutablekeysdict
  1. Install development dependencies:
pip install -e ".[dev]"

This installs the package in editable mode along with:

  • pytest - Testing framework
  • pytest-cov - Coverage reporting
  • mypy - Type checking
  • ruff - Fast linting
  • black - Code formatting

Running Tests

Run the full test suite:

pytest tests/

With coverage report:

pytest tests/ --cov=mutablekeysdict --cov-report=term-missing

Run specific test:

pytest tests/test_mutablekeysdict.py::TestMutableKeysDictBasics::test_empty_init

Code Quality

Linting:

ruff check mutablekeysdict tests

Auto-fix issues:

ruff check --fix mutablekeysdict tests

Formatting:

black mutablekeysdict tests

Type checking:

mypy mutablekeysdict

Run all checks:

ruff check mutablekeysdict tests && \
black --check mutablekeysdict tests && \
mypy mutablekeysdict && \
pytest tests/

Project Structure

mutablekeysdict/
├── mutablekeysdict/
│   ├── __init__.py          # Package initialization
│   └── mutablekeysdict.py   # Main implementation
├── tests/
│   └── test_mutablekeysdict.py  # Test suite
├── pyproject.toml           # Project configuration
├── README.md                # This file
└── CHANGELOG.md             # Version history

License

This project is licensed under the MIT License - see the LICENSE file for details.

Author

Odos Matthews

Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mutablekeysdict-0.1.1.tar.gz (8.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mutablekeysdict-0.1.1-py3-none-any.whl (5.9 kB view details)

Uploaded Python 3

File details

Details for the file mutablekeysdict-0.1.1.tar.gz.

File metadata

  • Download URL: mutablekeysdict-0.1.1.tar.gz
  • Upload date:
  • Size: 8.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.13

File hashes

Hashes for mutablekeysdict-0.1.1.tar.gz
Algorithm Hash digest
SHA256 cb7e7a8b3d3cacb1ca0b0966b6f4a49d9003daede3af49be7aa2a9eb1fbb2145
MD5 b94981e5a62251f3ae59c7d64c10c327
BLAKE2b-256 92891da7a0b5f668c74f8ebd2128ee24ac68fd5736f9dc958afa7cd15272a3c6

See more details on using hashes here.

File details

Details for the file mutablekeysdict-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for mutablekeysdict-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c4541aae8e026946c75852d7ef7932471591e6d5b529f5f398df398a2d028df4
MD5 7a477ba73537a68cd37870a8d2931330
BLAKE2b-256 b2cf16b99a9a603aa0c5cffbaf72c63306503e4e67aa76434cf41d81ac135719

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page