Generic Mapper in python

Hi,

Goal

I’d like to create a generic mapper in Python that enforces type relationships between keys and values in a dictionary. I’m looking for a way to enforce these key-value type relationships in Python, similar to how generics work in TypeScript. Would this be possible with Python’s typing system?

Example: Nullable Dictionary

For instance, I want to define a NullableDict where:

  • If the key is of type T,
  • Then the value must be either T or None.
from typing import TypeVar 
T = TypeVar("T") 
NullableDict = dict[T, T | None]  # Key: T → Value: T or None

This ensures that if a key is a str, its value can only be a str or None.


Real-World Use Case: SQLAlchemy Updates

A practical example is in SQLAlchemy, where we want to prevent incorrect column updates using the toolkit (not ORM).

Problem

Currently, this doesn’t raise a type error during type check, but it’s logically wrong:

sa.update(User).values({User.created_at: True}) # created_at is datetime, but we pass a bool!

Desired Solution

If we could define a dictionary with generic constraints:

dict[InstrumentedAttribute[T], T]

* The key is `InstrumentedAttribute[datetime]` (e.g., `User.created_at`).
* The value **must** be `datetime` (not `bool` or other types).

This would help catch type mismatches at type check time (with a type checker like mypy).

dict[IntstrumentedAttribute[T], T], though, would only allow a dict with date-related keys that map to datetime values. It’s not enough to specify the full kind of “column type determines value type” that you seem to want. For example, TypedDict kind of does what you want, but restricts the key types to specific literal strings, rather than arbitrary types.

1 Like

As you mentioned, key would be literal and their type would not be generic or as you said “column type determines value type”

I don’t think this would actually even does what you said, it doesn’t do anything. it’s wrong, this thread is suggesting the idea of having generic on mapper.

I could even implement something very basic, that kinda does what i want to do, but would be nicer to have this on python. Other languages already support this, like typescript and java if i’m not wrong.

from typing import TypeVar

from sqlalchemy.orm import InstrumentedAttribute

T = TypeVar("T")


class GenericMapper:
    def __init__(self):
        self._data = {}

    def __get__(self, item: InstrumentedAttribute[T]):
        return self._data[item]

    def __set__[T](self, item: InstrumentedAttribute[T], value: T):
        self._data[item] = value
        return self

I don’t know about TypeScript, but Java doesn’t really have that. java.util.Map::values is a collection, so U can’t require specific value to be of different type. Also, java.util.HashMap doesn’t accept a hasher, but it is using polymorphic call, so the key type is (relatively) invariant.

Regardless, I have never seen it in any language. It seems like a special case, not fit for std lib.

from typing import TypeVar, Any, Protocol
from collections.abc import MutableMapping

_V = TypeVar("_V")
class GenericKey(Protocol[_V]):
    V: type[_V]

class GenericKeyMapping(MutableMapping[
    GenericKey[Any], Any #Not generic.
]):
    def __init__(self):
        self._raw = {}

    def __getitem__(self, key: GenericKey[_V]) -> _V:
        return self._raw[key]

    def __setitem__(self, key: GenericKey[_V], value: _V):
        self._raw[key] = value

    def __delitem__(self, key: GenericKey[Any]):
        del self._raw[key]

    def __len__(self):
        return len(self._raw)

    def __iter__(self):
        return iter(self._raw)

To make it generic, I can see only 1 solution: make TypeVar (bound by subscript-able type) subscript-able.

I’m looking for more or less the same thing, the ability to enforce a generic key/value relationship. In my example, I want to map type objects to callables that that take a string and return instances of that same type, i.e. dict_entry[type[T], Callable[[str], T]]. a key of re.Pattern would correspond to a value of re.compile, bytes to str.encode, int to int etc.

The best solution I could come up with was basically wrapping a dictionary in another class that enforced the constraint via method-scoped type parameters & casting the return value of my wrapper class’s __getitem__, but it would be cool to have more general support for “generic items” such that an individual item in a collection (say, a tuple in a list of tuples) could enforce a relationship/constraint internal to that item, and the containing collection would then only accept items enforcing the generic relationship.

I guess the syntax would have to be something like:

from typing import TypedItem, Callable

type StringConstructors[T] = dict[TypedItem[type[T], Callable[[str], T]]

or (if this made more sense to be limited to only a few types, like dictionaries & tuples) :

from typing import DictItem

type StringConstructor[T] = DictItem[type[T], Callable[[str], T]]

constructors: dict[StringConstructor.key, StringConstructor.value] = {}