Well you have the values as they're passed in; just (optionally?) include the type in the signature if you want type differentiation
def anamedtuple(**kwargs): ... signature = " ".join([f"{k}: {type(v)}" for k, v in kwargs.items()]) ... _type = anamedtuple.__dict__.get(signature) ... if not _type: ... _type = namedtuple(anamedtuple.__name__, tuple(kwargs)) ... anamedtuple.__dict__[signature] = _type ... return _type(**kwargs) ... type(anamedtuple(x=1)) == type(anamedtuple(x='a')) False
And I would disagree with the idea that memory growth would be "uncontrollable" - new types are generated only when a developer requests a new type, and type generation in this case is idempotent, since types are cached.