Search code examples
pythonmatch-case

What does <type>.<attr> do during a match/case?


In general, the code <type>(arg1=value1) is changed to the code <type>(attr1=value1) during a case in a match statement. This allows for some very interesting and complex capturing.

For example, we can ask "does my instance have a attr called 'sort', if so capture it":

match [1,2,3]:
    case list(sort=f): # this isn't a call, it is an attr-capture statement
        print(f)

Since the first (and only) positional argument of the list instance is the list values, you can capture those items as well:

match [1,2,3]:
    case list((x,*y),sort=f): # here we are capturing the 'attr' called 'sort' as 'f'
        print(x,y,f)

Likewise, you can specify the matching values instead of capturing them into variables:

g = list.sort
match [1,2,3]:
    case list((1,*y),sort=g):  # here we ask if the attr 'sort' is the same as 'g'
        print(y)

However, I am now confused by the following:

match [1,2,3]:
    case list(x,sort=list.sort):  # fails to match, but why???
        print(x)

I would have thought that this fails because list.sort means something different than to specify "the function 'sort' of 'list'". Maybe sort=list.sort means "do I have an attr called 'sort' which itself is of 'type' 'list' that also has an attr called 'sort'". So I tried:

match [1,2,3]:
    case list.sort:  # also fails, why?
        print('ya')

So, list.sort does not mean that I want type list with an attr of sort. So, then I tried:

match list.sort:
    case list.sort: # does match, wait, what?
        print('ya')

Which works. So, list.sort during a case does mean to use the function of sort from the type of list, but then why did list(sort=list.sort) fail?


Example of capturing an attr 'y' and matching an attr 'x':

class P:
    def __init__(self,a,b):
        self.x,self.y = a+b,a-b
match P(5,6):
    case P(x=11,y=a): # a is a new variable, 11 is a match
        print(a)

Solution

  • list.sort is a descriptor, so it's not going to match sort= which will be a method or function.

    You can see the type of f is a built-in method:

    match [1,2,3]:
        case list(x,*y), sort=f): # inspect the capture of `sort` as `f`
            print('match', f) # match <built-in method sort of list object at 0x104d94c80>
    

    But the type of list.sort is not:

    >>> type(list.sort)
    method_descriptor
    >>> list.sort is [1,2,3].sort
    False
    

    But if you use the method from the same list instance, this works:

    foo = [1,2,3]
    match foo:
        case list((x, *y), sort=foo.sort):
            print('matched')
    

    But this doesn't make much sense to me to match on the sort attribute, as it's different for each instance of a list:

    >>> x = [1,2,3]
    >>> y = [1,2,3]
    >>> x.sort is y.sort
    False
    >>> x.sort == y.sort
    False