Raymond Hettinger
Raymond Hettinger

Reputation: 226221

Avoiding accidental capture in structural pattern matching

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?

Upvotes: 11

Views: 541

Answers (1)

Raymond Hettinger
Raymond Hettinger

Reputation: 226221

Best practice

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.

How it works

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.

Fixing the first example

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

Fixing the second example

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

Upvotes: 11

Related Questions