I want to use match
to determine an action to perform based on a class type
. I cannot seem to figure out how to do it. I know their are other ways of achieving this, I would just like to know can it be done this way. I am not looking for workarounds of which there are many.
class aaa():
pass
class bbb():
pass
def f1(typ):
if typ is aaa:
print("aaa")
elif typ is bbb:
print("bbb")
else:
print("???")
def f2(typ):
match typ:
case aaa():
print("aaa")
case bbb():
print("bbb")
case _:
print("???")
f1(aaa)
f1(bbb)
f2(aaa)
f2(bbb)
The output is as follows:
aaa
bbb
???
???
Try using typ()
instead of typ
in the match
line:
class aaa():
pass
class bbb():
pass
def f1(typ):
if typ is aaa:
print("aaa")
elif typ is bbb:
print("bbb")
else:
print("???")
def f2(typ):
match typ():
case aaa():
print("aaa")
case bbb():
print("bbb")
case _:
print("???")
f1(aaa)
f1(bbb)
f2(aaa)
f2(bbb)
Output:
aaa
bbb
aaa
bbb
UPDATE:
Based on OP's comment asking for solution that works for classes more generally than the example classes in the question, here is an answer addressing this:
class aaa():
pass
class bbb():
pass
def f1(typ):
if typ is aaa:
print("aaa")
elif typ is bbb:
print("bbb")
else:
print("???")
def f2(typ):
match typ.__qualname__:
case aaa.__qualname__:
print("aaa")
case bbb.__qualname__:
print("bbb")
case _:
print("???")
f1(aaa)
f1(bbb)
f2(aaa)
f2(bbb)
Output:
aaa
bbb
aaa
bbb
UPDATE #2:
Based on this post and some perusal of PEP 364 here, I have created an example showing how a few data types (a Python builtin, a class from the collections module, and a user defined class) can be used by match
to determine an action to perform based on a class type (or more generally, a data type):
class bbb:
pass
class namespacing_class:
class aaa:
pass
def f1(typ):
if typ is aaa:
print("aaa")
elif typ is bbb:
print("bbb")
else:
print("???")
def f2(typ):
match typ.__qualname__:
case aaa.__qualname__:
print("aaa")
case bbb.__qualname__:
print("bbb")
case _:
print("???")
def f3(typ):
import collections
match typ:
case namespacing_class.aaa:
print("aaa")
case __builtins__.str:
print("str")
case collections.Counter:
print("Counter")
case _:
print("???")
'''
f1(aaa)
f1(bbb)
f2(aaa)
f2(bbb)
'''
f3(namespacing_class.aaa)
f3(str)
import collections
f3(collections.Counter)
Outputs:
aaa
str
Counter
As stated in this answer in another post:
A variable name in a case clause is treated as a name capture pattern. It always matches and tries to make an assignment to the variable name. ... We need to replace the name capture pattern with a non-capturing pattern such as a value pattern that uses the . operator for attribute lookup. The dot is the key to matching this a non-capturing pattern.
In other words, if we try to say case aaa:
for example, aaa
will be interpreted as a name to which we assign the subject (typ
in your code) and will always match and block any attempts to match subsequent case
lines.
To get around this, for class type names (or names generally) that can be specified using a dot (perhaps because they belong to a namespace or another class), we can use the dotted name as a pattern that will not be interpreted as a name capture.
For built-in type str
, we can use case __builtins__.str:
. For the Counter
class in Python's collections
module, we can use case collections.Counter:
. If we define class aaa
within another class named namespacing_class
, we can use case namespacing_class.aaa:
.
However, if we define class bbb
at the top level within our Python code, it's not clear to me that there is any way to use a dotted name to refer to it and thereby avoid name capture.
It's possible there's a way to specify a user defined class type
in a case
line and I simply haven't figured it out yet. Otherwise, it seems rather arbitrary (and unfortunate) to be able to do this for dottable types and not for non-dottable ones.