How can we use Python's structural pattern matching (introduced in 3.10) to match the type of a variable without invoking the constructor / in a case where a new instantiation of the class is not easily possible?
The following code fails:
from pydantic import BaseModel
# instantiation requires 'name' to be defined
class Tree(BaseModel):
name: str
my_plant = Tree(name='oak')
match type(my_plant):
case Tree:
print('My plant is a tree.')
case _:
print('error')
with error message SyntaxError: name capture 'Tree' makes remaining patterns unreachable
An alternative attempt was to re-create an instance during matching (dangerous because of instantiation during matching, but worth a shot...) - it also fails:
match type(my_plant):
case type(Tree()):
print('My plant is a tree.')
case _:
print('error')
TypeError: type() accepts 0 positional sub-patterns (1 given)
Checking against an instance of Tree() resolves the SyntaxError, but does not lead to working output, because it always produces "error". I do not want to use the workaround to match against a derived bool (e.g., type(my_plant) == Tree)) because it would limit me to only compare 2 outcomes (True/False) not match against multiple class types.
To expand on what I said in comments:
matchintroduces a value, butcaseintroduces a pattern to match against. It is not an expression that is evaluated. In case the pattern represents a class, the stuff in the parentheses is not passed to a constructor, but is matched against attributes of thematchvalue. Here is an illustrative example:Note here that outside
case,Tree(kind="oak")would be an error:And, conversely,
case Tree(name="oak")would never match, sinceTreeinstances in my example would not normally have an attribute namedname.This proves that
casedoes not invoke the constructor, even if it looks like an instantiation.EDIT: About your second error: you wrote
case type(Tree()):, and gotTypeError: type() accepts 0 positional sub-patterns (1 given). What happened here is this:case type(...)is, again, specifying a pattern. It is not evaluated as an expression. It says the match value needs to be of typetype(i.e. be a class), and it has to have attributes in the parentheses. For example,This would match, and print something like
The same
casewould not match an object of typeTree, only the type itself!However, you can only use keyword arguments in this example. Positional arguments are only available if you define
__match_args__, like the documentation says. The typetypedoes not define__match_args__, and since it is an immutable built-in type,case type(subpattern, ...)(subpattern! not subexpression!), unlikecase type(attribute=subpattern, ...), will always produce an error.