Does python match does not work with list?

Viewed 71

I tried to solve some basic CS50 python problems and got stuck with this one (I know it can be done just by using ifs). Basically you need to print the file extension, so i split the filename to get ["filename", "extension"], and to this point it does work ok.

However no matter what i input it always print "error". Here's the code:

filename = input("Enter filename: ")
extension = filename.split(".")

match extension:
    case ["jpg"]:
        print("this is a jpg file")
    case ["gif"]:
        print("this is a gif file")
    case _:
        print("error")

I also tried some examples from different sites and it always prints the last case. Python version is 3.10.6. Any ideas?

5 Answers

extension is a list of the form ["filename", "extension"], it will never match a single-element list.

You need to match only on the second element:

case [_, "jpg"]:
    print("this is a jpg file")
case [_, "gif"]:
    print("this is a gif file")

However, if the filename includes multiple . then this will break because extension will have an unknown number of elements. This can be resolved in several ways:

  • Using *_ instead of _

  • Using os.path.splitext(filename)[-1] to get the extension (with the .) in the safest manner:

    import os
    
    extension = os.path.splitext(filename)[-1]
    
    match extension:
        case ".jpg":
            print("this is a jpg file")
        case ".gif":
            print("this is a gif file")
        case _:
            print("error") 
    

When you split your string, it creates a list to contain all the fragments.

extension = filename.split(".")

>>> print(type(extension), extension)
<class 'list'> ['hello', 'jpg']

You should select the specific string you are interested in. In this case, it would be the last element in the list, presumably. To achieve this, you could use:

extension[-1]

>>> print(type(extension[-1]), extension[-1])
<class 'str'> jpg

Issue: You are using split() which returns a list (e.g. ['data', 'gif']). You are comparing this with a list that contains just the file extension (e.g. ['gif']). This will result in False.

Assumption: The filename is valid and has an extension e.g. image.gif.

Solution 1 - Using index: In this case, the last element in the list is the extension so you can use this for matching. An index of -1 will return the last element in the list.

filename = input('Enter filename: ')
extension = filename.split('.')

match extension[-1]:
    case 'jpg':
        print('this is a jpg file')
    case 'gif':
        print('this is a gif file')
    case _:
        print('error')

Solution 2 - Using list: Alternatively, if you wish to use the result of split() directly then you can do so as follows:

filename = input('Enter filename: ')
extension = filename.split(".")

match extension:
    case [*_, 'jpg']:
        print('this is a jpg file')
    case [*_, 'gif']:
        print('this is a gif file')
    case _:
        print('error')

Note, we use *_ as there could be file names with multiple . thus resulting in a list with more than two items.

Match on Pathlib.suffix :

from pathlib import Path

filename = input("Enter filename: ")
extension = Path(filename).suffix

match extension:
    case ".jpg":
        print("this is a jpg file")
    case ".gif":
        print("this is a gif file")
    case _:
        print("error")

Yet another option is to use os.path.splitext on the file name - although it includes the dot:

import os

path = input("Enter path: ")
match os.path.splitext()[-1]:
    case ".jpg":
        print("jpg")
    case ".png":
        print("png")

If you want to also match double extensions like in data.tar.gz, you need to use str.split combined with str.join - additionally, this code also checks for filenames with a single leading dot (so not having an extension):

path = input("Enter path: ")

split = path.split(".")
if len(split) == 1 or os.path.split(path)[-1].startswith("."):
    ext = ""
elif len(split) == 2:
    ext = split[1]
else:
    ext = split[-2] + "." + split[-1]

match ext:
    case ".jpg":
        print("jpg")
    case ".png":
        print("png")
Related