Applying Advanced Patterns
00:00 Say you want to create a three-dimensional point class. You’d like to be able to instantiate new objects of this point class by passing a sequence of coordinates to the constructor.
00:09
You can use pattern matching to validate that the points are numerics, integer, or floats, and that there are only three of them. We’ll start with an empty __init__()
, so class Point: def __init__()
, taking the arguments, .self
and coords
, and then you’ll also add a __repr__()
, so def __repr__()
, taking in the argument .self
, which returns the f-string: `f”Point(x={self.x}, y={self.y}, z={self.z})”`.
00:42
Basically, the __repr__()
shows the x, y, and z values of the given point instance. From here, you can use a match
and come up with a pattern to enforce that there’s both the right number of items in coords
and that they are the appropriate types.
00:56
match coords: case
and then go wrap this in square brackets because you’re going to use a sequence pattern: int
| float as x, int
| float as y, int
| float as z
.
01:16
Combining class patterns with aliases, just as we saw before. If that is indeed the shape, set .self.x
to float(x)
01:32
and .self.z
to float(z)
.
01:36
Now you want to handle the case where you do have a sequence with three elements, but at least one of the types is incorrect: case _
with the sequence pattern, _, _, _
.
01:48
In this case, these are all wildcards. Because this comes after the case of three numerics, you know that at least one of these elements is not numeric, and so you raise the TypeError("Invalid coordinate types")
.
02:02
Then just in case someone throws anything else into the point constructor, you’ll add a wildcard clause: case _: raise
TypeError("Invalid input")
.
02:16
Save and give it a try. Open the Python REPL, import the Point
class from point
or whatever you’ve named this file, and now you can try instantiating a point object with a tuple: (1, 2, 3)
. Point(x=1.0,
y=2.0, z=3.0)`.
02:35
Perfect. You can also do this with a list, Point([1, 2, 3])
, and if I try something else, like using strings,
02:52
the specified error “Invalid coordinate types” is raised. Excellent. Now, what if you wanted to extend this to mapping types? Say, to have your point class accept a dictionary with the keys x
, y
, and z
?
03:06
No problem. Add two more case clauses to your point class. The first becomes the new first case clause: case
and then this pattern looks just like a dictionary literal. Wrapped in curly brackets, you have your first key, the string x
and the value x
.
03:23
This is a capture pattern. Then you have your key, the string y
and the value y
. Another capture pattern. Again, you have the string z
as a key and the value z
.
03:35
This pattern will match any mapping that has the three keys, x
, y
, and z
. If there’s a match, instead of doing any extra validation yourself, you can call the __init__()
function again and pass in x
, y
, and z
as a tuple.
03:52
The other case clauses you already wrote will take care of the rest, and for completeness, you can add one more case clause to catch any mapping that doesn’t include x
, y
, and z
as keys.
04:04
This can go right before the wildcard case: case {}
of curly brackets, which matches any possible mapping. Raise ValueError("Invalid mapping")
.
04:19 Now save and give it a go.
04:22
Open the REPL, import your Point
class again. Create a dictionary to work with: coord_dict = {"x": 5,
"y": 6,
"z": 7}
.
04:38
Now pass this coord_dict
to the point constructor and see if it works. Perfect. Point(x=5, y=6, z=7)
. You can also try it with an empty dictionary and see what you get.
04:54 Perfect. The error you just wrote, “Invalid mapping,” is what comes up just as expected. Ooh, you’ve learned almost all there is to know about structural pattern matching.
05:04 In the next lesson, you’ll take what you’ve learned and use it to build a command line app that can read and display JSON data from the GitHub API. It’s really neat and it doesn’t take much code at all.
Become a Member to join the conversation.