17

I came across a weird issue while using the new match/case syntax in Python3.10. The following example seems like it should work, but throws an error:

values = [
    1,
    "hello",
    True
]

for v in values:
    match type(v):
        case str:
            print("It is a string!")
        case int:
            print("It is an integer!")
        case bool:
            print("It is a boolean!")
        case _:
            print(f"It is a {type(v)}!")
$ python example.py
  File "/.../example.py", line 9
    case str:
         ^^^
SyntaxError: name capture 'str' makes remaining patterns unreachable
  • It is mentioning that the first case (the value str) will always result in True.

Wondering if there is an alternative to this other than converting the type to a string.

Neil Graham
  • 593
  • 1
  • 5
  • 17
  • `case` is followed by a pattern. Identifiers in patterns are variables that will be assigned from the corresponding part of the matching value, not they're not variables that are evaluated. – Barmar May 18 '22 at 20:51
  • So `case str:` means that if the value is a single value, assign the value to `str` and execute that case body. It doesn't mean `if type(v) == str:` – Barmar May 18 '22 at 20:51
  • You seem to be confusing this with `switch/case` from PHP or JavaScript. – Barmar May 18 '22 at 20:52

1 Answers1

16

Rather than match type(v), match v directly:

values = [
    1,
    "hello",
    True,
]

for v in values:
    match v:
        case str():
            print("It is a string!")
        case bool():
            print("It is a boolean!")
        case int():
            print("It is an integer!")
        case _:
            print(f"It is a {type(v)}!")

Note that I've swapped the order of bool() and int() here, so that True being an instance of int doesn't cause issues.

This is a class pattern match.

wim
  • 338,267
  • 99
  • 616
  • 750
  • 1
    what about performance of instantiating new object for simple comparison? – iperov Jan 06 '23 at 12:14
  • 1
    @iperov Type matching in case does not actually instantiate a new object. You can see this for yourself by defining a custom type and putting a print in the init - it won't be called. – wim Jan 06 '23 at 19:04
  • This fails for numpy integer types. – Soerendip Jan 25 '23 at 18:17
  • @Soren Can not reproduce. Could you show an example? – wim Jan 25 '23 at 18:24
  • take `numpy.int64(1)` as input – Soerendip Jan 25 '23 at 19:36
  • @Soren I have tried, it all works normally for me. Note that `isinstance(np.int64(1), int)` is False, and numpy ints are not a subclass of `int`. Did you expect otherwise? Another case statement, or adding a union to the existing `case int()`, would be needed to also catch numpy dtypes (which are a different type entirely). Maybe I'm misunderstanding what failure you mean.. – wim Jan 25 '23 at 19:47
  • Why would `True` being an instance of `int` cause an issues otherwise? Isn't the order of the cases supposed to be irrelevant? – HelloGoodbye Feb 22 '23 at 15:13
  • Also, doesn't `str()` create an empty string? Or what is going on here? I thought a `match`–`case` statement compared the value of the expression after `match` with the values of the expressions after each `case`. – HelloGoodbye Feb 22 '23 at 15:15
  • The order of the cases is not irrelevant, they are checked in order with first match winning. The `str()` in a `case` block is part of the [class pattern](https://peps.python.org/pep-0634/#class-patterns) syntax, it is not an expression. Without the parens, it would be a name capture (rebinding the name `str`!) – wim May 24 '23 at 19:45