Clearly Spam(a=1, b=2) does not necessarily result in an instance with
attributes a and b. But the pattern `Spam(a=1, b=2)` is intended to be
equivalent to (roughly):
if (instance(obj, Spam)
and getattr(obj, a) == 1
and getattr(obj, b) == 2)
it doesn't imply that obj was *literally* created by a call to
the constructor `Spam(a=1, b=2)`, or even that this call would be
possible.
I think this explanation makes me not worry about the fact that `Spam(a=1, b=2)` in a pattern looks a lot like a constructor. Like some other commenters, I was vaguely bothered that the identical spelling might have these different meanings in different contexts. But I think a match case just clearly enough IS a different context that using slightly different intuitions is no real conceptual stretch for remembering or teaching it.
As a strawman, we could use different syntax for "match the thing of class Spam that has attributes with these values:
match eggs:
case Spam[a=1, b=2]: ...
Or:
match eggs:
case Spam{a=1, b=2}: ...
Well, the square brackets COULD mean something different if PEP 637 is adopted. But even supposing the curly braces could be fit into the grammar. Yes, it sort of suggests the connection between dictionaries and Spam.__dict__. But it still reads as "this is something special that I have to think about a little differently."
Even where there are capture variables, I think I'd be completely comfortable thinking about the different context for:
match eggs:
case Spam(a=x, b=2): ...
The dead increasingly dominate and strangle both the living and the
not-yet born. Vampiric capital and undead corporate persons abuse
the lives and control the thoughts of homo faber. Ideas, once born,
become abortifacients against new conceptions.