Using Basic Patterns: Literal, Value, and Wildcard
00:00 In this lesson, you’re going to explore the most basic types of structural patterns. Literal patterns, which match on Python literals like string literals or integer literals, value patterns, which are a way to match on constant values.
00:13 And this is a little trickier than it sounds, trust me, and the wild card pattern, which matches to any subject.
00:21
Here’s an example of a literal pattern: match subject: case
"Python": print("It's Python") case
42: print("It's 42")
. In this example, both “Python” and 42 are Python literals.
00:37 Subject will be matched based on its equality to these case clauses.
00:42
Python has quite a few kinds of literals, actually. The bytes literal is enclosed with single or double quotes and prepended by lowercase b
.
00:50 It represents a series of bytes. String literals are enclosed in single or double quotes. Integer literals look like numbers and can include a negative sign.
01:00 Float literals are numbers that include a decimal point. Complex literals represent complex numbers with real and imaginary components on the left and right-hand side of the plus sign, respectively.
01:11
The imaginary part is denoted by a trailing lowercase j
. If you’re really into math, I’m sorry to disappoint you, but there won’t be any complex analysis in this course.
01:20
The Boolean literals are capital T
True
and capital F
False
, and the None
type literal is capital N
None
.
01:28
The last column in this table represents how these patterns are matched in structural pattern matching. You can see how most of them are matched by equality, but the Boolean and None
types are matched by identity.
01:39
This is because True
, False
, and None
are what’s known as singletons in Python. They each share a single object in memory that all references to them point towards.
01:49
Matching by equality is equivalent to using the equality test operator, the double equal sign, whereas matching by identity is done using the Python keyword is
.
01:59 This is a subtle distinction, but it’s one to be aware of.
02:04 Next up are value patterns. As structural patterns, value patterns emulate the concept of pattern matching on constants. As developers, we typically try to avoid hard coding literals throughout our programs.
02:17 Instead, we prefer to define constants separately from the logic in our code. But if you wish to match against constant values using structural pattern matching, you have to do things a little bit differently.
02:27 Your constant values must be enclosed within a namespace. This can be something like a class, enum, or module, for example. Once your constant is so enclosed, you must refer to it by its fully qualified name, using at least one dot accessor, and this is really important.
02:43 If you aren’t accessing the constant by its fully qualified name, you’ll actually be creating a capture pattern instead, which is something entirely different.
02:50 You’ll see capture patterns more in the next lesson.
02:55
So here’s how you can adapt the previous example using value patterns instead. Create a class Subject
with the class attributes: Python = "Python"
and forty_two = 42
.
03:09
Now match subject: case Subject.
Python: print("It's Python") case Subject.
forty_two: print("It's 42")
. The Subject
class here provides the namespace for the values “Python” and 42.
03:28
Moving on to the wild card pattern. As a structural pattern, the wild card pattern uses underscore as a pattern. For example, match subject: case _ : print("
It's anything")
because the wild card matches to any subject, so subject could be anything in this case. It serves as a default pattern, and because it matches to anything, it should be the last case clause that you provide to your match.
03:53 Now let’s move to the IDE and get started using these patterns.
03:58 You’re going to build your own REPL. The interactive read-evaluate-print loop that comes built in with Python. Handling user-supplied inputs like this is a great application of pattern matching.
04:08
You’ll start with a simple literal pattern and build from there. Here’s the starting script. You’re gonna work with import sys
and import traceback
.
04:16
You’ll use these later. Next, the constant PROMPT
contains the string "N{snake}"
with a space at the end. This will be the prompt for your REPL.
04:28
It’s the Unicode escape sequence for the snake emoji, and you’ll see it when you run the code. Create the function main()
. This will hold all of the logic of your REPL: def main():
print("Type help
for more information.")
This serves as a welcome message.
04:45
Next, while True:
starts a while loop that will run indefinitely, and you’ll wrap your pattern matching logic in a try
-except
block.
04:52
match input(PROMPT)
, this is where your user input comes from, case "help":
05:01
create the variable message
and store an f-string that prints Python along with the current version number as retrieved by sys.version
.
05:09
Then print(message)
. Next, some error handling: except KeyboardInterrupt:
That’ll be the result of typing Control plus C into the prompt.
05:19
And print()
a new line with the message “Keyboard Interrupt”.
05:23
except EOFError:
That’s end of file error and that will be the result of typing Control plus D into the prompt. print()
nothing, which prints a new line and call the built-in exit()
function, which will exit the currently running Python program.
05:38
And one more broad except Exception:
and this you’ll use later to pass error information back onto the user via the line traceback.
print_exc(file=sys.stdout)
, which points to the standard output.
05:52
In this case, the user’s REPL. Finally, use the if __name__ == "__main__":
guard and run the main()
function. This is another Python idiom that prevents main()
from being run unless repl.py
is the entry point to the program.
06:09
Open a shell and run python repl.py
.
06:15
Here’s your prompt. Now you can try things out: type “help”. Here’s the info from sys.version
. Type anything. You can see nothing happens because you aren’t yet handling this condition.
06:30 Type Control plus C, and there’s your “Keyboard Interrupt”. Finally, to close, type Control plus D,
06:38 and it closes. Very basic, but a good proof of concept. Next, let’s add a couple things. Let’s expand the available commands using value patterns and add a wild card condition for unexpected inputs. I’ll put away the terminal for now.
06:52
First, add a class Command
that will store the command options.
06:57
Command
has three class attributes: HELP
storing the string, “help”, EXIT
storing the string “exit”, and QUIT
storing the string “quit”. Now you can modify your welcome message to include the new commands,
07:15 and now you can modify your existing case clause and add two new ones to handle these commands.
07:22
Change the first case to match Command.
HELP:
07:31
add a case for handling Command.EXIT
where you break the loop,
07:37
and add a case for handling Command.
QUIT
to also break the loop. And in case you get any unexpected inputs, you can add a wild card condition to handle that as well.
07:51
So for any unknown inputs you’ll print()
, “Please type a command.” Now save the file and bring up the terminal again, python
repl.py
.
08:05 Try out some commands: “help”, something random,
08:12
and “exit”. “Help” matches Command.HELP
. Something random gets caught by the wild card condition and prints the line, “Please type a command.” And “exit” matches with Command.
EXIT
, just like you planned, breaking the while loop and exiting the function.
08:30
Okay, you started off with a literal pattern and moved on to implement the value and wild card patterns, but there’s still a lot of work left. For instance, Command.EXIT
and Command.
QUIT
both do the same thing, don’t they?
08:42 In the next lesson, you’ll learn how this can be improved among other techniques.
Become a Member to join the conversation.