Skip to main content

SubclassDict

CI PyPI version Python versions Code coverage License: MIT

A TypeDict that allows subclasses of type keys to be used as keys. When a key is not found, it automatically looks for the closest superclass key, enabling polymorphic behavior in dictionary lookups.

Features

  • Polymorphic lookups: Subclasses automatically find values stored under their parent class keys
  • Performance optimized: Cached subclass lookups for O(1) performance after first access
  • Full dict compatibility: Implements all standard dictionary methods
  • Type safe: Full type hints with generic support
  • Comprehensive: Includes utility functions for class hierarchy traversal

Installation

pip install subclassdict

Quick Start

from subclassdict import SubclassDict

# Define a class hierarchy
class Animal:
    pass

class Dog(Animal):
    pass

class Puppy(Dog):
    pass

# Create a SubclassDict
d = SubclassDict()

# Store values under parent classes
d[Animal] = "I'm an animal"
d[Dog] = "I'm a dog"

# Subclasses can access parent values
print(d[Puppy])  # Output: "I'm a dog" (finds Dog's value)
print(d[Dog])    # Output: "I'm a dog" (direct match)
print(d[Animal]) # Output: "I'm an animal" (direct match)

Real-World Examples

Plugin System

from subclassdict import SubclassDict

class Plugin:
    def __init__(self, name: str):
        self.name = name

class DatabasePlugin(Plugin): pass
class WebPlugin(Plugin): pass
class APIPlugin(WebPlugin): pass

# Register plugins
plugins = SubclassDict()
plugins[Plugin] = "base_plugin"
plugins[DatabasePlugin] = "database_plugin"
plugins[WebPlugin] = "web_plugin"

# Plugin resolution
print(plugins[APIPlugin])  # "web_plugin" (finds WebPlugin)
print(plugins[DatabasePlugin])  # "database_plugin" (direct match)

Event Handling

from subclassdict import SubclassDict

class Event: pass
class UserEvent(Event): pass
class LoginEvent(UserEvent): pass
class LogoutEvent(UserEvent): pass

# Register event handlers
handlers = SubclassDict()
handlers[Event] = "generic_handler"
handlers[UserEvent] = "user_handler"

# Event handling
print(handlers[LoginEvent])   # "user_handler"
print(handlers[LogoutEvent])  # "user_handler"
print(handlers[Event])        # "generic_handler"

Serialization System

from subclassdict import SubclassDict

class Serializable: pass
class JSONSerializable(Serializable): pass
class XMLSerializable(Serializable): pass
class JSONAPISerializable(JSONSerializable): pass

# Register serializers
serializers = SubclassDict()
serializers[Serializable] = "generic_serializer"
serializers[JSONSerializable] = "json_serializer"
serializers[XMLSerializable] = "xml_serializer"

# Serialization
print(serializers[JSONAPISerializable])  # "json_serializer"
print(serializers[XMLSerializable])       # "xml_serializer"

API Reference

SubclassDict

The main class that provides polymorphic dictionary behavior.

Methods

  • __getitem__(key): Get value by key, with subclass lookup
  • __setitem__(key, value): Set value for key
  • __delitem__(key): Delete key
  • __contains__(key): Check if key exists (with subclass lookup)
  • get(key, default=None): Get value with default
  • setdefault(key, default=None): Set default if key not present
  • pop(key, default=None): Pop value with default
  • popitem(): Pop arbitrary item
  • copy(): Create shallow copy
  • clear(): Clear all items and cache

Performance Characteristics

  • First lookup: O(n) where n is the number of keys (searches for superclass)
  • Subsequent lookups: O(1) (cached)
  • Memory: O(k) where k is the number of unique keys accessed

Utility Functions

subclasses(cls)

Get all subclasses of a class recursively.

from subclassdict import subclasses

class Animal: pass
class Dog(Animal): pass
class Cat(Animal): pass
class Puppy(Dog): pass

print(subclasses(Animal))  # [Dog, Cat, Puppy]
print(subclasses(Dog))      # [Puppy]

Advanced Usage

Custom Type Keys

SubclassDict works with any hashable type, not just classes:

d = SubclassDict()
d[str] = "string type"
d[int] = "integer type"

# Non-type keys work normally (no subclass lookup)
d["string"] = "string value"

Cache Management

The cache is automatically managed, but you can inspect it:

d = SubclassDict()
d[Animal] = "animal"

# Access to populate cache
d[Dog]  # This populates the cache

# Inspect cache
print(d._subclass_cache)  # {Dog: Animal}

Performance with Large Hierarchies

SubclassDict is optimized for large class hierarchies:

# Create a deep hierarchy
classes = []
prev_class = None
for i in range(100):
    if prev_class is None:
        new_class = type(f'Class{i}', (), {})
    else:
        new_class = type(f'Class{i}', (prev_class,), {})
    classes.append(new_class)
    prev_class = new_class

d = SubclassDict()
d[classes[0]] = "root"

# Even deep lookups are fast after caching
print(d[classes[-1]])  # Fast O(1) lookup

Comparison with Alternatives

Feature SubclassDict Regular dict Custom lookup
Polymorphic lookups ✅ Automatic ❌ Manual ✅ Manual
Performance ✅ O(1) cached ✅ O(1) ❌ O(n)
Type safety ✅ Full typing ✅ Full typing ❌ Manual
Standard dict methods ✅ All methods ✅ All methods ❌ Manual
Memory usage ✅ Minimal ✅ Minimal ❌ Can be high

Development

Setup

git clone https://github.com/eddiethedean/subclassdict.git
cd subclassdict
pip install -e .[dev]

Running Tests

pytest

Code Quality

# Linting
ruff check .

# Formatting
ruff format .

# Type checking
mypy src/

Pre-commit Hooks

pre-commit install

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests
  5. Run the test suite
  6. Submit a pull request

See CONTRIBUTING.md for detailed guidelines.

License

MIT License - see LICENSE for details.

Changelog

See CHANGELOG.md for version history.

Troubleshooting

Common Issues

Q: Why doesn't my subclass find the parent's value? A: Make sure the parent class key exists in the dictionary. SubclassDict only looks up the hierarchy, not down.

Q: Performance seems slow with many keys? A: The first lookup for each key is O(n), but subsequent lookups are O(1) due to caching.

Q: Can I use non-class keys? A: Yes, but subclass lookup only works with type objects. Non-type keys work like a regular dictionary.

Performance Tips

  1. Pre-populate cache: Access keys you'll use frequently to populate the cache
  2. Use specific keys: Store values under the most specific class possible
  3. Avoid deep hierarchies: Very deep inheritance chains can impact first-lookup performance

Support

Metadata

Release files for subclassdict 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for subclassdict 0.1.1
File Size Uploaded
subclassdict-0.1.1.tar.gz 15.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for subclassdict 0.1.1
File Interpreter ABI Platform
subclassdict-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 23.7 kB

Release files / subclassdict-0.1.1.tar.gz

Download URL subclassdict-0.1.1.tar.gz
Size 15.5 kB
Tags Source
SHA-256 checksum
How to use checksums
d5b84edbcee800182cf8f1782f7e28b05b25020bf6fcb3c504686f12a94ccc45
BLAKE2b-256 checksum
How to use checksums
e08ef02e2255a12fbf36e971a0878a8d57cff9f7ba0212d6ac8de7fddc62e45c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.13

Release files / subclassdict-0.1.1-py3-none-any.whl

Download URL subclassdict-0.1.1-py3-none-any.whl
Size 8.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
89839c6cc1a6a5082bb60d3e42d0711ae6edd0ccd3153b39f7bbbbf5c303846a
BLAKE2b-256 checksum
How to use checksums
717d048b619d25f48891a17f3e053c5582cc6ba8528f47f9f16cd996d821623a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.13

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page