This PEP proposes a new data structure for the collections module,
called "TransformDict" in this PEP. This structure is a mutable mapping
which transforms the key using a given function when doing a lookup, but
retains the original key when reading.

Numerous specialized versions of this pattern exist. The most common
is a case-insensitive case-preserving dict, i.e. a dict-like container
which matches keys in a case-insensitive fashion but retains the original
casing. It is a very common need in network programming, as many
protocols feature some arrays of "key / value" properties in their
messages, where the keys are textual strings whose case is specified to
be ignored on receipt but by either specification or custom is to be
preserved or non-trivially canonicalized when retransmitted.

Another common request is an identity dict, where keys are matched
according to their respective id()s instead of normal matching.

Both are instances of a more general pattern, where a given transformation
function is applied to keys when looking them up: that function being
str.lower or str.casefold in the former example and the built-in
id function in the latter.

(It could be said that the pattern projects keys from the user-visible
set onto the internal lookup set.)

TransformDict is a MutableMapping implementation: it faithfully
implements the well-known API of mutable mappings, like dict itself
and other dict-like classes in the standard library. Therefore, this PEP
won't rehash the semantics of most TransformDict methods.

The transformation function needn't be bijective, it can be strictly
surjective as in the case-insensitive example (in other words, different
keys can lookup the same value):

As shown in the examples above, creating a TransformDict requires passing
the key transformation function as the first argument (much like creating
a defaultdict requires passing the factory function as first argument).

The constructor also takes other optional arguments which can be used
to initialize the TransformDict with certain key-value pairs. Those
optional arguments are the same as in the dict and defaultdict
constructors:

Using a function pair isn't necessary, since the original key is retained
by the container. Moreover, an encoder / decoder pair would require the
transformation to be bijective, which prevents important use cases
like case-insensitive matching.

Dictionary values are not used for lookup, their semantics are totally
irrelevant to the container's operation. Therefore, there is no point in
having both an "original" and a "transformed" value: the transformed
value wouldn't be used for anything.

It was asked why we would provide the generic TransformDict construct
rather than a specialized case-insensitive dict variant. The answer
is that it's nearly as cheap (code-wise and performance-wise) to provide
the generic construct, and it can fill more use cases.

Even case-insensitive dicts can actually elicit different transformation
functions: str.lower, str.casefold or in some cases bytes.lower
when working with text encoded in an ASCII-compatible encoding.