Skip to content

Instantly share code, notes, and snippets.

@jorenham
Created October 17, 2021 12:20
Show Gist options
  • Save jorenham/1c241a1cf33d2cc8235631b63fa8f279 to your computer and use it in GitHub Desktop.
Save jorenham/1c241a1cf33d2cc8235631b63fa8f279 to your computer and use it in GitHub Desktop.
Generic type argument matching on builtin collections using slicing syntax
class FancyGeneric:
def __class_getitem__(cls, params):
if not isinstance(params, tuple):
params = (params,)
newparams = []
for param in params:
if isinstance(param, slice):
assert param.start is None and param.step is None
type_pattern = param.stop
if isinstance(type_pattern, tuple):
assert len(type_pattern) > 0
newparam = tuple[type_pattern]
elif isinstance(type_pattern, (list, set)):
newparam = list[tuple(type_pattern)]
elif isinstance(type_pattern, dict):
# I see potential for TypedDict's here
kv_type = next(iter(type_pattern.items()))
newparam = dict[kv_type]
else:
raise TypeError(
f'Type pattern parameters must be either a tuple, list, '
f'set or dict, Got {type_pattern!r}.'
)
else:
newparam = param
newparams.append(newparam)
params_repr = ', '.join(map(repr, newparams))
print(f'{cls.__name__}[{params_repr}]')
# return the generic alias here
# FancyGeneric[tuple[str, ...]]
TupleSpam = FancyGeneric[:(str, ...)]
# FancyGeneric[list[float]]
ListSpam = FancyGeneric[:[float]]
# FancyGeneric[list[int]]
SetSpam = FancyGeneric[:{int}]
# FancyGeneric[dict[str, int]]
DictSpam = FancyGeneric[:{str: int}]
# FancyGeneric[tuple[str, ...], <class 'int'>, dict[str, int], Ellipsis]
MixedSpam = FancyGeneric[:(str, ...), int, :{str: int}, ...]
# TypeError
BadSpam = FancyGeneric[:"not a builtin collection"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment