Right, although I would add that the typecheckers would need to copy any constraints and bounds from the class-level typevar to the replace typevar.
I’m not advocating against a safer replace here, I’m just advocating for the @final approach to also work.
I can share some context: very recently I’ve spearheaded an effort to make all core models at my day job frozen. The end result leans very heavily on attrs.evolve and dataclasses.replace, significantly reducing readability. We still did it since the trade-offs are worth it for us, but having to switch to an even more verbose replacewould have made the readability problem even worse.
However, making the models also @final would have been no problem at all in 95% of cases, and arguably still the way to go in the other 5%. I think the old ‘favor composition over inheritance’ adage really is good fundamentals regardless.
The plural of anecdote is not data and all that, but still, this was my situation.