Package: pattern-matching-pep634
Pattern matching PEP 634This is a python package implementing pattern-matching similar to whatPEP-634proposes.InstallationUse the package managerpipto install pattern-matching-pep634.pipinstallpattern-matching-pep634UsageThere are 4 different implementations of pattern matching in here.The safest one is this:frompattern_matchingimportMatchermatch=Matcher(Click,KeyPress,Quit)withmatch(event.get())asm:ifm.case('Click(position=(x, y))'):handle_click_at(m.x,m.y)elifm.case('KeyPress(key_name="Q") | Quit()'):m.game.quit()elifm.case('KeyPress(key_name="up arrow")'):m.game.go_north()elifm.case('KeyPress()'):pass# Ignore other keystrokeselifm.case('other_event'):raiseValueError(f"Unrecognized event:{m.other_event}")(This is based on an example inPEP-636)Note how you have to specify which names you will be using, and how you always have to usem.to access the capture values.
This is the so calledno_magicimplementation. This implementation will work even in Python3.7 and other implementations than CPython that support the same features as 3.7auto_lookupIf you don't want to specify which classes you will be using, but don't want to have the pattern matching messing with the locals, you can useauto_lookupfrompattern_matching.auto_lookupimportmatchwithmatch(event.get())asm:ifm.case('Click(position=(x, y))'):handle_click_at(m.x,m.y)elifm.case('KeyPress(key_name="Q") | Quit()'):m.game.quit()elifm.case('KeyPress(key_name="up arrow")'):m.game.go_north()elifm.case('KeyPress()'):pass# Ignore other keystrokeselifm.case('other_event'):raiseValueError(f"Unrecognized event:{m.other_event}")You still have to usem., but at least you don't have to duplicate the class names and carry around aMatcherinstance.Note that this might have weird edge cases and fail for some reason sometimes. (Most notably conflict between what you think is visible in a function vs. what really is)injectingThis is for those who don't want to typem.everywhere.frompattern_matching.injectingimportmatch,casewithmatch(event.get()):ifcase('Click(position=(x, y))'):handle_click_at(x,y)elifcase('KeyPress(key_name="Q") | Quit()'):game.quit()elifcase('KeyPress(key_name="up arrow")'):game.go_north()elifcase('KeyPress()'):pass# Ignore other keystrokeselifcase('other_event'):raiseValueError(f"Unrecognized event:{other_event}")This will 'infect' the name space and put the captured names into it. It also doesauto_lookup.This is will only work on Python3.9. It might also randomly break with debuggers/coverage/tracing tools.Note that this heavily suffers from the problem of what locals are defined and what aren't, e.g. the problem of where names are looked up.full_magicThis is the one that is as close as possible to the syntax proposed in PEP-634frompattern_matching.full_magicimportmatchwithmatch(event.get()):ifClick(position=(x,y)):handle_click_at(x,y)elifKeyPress(key_name="Q")|Quit():game.quit()elifKeyPress(key_name="up arrow"):game.go_north()elifKeyPress():pass# Ignore other keystrokeselifother_event:raiseValueError(f"Unrecognized event:{other_event}")(only differences to PEP-634 iswith matchinstead of match,if/elifinstead ofcaseand:=instead ofas)This does source-code analysis to figure out what cases to take and which names do bind. This is also Python3.9 only, and might break with any minor release of python. But that is unlikely.Note: Sometimes, the small amount of optimization that python does can still break this:frompattern_matching.full_magicimportmatchwithmatch(n):if1|2:print("Smaller than three")elif3:print("Equal to three")elif_:print("Bigger than three")Python's peephole optimizer will (normally) correctly figure out that only the first branch can be taken and throw away the rest of the code.
This means thatmatchcan not correctly jump to the other lines. To circumvent the optimizer,c@('case') can be added to prevent the optimizationfrompattern_matching.full_magicimportmatchwithmatch(n):ifc@1|2:print("Smaller than three")elifc@3:print("Equal to three")elifc@_:print("Bigger than three")This doesn't have to be done always, but it is a good first attempt if you get weird error messages.GuardsGuards are not directly implemented, but are supported via a simpleandthat can be added to a case:frompattern_matching.injectingimportmatch,casewithmatch(p):ifcase('(x, y)')andx==y:print("X=Y, at",x)else:print("Not on a diagonal")This works similar for all optionsLicenseMIT