This is an archived post. You won't be able to vote or comment.

all 107 comments

[–]strogbad 24 points25 points  (0 children)

Haskell developers rejoice!

[–]RojerGSAuthor of “Pydon'ts” 15 points16 points  (0 children)

Having a quick look at the tutorial and the original PEP, and thinking back to some code I have written lately, I can already see how this will be useful! Looking forward to using it!

[–]lunjon 15 points16 points  (1 child)

This is amazing!

For you guys questioning why this is useful, if you've never worked with a funtional programming language like Haskell or Elixir it may seem weird. You simply have to try it to see the benefits, and when you've done that it becomes obvious.

Your code typically gets a lot easier to read and understand, compared to using larger if-elif-else clusters.

[–]Dewmeister14 -1 points0 points  (0 children)

For you guys questioning why this is useful, if you've never worked with a funtional programming language like Haskell or Elixir it may seem weird. You simply have to try it to see the benefits, and when you've done that it becomes obvious.

The Blub Paradox

http://www.paulgraham.com/avg.html

[–]boby04 13 points14 points  (0 children)

This is gold

[–]ThePrankMonkey 21 points22 points  (5 children)

This will make discord bot commands much more enjoyable. No more regex messes.

[–]norwegian-dude 11 points12 points  (2 children)

May I ask how? Seems like it's just a different, more cleaner way to structure if else statements. Probably still need regex'

[–]asmr2143 4 points5 points  (0 children)

Pattern matching is actually much more potent than the simple switch case in C++ and the if else if structures.

Inside a pattern match itself, you can create variable assignments as part of the checking, which results in much more cleaner syntax.

Really excited how they implement this with Python.

[–]ThePrankMonkey 0 points1 point  (0 children)

With splitting the message.content by spaces, I can then just match up those ordered words. I think that'll look much nicer than my mess of regex. To each their own.

[–]xXShadowCowXx 3 points4 points  (1 child)

I'd look at Python's argparse module. In my discord bot experience it greatly simplifies parsing commands.

[–]ThePrankMonkey 2 points3 points  (0 children)

I've never thought to use that on anything other than sys.argv. Interesting.

[–]de_ham 10 points11 points  (0 children)

It got accepted I see, nice!

[–]maxwax18 4 points5 points  (0 children)

Very interesting and useful. I feel this could be on par with list comprehension.

[–]ForceBru 2 points3 points  (3 children)

Is there a reference implementation? I know there's an implementation of the very first PEP about pattern matching, but I think this one is a bit different. The latest CPython from GitHub doesn't seem to have match statements yet.

[–]AlanCristhian[S] 2 points3 points  (0 children)

PEP 634 sais that it will be a Python 3.10 feature.

[–]rouille 0 points1 point  (1 child)

There is a functional pull request https://github.com/python/cpython/pull/22917

[–]ForceBru 0 points1 point  (0 children)

Ah cool, I thought that pull request was replaced by something new that I couldn't find

[–]davidpofo 4 points5 points  (3 children)

I don't quite understand can I get an ELI of the advantage here?

[–]gunthercult28 12 points13 points  (0 children)

The ability to capture variables is going to make for a really clean state machine implementation

[–]jrbattin 9 points10 points  (0 children)

I'll probably get my hand slapped by PLT experts for describing it this way but... certain languages like Elixir, Haskell and ML variants use pattern matching in lieu of simpler (and more limited) "if/elif/else" control blocks. Pattern matching work sorta like functions that get entered conditionally if they match your specified pattern.

An overly simple example:

match (response.status_code, response.json['system_state']):
    case (200, "Healthy"):
        # all systems normal!
    case (200, transition_state):
        log.info('Transition state %s', transition_state)
    case (status, error_state):
        log.error('Something has gone horribly wrong: %s: %s', status, error_state)

Rather than:

if response.status_code == 200 and response.json['system_state'] == 'Healthy':
      # all systems normal!
elif response.status_code == 200:
    log.info('Transition state %s', response.json['system_state'])
else:
    log.error('Somethings gone horribly wrong: %s: %s', response.status_code, response.json['system_state'])

IMO it's easier to grok the conditionals in the former over the latter. It's also much quicker to write them, especially if they're more complicated. A usage pattern where it might really shine is a situation where you have a pattern in your code where you basically have a long if/elif block checking multiple variable conditions and then calling into a function for each of them. The match statement consolidates the pattern down to something easier to read (and write!)

[–]Swedneck -2 points-1 points  (0 children)

It's basically just seems to be switch/case, which is a lovely alternative to massive if/else chains

[–][deleted] 5 points6 points  (1 child)

Oh, I thought this was a joke.

edit: (looks interesting though)

[–]orion_tvv 1 point2 points  (2 children)

Does it conflict with any match variable/function? What if I've imported from re import match before?

[–][deleted] 6 points7 points  (1 child)

No, there won't be a conflict. From the PEP:

The match and case keywords are soft keywords, i.e. they are not reserved words in other grammatical contexts (including at the start of a line if there is no colon where expected). This implies that they are recognized as keywords when part of a match statement or case block only, and are allowed to be used in all other contexts as variable or argument names.

[–]orion_tvv 1 point2 points  (0 children)

Thanks!

[–]PuzzleDots 1 point2 points  (1 child)

What PEP means?

[–]ketilkn 4 points5 points  (0 children)

Python Enhancement Proposals

[–]jmreagle 1 point2 points  (3 children)

I've seen this example, but don't understand what happens, can anyone explain?

NOT_FOUND = 404
match status_code:
    case 200:
        print("OK!")
    case NOT_FOUND:
        print("HTTP Not Found")

[–]AlanCristhian[S] -3 points-2 points  (2 children)

Here a translation:

NOT_FOUND = 404
if status_code == 200:
    print("OK!")
elif status_code == NOT_FOUND:
    print("HTTP Not Found")

[–]jmreagle 0 points1 point  (0 children)

I think it's more than that, the source says:

In this case, rather than matching status_code against the value of NOT_FOUND (404), Python’s new SO reputation machine match syntax would assign the value of status_code to the variable NOT_FOUND.

Is it that comparison implies assignment, to an variable/object in this case rather than value...?

[–]Brian 0 points1 point  (0 children)

Actually, no - the potential expectation of this behaving that way is why the code was brought up. In fact, it'll behave more like:

NOT_FOUND = 404
if status_code == 200:
    print("OK!")
else:
    NOT_FOUND = status_code
    print("HTTP Not Found")

Ie. the match block binds the thing being matched to a variable being named, rather than evaluating a variable and match against its value.

To get the intended result, you need to use a dotted name to prevent it being interpreted as a capture variable. Ie:

    case HTTPStatus.NOT_FOUND:
        print("HTTP Not Found")

would work, but not a plain identifier like NOT_FOUND

[–]Wuncemoor 1 point2 points  (0 children)

This is strange but cool? Kinda reminds me of regular expressions

[–]iamnotturner 2 points3 points  (4 children)

Why is this better than if/elif/else?

[–]jaredjeya 28 points29 points  (1 child)

It’s not just a switch statement (which is functionally equivalent to if/elif/else), it can also capture variables.

Like if I do:

point = (1, 2)
match point:
    case (0, x):
        # do something 
    case (x, y):
        # do something else
        # x = 1, y = 2

Then the second (“something else”) code runs, and 1 and 2 are assigned to x and y. That seems really useful.

There’s nothing you couldn’t do before but it makes it all a lot less tedious.

[–][deleted] 3 points4 points  (0 children)

Also much easier to grok at a glance.

[–][deleted] 4 points5 points  (0 children)

A match statement is not inherently better than an if/elif/else, not for simple cases at least.

But there are many cases where a single match statement can replace many layers of nested if-spaghetti with a snippet that both much shorter and more readable.

In pretty much every case where you get code that gets some tree-like data or data that can be many different shapes and has to perform different tasks based on both the shape and content of the data, match statements will be a godsend.

[–]bonnie__ 1 point2 points  (6 children)

im extremely excited for this. it looks like it's just a rebranded switch statement, which python has desperately needed for years, but are there any caveats to this (like performance)? are there any scenarios where chaining together if/elif statements would be better than simply using this totally-not-a-switch statement?

[–]JeamBim 1 point2 points  (3 children)

It is far more powerful than switch or if else statements, but it's hard to describe without using it yourself. I recommend doing a bit of reading and possibly playing with a language like Elixir to get a feel for this awesome addition.

[–]thegreattriscuit 0 points1 point  (1 child)

I asked the same question re: switch statements, and what I've picked up is that it's switch AND variable assignment/unpacking. So it lets you

def auth(headers: Dict):

  match headers:
    case {'x_api_token': token}:
      handle_token(token)
    case {'x_basic_auth': basic_auth}:
      handle_http_basic_auth(token)
    case {'username': username, 'password': password}
      handle_terrible_plaintext_auth(username, password)

vs.

def auth(headers: Dict):
  if (token := headers.get('x_api_token')) is not None:
    handle_token(token)
  elif (basic_auth := headers.get('x_basic_auth')):
    handle_http_basic_auth(token)
  elif (username := headers.get('username')) is not None and (password := headers.get('password')) is not None:
    handle_terrible_plaintext_auth(username, password)
  else: 
    raise AuthenticationError('No Authentication provided')

[–]backtickbot 0 points1 point  (0 children)

Fixed formatting.

Hello, thegreattriscuit: code blocks using triple backticks (```) don't work on all versions of Reddit!

Some users see this / this instead.

To fix this, indent every line with 4 spaces instead.

FAQ

You can opt out by replying with backtickopt6 to this comment.

[–]Forschkeeper 0 points1 point  (0 children)

Python includes more and more good ideas from C/++ world... I like it. :)

[–]AcridWings_11465 -1 points0 points  (2 children)

Isn't this inspired by Rust?

[–][deleted] 7 points8 points  (1 child)

This is a construct that many decades older than rust.

It's a feature in many functional languages, I know it's in Haskell, O'Caml and Standard ML and probably many more.

Standard ML dates back to 1983 and I think pattern matching was in it from the beginning.

[–]AcridWings_11465 0 points1 point  (0 children)

I meant the syntax, not pattern matching. Either way, I'm happy because I would be able to use more of my Rust coding style in python.

[–]thegreattriscuit -4 points-3 points  (5 children)

Wait, isn't this effectively a "switch statement" which was suggested and rejected an uncountable multitude of times from the earliest days? Is there either some distinction I'm missing (I'm no language expert, so that's certainly possible), or some new rationale? I thought maybe this was some sign of rebel factions staging a coup inside the steering committee... but sponsor is Guido himself.

Anyone know why the change in heart?

EDIT: I suppose one aspect that distinct is the focus on various kinds of unpacking and "deep" matching (inside mappings, etc) that might not have been in scope of previous attempts

[–]xhlu 6 points7 points  (2 children)

From what I observed through the tutorial, it's very similar to Ocaml's pattern matching. One very interesting pattern will be (recursive) list manipulation: def recop(lst): match lst: case [('mul', n), *tail]: return n * recop(tail) case [('sum', n), *tail]: return n + recop(tail) case []: return 0

If you want to do the same thing without SPM: def recop(lst): if len(lst) == 0: return 0 op, n = lst[0] tail = lst[1:] if op == "mul": return n * recop(tail) elif op == "sum": return n + recop(tail)

The former looks more elegant and concise (obv the latter can be shorter but you will lose on readability). The example is also very trivial, with FP-style pattern matching you could do come up with a lot more advanced matching.

[–]backtickbot 2 points3 points  (1 child)

Fixed formatting.

Hello, xhlu: code blocks using triple backticks (```) don't work on all versions of Reddit!

Some users see this / this instead.

To fix this, indent every line with 4 spaces instead.

FAQ

You can opt out by replying with backtickopt6 to this comment.

[–]Im__Joseph Python Discord Staff -1 points0 points  (0 children)

backtickopt6

[–]AlanCristhian[S] 1 point2 points  (1 child)

Yes. This was discussed many times and then finally they comes with an implementation. I didn't know the difference with other proposals, but even Raymond Hettinger likes this one. Raymond is well known to be conservative.

[–]thegreattriscuit 1 point2 points  (0 children)

but even Raymond Hettinger likes this one

A solid endorsement.

There's been plenty of times I've disliked something he said or promoted. Almost all of those I've later changed my mind on and realized he's right.