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.
Answer from wim on Stack OverflowRather 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.
You can match directly against the type of v, but you need a value pattern to refer to the types to match, as a "dotless" name is a capture pattern that matches any value. For example,
import builtins
values = [
1,
"hello",
True
]
# Caveat: this will continue to work even if someone
# rebinds the built-in, but not, for example, if builtins.str
# itself is rebound.
for v in values:
match type(v):
case builtins.str:
print("It is a string!")
case builtins.int:
print("It is an integer!")
case builtins.bool:
print("It is a boolean!")
case _:
print(f"It is a {type(v)}!")
Note that a value pattern must be a dotted name; it's not an arbitrary expression that can evaluate to a specific value.
(Whether you really want to match against the actual type of a value, or really want to determine if a value is an instance of a given type, is another matter. In the latter case, an if-elif statement is needed.
if isinstance(v, bool):
print("It is a boolean!")
elif isinstance(v, int):
print("It is an int!")
elif isinstance(v, str):
print("It is a string!")
else:
print(f"It is a {type(v)}!")
There is no pattern that lets you use the result of calling isinstance as the case to match against.
)
How can you match on a union of types?
How to use python's Structural Pattern Matching to test built in types? - Stack Overflow
Opinions on match-case?
How to do structural pattern matching in Python 3.10 with a type to match? - Stack Overflow
Videos
If you just pass a type directly, it will consider it to be a "name capture" rather than a "value capture." You can coerce it to use a value capture by importing the builtins module, and using a dotted notation to check for the type.
import builtins
from typing import Type
def main(type_: Type):
match (type_):
case builtins.str: # it works with the dotted notation
print(f"{type_} is a String")
case builtins.int:
print(f"{type_} is an Int")
case _:
print("\nhttps://en.meming.world/images/en/0/03/I%27ve_Never_Met_This_Man_In_My_Life.jpg")
if __name__ == "__main__":
main(type("hello")) # <class 'str'> is a String
main(str) # <class 'str'> is a String
main(type(42)) # <class 'int'> is an Int
main(int) # <class 'int'> is an Int
As its name suggests, structural pattern matching is more suited for matching patterns, not values (like a classic switch/case in other languages). For example, it makes it very easy to check different possible structures of a list or a dict, but for values there is not much advantage over a simple if/else structure:
if type_to_match == str:
print("This is a String")
elif type_to_match == int:
print("This is an Int")
else:
print("\nhttps://en.meming.world/images/en/0/03/I%27ve_Never_Met_This_Man_In_My_Life.jpg")
But if you really want to use SPM, you could use the guard feature along with issublcass to check if a type is or is a child of any other:
match type_to_match:
case s if issubclass(type_to_match, str):
print(f"{s} - This is a String")
case n if issubclass(type_to_match, int):
print(f"{n} - This is an Int")
case _:
print("\nhttps://en.meming.world/images/en/0/03/I%27ve_Never_Met_This_Man_In_My_Life.jpg")
I am curious on the Python community’s opinions on the match-case structure. I know that it has been around for a couple of years now, but some people still consider it relatively new.
I personally really like it. It is much cleaner and concise compared if-elif-else chains, and I appreciate the pattern-matching.
match-case example:
# note that this is just an example, it would be more fit in a case where there is more complex logic with side-effects
from random import randint
value = randint(0, 2)
match value:
case 0:
print("Foo")
case 1:
print("Bar")
case 2:
print("Baz")This is a common "gotcha" of the new syntax: case clauses are not expressions. That is, if you put a variable name in a case clause, the syntax assigns to that name rather than reading that name.
It's a common misconception to think of match as like switch in other languages: it is not, not even really close. switch cases are expressions which test for equality against the switch expression; conversely, match cases are structured patterns which unpack the match expression. It's really much more akin to generalized iterable unpacking. It asks the question: "does the structure of the match expression look like the structure of the case clause?", a very different question from what a switch statement asks.
For example:
t = 12.0
match t:
case newvar: # This is equal to `newvar = t`
print(f"bound a new variable called newvar: {newvar}")
# prints "bound a new variable called newvar: 12.00000000"
# this pattern matches anything at all, so all following cases never run
case 13.0:
print("found 13.0")
case [a, b, c]: # matches an iterable with exactly 3 elements,
# and *assigns* those elements to the variables `a`, `b` and `c`
print(f"found an iterable of length exactly 3.")
print(f"these are the values in the iterable: {a} {b} {c}")
case [*_]:
print("found some sort of iterable, but it's definitely")
print("not of length 3, because that already matched earlier")
case my_fancy_type(): # match statement magic: this is how to type check!
print(f"variable t = {t} is of type {my_fancy_type}")
case _:
print("no match")
So what your OP actually does is kinda like this:
t = 12.0
tt = type(t) # float obviously
match tt:
case int: # assigns to int! `int = tt`, overwriting the builtin
print(f"the value of int: {int}")
# output: "the value of int: <class 'float'>"
print(int == float) # output: True (!!!!!!!!)
# In order to get the original builtin type, you'd have to do
# something like `from builtins import int as int2`
case float: # assigns to float, in this case the no-op `float = float`
# in fact this clause is identical to the previous clause:
# match anything and bind the match to its new name
print(f"match anything and bind it to name 'float': {float}")
# never prints, because we already matched the first case
case float(): # since this isn't a variable name, no assignment happens.
# under the hood, this equates to an `isinstance` check.
# `float` is not an instance of itself, so this wouldn't match.
print(f"tt: {tt} is an instance of float") # never prints
# of course, this case never executes anyways because the
# first case matches anything, skipping all following cases
Frankly, I'm not entirely sure how the under-the-hood instance check works, but it definitely works like the other answer says: by defintion of the match syntax, type checks are done like this:
match instance:
case type():
print(f"object {instance} is of type {type}!")
So we come back to where we started: case clauses are not expressions. As the PEP says, it's better to think of case clauses as kind of like function declarations, where we name the arguments to the function and possibly bind some default values to those newly-named arguments. But we never, ever read existing variables in case clauses, only make new variables. (There's some other subtleties involved as well, for instance a dotted access doesn't count as a "variable" for this purpose, but this is complicated already, best to end this answer here.)
Lose the type() and also add parentheses to your types:
t = 12.0
match t:
case int():
print("int")
case float():
print("float")
I'm not sure why what you've wrote is not working, but this one works.