Pattern matching isn't just about having a switch statement, but about what you can do in the 'case' part of the statement: match complex data and extract variables.
A good example of how pattern matching simplifies code is given in the 'Patterns and Functional Style' section of PEP 635 [1]:
match json_pet:
case {"type": "cat", "name": name, "pattern": pattern}:
return Cat(name, pattern)
case {"type": "dog", "name": name, "breed": breed}:
return Dog(name, breed)
case _:
raise ValueError("Not a suitable pet")
The equivalent if statement would be kind of gross:
if 'type' in json_pet:
if json_pet['type'] == 'cat' and 'pattern' in json_cat:
return Cat(json_pet['name'], json_pet['pattern'])
elif json_pet['type'] == 'dog' and 'breed' in json_pet:
return Dog(json_pet['name'], json_pet['breed'])
raise ValueError("Not a suitable pet")
One can imagine extending that to parse 6 or 7 types of messages.
Another example is matching tuples or sequences: the equivalent if statement requires checking the length of the sequence, then extracting the variables, etc. The 'case' statement does all of that in one line.
In other words, as one of my university professors would put it: 'there's nothing that can't be written using plain ifs, adds and jumps, but that doesn't mean we all want to write in assembly with just 3 instructions'.
Thanks for the clarification though your example is unpythonic. Here's refactored to be more pythonic and I'd argue it's more readable than pattern match alternative:
pet = json_pet.get('type')
if pet == 'cat':
return Cat(json_pet['name'], json_pet['pattern'])
if pet == 'dog':
return Dog(json_pet['name'], json_pet['breed'])
Again it goes against the pep20 which is really the philosophical foundation of the language: "There should be one-- and preferably only one --obvious way to do it."
As danohuiginn clarified, the reason I'm adding the checks is because assuming there's such a key as 'breed' in the json_pet dictionary can (and will!) lead to KeyError exceptions.
Even if you were using pre-processed data, pattern matching helps to make the code easier to read. I guess I can think of clearer examples in other languages, like Rust, that supported tagged unions:
// Given this enum
enum NumOrStr {
Num(i32),
Str(string)
}
// That allows you to define values like this
let x = NumOrStr::Num(10);
let y = NumOrStr::Str("hello");
// And some random code that produces a value z that is of type NumOrStr
let z: NumOrStr = ...
match z {
Num(50) => println!("Got my favorite number: fifty!"),
Num(n) => println!('Got {}', n),
String("hello") => say_hello(),
String(s) => do_something_with_string(s)
_ => println!("Default case, x = {:?}", x),
}
A good example of how pattern matching simplifies code is given in the 'Patterns and Functional Style' section of PEP 635 [1]:
The equivalent if statement would be kind of gross: One can imagine extending that to parse 6 or 7 types of messages.Another example is matching tuples or sequences: the equivalent if statement requires checking the length of the sequence, then extracting the variables, etc. The 'case' statement does all of that in one line.
In other words, as one of my university professors would put it: 'there's nothing that can't be written using plain ifs, adds and jumps, but that doesn't mean we all want to write in assembly with just 3 instructions'.
[1] https://www.python.org/dev/peps/pep-0635/#patterns-and-funct...