How to make the match statement work?

Trying to learn Python 3.10 pattern matching. Tried this example after reading 8.6.4.9. Mapping Patterns

    >>> match 0.0:
    ...  case int(0|1):
    ...   print(1)
    ...
    Traceback (most recent call last):
      File "<stdin>", line 2, in <module>
    TypeError: called match pattern must be a type
    >>>

Especially the note on built-in types, including int. How should I code to test for an integer value of 0 or 1 (the example in the doc) and not get this error?

Hello Gerald,

the match pattern is here “0.0” - but <.> this little point makes a float
out of this pattern ?! You cannot use then “int” for to check this pattern ?
I have to re-think a while … this answer only as hint to you at first.

Hi Gerald,

You should read the tutorial:

and remember that match statements are overpowered if you’re just trying
to match a simple value. For that you should probably stick to a simple
if…elif… chain.

if x == 0:
    ...

But as a learning exercise, try this:

num = 0.0
match num:
    case int(n) if n in (0, 1):
        print(f"matched int {n}")
    case float(n) if n in (0, 1):
        print(f"matched float {n}")

which will print “matched float 0.0”. We can combine the two tests:

match num:
    case int(n) | float(n) if n in (0, 1):
        print(f"matched int or float {n}")

As you can see, if num in (0, 1) is much simpler if that is all you
are doing. Using a match statement for this is like using a nuclear
powered bulldozer to crack a peanut.

The above translates to something roughly like this, using the walrus
operator:

if isinstance(n := num, int) or isinstance(n := num, float):
    if n in (0, 1):
        print(...)
1 Like

Well, yes it is overpowered for the simple test but I’m not trying to solve a IRL code problem, I’m trying to learn by moving from simple to complex. Simplest for me is all constants.

>>> match 1:
...  case 1:
...   print ('one')
...
one
>>>

cool, next I wanted to match by type:

The doc says I can do this:

class_pattern ::= name_or_attr “(” [pattern_arguments “,”?] “)”

but, apparently not, even though built-in types are explicitly mentioned just below:

>>> match 'str':
...  case str():
...   print('str')
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: called match pattern must be a type
>>>

So, I am confused. The tutorial doesn’t seem to address this kind of thing.

then copying the example on the linked page:

int(0|1) matches the value 0

leads to the error I posted. Now, super confused.

You can match with type.

your_number = 5
match your_number:
    case number if type(your_number) is int:
        print(number)

Hi Gerald,

have you been shadowing the names of built-in types like int and str with your own objects?

>>> match 0.0:
...     case int(0|1):
...         print(1)
...
>>> int = "something other than the builtin 'int'"
>>> match 0.0:
...     case int(0|1):
...         print(1)
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: called match pattern must be a type
>>>

It looks like the error messages should be improved. And watch out for
the gotcha!

First, here is the gotcha to watch out for. Matching on a type like this
appears to work.

match 'this is a string':
    case str:
        print("matched!")

prints “matched!” as you expect. But what we’ve actually done is
not match on the type str, but matched the pattern ‘str’, which
represents a name. So it binds the matched values to the pattern, and we
get:

print(str)
# prints 'this is a string', not <class 'str'>

Gotcha!

So run del str to get rid of that pesky variable shadowing the
built-in str type, and let’s match it properly.

del str
match 'this is a string':
    case str():
        print("matched!")

When matching against a type, you need parentheses after the type,
otherwise you’re just matching against a name, which is equivalent to
doing str = 'this is a string'.

Now we can improve it by matching against the type and binding to a name
in the same test, which then lets us refer to the name that we used:

match 'this is a string':
    case str(s):
        print(f"matched the string {s!r}")

We can add a guard clause:

match 'this is a string':
    case str(s) if s.islower():
        print(f"matched the lowercase string {s!r}")

Remember, match…case is not like a switch statement. Its a disguised
assignment!

@tjol maybe, see below
@steven.daprano Are you saying that when I write:

>>> match int(1):
...  case str:
...   print(1)
...
1
>>>

“case str” matches everything and assigns it to the name “str”? That would explain my what I’m seeing and also explain my top post, since I had earlier, incorrectly written “case int” which effectively shadowed the int builtin function as @tjol asked

And this would indeed be a big “gotcha!”

It would be nice to see simple type matching covered in the tutorial. Annd, the doc page I linked talks about type matching but doesn’t have simple, complete examples which makes it harder for me to figure out what I can and cannot do.

Hi Gerald,

Yes, that’s exactly what I’m saying!

Its an easy mistake to make. I can see this becoming a gotcha for Python
programmers, but new and experienced, for decades to come.