[Python-ideas] Pattern Matching Syntax

Ed Kellett e+python-ideas at kellett.im
Fri May 4 12:07:02 EDT 2018


On 2018-05-04 08:26, Jacco van Dorp wrote:
> Would this be valid?
> 
> # Pattern matching with guards
> x = 'three'
> 
> number = match x:
>     1 => "one"
>     y if y is str => f'The string is {y}'
>     z if z is int => f'the int is {z}'
>     _ => "anything"
> 
> print(number)  # The string is three
> 
> If so, why are y and z both valid here ? Is the match variable rebound
> to any other ? Or even to all names ?

In the match case here:

    match x:
        y if y > 3 => f'{y} is >3' # to use an example that works

there are three parts:

"y" is a pattern. It specifies the shape of the value to match: in this
case, anything at all. Nothing is bound yet.
"if" is just the word if, used as a separator, nothing to do with "if"
in expressions.
"y > 3" is the guard expression for the match case. Iff the pattern
matches, "y > 3" is evaluated, with names appearing in the pattern
taking the values they matched.

It's important to note that the thing on the left-hand side is
explicitly *not* a variable. It's a pattern, which can look like a
variable, but it could also be a literal or a display.

> ofc, you could improve the clarity here with:
> 
> number = match x as y:
> 
> or any variant thereof. This way, you'd explicitely bind the variable
> you use for testing. If you don't, the interpreter would never know
> which ones to treat as rebindings and which to draw from surrounding
> scopes, if any.

I don't think anything in the pattern should be drawn from surrounding
scopes.

> I also haven't really seen a function where this would be better than
> existing syntax, and the above is the only one to actually try
> something not possible with dicts. The type checking one could better
> be:
> 
> [snip]
> 
> The production datetime code could be:
> 
> def convert_time_to_timedelta_with_match(unit:str, amount:int, now:date):
>    return {
>         "days":timedelta(**{unit: amount}),
>         "hours":timedelta(**{unit: amount}),
>         "weeks":timedelta(**{unit: amount}),
>         # why not something like subtracting two dates here to get an
> accurate timedelta for your specific interval ?
>         "months":timedelta(days = 30*amount),  # days = (365.25 /
> 12)*amount ? Would be a lot more accurate for average month length.
> (30.4375)
>         "years":timedelta(days=365*amount),  # days = 365.25*amount ?
>         "cal_years":timedelta(now - now.replace(year=now.year - amount)),
>       }.get(unit)

Don't you think the repetition of ``timedelta(**{unit: amount})'' sort
of proves OP's point?

Incidentally, there's no need to use the dict trick when the unit is
known statically anyway. I can't decide if that would count as more
reptition or less.

> I honestly don't see the advantages of new syntax here.
> Unless you hate the eager evaluation in the dict literal getting
> indexed, so if it's performance critical an if/else might be better.
> But I can't see a match statement outperforming if/else. (and if you
> really need faster than if/else, you should perhaps move that bit of
> code to C or something.)

It's not really about performance. It's about power. A bunch of if
statements can do many things--anything, arguably--but their generality
translates into repetition when dealing with many instances of this
family of cases.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: OpenPGP digital signature
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20180504/939036a3/attachment.sig>


More information about the Python-ideas mailing list