So, what I'm going to talk about is the match statement and Python.
Maybe a few words about myself.
I'm Mark Lemberg.
I've been around for lots of time, so.
I've done Python for many, many years.
I'm a code developer.
And if you're working with Unicode, if you're working with a DBAPI or the platform module, these things are basically where I helped.
I also founded a user group in this regard, that's where I come from.
And I was a chair of the European Society for a number of years.
I was a director of the PSF for a number of years.
So, I've been around a while, and done lots of things.
But that's not what I'm going to talk about today.
I'm going to talk about the match statement today.
How many people of you know what the match statement actually is?
Good number.
How many of you have actually used the match statement?
So, not that many.
That's interesting.
So, what I'm going to talk about is a bit more advanced stuff in using the match statement.
And we're going to do a quick recap of how the match statement works.
So, this is the match statement.
And there are lots of things in here happening.
And on the next slide, I'm going to show a number of things that you have to be aware of.
If you look at this slide, you'll immediately see that using the match statement is not necessarily trivial.
So, there are lots of things that you have to be aware of.
And lots of terms that you have to know about in order to actually use it properly.
So, the main thing is that you want to match something.
So, you put the match statement, the object that you want to match at the top.
So, match object, that's your match object.
Then you have lots of case statements over here.
Let me see what it works.
Over here, you have the case statements.
And the case statements, they basically tell Python what to match on.
And in each case statement, you have a type pattern.
Or you have an instant or a sequence or a mapping or a y-code down here.
So, you basically try to identify the particular object that or object case that you want to handle.
And then once Python has actually found that this is a match.
Let's say the object is a list.
For example, then Python will put the object into this.
The match object into this variable here.
And this is called a capturing variable.
There are two ways to use these capturing variables.
One is the explicit way where you say as.
And then the name of the capturing variable.
Another way to do it is, for example, down here in the mapping.
What happens here is if you have a mapping that has a name entry in it.
Then the value of that particular entry in that mapping will get stored into this capturing variable.
Or name in that case or value in this case.
Or in this case, this is a special case where basically you,
you can put everything that has not been matched in your particular pattern.
And you can then make use of that.
There's a special value that you typically put at the very bottom of your match statement,
which is like a y-card.
If you look at the documentation in Python,
typically what gets uses the underscore.
But I find the underscore not that necessarily helpful when reading the code,
because you see an underscore.
You don't know what it means.
Can mean many different things in Python, actually.
So what I typically do is I use a variable, like for example,
unknown or maybe like, you know, every situation or whatever.
And then I'll just output some text here to show the user that something went wrong.
So there are two more important things are actually three.
One is very important one is the instance pattern.
So what you can do is you can actually match on certain types of classes,
including attributes on those classes.
So that's the instance pattern.
The sequence pattern looks like a Python list.
It actually matches a lot more than just a Python list.
It matches anything that has the sequence protocol in Python.
And same with the mapping.
The mapping here looks like the Python dictionary,
but it will actually match anything that implements the mapping protocol.
So that was the short recap.
I'm not going to go into much detail here.
The reason why I'm giving these talks.
I gave it talk about the basic match statement last year at Frostham.
And this is the more advanced one.
The reason I'm giving these talks is the match statement is not that well known yet.
And what's worse, it's not to use that much.
And it's really excellent new tool that we have in Python.
And we've had it since Python 3.10, which got released in October 21.
So that's three and a half years ago.
And if you look at the statistics here, the statistics are not that new,
because the website I took it off, they haven't updated the statistics.
So it's from July 2023.
But as you can see here, the match statement is only used in about .58%
of all the packages on Python.
So not that much, and I'm going to, you know,
I want to basically increase that usage a bit by giving these talks.
One of the reasons why it's probably not being used that much is because
the documentation is a bit lacking.
So essentially what you have is you have these three peps.
That were posted in order to basically get the match statement into Python.
The first one is just the motivation, why it's good, why you, why it should be used,
discusses the syntax and everything, but it's very technical.
Same with the last one, the 6341.
That's actually a specification.
So if you read that one, that goes into all the details that you have
in with the match statements.
If you want to start learning about the match statement,
the best way to do that is to read the peps 636.
That's a tutorial.
And by reading that, you can actually, you know, try to understand
how the match statement works.
The problem with all of this is that,
normally what happens is you write the peps to get something into Python
and then you write proper documentation.
Well, that last part hasn't happened for the match statement.
So essentially, the match statement documentation that you find
in the regular Python documentation is more or less the same thing
as what you have in the peps.
So, yeah.
Could be improved, let's say.
So patches are welcome, of course.
So some things that I did not have on the other slide,
some more advanced features that you have in the match statement.
I put on this slide.
So the first thing is that you can do or parsing.
So if you let's say you have multiple values that you want to match
and you can use the or operator for that.
You can match optional things and put them somewhere
in capturing variables.
For example, the dot arcs would match anything that's in a list
let's say after the first one and two.
You can use underscores if you want to.
If you're not interested in certain things,
basically these are throw away kind of capturing variables.
And the same thing works with dictionaries.
I already mentioned the more.
Basically capturing variable to capture anything that is not in that pattern.
Very important is the instance parsing.
We're going to go into more detail on that one.
And then sometimes what you want to do is you want to put local variables
in your patterns as well.
And the way that works is, well, let's say it's a bit of a hack.
So normally what happens is whenever Python finds a variable name in your pattern,
it will basically interpret that as a capturing variable.
Now in order to make Python aware that you actually want to reference a local variable in your context,
you have to put a dot into the variable reference somewhere.
And a typical way to do that is to basically just maybe use a class or data class or whatever
and then you put your stuff in there.
And then inside the pattern, you then write for example,
ParamStot and then variable name.
There are lots more things that you can do.
You should have a look at the Python detail.
Yeah, talk from Raymond Hedinger in 2022.
For more things that you can do with the more advanced stuff.
So I'm going to show a number of examples here and go into the details.
So I'm going to start with Jason to examples there.
Then have a look at XML, which we all love.
And then I have a really complicated example using the AST, the Python AST.
So let's start with a simple one.
This is basically just tabular data that you put into Jason.
And what you see here is you have to schema up there.
So basically you have entry, dictionaries, which have a price entry,
a name entry and then the price is a number and the name is a string.
And then these are some examples of things that you can see.
In the data, sometimes you get extra data.
For example, in data three, you have this color entry in there.
And you have to deal with these things.
And what you typically have is you have a list of these dictionaries.
And then you try to process them.
So how would you do that with the match statement?
As I just mentioned, there's a QR code up there.
In case you cannot read this, you can download the slides.
And then you have a better way of reading these things.
I did want to put lots of code on the slides.
So I have to make the font size a bit smaller.
What you do here is it's very typical.
You start with a single record.
So you basically match one of these instances.
Then you want to make sure that it's actually a dictionary.
So you have a case dict.
And then you work on that particular dictionary.
If it's not a dict, then it goes down here and says wrong value.
And then you can do some error processing here.
And then what you do is you have another match statement.
And you look into that particular dictionary.
And you parse it in here.
And as you can see here, you can go in and say, OK, the name has to be a string value.
And I'm going to store it in the capturing variable name.
And the price has to be an int or a float.
And you use the ore pad on here.
You don't, well, what you do is here is you implicitly
store it in the capturing variable price in both cases.
And then if you have anything extra, then you can do some error processing here,
like what I mentioned with the color entry.
And then in normal cases, you process your data down here.
And again, if you have anything that does not match these things.
Let's say the name is not a string.
Maybe an integer.
Then you would go down here and see wrong values.
So very straightforward, very readable.
And easy to understand.
Now, of course, if you have multiple of these records,
then you would put them into a list.
And then again, you do the same thing.
What I typically do is I put the actual, I split the parsing in multiple functions
or methods so that the code becomes more readable.
So what I do here is I explicitly say, OK, this has to be a list.
If it's not a list, something is wrong.
And I go here.
If it is a list, then I basically look at all the different dictionaries I have in there
and then call the other function to do the parsing.
So that was very easy.
Now, a bit more complex in case of if you want to use geo JSON.
How many of you know what geo JSON is?
Yeah, a few.
So basically it's about storing a spatial data in JSON.
It's a standard format.
And the example I took here is directly from the website.
This is referencing a tiny island down here in Philippines called the denogat islands.
And basically what it does is it's just that there is a point that you want to reference.
It gives you the coordinates and then it assigns a name as a property to that particular point.
So this is what you want to parse and there are different geo JSON types in here.
So you have not only point, but also feature and feature collection and lots of other stuff.
And you want to make sure that when you parse these things that only these types get used.
And so that you don't get any wrong data.
So again, you go in here.
You take one of these instances that you have.
And then you go in here into your first case.
You analyze first you analyze just a type part of that particular case.
Sorry, the particular instance.
And then you put the everything else that you've matched into other members.
Then you can look into that later on.
So the first thing that you do is you make sure that only the geo JSON types that you have defined in the standard actually being used.
Otherwise you store an exception or print something.
And then you go into the other members and analyze all the other members that you have in here.
Now what's happening here is that this is actually this is nested data, right?
So it's not tabular as in the first example.
And so you can do you can have a four loop in here and then for the nesting you typically use another match statement that you have here.
And then you get the different entries that you can have as other members and again you do some error processing down here.
I'm not showing any processing of these things.
I just want to show that you can extract the data and easily then use it.
What's very nice is that because you have these capturing variables you can directly tap into the data.
So you can do both validation of the data.
So I'm making sure that it's a proper format.
And you can immediately start processing everything.
So I have eight minutes and forty left, which is not a lot.
So I'm going to basically rush a bit.
So the next is the XML data we all love, right?
So this is basically taking from the Python documentation.
This is an excellent.
These are XML records that are again a bit more complex because you're storing neighbors of countries in here and you store a rank and the GDPC.
And so you want to parse this.
The way you do it is you of course use elementary for these things.
Elementary has an instance type called element and this is what we're using here.
So we basically parse for the element and then we make sure that the element that we first parse is the country element.
We extract the attributes on that particular element into the variable name.
And we store the whole thing as country as the country.
They're capturing variable.
And then we go into country and look at the different entries that we find in the XML.
So you have the example over here so you can compare that.
So the first things are very easy because they can just occur once.
So you basically go through these things and just make sure that you get the proper values, the proper types.
And then you go down here and to neighbors because you can have multiple neighbors.
You basically parse every neighbor entry that you have in here and you put them into a dictionary.
And then later on you combine everything into a proper dictionary that you can use in processing and then you go on with the processing.
So again, I think this is fairly straightforward, especially if you what you normally do it with when you parse XML you go into some kind of you have a parser object.
And you have handlers on that and you use that kind of approach.
Or you have long if if if if elephant and so on kind of patterns which are not easy to read, not easy to write.
And I think even though this is a bit you know compressed on this slide, I think this is actually quite readable.
If you look at that code, you can actually understand what it's doing.
I did leave out some details that you would normally put in here, for example, you would make sure that you only get one rank entry in there and one year.
But it doesn't want to make it too complex, but that's certainly all possible to do.
Now let's get asked last example, let's get to the AST. How many of you have worked with the Python ASTs?
That's interesting. So what you see there on the right and very tiny font is part of an AST.
The AST I'm showing here is actually the AST of this function, the example function on the left.
As you can see, it's highly nested. It looks a bit, for example, you have a linear structure here, you have if L of L of L is right.
But if you look at the AST, what happens here is actually a nested structure. So the first is a top note and then you basically have in that if you have other instances, I can't really see that from here.
It doesn't show. So it's probably in this lots more down here. So this is basically parsing just the first if part.
And then you have the body of that if statement, which is the print over here. And then further down below, you have two parts. You always have with the if AST, you always have the matching part and then you have the L's part.
So everything that you see here on the L of would then go into the L's part.
If you think about the tree that gets generated, it's actually a deeply nested tree for the if L of it's not a list as you might think when looking at these things.
So what's the idea here I want to show very quickly. Obviously you want to refactor this if L of chain into something that uses the match statement automatically programatically.
And this is how it works. So it's a bit more involved. So what I'm going to do is I'm going to first.
This is the main entry point here, the if match refactor function.
What it does is it basically goes into AST, finds the function definitions, finds the, you know, the some variable names in here.
And then it goes into function refactor, which is over here, which then tries to find in that function definition, the if L of L's chains in here.
So the way it works is you go in here, you check all the notes, you just interested in the if notes that you have in here.
And then if you find an if note, you go into another function scan if, which is on the next slide.
You parse that and then you check the return values and then you replace the the note, not here you look.
You you create and you note, you want to replace the if statement with the match statement, that's what you do here.
And then you replace that note in the function body and return it and then you get the modified function.
So this is scan if, again, this is the what I mentioned already, the trick to do local variables.
So I just use an empty parent's class for that.
And then I put the variable name in here because I need that and then I go into the into deeper into the the chain of the if statement.
So I'm going to look at the condition that you have in the if statement.
I'm going to look for a first case, which is calling is instance to find at the type that you normally use in these if chains when you do something like a matching pattern and then I'm going to go into.
Another case down here, which is you call these instance plus you have an extra condition that you want to handle.
So I'm going to have to skip over this a bit unfortunately the code is all available on the on the in the slides that you you can download from the QR code up there.
What's happening here is basically you you go in here and then you you find all these cases that you have in here.
And then you recursively continue because like I said it's it's deeply nested structure you recursively go into the other cases that you have in your chain.
And basically apply the restructuring in in all the different cases that you find that you can actually handle everything else that you cannot handle is not being touched.
So this will only look at these if chains.
And then what you of course want to do is you want to then replace that if statement with the match statement the way you do that is you take all your cases that you've collected here and then you create new nodes in the AST by using match class and then and the match case here.
And then you put all the variables in here this is what Python needs in order to represent a not statement in the AST and then you append all the different cases and you return that.
Down here and what you get is code that actually goes from left to right automatically and it doesn't touch anything else if you have something if you have code before the if or after the if.
It will just remain unchanged.
It will also only work on things that it actually recognizes so everything else that is not recognized.
It won't touch you could put extra code in there that may be maybe it's just a warning that something didn't work.
But that's pretty much it.
Right so what about performance in the last few seconds I have I'm just kind of very quickly show this.
And these are different cases that I've looked at as you can see them the match variable variant is still a bit slower than the if variant.
Except for one case where you have over here you have a.
Basically a list of integers that you want to match then the match statement is actually faster.
There's a strange case over here with the inline capturing variables there's something wrong there and in Python obviously because if you inline something else.
It's just very very slow compared to the generic case where use the ass.
It's probably a box somewhere.
Thing is this is still the first implementation of the match statement.
So there's still you know lots of low hanging food there could be lots of ways of improving things for example even compiling stuff maybe into byte code.
To to make things run faster.
But for now it's the the most important thing about the match statement is that you can actually use it to get your code more readable moment moment attainable.
And as always in Python you know we start with something that is basic and then over time we improve the performance so after let's say if you're really system the performance will be good as well.
And so there shouldn't be any issues there.
So basically conclusion match isn't fast yet but of course we can make it so.
What's very good is if you compare the left side to the middle box there with the match statement this is a lot more readable right I guess you would agree and so.
I would recommend starting to use it.
I mean take away never stop learning August right and things.
Thank you.
So I think we don't have time for questions if you have any questions you can just come to me approach me and then we can.
Hashtag.
Thank you.