4 ms·
> There should be one-- and preferably only one --obvious way to do it. I honestly don't see what this adds over if statements. You save typing `match ==`? I g
by wraptile 6y ago
> There should be one-- and preferably only one --obvious way to do it.
I honestly don't see what this adds over if statements. You save typing `match ==`? I guess indent block is visually more clear than series of ifs/elifs? That's kinda already solved with pep8 styling of separating logic blocks with newlines.
match response.json()["status"]:
case "500":
raise DeadServer()
case "404":
raise BanServer()
vs
status = response.json()["status"]
if status == "500":
raise DeadServer()
elif status == "404":
raise BanServer()
there's some clarity here but at the same time it's an extra indent layer, soon we'll have:
class Client:
def download(url):
with open_connection() as session:
while True:
response = session.get(url)
match response.json()["status"]:
case "500":
raise BadServer()
case "200":
return response.json()
As much as I love python's indentation this is getting a bit out of hand, isn't it?
Did we run out of things to add to python?
- dguaraglia 6y agoPattern 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'. [1] https://www.python.org/dev/peps/pep-0635/#patterns-and-functional-style https://www.python.org/dev/peps/pep-0635/#patterns-and-funct...
- wraptile 6y agoThanks 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."
- danohuiginn 6y agoThe difference is the error handling. Suppose the json includes a dog with no breed specified.
- dguaraglia 6y agoAs 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.
- wraptile 6y agoSeems like an issue of data flow then. You usually don't work with dynamic random content and sanitize/clean up your data before working with it.
- dguaraglia 6y agoEven 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), }
- fireattack 6y agoMatching literal pattern isn't the only thing it adds here. I think the capturing part is the (more) useful one and can save you more typing than your example.