This example is being discussed as likely "gotcha" when using pattern matching:
NOT_FOUND = 400
retcode = 200
match retcode:
case NOT_FOUND:
print('not found')
print(f'Current value of {NOT_FOUND=}')
This is an example of accidental capture with structural pattern matching. It gives this unexpected output:
not found
Current value of NOT_FOUND=200
This same problem comes up in other guises:
match x:
case int():
pass
case float() | Decimal():
x = round(x)
case str:
x = int(x)
In this example, str
needs to have parentheses, str()
. Without them, it "captures" and the str builtin type is replaced with the value of x.
Is there a defensive programming practice that can help avoid these problems and provide early detection?
__match_args__ allows to define a default order for arguments to be matched in when a custom class is used in a case. built-in Python classes can be used in case statements to validate types.
Structural pattern matching introduces the match/case statement and the pattern syntax to Python. The match/case statement follows the same basic outline as switch/case . It takes an object, tests the object against one or more match patterns, and takes an action if it finds a match.
Python Structural Pattern Matching — Basic Syntax The whole thing is based on two keywords: match — The subject you want to evaluate conditions for. It could be a status code returned by an API request. case — An individual condition evaluated to see if a match is confirmed.
Pattern matching is the process of checking whether a specific sequence of characters/tokens/data exists among the given data. Regular programming languages make use of regular expressions (regex) for pattern matching.
Is there a defensive programming practice that can help avoid these problems and provide early detection?
Yes. Accidental captures are easily detected by always including what PEP 634 describes as an irrefutable case block.
In plain language, that means a catchall case that always matches.
Accidental captures always match. No more than one irrefutable case block is permitted. Therefore, accidental captures are detected immediately when an intentional catchall is added.
Just add a catchall wildcard pattern to the end:
match retcode:
case NOT_FOUND:
print('not found')
case _:
pass
That detects the problem immediately an gives the following error:
SyntaxError: name capture 'NOT_FOUND' makes remaining patterns unreachable
Add a catchall wildcard pattern to the end:
match x:
case int():
pass
case float() | Decimal():
x = round(x)
case str:
x = int(x)
case _:
pass
Again the problem is detected immediately:
SyntaxError: name capture 'str' makes remaining patterns unreachable
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With